---
title: "Article movements"
method: GET
path: "/v1/articles/{id}/movements"
tags: ["Articles"]
---

# Article movements

`GET /v1/articles/{id}/movements`

## Request

- Base URL: `https://api.officina.it`
- URL: `https://api.officina.it/v1/articles/{id}/movements`
- Auth: HTTP bearer

The "Storico lavorazioni" for an article / lavorazione: the sale
movements where the item was used (worksheet line items, order items,
standalone receipts), newest first — same set as the web service-catalog
detail (sale-type only). Paged from revision 2026-10-13 (`limit`, `starting_after` → `next_starting_after`); older revisions get every row (`has_more` false).

## Query parameters

- `limit` integer
- `starting_after` string

## Response `200`

success

- object
  - `object` 'list', required: Polymorphic discriminator.
  - `data` ArticleMovement[], required: Page of items. Each item carries its own `object` field.
    - `object` string, required: always "article_movement"
    - `id` string, required
    - `quantity` number, required
    - `source` string, required: "worksheet" | "order" | "receipt" | ""
    - `source_id` string, nullable, required
    - `seq_number` integer, nullable, required
    - `document_date` string, date-time, nullable, required
    - `customer_name` string, nullable, required
    - `vehicle_name` string, nullable, required
    - `total` number, nullable, required
    - `created_at` string, date-time, required
  - `has_more` boolean, required: True if more results exist beyond this page. Use this for the "load more" UI — `data.length` alone is unreliable because a full page may still be the last page.
  - `next_starting_after` string: Opaque cursor for the next page. Present (and non-empty) when `has_more` is `true`; omitted otherwise. Pass the value verbatim as `?starting_after=` on the follow-up request. Clients MUST NOT parse it — today it encodes an offset, tomorrow it may encode a `(created_at, id)` tuple without a wire-visible change.
  - `url` string, required: The request path that produced this list.

## Other responses

- `401`: Missing, malformed, expired, or wrong-audience bearer token.
- `402`: The company has no active subscription, or its plan does not include API access. API access is included in the Business and Illimitato plans.
- `403`: The authenticated user lacks the required permission.
- `404`: Resource does not exist OR belongs to a different company. The two cases are intentionally indistinguishable.
- `500`: Unhandled server error. Detail is logged, not exposed.

---

[API](https://skmtc.dev/officina/apis/officina-it-api.md) · [All operations](https://skmtc.dev/officina/apis/officina-it-api/llms.txt) · [OpenAPI document](https://skmtc.dev/officina/apis/officina-it-api/revisions/7da3fccfe551?raw)
