Skip to main content
Add card-on-file payments to an ElevenLabs agent by call transfer. This works like a hosted web checkout, over the phone: when the order is confirmed, your agent creates a Checkout Session and transfers the live call to a Ringup payment line, the caller pays there, and the call optionally returns to your agent. Ringup recognizes the caller, charges their saved card, settles it against the order, and texts a receipt. Your server reconciles from a webhook. If the model is new to you, read Call transfer first. This page is end to end: first-time setup, the call, the return, and what happens after payment.

How the pieces map to ElevenLabs

Ringup pieceOn ElevenLabs
create_checkoutA server tool on your agent that creates a Checkout Session and returns where to transfer
The transfer targetThe transfer_to SIP URI the Checkout Session returns
The transferThe transfer_to_number system tool with transferType: sip_refer and customSipHeaders
The resultA checkout.completed webhook to your server

Prerequisites

  • An ElevenLabs agent you already run, with a phone number attached over a SIP trunk (custom SIP headers require a SIP trunk, not a native-Twilio number; see the warning below).
  • Your ElevenLabs API key.
  • A Ringup account and API key (from your Ringup dashboard). Test mode works out of the box on a shared sandbox, so there is nothing to connect. See Testing.

Step 1: Add the Ringup checkout tool to your agent

This is the one-time setup. On your ElevenLabs agent, add a server tool that calls Ringup’s create_checkout endpoint with your Ringup API key (Agent, then Tools, then Add tool, then Webhook), and confirm the built-in transfer_to_number system tool is enabled.
{
  "type": "webhook",
  "name": "create_checkout",
  "description": "Create a Ringup Checkout Session for the order and return where to transfer the call.",
  "url": "https://api.ringup.dev/v1/checkouts",
  "method": "POST",
  "request_headers": { "Authorization": "Bearer <YOUR_RINGUP_KEY>" },
  "body_parameters": {
    "amount_cents": { "type": "number", "description": "Order total in cents" },
    "order_id":     { "type": "string", "description": "Your processor order id, if you have one" }
  }
}
Then add one line to your agent’s prompt:
When the caller has confirmed their order, call create_checkout with the order total, then transfer to the returned target.

Step 2: Create a Checkout Session when the order is confirmed

Your agent calls the tool from Step 1. It takes the same inputs a hosted checkout session takes (amount, line items, order), plus how you want the call to end, and returns where to send the call:
create_checkout({
  "amount_cents": 2500,
  "line_items": [{ "name": "Large pepperoni", "quantity": 1, "amount_cents": 2500 }], // optional
  "order_id": "sq_abc",                              // optional: settle against an existing order
  "return_to": "sip:agent@your-eleven-number",       // optional: transfer the caller back after payment
  "success_message": "You are all set, your order will be ready shortly."  // optional (if no return_to)
})

// response (the Checkout Session)
{
  "id": "cs_x9f2",                                   // Checkout Session id; the correlation token
  "payment_required": "required",                    // "required" | "optional" | "none"
  "transfer_to": "sip:cs_x9f2@transfer.ringup.dev"   // where to transfer; embeds the id
}
If payment_required is none, do not transfer: carry on and end the call normally. Otherwise transfer to transfer_to.

Step 3: Transfer with SIP REFER

Call the transfer_to_number system tool with a SIP-URI destination and the Checkout Session id as a custom SIP header. The client_message is spoken to the caller as the handoff line, so write it naturally rather than as a terse “please hold”:
transfer_to_number({
  "transferType": "sip_refer",
  "transferDestination": { "type": "sip_uri", "sipUri": "{{transfer_to}}" },
  "customSipHeaders": { "X-Session-Id": "{{id}}" },
  "client_message": "One moment, connecting you to secure payment."
})
Ringup matches the call by the X-Session-Id header, with the id also embedded in the SIP URI as a backstop, so correlation holds even if a middlebox strips the header.
Use a SIP trunk number and transferType: sip_refer. Custom SIP headers survive to a SIP endpoint but are stripped on a native-Twilio ElevenLabs number, which would drop the header match.

Step 4: The return, and the result (the webhook)

Ringup answers the transferred call, recognizes the Caller, charges the saved Card, and texts a receipt. A first-time caller with no saved card is texted a secure pay link instead. Then the call ends one of three ways, from what you passed in Step 2:
  • return_to set: Ringup transfers the caller back to your agent with the outcome in SIP headers (X-Payment-Status, X-Confirmation), so your agent resumes and closes in its voice.
  • success_message / failure_message set (no return_to): Ringup reads your line and ends.
  • Neither: Ringup reads a short default and ends. The receipt is the proof.
A return_to that cannot connect falls back to reading your success_message (or the default), so a failed return never strands the caller. Either way, your server reconciles from a webhook:
{
  "id": "evt_9f2",
  "type": "checkout.completed",         // or "checkout.failed"
  "data": {
    "checkout_id": "cs_x9f2",
    "payment_id": "pay_abc",
    "amount_cents": 2500,
    "order_id": "sq_abc",               // echoed back
    "confirmation": "2PJJZY",
    "caller": "+14155551234",
    "card": { "brand": "VISA", "last_4": "5858" }
  }
}
Match on the order_id you passed, store the payment_id, and flip your order to paid. See Webhooks for the full catalog, statuses, signature verification, and idempotency.

Validation status

ElevenLabs’ transfer_to_number with sip_refer and customSipHeaders is documented by ElevenLabs. Ringup’s hosted transfer endpoint is rolling out; confirm access in your Ringup dashboard before relying on this in production. The recognition, charge, receipt, and webhook behavior is the same payment path Ringup runs everywhere.