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

# Create Payment Link

> A hosted page the buyer pays on, for agents that cannot mint a payment token

# Create Payment Link

<Tip>
  **Usually you don't need this call.** Every payable session already carries `continue_url`, the standard checkout handoff: send that to the buyer and it opens this same payment page with no credential. Use this endpoint when you want the payment page's own URL and expiry.
</Tip>

Returns a hosted payment page for a priced checkout session. Send its URL to the buyer: they pay with a card, Apple Pay or Google Pay, with **no Gomry account and no sign-in code**, and the tickets are issued when they pay.

Use it when your agent cannot mint a Stripe Shared Payment Token for [Complete Checkout Session](/api-reference/checkout-sessions/complete-checkout-session). It is a Gomry extension to the Agentic Commerce Protocol: a strict ACP client never sees it unless it asks for it.

<Note>
  Requires `checkout: write`, with the same credentials as every other checkout endpoint: an organization API key, a self-registered agent token, a Personal Agent Protocol session, or a signed-in person's OAuth token granted `checkout:write`, or none at all for a session created without one. Only the credential that created the session can make its link.
</Note>

The session must be `ready_for_payment` with a total above zero. A free order needs no payment: call `complete` with no `payment_data`.

## Path Parameters

<ParamField path="checkout_session_id" type="string" required>
  The session to pay.
</ParamField>

## Body

The body is optional.

<ParamField body="buyer" type="object">
  Fills or replaces the session's buyer. The payment page is prefilled with this email, and the receipt and tickets are sent to it. Required here if the session has no buyer yet (a signed-in person's token defaults to their verified email).
</ParamField>

## Response

`200` with the [session](/api-reference/checkout-sessions/get-checkout-session#response), carrying `payment_link`:

<ResponseField name="payment_link" type="object">
  <Expandable title="Payment link">
    <ResponseField name="status" type="string">
      `open` (payable now), `paid` (tickets issued), `refunded` (paid, but the order could no longer be honoured), or `expired`.
    </ResponseField>

    <ResponseField name="url" type="string">
      The page to send the buyer. Present only while `status` is `open`.
    </ResponseField>

    <ResponseField name="amount" type="integer">
      Minor units: exactly the session total, fees included.
    </ResponseField>

    <ResponseField name="currency" type="string">
      The session currency.
    </ResponseField>

    <ResponseField name="expires_at" type="string">
      When the link stops accepting payment. Links are open for one hour.
    </ResponseField>

    <ResponseField name="refund_reason" type="string">
      Why a payment was refunded. Present only when `status` is `refunded`.
    </ResponseField>
  </Expandable>
</ResponseField>

```json theme={null}
"payment_link": {
  "status": "open",
  "url": "https://checkout.stripe.com/c/pay/cs_live_...",
  "amount": 1465,
  "currency": "usd",
  "expires_at": "2026-10-14T18:00:00.000Z"
}
```

## After the buyer pays

Poll [Get Checkout Session](/api-reference/checkout-sessions/get-checkout-session) (every few seconds while the buyer is paying is fine):

| What you read | What happened |
| - | - |
| `status: "completed"` and an `order` | Paid and issued. Show the buyer `order.permalink_url`; the tickets are emailed too. |
| `payment_link.status: "refunded"` | The buyer paid, but the cart could not be sold (sold out, or the price moved while they paid). The payment was refunded in full and nothing was issued. `refund_reason` says why. |
| `payment_link.status: "expired"` | Nobody paid in time. Ask for a new link. |

Nothing is held while the buyer is on the page, so price, availability and the organizer's required answers are checked again when they pay.

## Calling it again

Asking again while the link is open and the total is unchanged returns **the same link**. If the cart changed, you get a new link and the old one stops accepting payment. Updating the session to a different total also closes an open link, so a buyer can never pay a stale amount.

Canceling the session closes its link. A payment that lands at the same instant is refunded.

## Failures

| Code | Status | What to do |
| - | - | - |
| `invalid_body` | `400` | No buyer email (`$.buyer.email`). Send `buyer`. |
| `invalid_session_state` | `409` | Already paid, canceled, expired, free, or not ready (a required answer or address is missing). Read the session. |
| `amount_mismatch` | `409` | The total changed since your last read. Re-read the session, confirm it with your buyer, then ask again. |
| `sold_out` | `409` | Inventory went while the session was open. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.