# Find one Fulfillment

`GET /fulfillments/{id}`

OMS API, Fulfillments.

Retrieve a fulfillment by its ID with items populated

**Authentication:** Send `Authorization: Bearer <token>`. Either an identity token issued by the auth service (dashboard flow; requires the selected merchant account id in the `X-Odus-Merchant` header), or a merchant secret key (`sk_...`) for server-to-server calls. Both are sent as `Authorization: Bearer <token>`.

## Parameters

### Path parameters

- `id` (string, required)

## Responses

### `200`

Type: object (FulfillmentDTO).

- `createdAt` (string (date-time), required): The date and time when the entity was created. Example: `2024-02-18T12:00:00Z`.
- `updatedAt` (string (date-time), required, nullable): The date and time when the entity was last updated. Example: `2024-02-21T12:15:00Z`.
- `metadata` (map of string to string, required, nullable): Metadata used by merchants to store additional information about the entity.
- `id` (string, required)
- `order` (string, required)
- `trackingUrl` (string, required)
- `trackingNumber` (string, required)
- `carrier` (string, required, nullable)
- `externalShipmentId` (string, required, nullable): Identifier of the shipment in the fulfillment provider's system (for example the ShipBob shipment id). `null` for a fulfillment recorded by hand or reported without one. Example: `123456789`.
- `shippedAt` (string (date-time), required)
- `awaitingLastMileTracking` (boolean, required): Whether the recorded tracking is provisional data from the 3PL, still awaiting its last-mile assignment. When the assignment arrives, the tracking number, carrier and URL are upgraded in place and a `fulfillment.updated` event is emitted. Example: `false`.
- `pspTrackingStatus` (string, required, nullable): One of `pending`, `awaiting_capture`, `awaiting_tracking`, `submitted`, `failed`, `skipped`.
- `pspTrackingAttempts` (number, required)
- `pspTrackingNextAttemptAt` (string (date-time), required, nullable)
- `pspTrackingSubmittedAt` (string (date-time), required, nullable)
- `pspTrackingLastError` (string, required, nullable)
- `fulfillmentItems` (array of object, required)
  - `createdAt` (string (date-time), required): The date and time when the entity was created. Example: `2024-02-18T12:00:00Z`.
  - `updatedAt` (string (date-time), required, nullable): The date and time when the entity was last updated. Example: `2024-02-21T12:15:00Z`.
  - `metadata` (map of string to string, required, nullable): Metadata used by merchants to store additional information about the entity.
  - `id` (string, required)
  - `quantity` (number, required)
  - `fulfillment` (string, required)
  - `orderItem` (object, required)
    - `createdAt` (string (date-time), required): The date and time when the entity was created. Example: `2024-02-18T12:00:00Z`.
    - `updatedAt` (string (date-time), required, nullable): The date and time when the entity was last updated. Example: `2024-02-21T12:15:00Z`.
    - `metadata` (map of string to string, required, nullable): Metadata used by merchants to store additional information about the entity.
    - `id` (string, required)
    - `name` (string, required)
    - `productName` (string, required)

### `202`

The merchant is entitled but its OMS environment is not provisioned yet. Provisioning has been kicked off (exactly once) and is in progress; retry the request — it succeeds once the environment is ready. Returned only for identity-token (dashboard) requests bound to an active-entitled merchant, not for secret-key API calls; any such endpoint can return it while provisioning is underway.

Type: object (ProvisioningInProgress).

- `status` (string, required): Signals the merchant OMS environment is still being provisioned. One of `provisioning`.
- `merchantAccountId` (string, required): The merchant account whose OMS environment is being provisioned. Example: `dAI3aa87ROOeupeBCjNSKCOBOisBCYjB`.

### `400`

The request was rejected. `type` is `invalid_request_error`; when the failure is a validation failure `code` is `validation_failed` and `errors` lists every problem found, with field-attributable entries prefixed by the field’s path.

Type: object (ValidationErrorResponse).

- `statusCode` (number, required): HTTP status code. Example: `400`.
- `requestId` (string, required): Unique identifier for the request. Example: `dfc10de3-36bb-4bcc-b081-316216194033`.
- `message` (string, required): The first error message from the validation errors. Example: `note must be a string`.
- `type` (string, required): Coarse, stable classifier for the failure. Present on every error response including 5xx. This is the field to branch on: `statusCode` is too coarse and `message` is human prose that may be reworded. The set is closed and every member is permanent, so it is safe to handle exhaustively. One of `invalid_request_error`, `authentication_error`, `permission_error`, `not_found_error`, `invalid_state_error`, `conflict_error`, `rate_limit_error`, `payment_error`, `api_error`. Example: `invalid_request_error`.
- `code` (string): Fine-grained identifier for the failure, present only where it adds something `type` does not — for example separating a replayed idempotency key from a held lock, or naming the reason a payment was declined. The set is open and grows over time, so treat an unrecognised value as absent and fall back to `type`. Example: `validation_failed`.
- `errors` (array of string): Complete list of validation errors.

### `401`

No credentials were supplied, or the identity token / secret key is not valid for the target merchant. `type` is `authentication_error`.

Type: object (ErrorResponse).

- `statusCode` (number, required): HTTP status code. Example: `400`.
- `requestId` (string, required): Unique identifier for the request. Example: `dfc10de3-36bb-4bcc-b081-316216194033`.
- `message` (string, required): Human-readable description of the failure. Example: `An error occurred`.
- `type` (string, required): Coarse, stable classifier for the failure. Present on every error response including 5xx. This is the field to branch on: `statusCode` is too coarse and `message` is human prose that may be reworded. The set is closed and every member is permanent, so it is safe to handle exhaustively. One of `invalid_request_error`, `authentication_error`, `permission_error`, `not_found_error`, `invalid_state_error`, `conflict_error`, `rate_limit_error`, `payment_error`, `api_error`. Example: `invalid_request_error`.
- `code` (string): Fine-grained identifier for the failure, present only where it adds something `type` does not — for example separating a replayed idempotency key from a held lock, or naming the reason a payment was declined. The set is open and grows over time, so treat an unrecognised value as absent and fall back to `type`. Example: `validation_failed`.

### `403`

The credentials are valid but lack the permission this operation requires. `type` is `permission_error`.

Type: object (ErrorResponse).

- `statusCode` (number, required): HTTP status code. Example: `400`.
- `requestId` (string, required): Unique identifier for the request. Example: `dfc10de3-36bb-4bcc-b081-316216194033`.
- `message` (string, required): Human-readable description of the failure. Example: `An error occurred`.
- `type` (string, required): Coarse, stable classifier for the failure. Present on every error response including 5xx. This is the field to branch on: `statusCode` is too coarse and `message` is human prose that may be reworded. The set is closed and every member is permanent, so it is safe to handle exhaustively. One of `invalid_request_error`, `authentication_error`, `permission_error`, `not_found_error`, `invalid_state_error`, `conflict_error`, `rate_limit_error`, `payment_error`, `api_error`. Example: `invalid_request_error`.
- `code` (string): Fine-grained identifier for the failure, present only where it adds something `type` does not — for example separating a replayed idempotency key from a held lock, or naming the reason a payment was declined. The set is open and grows over time, so treat an unrecognised value as absent and fall back to `type`. Example: `validation_failed`.

### `404`

No resource exists with the requested identifier. `type` is `not_found_error`.

Type: object (ErrorResponse).

- `statusCode` (number, required): HTTP status code. Example: `400`.
- `requestId` (string, required): Unique identifier for the request. Example: `dfc10de3-36bb-4bcc-b081-316216194033`.
- `message` (string, required): Human-readable description of the failure. Example: `An error occurred`.
- `type` (string, required): Coarse, stable classifier for the failure. Present on every error response including 5xx. This is the field to branch on: `statusCode` is too coarse and `message` is human prose that may be reworded. The set is closed and every member is permanent, so it is safe to handle exhaustively. One of `invalid_request_error`, `authentication_error`, `permission_error`, `not_found_error`, `invalid_state_error`, `conflict_error`, `rate_limit_error`, `payment_error`, `api_error`. Example: `invalid_request_error`.
- `code` (string): Fine-grained identifier for the failure, present only where it adds something `type` does not — for example separating a replayed idempotency key from a held lock, or naming the reason a payment was declined. The set is open and grows over time, so treat an unrecognised value as absent and fall back to `type`. Example: `validation_failed`.

### `429`

Too many requests. The rate limit is applied per client across all operations. `type` is `rate_limit_error`.

Type: object (ErrorResponse).

- `statusCode` (number, required): HTTP status code. Example: `400`.
- `requestId` (string, required): Unique identifier for the request. Example: `dfc10de3-36bb-4bcc-b081-316216194033`.
- `message` (string, required): Human-readable description of the failure. Example: `An error occurred`.
- `type` (string, required): Coarse, stable classifier for the failure. Present on every error response including 5xx. This is the field to branch on: `statusCode` is too coarse and `message` is human prose that may be reworded. The set is closed and every member is permanent, so it is safe to handle exhaustively. One of `invalid_request_error`, `authentication_error`, `permission_error`, `not_found_error`, `invalid_state_error`, `conflict_error`, `rate_limit_error`, `payment_error`, `api_error`. Example: `invalid_request_error`.
- `code` (string): Fine-grained identifier for the failure, present only where it adds something `type` does not — for example separating a replayed idempotency key from a held lock, or naming the reason a payment was declined. The set is open and grows over time, so treat an unrecognised value as absent and fall back to `type`. Example: `validation_failed`.

### `500`

The request could not be completed because of an unexpected error. `type` is `api_error`.

Type: object (ErrorResponse).

- `statusCode` (number, required): HTTP status code. Example: `400`.
- `requestId` (string, required): Unique identifier for the request. Example: `dfc10de3-36bb-4bcc-b081-316216194033`.
- `message` (string, required): Human-readable description of the failure. Example: `An error occurred`.
- `type` (string, required): Coarse, stable classifier for the failure. Present on every error response including 5xx. This is the field to branch on: `statusCode` is too coarse and `message` is human prose that may be reworded. The set is closed and every member is permanent, so it is safe to handle exhaustively. One of `invalid_request_error`, `authentication_error`, `permission_error`, `not_found_error`, `invalid_state_error`, `conflict_error`, `rate_limit_error`, `payment_error`, `api_error`. Example: `invalid_request_error`.
- `code` (string): Fine-grained identifier for the failure, present only where it adds something `type` does not — for example separating a replayed idempotency key from a held lock, or naming the reason a payment was declined. The set is open and grows over time, so treat an unrecognised value as absent and fall back to `type`. Example: `validation_failed`.

### `503`

A dependency needed to serve the request is temporarily unavailable — for every operation the auth service that verifies credentials and resolves entitlement, and on the provisioning routes also the ODUS API the OMS reconciles against. `type` is `api_error`. Unlike a plain 500 the condition is transient rather than a fault in the request, so it is worth retrying with backoff.

Type: object (ErrorResponse).

- `statusCode` (number, required): HTTP status code. Example: `400`.
- `requestId` (string, required): Unique identifier for the request. Example: `dfc10de3-36bb-4bcc-b081-316216194033`.
- `message` (string, required): Human-readable description of the failure. Example: `An error occurred`.
- `type` (string, required): Coarse, stable classifier for the failure. Present on every error response including 5xx. This is the field to branch on: `statusCode` is too coarse and `message` is human prose that may be reworded. The set is closed and every member is permanent, so it is safe to handle exhaustively. One of `invalid_request_error`, `authentication_error`, `permission_error`, `not_found_error`, `invalid_state_error`, `conflict_error`, `rate_limit_error`, `payment_error`, `api_error`. Example: `invalid_request_error`.
- `code` (string): Fine-grained identifier for the failure, present only where it adds something `type` does not — for example separating a replayed idempotency key from a held lock, or naming the reason a payment was declined. The set is open and grows over time, so treat an unrecognised value as absent and fall back to `type`. Example: `validation_failed`.

### `504`

The request exceeded the processing time limit and was abandoned. `type` is `api_error` and `code` is `timeout` — unlike a plain 500 the request may still have taken effect.

Type: object (ErrorResponse).

- `statusCode` (number, required): HTTP status code. Example: `400`.
- `requestId` (string, required): Unique identifier for the request. Example: `dfc10de3-36bb-4bcc-b081-316216194033`.
- `message` (string, required): Human-readable description of the failure. Example: `An error occurred`.
- `type` (string, required): Coarse, stable classifier for the failure. Present on every error response including 5xx. This is the field to branch on: `statusCode` is too coarse and `message` is human prose that may be reworded. The set is closed and every member is permanent, so it is safe to handle exhaustively. One of `invalid_request_error`, `authentication_error`, `permission_error`, `not_found_error`, `invalid_state_error`, `conflict_error`, `rate_limit_error`, `payment_error`, `api_error`. Example: `invalid_request_error`.
- `code` (string): Fine-grained identifier for the failure, present only where it adds something `type` does not — for example separating a replayed idempotency key from a held lock, or naming the reason a payment was declined. The set is open and grows over time, so treat an unrecognised value as absent and fall back to `type`. Example: `validation_failed`.

---

Generated from the OMS API OpenAPI document, https://sandbox-oms-api.odus.com/openapi.json. Interactive page: https://docs.odus.com/docs/api/oms/fulfillment-controller-find-one
