Skip to main content

Troubleshooting: secure-payment capture failures

The agent-assisted secure-payment flow has one request surface — the secure-payment/start, secure-payment/complete, and secure-payment/cancel endpoints on a live call — and five customer-facing error codes. Each code has a different cause and a different fix; none of them resolve by retrying the same request unchanged. This page works each one as a decision path. For the flow itself — what the capture does to recording and DTMF, and how the PSP tokenizes the card — read Secure payment capture first. Read the error envelope first: the error.code field on the response tells you which gate rejected the request. A typical envelope:

Cause table

Setup gate — SECURE_PAYMENT_UNSUPPORTED (422)

Start rejects with SECURE_PAYMENT_UNSUPPORTED when the call has no Jambonz-bridged leg. Capture works by masking the caller’s keypad tones and pausing recording on the bridged voice path — a call that never went through the Jambonz bridge (a bare SIP-trunk call, or an AI-agent pipeline call with no device leg) carries no bridged leg to mask, so start refuses rather than run an unmasked capture. Check first:
  1. Did the call actually reach the bridge? Read the call record: GET /api/v1/voice/calls/:id and look at metadata.jambonz_call_sid. A start on a call with no jambonz_call_sid rejects deterministically — the same call fails every time, so retrying is wasted.
  2. How was the call routed? If your integration flagged the call as an AI-agent pipeline or a pure SIP-trunk dial-out, the caller’s keypad never surfaced onto a bridge the capture can control. Fix the route so an agent answers on a bridged softphone leg, and run the capture on that call.
This is a routing problem, not a capture-permissions problem — the 422 is deterministic and resolves only when the call route changes.

Session lifecycle — ALREADY_ACTIVE, SESSION_NOT_FOUND, NOT_ACTIVE

The session has one live state (capturing) and two terminal states (captured, cancelled). Start holds at most one capturing session per call, so the three lifecycle errors come from operating on the wrong session or at the wrong point in its life. Poll before you act: GET /api/v1/voice/calls/:id returns the secure_payment_sessions array on the call metadata, each entry with its session_id and status. Read it before completing or cancelling — confirm the session you hold is still capturing.
  • 409 ALREADY_ACTIVE on start. Another session is capturing on the call — either this integration started one earlier, or the agent hit Secure payment on the softphone. Find the active session id on the call and drive it to a terminal state: complete it if the caller entered the card, cancel it if they bailed or the 409 was a stale, abandoned session. Then retry start. The 409 is terminal for the new start — it cannot queue behind the active capture.
  • 404 SESSION_NOT_FOUND on complete or cancel. The session_id on the request matched nothing on the call. Common causes: a copy-pasted id from a different call, a stale id after the session was replaced by a retry, or a typo. Re-read the call and use one of the ids actually on secure_payment_sessions.
  • 409 NOT_ACTIVE on complete or cancel. The session exists but has already reached its terminal state — another actor (a second agent tab, a webhook listener, or a retry) completed or cancelled it first. Read the session status; if it is already captured, do not re-complete — the card is already stored and a second finalize attempt always 409s. To take the card again, start a new session.

PAN-mask validation — SECURE_PAYMENT_INVALID_MASK (422)

Complete rejects card_last4 that is not exactly four digits. The boundary exists for one reason: a full card number passed as “the last 4” would otherwise land in the call metadata and the recordings, dragging your account back into PCI scope. Orbit fails closed: it rejects the request and stores nothing. If you see this 422, the calling code sent the full PAN — or a masked string like ************4242 — where only the last four belong. Fix the source: extract exactly the final four digits before calling complete, and never log, store, or re-send the full number anywhere else. The retry is safe — the 422 lands before the session changes state, so the session is still capturing and complete can be re-sent with the corrected value.

Retry-safety and escalation

Three of the five codes are deterministic — retrying the identical request returns the identical failure:
  • UNSUPPORTED is a property of the call route; nothing about the capture request changes it.
  • INVALID_MASK is a property of the payload; fix the card_last4 source before resending.
  • ALREADY_ACTIVE is terminal for the new start; retry only after the active session is completed or cancelled.
SESSION_NOT_FOUND and NOT_ACTIVE are fixed by reading the live session state, not by retrying the same id. Escalate to Devotel support only after the cause table points at a routing problem you cannot change (for example, a call that should have reached the Jambonz bridge but has no jambonz_call_sid). Attach the request_id from the error envelope, the call id, and the session id you were using — the support team reads the same state on the call metadata that this page polls.

Cross-references