> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshooting: secure-payment capture failures

> Triage the five customer-facing secure-payment errors — UNSUPPORTED, ALREADY_ACTIVE, SESSION_NOT_FOUND, NOT_ACTIVE, and INVALID_MASK — on the agent-assisted in-call card capture.

# 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](/voice/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:

```json theme={null}
{
  "error": {
    "code": "SECURE_PAYMENT_ALREADY_ACTIVE",
    "message": "A secure-payment capture session is already active on this call; complete or cancel it before starting another"
  },
  "request_id": "req_8f3d2a"
}
```

## Cause table

| Code                               | HTTP | The gate that rejected you                                                   | Fix                                                                           |
| ---------------------------------- | ---- | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| `SECURE_PAYMENT_UNSUPPORTED`       | 422  | The call has no Jambonz-bridged leg — start is gated to a bridged device leg | Route the call through the Jambonz bridge first, then start                   |
| `SECURE_PAYMENT_ALREADY_ACTIVE`    | 409  | Another capture is still `capturing` on this call                            | Complete or cancel the active session, then retry start                       |
| `SECURE_PAYMENT_SESSION_NOT_FOUND` | 404  | The `session_id` you sent does not exist on this call                        | Read the call and use a session id from its `secure_payment_sessions` list    |
| `SECURE_PAYMENT_NOT_ACTIVE`        | 409  | The session is already finished (`captured` or `cancelled`)                  | Poll the call before completing or cancelling; a finished session is terminal |
| `SECURE_PAYMENT_INVALID_MASK`      | 422  | `card_last4` was not exactly four digits                                     | Send only the last four digits — never the full card number                   |

## 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](https://devotel.io) 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

* [Secure payment capture](/voice/secure-payment-capture) — the full flow:
  masked-DTMF entry, recording pause, PSP tokenization, and webhooks
* [Error codes](/reference/error-codes) — the full reference table for
  every `SECURE_PAYMENT_*` code
* [Troubleshooting hub](/reference/troubleshooting-hub) — the index of all
  runbooks
