---
title: "Record a scan"
method: POST
path: "/stations/scan"
tags: ["stations"]
---

# Record a scan

`POST /stations/scan`

## Request

- Base URL: `https://0.0.0.0:3000/api/v1`
- URL: `https://0.0.0.0:3000/api/v1/stations/scan`
- Auth: API key in header `x-api-key`

A worker scanned a parcel. Every order under that tracking number still sitting at ASSIGNED moves to IN_PRODUCTION together, because the floor works a box, not a line.

This is the code the station screen itself runs, so the gate, the status move and the seller's webhook are the same whichever door you come through.

A parcel of more than 20 orders answers with `meta.requires_confirmation` and moves nothing. Send it again with `confirmed: true` once a human has agreed — and if you are replaying a queue, carry the answer the operator gave at the time rather than deciding it now.

`Idempotency-Key` is REQUIRED. This endpoint is built for an unattended replayer, and a retry must not scan twice.

## Headers

- `Idempotency-Key` string, required

## Request body

- object
  - `mode` string, required: `tracking` matches a tracking number; `order` names one order id and widens to its parcel.
  - `value` string, required: The scanned code. A tracking number is at least 10 characters; an order id is digits.
  - `confirmed` boolean: `true` to go past the 20-order confirmation gate.
  - `occurred_at` string: When the worker scanned, if that is not now — an offline queue replaying. ISO-8601, no more than 5 minutes ahead of our clock and no more than 30 days old. It reaches the seller as `occurred_at` and is recorded beside our own timestamp, never instead of it.

## Response `200`

The parcel as it now stands, with every order in it. `meta.requires_confirmation` is null unless the gate stopped the scan.

## Other responses

- `401`: No key, or a key that is unknown, revoked or expired. One message for all four — telling you which would confirm to a guesser that a key exists.
- `403`: The key is valid but its owner may not do this.
- `404`: No such record — including records that exist but belong to someone else. The API never confirms another seller's ids.
- `409`: The request contradicts the current state of the record.
- `422`: The body or query is malformed. Field-level detail arrives in `error.fields`.
- `429`: Over the per-key limit. `Retry-After` says how many seconds to wait.
- `500`: Our fault. `error.request_id` matches the X-Request-Id header — quote it to support.

---

[API](https://skmtc.dev/opcreative/apis/opcreative-api.md) · [All operations](https://skmtc.dev/opcreative/apis/opcreative-api/llms.txt) · [OpenAPI document](https://skmtc.dev/opcreative/apis/opcreative-api/revisions/105bb30aa87e?raw)
