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

# Order fulfillment API

> Ship, cancel, print shipping labels, and update tracking for marketplace orders.

Build a packing station or fulfillment screen on top of the same order actions as the workspace Orders page. The marketplace call runs in the background: you queue an action, then poll for its job.

## Authentication

| Endpoint | Required scope |
| - | - |
| `GET /api/v1/orders/{orderId}/action-options` | `orders:write` |
| `POST /api/v1/orders/{orderId}/actions` | `orders:write` |
| `GET /api/v1/orders/{orderId}/actions` | `orders:read` |

## Actions

| Action | Does |
| - | - |
| `ship` | Arranges shipment (pickup or drop-off). |
| `labels` | Generates shipping labels. |
| `update_tracking` | Sends a tracking number. |
| `cancel` | Cancels the order. |

Not every marketplace supports every action, and each action is only available in some order statuses. Shopee labels need the order shipped first; Lazada labels need it packed. An unavailable action returns `400` with the reason.

## 1. Get the choices

Some actions need a choice from the marketplace, such as pickup or drop-off:

```http theme={null}
GET /api/v1/orders/{orderId}/action-options?action=ship
```

```json theme={null}
{
  "data": {
    "action": "ship",
    "choices": [
      {
        "id": "pickup",
        "label": "Pickup",
        "payload": { "deliveryType": "pickup" }
      },
      {
        "id": "dropoff",
        "label": "Drop-off",
        "payload": { "deliveryType": "dropoff" }
      }
    ]
  }
}
```

A choice with `requiresInput: true` needs extra fields, such as `trackingNumber`.

## 2. Queue the action

```http theme={null}
POST /api/v1/orders/{orderId}/actions
```

```json theme={null}
{
  "action": "ship",
  "payload": { "deliveryType": "pickup" },
  "idempotencyKey": "ship-ORDER123"
}
```

Send the chosen choice's `payload` unchanged. The response is `202` with `jobId`. Sending the same `idempotencyKey` again returns `200` with the original job instead of calling the marketplace twice.

## 3. Poll the job

```http theme={null}
GET /api/v1/orders/{orderId}/actions
```

Returns the order's 20 most recent jobs, newest first. `status` moves from `queued` to `running`, then `succeeded`, `failed` (see `errorMessage`), or `not_supported`.

A succeeded `labels` job lists its files in `labels`:

```json theme={null}
{
  "id": "4b1e…",
  "action": "labels",
  "status": "succeeded",
  "labels": [
    {
      "url": "https://…/shipping-labels/…/label.pdf",
      "name": "label.pdf",
      "mimeType": "application/pdf"
    }
  ]
}
```

Download or print the label from `url`. The latest successful label job stays in the list even after newer jobs.

## SDK

```ts theme={null}
const options = await client.orders.getActionOptions({
  orderId,
  action: "ship",
});
await client.orders.queueAction({
  orderId,
  action: "ship",
  payload: options.data.choices[0].payload,
  idempotencyKey: `ship-${orderId}`,
});
const jobs = await client.orders.listActions({ orderId });
```

```bash theme={null}
omni orders queue-action --order-id "$ORDER_ID" --json '{"action":"labels"}'
omni orders list-actions --order-id "$ORDER_ID"
```

Order changes also arrive as [order webhooks](/webhooks/orders).


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