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

# Place Order

> Buy a listing. The one endpoint that spends money.

# Place Order

Places an order for a listing. This is the endpoint that spends real money, and it is designed around one assumption: **a reply can be lost after the order was placed.**

<Warning>
  **Two submissions with the same reference can mean two real purchases** unless your system deduplicates on it. `reference` is a search key for reconciliation, not a guarantee. Retail calls this endpoint **once** per order and never retries it.
</Warning>

## What happens when a call fails

Retail cannot tell "the request never arrived" from "it arrived and the reply was lost". So **any** failure here, whether a timeout, a dropped connection or a non-2xx status, is treated the same way: the order becomes *unknown*, and Retail finds out what happened by calling [Search orders](/vendor-api/search-orders) with the reference. It only submits again if that search proves no order exists.

That makes two things your job:

1. **Make a replay safe.** Before placing anything, check whether an order already exists for this `reference`. If it does, return that order with `200`. Never place a second one.
2. **Do not retry internally.** If your upstream call fails or times out, return `502` and let Retail search. An internal retry is exactly the double purchase this design exists to prevent.

## Body

<ParamField body="vendorKey" type="string" required>
  Your supplier identifier.
</ParamField>

<ParamField body="reference" type="string" required>
  Retail's reference for this order. Store it on your order so [Search orders](/vendor-api/search-orders) can find it. It is the same value the [quote](/vendor-api/quote) carried.
</ParamField>

<ParamField body="vendorListingId" type="string" required>
  The listing being bought.
</ParamField>

<ParamField body="quantity" type="integer" required>
  Tickets to buy. One of the listing's `splits`.
</ParamField>

<ParamField body="unitCost" type="number" required>
  The per-ticket wholesale price Retail expects to pay, from the listing. Refuse the order if your price is now higher.
</ParamField>

<ParamField body="currency" type="string" required>
  ISO 4217 code for `unitCost`.
</ParamField>

<ParamField body="taxSignature" type="string" required>
  The signature from the quote this order was priced from. Refuse the order if it does not match: it was priced against a different quote, or none.
</ParamField>

<ParamField body="retailTax" type="number">
  The retail sales tax from that same quote, for your order record.
</ParamField>

<ParamField body="listingSignature" type="string">
  The price-lock token from the listing, if you issued one.
</ParamField>

<ParamField body="format" type="string" default="eticket">
  The listing's delivery format, so you can address the order correctly. The same values as on [Listings](/vendor-api/listings). Taken from the listing at the moment the buyer paid.
</ParamField>

<ParamField body="shippingAddress" type="object">
  Where to courier a **physical** ticket. Present only when `format` is `physical`.

  <Expandable title="Shipping address">
    <ParamField body="recipientName" type="string" required>One full name. The person who can receive the parcel.</ParamField>
    <ParamField body="phone" type="string" required>Contact phone for the carrier.</ParamField>
    <ParamField body="line1" type="string" required>Street address.</ParamField>
    <ParamField body="line2" type="string">Unit or apartment.</ParamField>
    <ParamField body="city" type="string" required>City.</ParamField>
    <ParamField body="state" type="string" required>State or region.</ParamField>
    <ParamField body="postalCode" type="string" required>Postal code.</ParamField>
    <ParamField body="countryCode" type="string" required>ISO 3166-1 alpha-2, for example `US`. A code, not a country name.</ParamField>
  </Expandable>
</ParamField>

<ParamField body="buyerEmail" type="string">
  The buyer's own email, for a seat **transferred into their account**. Present only when `format` is `mobile_transfer` or `flash_seats`. The box office moves the seat into the account that owns this mailbox, so it must be the buyer's real address.
</ParamField>

### Refuse what you cannot deliver

<Warning>
  * **`format: "physical"` without `shippingAddress`: refuse with `400`.** Many systems treat a missing recipient as "ship to the buying account", which is right for an e-ticket and wrong for a parcel: it ships to Gomry Retail, silently, and the buyer never gets a ticket.
  * **`mobile_transfer` or `flash_seats` without `buyerEmail`: refuse with `400`.** Never fall back to any address of your own or of Gomry's.
</Warning>

`shippingAddress` and `buyerEmail` are the only buyer data that ever reaches your integration. Pass them to your system on this order and nowhere else. Do not store them, and keep them out of logs and error reports.

## Response

<ResponseField name="vendorOrderId" type="string" required>
  Your id for the order. Retail uses it on [Fulfilment](/vendor-api/fulfilment) and [Credentials](/vendor-api/credentials).
</ResponseField>

<ResponseField name="state" type="string" required>
  The order's state in your system right now:

  | Value | Meaning |
  | - | - |
  | `pending` | Placed, waiting for the seller to accept. The normal answer when a human accepts. |
  | `accepted` | The seller accepted. Gomry Retail now owes you for it. |
  | `rejected` | The seller refused. No ticket will be delivered. |
  | `cancelled` | Cancelled after placement. |
  | `unknown` | You could not determine the state. Say so rather than guess. |
</ResponseField>

<ResponseField name="totalCost" type="number" required>
  What Gomry Retail will pay for the whole order.
</ResponseField>

<ResponseField name="currency" type="string" required>
  ISO 4217 code for `totalCost`.
</ResponseField>

<ResponseField name="raw" type="any">
  Your system's order payload, stored as evidence. Must not echo the shipping address or buyer email.
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://integration.example.com/api/vendor/v1/orders" \
    -H "X-API-Key: $GOMRY_RETAIL_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "vendorKey": "acme",
      "reference": "8b1f3c5e2a9d4f7081c6e2b4a0d9f3e1",
      "vendorListingId": "tg_88412093",
      "quantity": 2,
      "unitCost": 96.0,
      "currency": "USD",
      "taxSignature": "lock_7d2e91c4",
      "retailTax": 17.76,
      "listingSignature": "sig_5f1c2a",
      "format": "eticket"
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "vendorOrderId": "ord_551204",
    "state": "pending",
    "totalCost": 192.0,
    "currency": "USD"
  }
  ```

  ```json 400 theme={null}
  {
    "error": {
      "code": "validation_error",
      "message": "taxSignature does not match the quote for this reference: re-quote before ordering"
    }
  }
  ```

  ```json 502 theme={null}
  {
    "error": {
      "code": "integration_error",
      "message": "Upstream order call timed out after 15000ms"
    }
  }
  ```
</ResponseExample>
