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

# Pipecat

> Add card-on-file payments to a Pipecat voice pipeline by call transfer: create a Checkout Session in your pipeline and Dial the caller to the Ringup SIP URI.

Add card-on-file payments to a Pipecat voice pipeline by call transfer. This works like a hosted
web checkout, over the phone: when the order is confirmed, your pipeline 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](/concepts/call-transfer) first.

Pipecat runs the pipeline; the telephony under it is your transport (Twilio, Telnyx, Plivo, or
Exotel WebSocket serializers, or Daily PSTN/SIP). Your transport owns the call leg, so the
transfer happens there.

This page is end to end: setup, the call, the return, and what happens after payment.

## How the pieces map to Pipecat

| Ringup piece        | On Pipecat                                                                |
| ------------------- | ------------------------------------------------------------------------- |
| `create_checkout`   | An HTTP `POST` from a function your LLM calls when the order is confirmed |
| The transfer target | The `transfer_to` SIP URI the Checkout Session returns                    |
| The transfer        | A `<Dial><Sip>` on your telephony transport to the Ringup SIP URI         |
| The result          | A `checkout.completed` webhook to your server                             |

## Prerequisites

* A running Pipecat pipeline with telephony (a Twilio/Telnyx/Plivo/Exotel WebSocket serializer,
  or Daily PSTN/SIP dial-in).
* The telephony account that owns the inbound call leg.
* A Ringup account and API key. Test mode works out of the box on a shared sandbox. See
  [Testing](/concepts/testing).

## Step 1: Create a Checkout Session when the order is confirmed

Register a function on your LLM (`FunctionSchema`) that the model calls once the order is
confirmed, and in its handler `POST` to Ringup with your API key. There is nothing to register
with Ringup; this is a plain HTTP call from your handler.

```python theme={null}
async def create_checkout(params: FunctionCallParams):
    r = await http.post(
        "https://api.ringup.dev/v1/checkouts",
        headers={"Authorization": f"Bearer {RINGUP_KEY}"},
        json={
            "amount_cents": 2500,
            "order_id": params.arguments.get("order_id"),          # optional
            "return_to": "sip:your-bridge@your-telephony",         # optional: bring the caller back
            "success_message": "You are all set, your order will be ready shortly.",  # optional
        },
    )
    await params.result_callback(r.json())
```

The response is the Checkout Session:

```jsonc theme={null}
{
  "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: finish the call normally. Otherwise transfer.

## Step 2: Transfer on your transport

Have the agent speak one short bridge line ("One moment, connecting you to secure payment"), then
transfer the caller on your telephony. On a Twilio transport, redirect the call to TwiML that
dials the Ringup SIP URI, carrying the Checkout Session `id`:

```xml theme={null}
<Response>
  <Dial>
    <Sip>sip:cs_x9f2@transfer.ringup.dev?X-Session-Id=cs_x9f2</Sip>
  </Dial>
</Response>
```

On Daily PSTN/SIP, use the equivalent SIP transfer to the same URI. Because your transport
controls the telephony, the header path is reliable: the URI parameter reaches the Ringup line,
which matches it to the Checkout Session.

## Step 3: 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 1:

* **`return_to` set**: Ringup transfers the caller back to your transport with the outcome in SIP
  headers (`X-Payment-Status`, `X-Confirmation`), so your agent resumes and closes the call.
* **`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:

```jsonc theme={null}
{
  "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](/concepts/webhooks) for the full catalog, statuses, signature verification, and
idempotency.

## Validation status

Because your transport owns the telephony, the transfer is standard `<Dial><Sip>` (or the Daily
SIP equivalent) on your provider. 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.
