Skip to content

Quickstart, Webhooks and agents

Receive webhooks and verify signatures

Every delivery is signed with your endpoint's secret and a timestamp. Check the signature over the raw body, refuse anything older than five minutes, and answer quickly with a 2xx.
Calls
X-PX-SignatureX-PX-TimestampX-Webhook-EventX-Webhook-Id
Cost
Free.
On this page
  1. Step 1Get an API key
  2. Step 2Fund the balance
  3. Step 3Make the request
  4. Step 4Handle the response
  5. Step 5Go further

Step 1

Get an API key

Open an account, then create a key under Dashboard, API keys. Live keys (wmmn_live_sk_) send real traffic and charge your balance. Test keys (wmmn_test_sk_) run the same request through routing and pricing without delivering it, and draw on test credit, which comes with an invite code.

Create webhook endpoints in the dashboard under Webhooks. API keys cannot create or edit them, so a leaked key can never redirect your events. The signing secret is shown once when you create the endpoint.

Step 2

Fund the balance

PacketExchange is prepaid. Top up from $5 by card or crypto, or from $100 by wire, under Dashboard, Billing. An agent can top itself up with x402. There is no contract and no monthly fee.

Nothing to fund. Webhooks cost nothing to receive.

Cost

Free.

Step 3

Make the request

X-PX-Signature is v1= followed by the hex HMAC-SHA256 of "<X-PX-Timestamp>.<raw body>", keyed with your secret. Verify it on the exact bytes you received, before parsing the JSON. The cURL tab sends your receiver a correctly signed test delivery.

# Send your receiver a signed test delivery, exactly as PacketExchange signs one:
# X-PX-Signature: v1=<hex HMAC-SHA256 of "<timestamp>.<raw body>">
body='{"event":"ping","data":{},"timestamp":"2026-01-01T00:00:00.000Z"}'
ts=$(date +%s)
sig=$(printf '%s.%s' "$ts" "$body" | openssl dgst -sha256 -hmac "$PACKETEXCHANGE_WEBHOOK_SECRET" | sed 's/^.* //')

curl -i http://localhost:3000/webhooks \
  -H "Content-Type: application/json" \
  -H "X-Webhook-Event: ping" \
  -H "X-PX-Timestamp: $ts" \
  -H "X-PX-Signature: v1=$sig" \
  --data-raw "$body"
Save as request.sh, run bash request.shNeeds curl, plus jq for the multi-step samplesSet PACKETEXCHANGE_WEBHOOK_SECRET to the endpoint's signing secret. The receivers listen on port 3000.

Step 4

Handle the response

Each delivery is a POST with a JSON body of the event name, its data and the time it happened.

POST to your endpointevent sms.sent
{
  "event": "sms.sent",
  "data": {
    "messageId": "<message id>",
    "to": "+447700900123",
    "from": "Acme",
    "status": "accepted",
    "segments": 1,
    "cost": "<USD, 6 decimals>",
    "submittedAt": "<ISO-8601 UTC>"
  },
  "timestamp": "<ISO-8601 UTC>"
}

Values in angle brackets are placeholders for your own ids, times and amounts.

Response fields

X-PX-Timestamp
Unix seconds when this attempt was sent. Reject it when it is more than 5 minutes from now.
X-PX-Signature
v1=<hex HMAC-SHA256 of "<timestamp>.<raw body>">. Compare in constant time.
X-Webhook-Id
The delivery id, to de-duplicate retries.
X-Webhook-Signature
The legacy body-only signature, still sent during the transition. Prefer v1.

When something goes wrong

  • Answer with any 2xx within 10 seconds, then do slow work afterwards. A failed delivery is retried up to 5 times.
  • Every delivery is in the dashboard's delivery log, where you can resend any your endpoint missed.
  • Events include sms.sent, call.completed, number.sms.received, number.call.received, topup.confirmed and balance.low.

Step 5

Go further

Run it with your own key

Open an account, add credit from $5 and create a key. You pay the carrier's rate plus a fee capped at $0.001.