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.
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.
CostFree.
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"Handle the response
Each delivery is a POST with a JSON body of the event name, its data and the time it happened.
{
"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.
- 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.