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

# Update order

> Corrects an open order before it is charged: the final consumer's billing details and the locator. It does not charge, does not close the order and emits no events.

An order created at the kiosk and paid at the counter stays **open** until it is charged. In the
meantime the cashier may need to correct it: the customer asks for an invoice with their tax ID, or
the locator number printed on their ticket has to be entered. This endpoint corrects that data
**without touching the payment**.

<Note>
  **Open orders only.** An order that was charged, cancelled or already invoiced has already
  produced its consequences and is not corrected retroactively.
</Note>

## Update order vs. Confirm payment

They are two separate endpoints on purpose. One corrects the order, the other charges it, and
neither writes what belongs to the other.

|                     | **Update order** (this one)         | [**Confirm payment**](/en/api-reference/confirm-payment) |
| ------------------- | ----------------------------------- | -------------------------------------------------------- |
| Method              | `PUT /orders/{orderId}`             | `POST /orders/{orderId}/confirm-payment`                 |
| Purpose             | change order **data**               | record **the money**                                     |
| What it writes      | buyer and billing details, locator  | payment methods, payment state                           |
| Changes the status? | **no** — the order stays `OPEN`     | **yes** — becomes `COMPLETED` when settled               |
| Emits events?       | **no**                              | yes — [`order.completed`](/en/events/order-completed)    |
| Records declines?   | not applicable                      | **yes** — declined attempts are kept for your metrics    |
| Idempotency         | `idempotencyKey`                    | each tender's `transaction_id`                           |
| Concurrency         | `expectedRevision`                  | the charge is all-or-nothing against the total           |
| How many times?     | as many as needed, while it is open | once: settling closes the order                          |

**Payment methods are not corrected here.** If the body carries `payments.paymentMethods` or a
`status`, the response is `400`: the order status is derived from the payment and only
[Confirm payment](/en/api-reference/confirm-payment) writes it. We reject it instead of ignoring it
because a silently dropped `APPROVED` would be money you consider collected and we don't.

### The full flow

```
GET  /orders/{orderId}                     → revision: 1
PUT  /orders/{orderId}   expectedRevision 1 → revision: 2   (locator)
PUT  /orders/{orderId}   expectedRevision 2 → revision: 3   (billing details)
POST /orders/{orderId}/confirm-payment      → COMPLETED + order.completed
```

**No extra step is needed for the invoice to carry the corrected data.** When the payment settles,
Fire builds the `order.completed` event by reading the order at that moment, so it travels with your
latest correction.

## Authentication

<ParamField header="x-api-key" type="string" required>
  Your Fire API key with scope `orders:write`. The key **must be vendor-scoped** — keys without a
  `vendorId` are rejected with `403`.
</ParamField>

## Path params

<ParamField path="orderId" type="string" required>
  Any of the order's three public references:

  | reference           | what it is                                  |
  | ------------------- | ------------------------------------------- |
  | `orders.id`         | Fire's internal UUID                        |
  | `order_external`    | the id you assigned when creating the order |
  | `metadata.order_id` | copy of the external id inside the order    |

  It is the same set accepted by [Get order](/en/api-reference/get-order) and
  [Confirm payment](/en/api-reference/confirm-payment).
</ParamField>

## Body

Two control fields, always, plus **one or more blocks**. Whatever you don't send is left untouched.

<ParamField body="expectedRevision" type="integer" required>
  The order `revision` you read with [Get order](/en/api-reference/get-order). If the order changed
  since then —another register corrected it—, the response is `409 STALE_REVISION` and nothing is
  written. That way two registers never overwrite each other unnoticed.
</ParamField>

<ParamField body="idempotencyKey" type="string" required>
  A unique identifier **per correction** (up to 200 characters), generated by you. If the response
  doesn't reach you and you retry with the same key, you get `200 duplicate` and the correction is
  not applied twice.

  Without this key, a retry would collide with `expectedRevision` —which already moved— and you
  couldn't tell whether your correction went in or someone else wrote.
</ParamField>

### Locator and kiosk — `additionalInfo`

**Applied field by field:** send only what changes. If you correct the locator, the buzzer name and
the invoice email stay as they were.

<ParamField body="additionalInfo.orderCode" type="string">
  The **locator**: the number printed on the customer's ticket and called out at pickup.
</ParamField>

<ParamField body="additionalInfo.kiosk.buzzer_name" type="string">
  Name used to call the customer.
</ParamField>

<ParamField body="additionalInfo.kiosk.invoice_email" type="string">
  Email the invoice is sent to.
</ParamField>

<ParamField body="additionalInfo.kiosk.invoice_print" type="boolean">
  Whether the customer wants a printed invoice.
</ParamField>

### Final consumer — `client`

<Warning>
  **This block REPLACES the whole buyer.** It is not a patch: whatever you don't send ends up empty.
  Sending `{ "uid": "…", "name": "Juan" }` on an order that had a tax ID **erases the tax ID**.

  This is deliberate. Name, document and address are **one piece of data**: mixing a new name with
  an old document produces a wrongly issued invoice, and that can only be fixed by voiding and
  reissuing. Always send **the complete buyer**, not the difference.
</Warning>

<ParamField body="client.uid" type="string" required>
  Customer identifier. Required precisely because the block replaces: without it the order would be
  left without a customer. Use the one the order already has.
</ParamField>

<ParamField body="client.govIdType" type="string">
  Document type: `CEDULA`, `RUC`, `PASAPORTE` (Ecuador); `CC`, `NIT` (Colombia); `CPF`, `CNPJ`
  (Brazil); or `FINAL_CONSUMER`.
</ParamField>

<ParamField body="client.govIdNumber" type="string">
  Document number. Dots, dashes and spaces are accepted; Fire strips them. A filler of repeated
  digits (`9999999999999`, `222222222222`) is treated as final consumer.
</ParamField>

<ParamField body="client.name" type="string">
  Name or company name.
</ParamField>

<ParamField body="client.lastName" type="string">
  Last name.
</ParamField>

<ParamField body="client.email" type="string">
  Buyer's email.
</ParamField>

<ParamField body="client.billingInformation" type="object">
  **The invoice recipient, and it takes precedence.** Fire builds the receipt's buyer by reading
  `billingInformation` first (`govIdType`, `govIdNumber`, `name` —or `businessName` when `name` is
  absent—, `email`, `address`) and
  only then the `client` fields. It exists separately because the invoice can go to a company other
  than the person.

  **If you correct the document, put it here.** Kiosk orders carry this block as final consumer:
  correcting only `client.govIdNumber` and resending `billingInformation` unchanged leaves the
  receipt as final consumer. If you don't send it, it ends up empty and `client` is used.
</ParamField>

From this block Fire recomputes **the buyer printed on the receipt**. You don't need to send it
separately.

### Products — not yet

<Info>
  Correcting products and totals is **not enabled yet**. If the body carries `order` or `payments`,
  the response is `400`. It will be enabled once taxes and the total are also validated against the
  lines, as they are when the order is created.
</Info>

## Request

<RequestExample>
  ```http Locator theme={null}
  PUT https://api.fire.rest/api/v1/fire/external/orders/EC-K000-KIOSK-0001
  x-api-key: <your_api_key>
  Content-Type: application/json

  {
    "expectedRevision": 1,
    "idempotencyKey": "pos-loc-7f3a",
    "additionalInfo": {
      "orderCode": "82"
    }
  }
  ```

  ```http Billing details theme={null}
  PUT https://api.fire.rest/api/v1/fire/external/orders/EC-K000-KIOSK-0001
  x-api-key: <your_api_key>
  Content-Type: application/json

  {
    "expectedRevision": 2,
    "idempotencyKey": "pos-cli-9b21",
    "client": {
      "uid": "EnB3vmqHvrgarov6PFmfJY1bpPH2",
      "name": "Comercial Andina",
      "lastName": "SA",
      "email": "facturas@andina.ec",
      "govIdType": "RUC",
      "govIdNumber": "1790012345001",
      "billingInformation": {
        "govIdType": "RUC",
        "govIdNumber": "1790012345001",
        "businessName": "Comercial Andina SA",
        "email": "facturas@andina.ec",
        "address": "Av. 9 de Octubre 123"
      }
    }
  }
  ```

  ```http Both together theme={null}
  PUT https://api.fire.rest/api/v1/fire/external/orders/EC-K000-KIOSK-0001
  x-api-key: <your_api_key>
  Content-Type: application/json

  {
    "expectedRevision": 1,
    "idempotencyKey": "pos-both-c410",
    "additionalInfo": {
      "orderCode": "82",
      "kiosk": { "buzzer_name": "Mesa 4" }
    },
    "client": {
      "uid": "EnB3vmqHvrgarov6PFmfJY1bpPH2",
      "name": "Ana",
      "lastName": "Pérez",
      "govIdType": "CEDULA",
      "govIdNumber": "1712345678"
    }
  }
  ```
</RequestExample>

## What Fire does with what you send

| situation                                      | `outcome`   | written?                               | `revision`      |
| ---------------------------------------------- | ----------- | -------------------------------------- | --------------- |
| something changed                              | `applied`   | yes                                    | goes up by 1    |
| same `idempotencyKey` as a previous correction | `duplicate` | no — returns what the original applied | the current one |
| you sent exactly what the order already had    | `noop`      | no                                     | unchanged       |

**Keep the `revision` from the response:** it is the one to send in your next correction.

## When the order can't be corrected

Each case has its own code, because each calls for a different action.

| code                     | the order                            | what to do                                                                                                                                                               |
| ------------------------ | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `STALE_REVISION`         | changed since you read it            | read it again and retry with the new `revision` (it comes in `data.currentRevision`)                                                                                     |
| `ORDER_NOT_OPEN`         | is no longer open                    | check `data.orderStatus`: `COMPLETED` = already charged with the previous data (what follows is a fiscal correction); `CANCELLED` or `FORCE_CLOSED` = nothing to correct |
| `ORDER_ALREADY_INVOICED` | has an invoice issued or in progress | an invoice is not modified: it is voided and reissued                                                                                                                    |

All are `409` and **write nothing**. The check happens at the same instant as the write, so if a
payment lands while you are correcting, one waits for the other: they never overwrite each other.

## Idempotency and concurrency

**Retrying is safe.** If the response didn't reach you, resend the same body with the **same**
`idempotencyKey`:

| what you send                         | what it is                   | response             |
| ------------------------------------- | ---------------------------- | -------------------- |
| an `idempotencyKey` Fire already has  | a retry                      | `200 duplicate`      |
| a new key with the current `revision` | a new correction             | `200 applied`        |
| a new key with an old `revision`      | another register wrote first | `409 STALE_REVISION` |

Use a **new key for each different correction**. Reusing a key for another change returns
`duplicate` and the new change is not applied.

## Events

This endpoint **emits no events**. The correction stays on the order, and when it is charged, the
[`order.completed`](/en/events/order-completed) event carries the corrected data: locator, buyer
and billing details.

<Note>
  There is no `order.updated`, on purpose. Event delivery is not ordered: a correction notice
  arriving after `order.completed` would be a stale fact your system might act on by mistake.
</Note>

## Validations

All return `400` unless stated otherwise. Branch on the **`code`**, not on the text: the message may
be rewritten, the code is contract.

| rule                                                                                 | code                            |
| ------------------------------------------------------------------------------------ | ------------------------------- |
| `expectedRevision` is an integer greater than zero                                   | `400`                           |
| `idempotencyKey` present                                                             | `400`                           |
| at least one block (`client` or `additionalInfo`)                                    | `400`                           |
| `client.uid` present if `client` is sent                                             | `400`                           |
| no `order` nor `payments` (products not yet)                                         | `400`                           |
| no `payments.paymentMethods`, `status`, `paymentStatus`, `settlement`, `completedAt` | `400`                           |
| the body's `orderId`, if sent, matches the URL's                                     | `400`                           |
| the body's account, vendor and store, if sent, match the order                       | `400`                           |
| valid API key                                                                        | `401`                           |
| scope `orders:write` and vendor-scoped key                                           | `403`                           |
| the order exists within your vendor                                                  | `404`                           |
| the reference matches more than one order                                            | `409 AMBIGUOUS_ORDER_REFERENCE` |
| the order hasn't changed since you read it                                           | `409 STALE_REVISION`            |
| the order is open (neither charged nor cancelled)                                    | `409 ORDER_NOT_OPEN`            |
| the order has no invoice issued or in progress                                       | `409 ORDER_ALREADY_INVOICED`    |

## Responses

<ResponseExample>
  ```json 200 — applied theme={null}
  {
    "success": true,
    "data": {
      "orderId": "da670a2c-386d-4848-a51b-444738c3250b",
      "orderCode": "EC-K000-KIOSK-0001",
      "outcome": "applied",
      "revision": 2,
      "fields": ["kds"],
      "changedColumns": ["metadata"],
      "amendmentId": "24c523b3-a435-4cbb-b163-7a88c5b10d32",
      "status": "OPEN",
      "paymentStatus": "PENDING"
    }
  }
  ```

  ```json 200 — retry with the same key theme={null}
  {
    "success": true,
    "data": {
      "orderId": "da670a2c-386d-4848-a51b-444738c3250b",
      "orderCode": "EC-K000-KIOSK-0001",
      "outcome": "duplicate",
      "revision": 2,
      "fields": ["kds"],
      "changedColumns": [],
      "amendmentId": "24c523b3-a435-4cbb-b163-7a88c5b10d32",
      "status": "OPEN",
      "paymentStatus": "PENDING"
    }
  }
  ```

  ```json 200 — nothing to change theme={null}
  {
    "success": true,
    "data": {
      "orderId": "da670a2c-386d-4848-a51b-444738c3250b",
      "orderCode": "EC-K000-KIOSK-0001",
      "outcome": "noop",
      "revision": 2,
      "fields": [],
      "changedColumns": [],
      "amendmentId": null,
      "status": "OPEN",
      "paymentStatus": "PENDING"
    }
  }
  ```

  ```json 409 — another register corrected it first theme={null}
  {
    "success": false,
    "error": "CONFLICT",
    "code": "STALE_REVISION",
    "message": "The order changed since you read it — refetch and retry",
    "data": {
      "orderId": "da670a2c-386d-4848-a51b-444738c3250b",
      "orderCode": "EC-K000-KIOSK-0001",
      "expectedRevision": 1,
      "currentRevision": 2
    }
  }
  ```

  ```json 409 — the order is already closed theme={null}
  {
    "success": false,
    "error": "CONFLICT",
    "code": "ORDER_NOT_OPEN",
    "message": "Order is closed and can no longer be updated",
    "data": {
      "orderId": "da670a2c-386d-4848-a51b-444738c3250b",
      "orderCode": "EC-K000-KIOSK-0001",
      "orderStatus": "COMPLETED"
    }
  }
  ```

  ```json 409 — already invoiced theme={null}
  {
    "success": false,
    "error": "CONFLICT",
    "code": "ORDER_ALREADY_INVOICED",
    "message": "Order already has a fiscal document in flight or authorized and cannot be updated",
    "data": {
      "orderId": "da670a2c-386d-4848-a51b-444738c3250b",
      "orderCode": "EC-K000-KIOSK-0001",
      "documentStatus": "authorized"
    }
  }
  ```

  ```json 400 — products theme={null}
  {
    "success": false,
    "error": "VALIDATION_ERROR",
    "message": "Datos de entrada inválidos",
    "details": [
      {
        "code": "custom",
        "path": ["order"],
        "message": "Products and totals cannot be updated yet — only client (fiscal data) and additionalInfo (locator) are accepted"
      }
    ]
  }
  ```

  ```json 400 — billing block without uid theme={null}
  {
    "success": false,
    "error": "VALIDATION_ERROR",
    "message": "Datos de entrada inválidos",
    "details": [
      {
        "code": "invalid_type",
        "path": ["client", "uid"],
        "message": "client.uid is required — the client block replaces, it does not merge"
      }
    ]
  }
  ```
</ResponseExample>

## Related

<CardGroup cols={2}>
  <Card title="Get order" icon="receipt" href="/en/api-reference/get-order">
    Read the `revision` before correcting.
  </Card>

  <Card title="Confirm payment" icon="money-bill" href="/en/api-reference/confirm-payment">
    Charge the order once it is corrected.
  </Card>
</CardGroup>
