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

# Custody history

> Follow one order's boxes, stock movements and delivery, reception and closure records, and read the same history from the API.

The custody detail tells the whole story of one order's delivery: which boxes were reserved, who picked and dispatched them, who confirmed the handoff, what the destination accepted or disputed, and who closed the order. Use it to follow a delivery day to day and to answer audit questions or disputes later.

## Who can see it

* Permission `fulfillment.read` (**View fulfillment**).
* The order's location in your **Location access**.

Without the permission the screen says "You cannot view deliveries. Ask your clinic's administrator for the permission." An order outside your locations, or one that has no custody record yet, answers "This delivery does not exist or you cannot see it."

## Where

* In **Deliveries**, open **To close** or **Delivery problems** and select the order number.
* On the order's page, **Follow the delivery** (shown to members with any custody permission). See [Create and track orders](/docs/en/orders/create-and-track).
* After confirming a reception, the link under the confirmation message.
* On the pick screen of an order that is no longer being prepared, the **Tracking order #1042** link.
* Direct path: `console.muveya.com/fulfillment/:orderId`.

## What the screen shows

The screen is titled **Tracking order #1042**, with **Back to deliveries** to return to the queue.

### Facts and next step

| Fact | What it shows |
| - | - |
| **Status** | The custody status, for example **Dispatched**. See the [status table](/docs/en/deliveries/overview#the-custody-lifecycle). |
| **Clinic** | The order's location. |
| **Deliver to** | The destination warehouse. |

Below the facts, the screen offers the next step when it is yours to take:

| Status | Link or panel | Shown to members with |
| - | - | - |
| **Stock reserved**, **Being prepared** | **Prepare the order** | `fulfillment.pick` |
| **Dispatched** | **Confirm the delivery** | `delivery.confirm` |
| **Delivered** | **Receive the order** | `receipt.confirm` |
| **Received**, **Partially received** | "Everything was received or returned. Close the order to finish it." with **Close order** | `fulfillment.close` |
| **Delivery problem** | "Resolution is pending with the clinic's owner. Nothing more can be done here for now." | Everyone who can see the page |

For closing, see [Close the order](/docs/en/deliveries/delivery-and-receipt#close-the-order).

### Boxes

**Boxes** lists every box assigned to the order. On a phone, each box is shown as a labeled card.

| Column | What it shows |
| - | - |
| **Box code** | The printed label code. |
| **Box state** | The box's state right now (see below). |
| **Supply** | The supply name and SKU. |
| **Quantity** | The quantity the box carries for this order. |
| **Lot** | The lot number, when the box has one. |
| **Expires** | The expiry date, when the box has one. |

| Box state | Meaning in custody |
| - | - |
| **Active** | Reserved or picked and still in the source warehouse, or accepted at the destination, or returned without quarantine. |
| **In transit** | Dispatched and not yet received or returned. |
| **Quarantined** | Returned by a dispute with quarantine, or quarantined for another reason (for example a [lot recall](/docs/en/inventory/lot-recall)). |
| **Expired**, **Used up**, **Disposed** | The box changed later, outside this delivery. |

When a box was separated at pick time, the list shows the new container, not the original box. If no box is assigned, the screen says "No boxes are assigned to this order."

### History

**History** lists what happened, one entry per event, each with the person and the date and time in your browser's format. People are named by their team name; the signed-in person reads as **You**, actions muveya took by itself as **Muveya (automatic)**, and people who left the team as **Former team member**.

**Stock movements** come first, oldest first. Each reads "movement · box code · quantity change", for example "Dispatched · BX-000123 · -10". Movements that do not change the quantity on hand omit the number, for example "Picked · BX-000123".

| Movement | Code | When it appears | Quantity change |
| - | - | - | - |
| **Reserved** | `reserve` | The automatic reservation, or a reservation moved to a separated container. | None (the box's reserved quantity grows) |
| **Released** | `release` | A reservation moved off the original box during a separation. | None |
| **Separated out** | `split_out` | The order's quantity left the original box. | Negative |
| **Separated in** | `split_in` | The order's quantity entered the new container. | Positive |
| **Picked** | `pick` | A box was scanned for the order. | None |
| **Dispatched** | `dispatch` | The box left the source warehouse. | Negative |
| **Received** | `receive` | The destination accepted the box. | Positive, at the destination |
| **Returned** | `return` | The destination disputed the box and it went back to the origin. | Positive, at the origin |

**Custody records** follow the movements:

| Record | How it reads |
| - | - |
| Delivery | "Delivered by Ana Torres", or "Delivery problem reported by Ana Torres: It arrived damaged" followed by the details that were typed. |
| Reception | "Received by Luis Prado", then "Accepted: BX-000123, BX-000124" and, in red, one line per disputed box such as "Disputed BX-000125: Damaged". |
| Closure | "Closed by Luis Prado". |

If nothing has happened yet, the section says "Nothing has happened yet."

<Note>
  Movements made on the original box of a separation show **Box no longer in the ledger** in place of its code, because the **Boxes** list only names the new container. Open the original box from [Boxes](/docs/en/inventory/boxes) to see its own history.
</Note>

## Use it for audits and disputes

| Question | Where to look |
| - | - |
| Which boxes, lots and expiry dates went to this destination? | **Boxes**: **Box code**, **Lot**, **Expires**. |
| Who picked each box, and when? | **Picked** movements. |
| Who dispatched, and when did the boxes leave? | **Dispatched** movements. |
| Who confirmed the handoff, and was there a problem? | The delivery record. |
| What did the destination accept or dispute, and why? | The reception record, then the **Returned** movements. |
| Where is a disputed box now? | **Box state** (**Quarantined** or **Active**), then the box's page in [Boxes](/docs/en/inventory/boxes). |
| Who closed the order? | The closure record. |

A typical dispute review:

<Steps>
  <Step title="Confirm the chain of people">
    Check that the person who picked, the person who dispatched and the person who confirmed the delivery are the ones you expect on the origin side, and that the reception was made by the destination side.
  </Step>

  <Step title="Compare delivered and received">
    Every box in the delivery must appear once in the reception, as accepted or disputed.
  </Step>

  <Step title="Follow each disputed box">
    Find its **Returned** movement and its current **Box state**. If it came back in quarantine, decide what to do with it from its box page.
  </Step>

  <Step title="Check the lot if needed">
    If the problem may affect a whole lot, continue in [Lot recall](/docs/en/inventory/lot-recall).
  </Step>
</Steps>

### What the screen does not show

* The **Evidence of the problem** text typed at reception. It is stored but not shown on the console or returned by the API today.
* The per-box notes of a dispute and the quarantine choice. The API returns both.
* The carrier reference typed at dispatch. `GET /v1/fulfillments` returns it as `carrierRef`.
* The audit log. There is no audit log screen on the console today.
* An export. To keep custody histories outside muveya, read them from the API.

The records are append-only: nothing in this history can be edited or deleted. A disputed box is corrected by a new **Returned** movement, never by changing the **Dispatched** one.

## Read it from the API

The operation `GET /v1/fulfillments/{orderId}` returns the same custody history for integrations and reporting.

* **Authentication:** an API key (`mvy_test_...`) in the `Authorization: Bearer` header. API keys are not created from the console today; see [Authentication](/docs/en/api-reference/authentication).
* **Scope:** `fulfillment:read`. See [Scopes](/docs/en/api-reference/scopes).
* **Reach:** the key's whole dental clinic account. There is no location filter on a key.
* **Read-only:** custody steps (pick, dispatch, delivery, reception, close) cannot be performed through the API or MCP.

<CodeGroup>
  ```bash curl theme={null}
  curl https://api.muveya.com/v1/fulfillments/66f0c1a2b3c4d5e6f7a8b9c0 \
    -H "Authorization: Bearer $MUVEYA_API_KEY"
  ```
</CodeGroup>

### Response

<ResponseField name="orderId" type="string" required>
  The order id.
</ResponseField>

<ResponseField name="status" type="string" required>
  The custody status: `allocating`, `allocated`, `picking`, `dispatched`, `delivered`, `exception`, `received`, `partially_fulfilled` or `closed`.
</ResponseField>

<ResponseField name="movements" type="object[]" required>
  The order's stock movements, oldest first. Each has `movementId`, `type`, `boxId`, `catalogItemId`, `quantityDelta`, `actorId`, `occurredAt` and `recordedAt`, and when they apply `reservedDelta`, `fromWarehouseId`, `toWarehouseId`, `reasonCode`, `secondActorId`, `idempotencyKey` and `correlationId`.
</ResponseField>

<ResponseField name="delivery" type="object">
  Present once the delivery was confirmed: `actorUserId`, `deliveredAt`, `boxIds` and, for a delivery problem, `exception` with `reason` and optional `note`.
</ResponseField>

<ResponseField name="receipt" type="object">
  Present once the reception was confirmed: `actorUserId`, `receivedAt`, `acceptedBoxIds` and `disputed`, a list of `boxId`, `reason`, `quarantine` and optional `note`.
</ResponseField>

<ResponseField name="closure" type="object">
  Present once the order was closed: `actorUserId` and `closedAt`.
</ResponseField>

```json Example response theme={null}
{
  "orderId": "66f0c1a2b3c4d5e6f7a8b9c0",
  "status": "partially_fulfilled",
  "movements": [
    {
      "movementId": "66f0c3000000000000000001",
      "type": "reserve",
      "boxId": "66e9a1000000000000000125",
      "catalogItemId": "66e1b2000000000000000045",
      "quantityDelta": 0,
      "reservedDelta": 10,
      "fromWarehouseId": "66e0f0000000000000000007",
      "actorId": "system:fulfillment",
      "occurredAt": "2026-09-14T13:02:11.000Z",
      "recordedAt": "2026-09-14T13:02:11.000Z"
    },
    {
      "movementId": "66f0c3000000000000000002",
      "type": "dispatch",
      "boxId": "66e9a1000000000000000125",
      "catalogItemId": "66e1b2000000000000000045",
      "quantityDelta": -10,
      "reservedDelta": -10,
      "fromWarehouseId": "66e0f0000000000000000007",
      "actorId": "66d7e4000000000000000031",
      "occurredAt": "2026-09-14T15:40:02.000Z",
      "recordedAt": "2026-09-14T15:40:02.000Z"
    },
    {
      "movementId": "66f0c3000000000000000003",
      "type": "return",
      "boxId": "66e9a1000000000000000125",
      "catalogItemId": "66e1b2000000000000000045",
      "quantityDelta": 10,
      "reservedDelta": 0,
      "reasonCode": "damaged",
      "actorId": "66d7e4000000000000000058",
      "occurredAt": "2026-09-15T09:12:44.000Z",
      "recordedAt": "2026-09-15T09:12:44.000Z"
    }
  ],
  "delivery": {
    "actorUserId": "66d7e4000000000000000031",
    "deliveredAt": "2026-09-15T08:55:10.000Z",
    "boxIds": ["66e9a1000000000000000125"]
  },
  "receipt": {
    "actorUserId": "66d7e4000000000000000058",
    "receivedAt": "2026-09-15T09:12:44.000Z",
    "acceptedBoxIds": [],
    "disputed": [
      {
        "boxId": "66e9a1000000000000000125",
        "reason": "damaged",
        "quarantine": true,
        "note": "Seal broken on arrival"
      }
    ]
  }
}
```

How the API differs from the screen:

* It returns ids, not names or box codes. Read a box's code and state with `GET /v1/inventory/boxes/{boxId}` (scope `inventory:read`), and a supply with `GET /v1/catalog/items/{itemId}` (scope `catalog:read`).
* `actorId` and `actorUserId` are member ids. Actions muveya took by itself carry `system:fulfillment`.
* The status `allocating` can appear briefly while the reservation runs; the console shows it as **Reserving stock**.
* It answers `404` when the order does not exist, belongs to another dental clinic account, or has no custody record yet (an approved order whose stock is not reserved).
* Other answers: `401` for a missing or invalid key, `403` when the key lacks `fulfillment:read`, `429` when the key exceeds its rate limit. See [Errors](/docs/en/api-reference/errors) and [Rate limits](/docs/en/api-reference/rate-limits).

To list custody records, use `GET /v1/fulfillments` with the optional `status` filter, `limit` (1 to 200, default 50) and `cursor`. Each item has `orderId`, `status`, `createdAt`, `updatedAt` and, when they apply, `sourceWarehouseId`, `dispatchedAt` and `carrierRef`. See [Pagination](/docs/en/api-reference/pagination).

<Info>
  The MCP tool `fulfillment.get_pick_list` needs the `fulfillment.pick` permission, which the current read-only OAuth scope set does not grant, so it is refused on the `/mcp` endpoint today. See [MCP tools](/docs/en/mcp/tools).
</Info>

## Related pages

<CardGroup cols={2}>
  <Card title="Deliveries overview" icon="truck" href="/docs/en/deliveries/overview">
    Lifecycle, permissions and parties.
  </Card>

  <Card title="Delivery and receipt" icon="clipboard-check" href="/docs/en/deliveries/delivery-and-receipt">
    How the delivery, reception and closure records are made.
  </Card>

  <Card title="Boxes" icon="box" href="/docs/en/inventory/boxes">
    The full history of a single box.
  </Card>

  <Card title="API scopes" icon="key" href="/docs/en/api-reference/scopes">
    Which scope each read needs.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.