Skip to content
Status

Twilio / Telnyx: How it works

Read this before choosing an integration method. It explains how cardholder data moves between the four parties involved in a Twilio Pay transaction, which is what you need in order to reason about PCI scope, understand what your systems can and cannot see, design around failure, and decide whether you want the IVR or the Agent Assist approach.

Party Role
Your platform Initiates the call flow and orchestrates the payment. Never touches raw card data.
Twilio The telephony provider. Captures card digits from the caller’s keypad inside its PCI DSS-compliant voice platform.
Shuttle Twilio’s payments partner. Receives card data from Twilio through the Shuttle Pay connector and routes it to one of many supported gateways.
Payment gateway Authorises the card and returns an approval or decline.

The relationship to keep in mind: Twilio handles telephony, Shuttle handles payments. Shuttle is the piece that lets a single Twilio connector fan out to whichever gateway your merchant uses.

Your platform is the initiator — the call routes to Twilio because your application’s voice webhook tells it to, and the session returns to your platform when Twilio finishes.

flowchart LR
A[Your Platform] -->|Routes call<br/>to Twilio| B[Twilio<br/>captures DTMF]
B -->|Card data<br/>via connector| C[Shuttle]
C -->|Authorise| D[Gateway]
D -->|Gateway token +<br/>reference + status| C
C -->|Masked card +<br/>Shuttle IDs +<br/>gateway IDs| B
B -->|Callback| A
C -.->|Webhook async| A

Raw card data only ever lives inside Twilio → Shuttle → Gateway. Your platform receives the masked PAN, non-sensitive Shuttle and gateway identifiers, and status — never the full PAN or CVV.

Two independent response paths land on your platform, and they carry different kinds of information.

Delivered to your action URL (IVR) or StatusCallback URL (Agent Assist) the moment the session completes. This is the detailed payload, including:

  • Masked PAN, card brand, and expiry (for display)
  • Shuttle identifiers — Shuttle payment ID (PaymentConfirmationCode) and Shuttle payment method ID (PayConnector_payment_method). Use these with the Shuttle API for a standardised interface that works identically across every supported gateway
  • Gateway identifiers — gateway transaction reference (PayConnector_gateway_reference) and gateway card token (PayConnector_gateway_token). Use these if you want to interact directly with the underlying gateway (native reconciliation, gateway-specific tooling)
  • Status fields (PayConnector_payment_status, PayConnector_gateway_status)

Neither identifier set is sensitive — they are references, not card data. Most integrations use the Shuttle IDs so they don’t need gateway-specific code, and keep the gateway IDs on file for reconciliation.

For the full list of fields, see the Shuttle Pay connector reference.

Delivered directly from Shuttle when a payment event occurs (including async ones: pending settlements, held transactions, recurring charges). The webhook is an event notification, not a full transaction record — it carries the event type and references to the affected resources (account, transaction, capture, contract). Use those references to fetch the full details via the Shuttle API when you need them.

For event types and payload shapes, see the webhook events reference.

The Twilio callback is reliable for the session itself — if the caller hangs up mid-flow, Twilio still fires the callback with Result=caller-hung-up rather than dropping it. Use the callback as your primary signal and the callback payload for display and immediate business logic.

Use the Shuttle webhook as the source of truth for anything that resolves after the call ends — pending settlements, held transactions, gateway responses that arrived late, and recurring/scheduled charges. By default Shuttle doesn’t retry a webhook that fails: set retries on your webhook target, as described in Webhooks.

Because Twilio intercepts the DTMF digits before they reach any of your systems, and suppresses the tones so agents on the call cannot hear them, card numbers stay out of your infrastructure. That greatly reduces your PCI DSS scope, but doesn’t remove it. A platform is its merchants’ service provider and validates its own compliance as one each year (SAQ D for service providers, or a Report on Compliance); a merchant still completes its own annual assessment. See Your PCI DSS obligations. Shuttle’s instance and secret keys are stored in the Twilio connector configuration, not on your platform, so the card-submission leg never touches your credentials either.

Confirm your specific obligations with your QSA — this is guidance, not legal advice.

Both integrations share the same data flow. They differ in how the session is initiated and controlled.

Your platform responds to Twilio’s voice webhook with TwiML containing a <Pay> verb. The caller interacts only with Twilio.

sequenceDiagram
participant Caller
participant Twilio
participant YourApp as Your Platform
participant Shuttle
participant Gateway
Caller->>Twilio: Calls number
Twilio->>YourApp: Voice webhook
YourApp->>Twilio: TwiML with <Pay>
Twilio->>Caller: Prompts for card
Caller->>Twilio: DTMF digits
Twilio->>Shuttle: Card data (connector)
Shuttle->>Gateway: Authorise
Gateway->>Shuttle: Approval + token
Shuttle->>Twilio: Masked card + IDs
Twilio->>YourApp: POST to action URL
Shuttle-->>YourApp: Webhook (async)

Your backend drives the session programmatically, letting an agent UI trigger each field capture. The agent cannot hear the DTMF tones.

sequenceDiagram
participant Caller
participant Twilio
participant Agent as Agent UI
participant Backend as Your Backend
participant Shuttle
participant Gateway
Agent->>Backend: Start payment
Backend->>Twilio: POST /Payments
loop For each field
Agent->>Backend: Capture field
Backend->>Twilio: POST Capture=...
Twilio->>Caller: Prompts for field
Caller->>Twilio: DTMF digits
Twilio->>Backend: StatusCallback
Backend->>Agent: Push UI update<br/>(WebSocket / poll)
end
Agent->>Backend: Complete
Backend->>Twilio: Status=complete
Twilio->>Shuttle: Card data (connector)
Shuttle->>Gateway: Authorise
Gateway->>Shuttle: Approval + token
Shuttle->>Twilio: Masked card + IDs
Twilio->>Backend: Final StatusCallback
Shuttle-->>Backend: Webhook (async)

A card gets saved in two scenarios:

  • Tokenisation session — you call <Pay> (or the Payments API) with no chargeAmount. The card is tokenised but not charged.
  • Charge + save — you pass save_card=true alongside a charge.

In both cases, the gateway creates the durable token and Shuttle stores a reference to it against a Shuttle payment method record (pm_...). You get both identifiers back: the Shuttle payment method ID and the gateway token.

Once the card is saved, subsequent charges don’t need Twilio — Twilio is only in the path when a human is on the line entering digits. You have two options for how to charge it:

flowchart LR
A[Later charge] -->|pm_... + amount| B[Shuttle API]
A -->|gateway_token + amount| D[Gateway directly]
B -->|Uses stored<br/>gateway_token| D
D -->|Approval| B
D -->|Approval| A
B -->|Payment result| A
  • Via Shuttle — pass the Shuttle pm_... ID to the Shuttle API. Shuttle looks up the gateway token and submits the charge. Your code stays identical across every supported gateway.
  • Via the gateway directly — use the gateway token with your own gateway integration. Useful if you already have gateway-native tooling or want to bypass Shuttle for specific flows.

Most integrations go through Shuttle so they don’t need gateway-specific code, but the gateway-direct path stays open.

There are two timeouts stacked on top of each other: Twilio’s deadline on Shuttle, and Shuttle’s deadline on the gateway.

Twilio enforces a 10-second response deadline on the call it makes to the Shuttle Pay Connector. If Shuttle hasn’t responded within that window, Twilio automatically retries the connector call, reusing the same transaction_id as an idempotency key. Retries continue up to 5 times (6 attempts total) before Twilio gives up.

Shuttle’s connector uses the transaction_id to dedupe — if the original authorisation eventually succeeds while Twilio is retrying, the retry returns the same result rather than creating a duplicate charge.

Shuttle enforces its own 45-second deadline on the underlying gateway. If the gateway hasn’t returned a definitive result by then, Shuttle marks the transaction as UNRESOLVED — meaning the outcome cannot be determined from the original request.

For gateways that support it, Shuttle schedules background reconciliation jobs — at +1 minute and +15 minutes — to check the gateway and confirm the outcome. When a job resolves the state, Shuttle:

  • Transitions the transaction to APPROVED or DECLINED
  • Fires the corresponding Shuttle webhook

Reconciliation support varies by gateway. Contact Shuttle support to confirm whether your gateway is covered.

In this edge case, the Twilio callback is not updated — the session already completed from Twilio’s perspective, so only the Shuttle webhook will carry the final state. This is unlikely to happen in production other than at scale, but worth designing for: if you see UNRESOLVED, trust the Shuttle webhook to deliver the final outcome rather than treating it as a terminal state.

The connector timeout and retry section above covers slow-gateway and unresolved scenarios. The table below covers the remaining cases.

Where it fails Symptom What to do
Caller hangs up mid-session Callback fires with Result=caller-hung-up, no Shuttle data Likely abandoned; no charge is typically attempted. Reconcile via the Shuttle webhook if a later UNRESOLVED arrives
Gateway decline PayConnector_payment_status = DECLINED, reason in PayConnector_gateway_status Show a reason, allow retry
Connector credentials wrong Twilio returns Result: success but no PayConnector_* fields Detect the missing PayConnector_payment_status and treat as failure
Credential Held by Purpose
Twilio Account SID / Auth Token Your backend (Agent Assist only) Authenticate to the Twilio Payments API
Shuttle instance key / secret key Twilio connector config Authenticate Twilio → Shuttle during card submission
Shuttle API key Your backend Post-payment operations (refunds, captures, reuse)

Rotating your Shuttle instance/secret keys requires updating the connector in the Twilio Console — they are not held on your platform during the call.

Was this page helpful?