Workspace webhooks
Some events belong to the workspace, not to a WhatsApp number: a phone line was renewed, a wallet top-up was paid. They go to workspace webhooks, a registry separate from the number webhooks. Workspace webhooks are the same registry as the phone line webhooks, seen from a wider surface:/v1/phone-lines/webhooksaccepts only phone-line events./v1/workspace/webhooksaccepts every workspace event: the phone-line events andwallet.topup_paid. It lists every webhook of the workspace, including the ones created on the phone-lines surface.
plwh_), same signature, same retries and the same delivery log on both. A number webhook — even one subscribed to "*" — never receives these events.
Configure
- In the dashboard: Workspace keys (
/workspace-api-keys), section Workspace webhooks, as an Owner or Admin. Webhooks listening only to phone-line events also show under Phone lines → Line webhooks. - Through the API: the endpoints below, with a workspace key or an account key with
x-workspace-id. A number key gets 403NUMBER_SCOPE_NOT_ALLOWED. With an account key or an OAuth/MCP token, the user must be an Owner or Admin (403 PERMISSION_DENIEDotherwise).
The request and response shapes, the name and URL rules and the errors are the same as on
/v1/phone-lines/webhooks — only the accepted events differ.
Create a webhook for paid top-ups
"*" and wallet events
"*" means every phone-line event, and only those. Wallet events are opt-in: a webhook receives wallet.topup_paid only if it names it in events. That keeps a webhook created for phone lines with "*" from starting to receive money events it never asked for.
To get everything, list both: ["*", "wallet.topup_paid"].
A webhook that names a wallet event can be read, deleted and have its deliveries listed on
/v1/phone-lines/webhooks too, but editing it there is refused with 400 INVALID_WEBHOOK, because that surface does not know wallet events. Edit it on /v1/workspace/webhooks.Events
Paid top-up event
Fires once per credited top-up order, on every rail: Pix, the hosted Stripe page, a transparent checkout, and also top-ups made in the dashboard. A redelivery or a duplicate confirmation from the payment provider does not fire it again. Charges made by the platform — renewals, numbers, lines — and balance adjustments by the Pilot Status team do not fire it.
Match the top-up by
orderId, and dedupe deliveries by the envelope’s id (also sent as the Idempotency-Key header). The webhook is sent only after the order is completed: when it arrives, GET /v1/billing/topups/{id} already answers status: "paid".
Payload, signature and retries
Every delivery is aPOST with { id, event, createdAt, data }, signed with x-pilot-status-signature (hex HMAC-SHA256 of the raw body with the webhook’s secret), with Idempotency-Key equal to id. Each delivery gets 6 attempts in total, over about 15 minutes. Verification code and details: Phone line webhooks → Verify the signature and Delivery and retries.