> ## 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.

# Unattended PCI capture in the IVR builder

> Collect card digits in a self-service IVR with the DTMF Input node's PCI capture mode: pause recording, mask the digits, exchange them once against your PSP tokenizer, and bind the capture to a commerce charge.

# Unattended PCI capture in the IVR builder

The DTMF Input node in the IVR builder has a **PCI capture mode** built
for pay-by-phone menus with no agent on the call: an after-hours
self-service balance line, a callback-led payment flow, an agent-free
billing IVR. When capture mode is on, the caller types their card on the
keypad, recording pauses for that window, and the raw digits never reach
logs, transcripts, or your flow context — only the digit count and a
per-call fingerprint are kept.

If a live agent is on the call instead, use the agent-assisted flow
covered in [Secure payment capture on the live softphone](/voice/secure-payment-capture)
— this page covers the unattended path only.

## 1. When unattended capture is the right tool

Choose the IVR path when the entire payment happens inside the menu and
no person participates:

* **After-hours self-service.** A caller reaches your number outside
  business hours, hears "press 1 to pay your balance," and types the
  card while recording pauses. No agent is staffed, so no agent can be
  descoped — the menu itself must be the trust boundary.
* **Callback-led pay.** Your system calls the customer back at a
  confirmed time and walks straight into a capture node.
* **Agent-free billing IVRs.** Recurring invoice or top-up lines where
  the caller self-enters a card or PIN every time.

Choose the [agent-assisted softphone flow](/voice/secure-payment-capture)
when a human is mid-conversation with the caller and needs to take
payment there and then — the same masked-DTMF semantics apply, but the
agent controls the session from the live softphone.

## 2. Configure it from the IVR builder

In the dashboard, go to **Voice → IVR Builder** and open your flow (or
start a new one — see [Design IVR flows on the visual builder](/guides/ivr-visual-builder)).

1. Drag a **DTMF Input** node onto the canvas and click it to open the
   config panel.
2. Set the collection shape for a card field: **Max Digits** (up to 20
   for a full card number), **Timeout**, and **Finish on Key**
   (`#` is the usual terminator). For a full capture, use one DTMF
   Input node per field — card number, expiry, CVV.
3. Toggle **PCI capture mode (secure entry)** on.
4. Optionally set **Tokenizer URL** to your payment service provider's
   (PSP) tokenization endpoint — for example
   `https://payments.example.com/tokenize`. The digits are exchanged
   once against this URL; its opaque reply token is the only value a
   downstream node can read, via the `{{pci_token}}` template
   placeholder. Leave the URL empty to pause recording and mask the
   digits without tokenizing.

Behind the capture there is nothing extra to wire: the runtime emits a
recording pause before the gather and a resume on the continuation
automatically.

## 3. Publish preflight

The builder's preflight validation (the **Validate** button, which also
runs on publish) rejects a malformed tokenizer URL, so a bad literal
never ships to the live snapshot. An unparseable URL fails validation
with an issue shaped like:

```json theme={null}
{
  "issues": [
    {
      "nodeId": "collect-card",
      "nodeName": "Collect card number",
      "message": "PCI capture: tokenizer URL is not a valid URL"
    }
  ]
}
```

Fix the URL until **Validate** returns clean, then **Publish** and
attach the flow to a number. An empty tokenizer URL is valid — capture
still pauses recording; only the token exchange is skipped.

## 4. Runtime semantics — what the platform actually does

Once a published flow reaches a PCI capture node on a live call:

1. **Recording pauses.** When the route records the call, recording is
   paused immediately before the digits are collected and resumed on the
   continuation — including error paths, so a call is never left with
   recording silently paused. If the number's route never started
   recording, the pause/resume pair is skipped.
2. **The digits are masked end to end.** The platform stores only the
   digit count and a salted, per-call fingerprint. The raw entry never
   lands in logs, in the call's flow context, or in transcripts, and
   downstream `{{dtmf_input}}` placeholders are explicitly cleared so
   they can never resolve to the captured digits.
3. **Optional token exchange, on your edge.** When the node carries a
   tokenizer URL, the digits are POSTed once to that URL — your PSP's
   PCI-scoped tokenization endpoint — before they are dropped. The URL
   passes an SSRF guard (internal addresses are refused), the request
   has a 5-second budget, and the response must be JSON with a top-level
   string `token` field. The token is stored as `{{pci_token}}` for
   downstream nodes (HTTP Request, Send Message, webhooks).
4. **Failure fails closed.** An SSRF block, timeout, non-2xx reply, or a
   response with no token ends the capture with a polite hangup — the
   digits stay dropped, recording resumes, and nothing half-captured is
   stored.

## 5. Bind the capture to a commerce charge

The capture on the call produces a masked, verified summary. Bind it to
a priced charge from your backend with
`POST /api/v1/commerce/secure-payment/charge`. The capture proof is
derived server-side from the call's stored session — the fields you send
are consistency hints, and any hint that disagrees with the stored
masked summary fails closed. Only a completed capture can bind.

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/commerce/secure-payment/charge \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "charge_id": "chg_after_hours_9182",
    "amount": 129.99,
    "currency": "USD",
    "description": "After-hours IVR balance payment",
    "capture": {
      "call_id": "call_9f2c4a",
      "psp_token_ref": "tok_3kD91Qxv",
      "card_last4": "4242",
      "card_brand": "visa"
    }
  }'
```

The equivalent with the Node SDK — the endpoint is reachable through the
client's generic request method against the resource you prefer:

```ts theme={null}
import { Orbit } from "@devotel/sdk-node";

const orbit = new Orbit({ apiKey: process.env.ORBIT_API_KEY! });

await orbit.request("POST", "/commerce/secure-payment/charge", {
  charge_id: "chg_after_hours_9182",
  amount: 129.99,
  currency: "USD",
  capture: {
    call_id: "call_9f2c4a",
    psp_token_ref: "tok_3kD91Qxv",
    card_last4: "4242",
    card_brand: "visa",
  },
});
```

`card_last4` must be exactly four digits — send a full card number
anywhere in this flow and the request is rejected at validation. Your
PSP settles the opaque token out of band; subscribe to
`commerce.secure_payment.charge_bound` to notify your order system when
a charge binds. See [Webhook events](/webhooks/events).

## 6. Tenant-owned security posture

Masked-DTMF capture is a **scope-reduction control you opt into per
node** — it defaults off. The platform handles only masked digits: the
raw card number never persists anywhere on Orbit, and the token exchange
runs against an endpoint **you** operate. That shape keeps Orbit outside
your cardholder data environment; it does not by itself make any party
PCI DSS compliant.

* The PCI scope of the full flow is owned by you: your tokenizer
  endpoint, your PSP, and your qualified security assessor determine
  where your boundary lands.
* Keep capture nodes collecting card data only — routing menus, account
  lookups, and confirmations stay on ordinary DTMF Input nodes.
* Confirm your tenant's recording-consent and PCI scope obligations with
  your assessor, and document the control in your own policy. See
  [PCI DSS posture](/compliance/pci-dss).

## 7. Limits

* A full card number is never accepted on any platform endpoint — a PAN
  sent as the `card_last4` hint is rejected at validation. The platform
  stores digit count plus a salted fingerprint, nothing more.
* There is no SIP-trunk or AI-agent pass-through for captured digits:
  collection runs on the platform's own voice path, and the only value
  that travels downstream is the tokenizer's opaque reply token.
* Tokenizer requests are bounded (5 seconds, response size capped) and
  fail closed — a slow tokenizing endpoint ends the call rather than
  holding the caller indefinitely.

## 8. Troubleshooting

* **Publish is blocked with "PCI capture: tokenizer URL is not a valid
  URL."** The tokenizer URL does not parse as a URL with a host. Fix it
  in the node's config panel and re-validate; an empty field is allowed.
* **Callers hear "we were unable to process your input" and the call
  hangs up.** The tokenizer exchange failed closed: the URL is
  unreachable, timed out past 5 seconds, answered non-2xx, returned a
  body without a top-level string `token`, or the SSRF guard refused it
  (internal address). Check your tokenizer endpoint's reachability and
  response shape.
* **The digits arrive but nothing is tokenized.** The tokenizer URL is
  empty — capture still masked and paused recording, but no exchange
  ran. Set the URL if downstream nodes need `{{pci_token}}`.
* **Capture behaves oddly on a non-bridged leg.** PCI capture is an
  in-call flow step: it runs on the answered leg the flow controls. A
  leg that is mid-transfer or pure SIP-trunk transit without the flow's
  control does not present a gather point — route through a DTMF Input
  node on the answered call. For the related agent-assisted codes, see
  [Troubleshooting: secure-payment capture failures](/troubleshooting/secure-payment-capture).

## See also

* [Secure payment capture on the live softphone](/voice/secure-payment-capture) —
  the agent-assisted counterpart.
* [Design IVR flows on the visual builder](/guides/ivr-visual-builder) —
  the canvas this guide configures.
* [Build and ship your first IVR flow](/guides/build-ivr-flow) — the
  DSL/API contract behind the canvas.
* [PCI DSS posture](/compliance/pci-dss) — the compliance surface for
  card flows.
