---
search:
  tags:
    - orders
    - POST
seo:
  description: >-
    Public for general admission (no auth, strict per-IP rate limit); orders
    with numbered… Reference for the POST /orders endpoint in the FreeTicket
    Public API.
sidebar:
  label: Create a B2C order and get the payment link (Mercado Pago)
  badge: POST
title: Create a B2C order and get the payment link (Mercado Pago)
type: openapi-operation
---
Public for general admission (no auth, strict per-IP rate limit); orders with numbered seats REQUIRE a buyer session. Creates a PENDING sale + stock reservation and returns the Mercado Pago `checkoutUrl` to send the buyer to. The reservation holds the inventory for the payment window configured for that date (30 minutes unless the organizer sets it shorter or longer; never less than 10). The response includes `expiresAt`: the EXACT instant (ISO 8601, UTC) at which that reservation expires, the same one stored server-side. This is the value that decides whether it still makes sense to send the buyer to pay: past that instant the inventory has already returned to stock —and, for numbered seating, the seat is already on someone else's map— so the `checkoutUrl` may still open and the payment may arrive late. Do not recompute it by adding minutes on the client: use the returned value. `GET /orders/{id}` repeats it while the order is still `pending` (and returns null once it no longer is), so a countdown can resume after a reload instead of restarting. The agent NEVER processes the payment. Not available for members-only tickets (members_only); PUBLISHED events with sales open, all from a single organizer. Inventory is protected by a Serializable transaction. NUMBERED ticket types (seats.io): `items[].seats` is required, with one EXACT label per unit (taken from `availableSeats` in GET /events/&#123;slug&#125;/availability) — the seat is held in seats.io with the same TTL as the stock reservation, and is released automatically if the order expires unpaid. An order with `items[].seats` additionally requires a BUYER SESSION: the `ft-customer` cookie or the `session_token` from the code login (POST /api/v1/customer/auth/request + POST /api/v1/customer/auth/verify) in the `X-Customer-Session` header; without it the response is 401 `CUSTOMER_SESSION_REQUIRED`. A seats.io hold can only be released by the sale that took it, so a seat held anonymously is inventory nobody can reclaim. The order is linked to that account: it shows up in GET /api/v1/customer/orders and its tickets in /api/v1/customer/tickets. General admission (no `seats`) remains anonymous and is not linked to any account. Dates with an active virtual queue: 409 `QUEUE_REQUIRED` (with `details.waitingRoomUrl`) — the waiting room is the only way in; the gate runs BEFORE the seats.io retrieve/hold. Check the status with `GET /orders/{id}`.

409 errors (`error.code`): `MEMBERS_ONLY` (members-only ticket), `NOT_AVAILABLE` (event or date not published, or date already past), `EXTERNAL_SALE` (the event sells elsewhere), `SALE_CLOSED` (outside the sales window), `QUEUE_REQUIRED` (the date is sold through a waiting room: `details.waitingRoomUrl` is the ABSOLUTE URL to send the buyer to — it points to the host where THAT date is sold, which may be the portal (`/eventos/{slug}/fila`) or the organizer's or artist's own subdomain (`/events/{slug}/fila`); use it as-is, do not rebuild it), `MULTIPLE_ORGS` (tickets from multiple organizers), `MIXED_CURRENCY` (mixed currencies), `SEAT_NOT_AVAILABLE` (seat already sold or held), `ADDON_SOLD_OUT` (a product bundled with the ticket, e.g. a drink combo, sold out: the order is not created). Products bundled with a ticket travel with it automatically —they are not requested in `items`— and their price is already included in `total`.

`POST /orders`
