Skip to content

Webhooks

Codes, pushed.

Instead of polling, give us a URL and we'll POST a signed event the moment a code arrives, an order expires or is cancelled, or a rented number gets an SMS.

Set up an endpoint

  • In the dashboard, open Settings and enter your webhook URL. In production it must be https:// on a public host.
  • We generate a signing secret (whsec_…) the first time. It stays the same when you change the URL; rotate it from the same page.
  • Press Send test event to receive a webhook.test event and check your signature verification end to end.

One URL per account receives every event type; switch on type.

What we send

A POST with a JSON body — the event envelope — and these headers:

Request headers
POST /webhooks/passcode HTTP/1.1
Content-Type: application/json
User-Agent: passcode.sh-webhooks/1
X-Passcode-Signature: t=1791381739,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
X-Passcode-Event: order.code_received
X-Passcode-Delivery: evt_6Jt2WqR9mK4xN7bLc3Vp
id
Unique event id (evt_…), also in X-Passcode-Delivery. Use it to skip duplicates.
type
The event type, also in X-Passcode-Event.
created_at
When the event was created (ISO 8601, UTC).
data
The payload. order.* events carry the full Order, exactly as the API returns it.
order.code_received
{
  "id": "evt_6Jt2WqR9mK4xN7bLc3Vp",
  "type": "order.code_received",
  "created_at": "2026-10-06T14:02:19.412Z",
  "data": {
    "order": {
      "id": "ord_7Hq2LmX9vB3kR8tN4cYp",
      "status": "received",
      "service": {
        "slug": "whatsapp",
        "name": "WhatsApp"
      },
      "country": {
        "iso2": "US",
        "name": "United States",
        "dial_code": "+1"
      },
      "phone_number": "+12025550147",
      "price": "0.895",
      "price_micros": 895000,
      "code": "482913",
      "messages": [
        {
          "id": "msg_4mQ8rXc2LpV7nZ1kT9bW",
          "from": "32665",
          "body": "Your WhatsApp code: 482-913\nDon't share this code with others",
          "code": "482913",
          "received_at": "2026-10-06T14:02:19.000Z"
        }
      ],
      "strategy": "best",
      "source": "api",
      "created_at": "2026-10-06T14:02:03.000Z",
      "expires_at": "2026-10-06T14:22:03.000Z",
      "received_at": "2026-10-06T14:02:19.000Z",
      "completed_at": null,
      "cancelled_at": null,
      "refunded_at": null,
      "can_cancel": false,
      "can_finish": true,
      "demo": true
    }
  }
}

Responding and retries

Acknowledge
Any 2xx status within 5 seconds counts as delivered.
Retries
One retry, about a second later, after a timeout, a network error, a 429 or a 5xx. Other statuses aren't retried.
Redirects
Not followed. Point the URL at the final destination.
Ordering
Not guaranteed. Use created_at and the order's own status, not arrival order.
Log
Every attempt is recorded on our side (the last 200 per account).

Best practices

  • Verify every signature before trusting the body — see Verifying signatures.
  • Respond fast. Return 2xx as soon as the event is stored; do slow work afterwards.
  • Deduplicate on the event id. A retry after a slow response can deliver the same event twice.
  • Expect new event types and fields. Ignore what you don’t recognise.

Event types