Skip to content

Webhooks

Webhooks let you receive a POST request the moment an email hits an inbox — no polling required.

Base URL: https://api.inboxical.com/v1


POST /webhooks

Registers a new webhook endpoint. A signing secret is generated automatically (or you can provide your own).

Request body

FieldTypeRequiredDescription
urlstringYesThe URL to receive POST requests
eventsstring[]NoEvents to subscribe to (default: ["message.received"])
secretstringNoHMAC signing secret. Auto-generated if omitted

Supported events: message.received

Example

Terminal window
curl -X POST https://api.inboxical.com/v1/webhooks \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.com/hooks/email",
"events": ["message.received"]
}'

Response 201

{
"id": "wh_abc123",
"url": "https://yourapp.com/hooks/email",
"events": ["message.received"],
"is_active": true,
"created_at": "2026-03-21T12:00:00Z",
"secret": "a1b2c3d4e5f6..."
}

GET /webhooks

Returns all webhook endpoints for the authenticated user.

Terminal window
curl https://api.inboxical.com/v1/webhooks \
-H "X-API-Key: $API_KEY"

Response 200

{
"endpoints": [
{
"id": "wh_abc123",
"url": "https://yourapp.com/hooks/email",
"events": ["message.received"],
"is_active": true,
"created_at": "2026-03-21T12:00:00Z"
}
]
}

GET /webhooks/:id

Returns a single endpoint along with its 20 most recent delivery attempts.

Terminal window
curl https://api.inboxical.com/v1/webhooks/wh_abc123 \
-H "X-API-Key: $API_KEY"

Response 200

{
"id": "wh_abc123",
"url": "https://yourapp.com/hooks/email",
"events": ["message.received"],
"is_active": true,
"created_at": "2026-03-21T12:00:00Z",
"recent_deliveries": [
{
"id": "del_xyz",
"event": "message.received",
"status": "delivered",
"attempts": 1,
"response_status": 200,
"created_at": "2026-03-21T12:01:00Z"
}
]
}

PUT /webhooks/:id

Update the URL, events, active status, or secret.

Request body (all fields optional)

FieldTypeDescription
urlstringNew URL
eventsstring[]New event subscriptions
is_activebooleanEnable or disable the endpoint
secretstringNew signing secret
Terminal window
curl -X PUT https://api.inboxical.com/v1/webhooks/wh_abc123 \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{ "is_active": false }'

DELETE /webhooks/:id

Permanently removes the endpoint and all its delivery history.

Terminal window
curl -X DELETE https://api.inboxical.com/v1/webhooks/wh_abc123 \
-H "X-API-Key: $API_KEY"

Response 200

{
"message": "Webhook endpoint deleted"
}

GET /webhooks/:id/deliveries

Paginated list of all delivery attempts for an endpoint.

Query parameters

ParamDefaultDescription
page1Page number
limit20Items per page (max 100)
Terminal window
curl "https://api.inboxical.com/v1/webhooks/wh_abc123/deliveries?page=1&limit=10" \
-H "X-API-Key: $API_KEY"

Response 200

{
"deliveries": [
{
"id": "del_xyz",
"webhook_endpoint_id": "wh_abc123",
"event": "message.received",
"payload": { "inbox_id": "ix_3f9a", "message_id": "msg_7b2c", "..." : "..." },
"status": "delivered",
"attempts": 1,
"last_attempt_at": "2026-03-21T12:01:00Z",
"response_status": 200,
"response_body": "OK",
"created_at": "2026-03-21T12:01:00Z"
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 42,
"pages": 5
}
}
StatusMeaning
pendingDelivery queued, not yet attempted
deliveredYour server responded with a 2xx status
failedNon-2xx response or network error (will be retried)
deadFailed after 3 attempts — no further retries

POST /webhooks/:id/test

Sends a test delivery to the endpoint with a sample payload. The delivery is recorded in the delivery log and the result is returned synchronously.

Terminal window
curl -X POST https://api.inboxical.com/v1/webhooks/wh_abc123/test \
-H "X-API-Key: $API_KEY"

Response 200

{
"delivery": {
"id": "del_test123",
"event": "message.received",
"status": "delivered",
"attempts": 1,
"response_status": 200,
"response_body": "OK",
"created_at": "2026-03-21T12:05:00Z"
}
}

The test payload looks like:

{
"test": true,
"inbox_id": "test-inbox-id",
"message_id": "test-message-id",
"from": "[email protected]",
"to": ["[email protected]"],
"subject": "Test webhook delivery",
"timestamp": "2026-03-21T12:05:00Z"
}

When a real message arrives, Inboxical sends a POST to your URL with:

{
"event": "message.received",
"inbox_id": "ix_3f9a",
"message": {
"id": "msg_7b2c",
"from": "[email protected]",
"subject": "Welcome to MyApp!",
"received_at": "2026-03-21T12:00:03Z"
}
}

Respond with any 2xx status to acknowledge.


Inboxical retries failed deliveries up to 3 times with an exponential backoff of attempts * 30 seconds. After 3 failures, the delivery is marked as dead and no further retries are attempted.


Every webhook delivery includes an X-Inboxical-Signature header containing an HMAC-SHA256 signature of the raw request body, signed with your endpoint’s secret.

HeaderDescription
X-Inboxical-EventEvent type (e.g. message.received)
X-Inboxical-Delivery-IdUnique delivery ID
X-Inboxical-SignatureHMAC-SHA256 hex digest of the request body
Content-Typeapplication/json
import crypto from 'node:crypto'
function verifyWebhook(body: string, signature: string, secret: string): boolean {
const expected = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expected, 'hex'),
)
}
// Express example
app.post('/hooks/email', (req, res) => {
const rawBody = req.body // must be the raw string, not parsed JSON
const signature = req.headers['x-inboxical-signature'] as string
if (!verifyWebhook(rawBody, signature, process.env.WEBHOOK_SECRET!)) {
return res.status(401).send('Invalid signature')
}
const payload = JSON.parse(rawBody)
console.log('New email:', payload.message.subject)
res.sendStatus(200)
})

Webhooks work well when your test environment is reachable from the internet (e.g. staging). For local development, prefer long-polling with ?wait=30.

// Staging — register webhook before test
const res = await fetch('https://api.inboxical.com/v1/webhooks', {
method: 'POST',
headers: { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ url: 'https://staging.myapp.com/hooks/test' }),
})
const { secret } = await res.json()
// Store secret for signature verification