Skip to main content

Register a webhook

curl -X POST https://api.torpedo.co.mz/api/v1/webhooks \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://myapp.com/webhooks/torpedo",
    "events": ["email.delivered", "email.bounced", "email.complained", "email.failed"]
  }'
The response includes a signingSecretsave it immediately, it is returned once only.

Available events

EventWhen it fires
email.deliveredAWS SES confirmed delivery to the recipient’s inbox
email.bouncedEmail bounced — hard bounces are suppressed automatically
email.complainedRecipient marked the email as spam
email.failedDelivery failed after retries
domain.verifiedDomain DNS records verified successfully
domain.failedDomain verification failed

Event payload shape

All events share this envelope:
{
  "event": "email.delivered",
  "data": {
    "id": "01960000-0000-0000-0000-000000000000",
    "workspaceId": "...",
    "to": "user@example.com",
    "from": "hello@myapp.com",
    "subject": "Welcome",
    "status": "delivered",
    "deliveredAt": "2026-04-15T10:00:00.000Z"
  }
}

Verifying the signature

Every request from Torpedo includes an X-Signature header:
X-Signature: sha256=a3f2c1d8...
Verify it to confirm the request is genuine:
import { createHmac, timingSafeEqual } from 'crypto'

function verifySignature(rawBody, signature, secret) {
const expected = 'sha256=' + createHmac('sha256', secret)
.update(rawBody)
.digest('hex')
return timingSafeEqual(Buffer.from(expected), Buffer.from(signature))
}

// Express example
app.post('/webhooks/torpedo', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-signature']
if (!verifySignature(req.body, sig, process.env.TORPEDO_WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature')
}
const { event, data } = JSON.parse(req.body)
res.status(200).send('ok')
})

Always verify the signature before processing webhook payloads. Use timing-safe comparison (timingSafeEqual, hmac.Equal, CryptographicOperations.FixedTimeEquals) to prevent timing attacks — never use plain string equality.

Delivery retries

Torpedo retries failed webhook deliveries with exponential backoff. Your endpoint should:
  • Return 2xx within 5 seconds
  • Be idempotent — the same event may be delivered more than once
View delivery history via GET /api/v1/webhooks/{id}/deliveries.