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

> Open an ACP checkout session for one event's tickets

# Create Checkout Session

Opens a checkout session and returns a priced cart.

<Note>
  Requires `checkout: write`, an allowlisted partner key, and a **signed request**. See [Agentic Commerce](/commerce/introduction#access).
</Note>

## Headers

<ParamField header="API-Version" type="string">
  `2025-09-12`. Always send it. A different value is refused with `unsupported_api_version`; an absent one is treated as the current version.
</ParamField>

<ParamField header="Signature" type="string" required>
  Base64 HMAC-SHA256 over `{timestamp}.{raw_body}`.
</ParamField>

<ParamField header="Timestamp" type="string" required>
  RFC 3339, within 5 minutes of our clock.
</ParamField>

<ParamField header="Idempotency-Key" type="string">
  Recommended. Echoed back on the response.
</ParamField>

## Body

<ParamField body="items" type="array" required>
  1–20 lines.

  <Expandable title="Item">
    <ParamField body="id" type="string" required>
      A **ticket type id** from `ticket_types[].id` on the [catalog detail endpoint](/api-reference/catalog/get-catalog-event). Not an event id.
    </ParamField>

    <ParamField body="quantity" type="integer" required>
      1–50, subject to the tier's own `max_per_order`.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="buyer" type="object">
  <Expandable title="Buyer">
    <ParamField body="name" type="string" required>
      **Required by Gomry, though optional in the ACP spec.** A ticket is admission for a named person: it prints the attendee's name and the door list is read by a human. A nameless ticket is technically valid and useless at the door.
    </ParamField>

    <ParamField body="email" type="string" required>Where the ticket is delivered.</ParamField>
    <ParamField body="phone_number" type="string">Optional.</ParamField>
  </Expandable>

  The whole object may be omitted at create and supplied later with [update](/api-reference/checkout-sessions/update-checkout-session) — but the session will not reach `ready_for_payment` without it.
</ParamField>

<ParamField body="attendee_answers" type="array">
  Up to 50 answers to the event's registration questions. A **Gomry extension** to ACP: additive and optional, so a conforming client that never sends it still works — but an event with required questions reports them as blocking messages until they arrive.

  <Expandable title="Answer">
    <ParamField body="question_id" type="string" required>From `registration_questions[].id`.</ParamField>

    <ParamField body="value" type="string" required>
      The answer as a string, whatever the question's type. For `dropdown` and `multipleChoice`, must be one of the published `options`.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="fulfillment_address" type="object">
  Accepted for spec conformance. ACP is shaped for physical goods; a ticket does not ship. Stored for the record, never used to fulfil — except for posted tickets, where it is required.
</ParamField>

<Warning>
  **Unknown fields are rejected with `400 invalid_body`,** not ignored. A body carrying something like `card_number` must fail loudly rather than be quietly accepted into our logs. No field on this API ever carries a card number.
</Warning>

## Response

`201` with a [checkout session](/api-reference/checkout-sessions/get-checkout-session#response).

Read `status` first:

| Status                  | What to do                                                                         |
| ----------------------- | ---------------------------------------------------------------------------------- |
| `not_ready_for_payment` | Read `messages[]`, fix what is missing, then update the session.                   |
| `ready_for_payment`     | Proceed to [complete](/api-reference/checkout-sessions/complete-checkout-session). |

## One session, one event

All items must belong to the same event and the same currency. Mixing them returns `multiple_events` or `currency_mismatch`. Sell two events as two sessions.

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://www.gomry.com/api/v1/checkout_sessions" \
    -H "X-API-KEY: your_api_key" \
    -H "Content-Type: application/json" \
    -H "API-Version: 2025-09-12" \
    -H "Timestamp: 2026-09-10T12:00:00Z" \
    -H "Signature: base64-hmac-sha256" \
    -H "Idempotency-Key: 8f14e45f-ea0c-4b9f-9c2a-1d3e5f7a9b0c" \
    -d '{
      "items": [{ "id": "TktAbCdEfGhIjKlMnOpQ", "quantity": 2 }],
      "buyer": { "name": "Ada Lovelace", "email": "ada@example.com" },
      "attendee_answers": [
        { "question_id": "QstAbCdEfGhIjKlMnOpQ", "value": "Vegetarian" }
      ]
    }'
  ```

  ```javascript JavaScript theme={null}
  import crypto from "crypto";

  const body = JSON.stringify({
    items: [{ id: "TktAbCdEfGhIjKlMnOpQ", quantity: 2 }],
    buyer: { name: "Ada Lovelace", email: "ada@example.com" },
  });
  const timestamp = new Date().toISOString();
  const signature = crypto
    .createHmac("sha256", process.env.GOMRY_ACP_SIGNING_SECRET)
    .update(`${timestamp}.${body}`)
    .digest("base64");

  const res = await fetch("https://www.gomry.com/api/v1/checkout_sessions", {
    method: "POST",
    headers: {
      "X-API-KEY": process.env.GOMRY_API_KEY,
      "Content-Type": "application/json",
      "API-Version": "2025-09-12",
      Timestamp: timestamp,
      Signature: signature,
      "Idempotency-Key": crypto.randomUUID(),
    },
    body, // the exact string that was signed
  });
  const session = await res.json();
  ```
</RequestExample>

<ResponseExample>
  ```json 201 theme={null}
  {
    "id": "acp_sess_AbCdEfGhIjKlMnOpQrSt",
    "buyer": { "name": "Ada Lovelace", "email": "ada@example.com" },
    "payment_provider": {
      "provider": "stripe",
      "supported_payment_methods": ["card"]
    },
    "status": "ready_for_payment",
    "currency": "usd",
    "line_items": [
      {
        "id": "li_1",
        "item": { "id": "TktAbCdEfGhIjKlMnOpQ", "quantity": 2 },
        "base_amount": 15800,
        "discount": 0,
        "subtotal": 15800,
        "tax": 0,
        "total": 16790
      }
    ],
    "fulfillment_address": null,
    "fulfillment_option_id": "digital",
    "fulfillment_options": [
      {
        "type": "digital",
        "id": "digital",
        "title": "Digital ticket",
        "subtitle": "Delivered by email",
        "subtotal": 0,
        "tax": 0,
        "total": 0
      }
    ],
    "totals": [
      { "type": "items_base_amount", "display_text": "Tickets", "amount": 15800 },
      { "type": "fee", "display_text": "Service fee", "amount": 990 },
      { "type": "total", "display_text": "Total", "amount": 16790 }
    ],
    "messages": [],
    "links": [
      { "type": "terms_of_use", "url": "https://www.gomry.com/terms" },
      { "type": "privacy_policy", "url": "https://www.gomry.com/privacy" }
    ]
  }
  ```

  ```json 201 blocked theme={null}
  {
    "id": "acp_sess_AbCdEfGhIjKlMnOpQrSt",
    "status": "not_ready_for_payment",
    "messages": [
      {
        "type": "error",
        "code": "missing",
        "param": "buyer.name",
        "content_type": "plain",
        "content": "A buyer name is required to issue a ticket."
      }
    ]
  }
  ```
</ResponseExample>
