Skip to main content

Phone line webhooks

Phone lines send their events to phone-line webhooks: a registry of their own, separate from the number webhooks. The two never mix:
  • A number webhook — even one subscribed to "*" — never receives a phone_line.* event.
  • A phone-line webhook receives only phone_line.* events, never message or number events.

Configure (dashboard only)

There is no public API for phone-line webhooks. Manage them in the dashboard under Phone lines → Line webhooks (/linhas/webhooks), as an Owner or Admin:
  • Destination URL — must be https.
  • Events — pick one or more of the events below, or all ("*"). Inside this registry "*" means every phone-line event. At least one is required.
  • Signing secret — shown once, when the webhook is created (it starts with plwh_). Store it right away: nothing shows it again, and there is no rotation. To change it, create a new webhook, move your receiver to its secret, and delete the old one.
  • Pause / resume, and delete. The dashboard does not edit a webhook’s URL or events: create a new webhook and delete the old one.
  • Delivery log — per webhook: event, status (pending, delivered, failed), attempts, the HTTP status your endpoint answered and the last error.
A workspace can have more than one phone-line webhook; each receives the events it subscribed to.

Events

phone_line.code_received fires only when a transcription completes. A request that ends TIMED_OUT, FAILED, or CAPTURED with a failed transcription sends no event — if you wait on the webhook, also watch the request’s pollDeadline, or poll GET /v1/phone-lines/activations/{activationId}.

Payload format

Every delivery is a POST with a JSON body:
Headers:
Numbers here have no +. number is E.164 digits without the plus sign (551148637200), exactly as in the /v1/phone-lines endpoints — unlike the number webhooks, whose phone fields carry +.

Payloads

lineId is the line’s id in GET /v1/phone-lines/{id}.
code is null when the call was transcribed but no 6-digit code was found in it (the request’s failureReason is then no_code_in_transcript). The recording can be played in the dashboard.
This payload carries the activation code, which is a credential. Verify the signature before trusting it, and do not log the body.
currentPeriodEnd is the end of the period just paid for.
status is one of:There is no public endpoint to read the verification; verificationId identifies it in support conversations.

Verify the signature

Every phone-line webhook has a secret, so every delivery is signed. x-pilot-status-signature is the hex-encoded HMAC-SHA256 of the raw request body, keyed with the webhook’s secret (the whole string, plwh_ prefix included). Compute it over the bytes exactly as received — before any JSON parsing — and compare in constant time:
It is the same computation as the number webhooks, so a receiver that already verifies those works here with this webhook’s secret. The signature covers the body only — there is no timestamp header — so rely on id to discard duplicates.

Delivery and retries

  • Answer with any 2xx within 10 seconds. Anything else — another status, a timeout, a connection error — counts as a failed attempt.
  • 6 attempts in total: the first right away, then retries about 30 s, 1 min, 2 min, 4 min and 8 min apart — normally about 15 minutes end to end. After the last one the delivery is marked failed in the log. A delivery that could not be queued at first is picked up again by an hourly sweep, so its first attempt can come later.
  • Redirects are not followed. A 3xx answer counts as a failed attempt.
  • At least once. A retry after your endpoint processed the event but did not answer in time delivers it again, with the same id. Order is not guaranteed.
  • Paused or deleted webhooks. An event is delivered only to webhooks that are active and subscribed when it happens. Every attempt checks the webhook again: a delivery whose webhook is paused when an attempt is due — including one already in its retries — is marked failed and is not sent later; deleting a webhook drops its pending deliveries. Events are never replayed to a webhook created or resumed afterwards.
  • Internal destinations are refused. A URL whose host resolves to a private or internal network address is not called, and is not retried. Such a URL is accepted when the webhook is created — only https and the event names are checked then — so every delivery to it is logged as failed. A DNS lookup that fails is different: it is retried like any other failure.
Treat these events as notifications, not as the source of truth. When one could have been missed — your endpoint was down for longer than the retries — read the current state with GET /v1/phone-lines.