Skip to Content
ConceptsWebhooks

Webhooks

Real-time notifications for launch events.

Overview

Webhooks let you receive HTTP callbacks when events occur:

  • Launch status changes
  • Vesting claimed
  • DAO proposal updates
  • Fee claims

Setting Up Webhooks

Per-Launch Webhook

Include webhook config in your launch request:

{ "name": "MyToken", "ticker": "MTK", "totalSupply": 1000000000, "webhookUrl": "https://yourapp.com/webhook/etch", "webhookSecret": "your-hmac-secret" }

Global Webhooks (Coming Soon)

Configure webhooks for all launches in your dashboard.

Webhook Events

Launch Events

EventDescription
launch.queuedLaunch added to queue
launch.processingLaunch execution started
launch.step_completeIndividual step completed
launch.completeAll steps finished
launch.failedLaunch failed

Vesting Events

EventDescription
vesting.createdVesting contract created
vesting.cliff_reachedCliff period ended
vesting.claimedTokens claimed by beneficiary
vesting.completeAll tokens vested

DAO Events

EventDescription
dao.createdDAO realm created
dao.proposal_createdNew proposal submitted
dao.vote_castVote recorded
dao.proposal_passedProposal approved
dao.proposal_failedProposal rejected

Webhook Payload

All webhooks follow this structure:

{ "event": "launch.complete", "timestamp": "2026-02-15T12:00:00Z", "launchId": "launch_abc123", "data": { // Event-specific data } }

Example: Launch Complete

{ "event": "launch.complete", "timestamp": "2026-02-15T12:00:30Z", "launchId": "launch_abc123", "data": { "mint": "ABC123...", "metadata": "META456...", "vestingContract": "VEST789...", "dao": { "realm": "REALM123...", "treasury": "TRES456..." }, "pool": { "address": "POOL789...", "lpMint": "LP012..." }, "transactions": [ { "type": "mint", "signature": "5abc..." }, { "type": "vesting", "signature": "5def..." } ] } }

Example: Launch Failed

{ "event": "launch.failed", "timestamp": "2026-02-15T12:00:30Z", "launchId": "launch_abc123", "data": { "error": { "code": "insufficient_balance", "message": "Wallet has insufficient SOL for transaction", "step": "create_pool" }, "partialResults": { "mint": "ABC123..." } } }

HMAC Verification

All webhooks are signed with HMAC-SHA256. Always verify the signature.

Headers

X-Etch-Signature: sha256=abc123... X-Etch-Timestamp: 1700000000 X-Etch-Event: launch.complete

Verification Code

import crypto from "crypto"; function verifyWebhook( payload: string, signature: string, timestamp: string, secret: string ): boolean { // Check timestamp to prevent replay attacks (5 min window) const now = Math.floor(Date.now() / 1000); const ts = parseInt(timestamp); if (Math.abs(now - ts) > 300) { return false; } // Compute expected signature const signedPayload = `${timestamp}.${payload}`; const expectedSig = crypto .createHmac("sha256", secret) .update(signedPayload) .digest("hex"); // Constant-time comparison const expected = `sha256=${expectedSig}`; return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected) ); } // Express middleware app.post("/webhook/etch", express.raw({ type: "application/json" }), (req, res) => { const signature = req.headers["x-etch-signature"]; const timestamp = req.headers["x-etch-timestamp"]; if (!verifyWebhook(req.body.toString(), signature, timestamp, SECRET)) { return res.status(401).send("Invalid signature"); } const event = JSON.parse(req.body); console.log("Received:", event.event); res.status(200).send("OK"); });

Retry Policy

If your endpoint returns a non-2xx status, Etch retries:

AttemptDelay
1Immediate
230 seconds
32 minutes
410 minutes
51 hour

After 5 failures, the webhook is marked as failed in your dashboard.

Circuit Breakers

Etch uses per-endpoint circuit breakers to prevent cascade failures. If your endpoint fails 3 times consecutively, the circuit “opens” and further deliveries are paused for 5 minutes.

Circuit Breaker States

StateDescription
closedNormal operation, webhooks delivered
openDeliveries paused (3+ failures)
half-openTesting if endpoint recovered

After 5 minutes, the circuit enters half-open state and attempts one delivery. If it succeeds, the circuit closes. If it fails, it re-opens.

Check circuit breaker status via the Health endpoint:

curl https://etch.film.fun/api/health | jq '.circuitBreakers'

Avoiding Circuit Opens

  1. Respond quickly — Return 200 within 10 seconds
  2. Use async processing — Acknowledge immediately, process later
  3. Monitor your endpoint — Set up uptime monitoring
  4. Handle errors gracefully — Return 500 (retryable) not 400 (permanent)

Best Practices

  1. Always verify signatures — Never trust unverified webhooks
  2. Respond quickly — Return 200 within 5 seconds, process async
  3. Handle duplicates — Use launchId as idempotency key
  4. Log everything — Keep webhook logs for debugging
  5. Use HTTPS — Webhook URLs must be HTTPS in production

Testing Webhooks

Use the test endpoint to simulate events:

curl -X POST https://etch.film.fun/api/webhooks/test \ -H "Authorization: Bearer TOKEN" \ -H "Content-Type: application/json" \ -d '{ "webhookUrl": "https://yourapp.com/webhook/etch", "event": "launch.complete" }'
Last updated on