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

# LiveKit Agents

> Add card-on-file payments to a LiveKit agent by call transfer: create a Checkout Session and transfer the SIP participant to the Ringup SIP URI.

Add card-on-file payments to a LiveKit 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](/concepts/checkout-session) first.

With LiveKit you own the agent in code, so setup is a function call, not a platform tool.

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

## How the pieces map to LiveKit

| Ringup piece        | On LiveKit                                                                    |
| ------------------- | ----------------------------------------------------------------------------- |
| `create_checkout`   | An HTTP `POST` your agent code makes when the order is confirmed              |
| The transfer target | The `transfer_to` SIP URI the Checkout Session returns                        |
| The transfer        | `transfer_sip_participant`, with the Checkout Session id in the REFER headers |
| The result          | A `checkout.completed` webhook to your server                                 |

## Prerequisites

* A running LiveKit agent with SIP inbound configured (the caller arrives as a SIP participant).
* Your LiveKit server credentials.
* A Ringup account and [API key](/authentication). Test mode works out of the box on a shared sandbox. See
  [Testing](/concepts/testing).

## Step 1: Recognize the caller at the start

Call `identify` from your agent code at the greeting (a plain HTTP call, like the checkout call),
not just at payment. It needs only the caller's phone number, which is available the instant the
call connects. For a returning caller it returns their name and saved card, so your agent greets
them by name and skips re-asking for anything recognition already provides (for example, never
ask "what name for the order?" when `identify` returned it). This is separate from and happens
earlier than creating the Checkout Session and transferring for payment. See
[Recognize at the start of the call](/concepts/identity).

## Step 2: Create a Checkout Session in your agent

When your agent determines the order is confirmed, `POST` to Ringup's `create_checkout` endpoint
with your Ringup API key. There is no platform tool to register; this is a plain HTTP call from
your agent code.

```bash theme={null}
POST https://api.ringup.dev/v1/checkouts
Authorization: Bearer <YOUR_RINGUP_KEY>
Content-Type: application/json

{
  "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:your-agent@your-sip-host",       // 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):

```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 3: Transfer the SIP participant

Speak one bridge line, then transfer the caller's SIP participant to `transfer_to`, attaching the
Checkout Session `id` as a REFER header:

```python theme={null}
# after saying: "One moment, connecting you to secure payment."
await livekit_api.sip.transfer_sip_participant(
    room_name=room,
    participant_identity=caller_identity,
    transfer_to=checkout["transfer_to"],                 # sip:cs_x9f2@transfer.ringup.dev
    headers={"X-Session-Id": checkout["id"]},            # cs_x9f2
    play_dialtone=False,                                 # no dial tone; the bridge line covers the gap
)
```

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.

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

LiveKit's `transfer_sip_participant` accepts REFER headers, so the header path is supported.
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.

## Next steps

<CardGroup cols={2}>
  <Card title="Testing" icon="flask" href="/concepts/testing">
    The sandbox, the test card, and the fixtures to run a full payment.
  </Card>

  <Card title="Webhooks" icon="webhook" href="/concepts/webhooks">
    Reconcile every payment from one signed event on your server.
  </Card>
</CardGroup>
