References
Overview
References are the buyer's article master data: the list of articles the buyer purchases, with their category, packaging options and commercial attributes. Suppliers' products are then matched to these references.
This endpoint enables buyers to:
- Push article master data from an ERP or PIM into a buyer workspace
- Keep it in sync with incremental, idempotent updates
- Describe each packaging option (logistics unit) of an article: box type, box weight, pieces per box, boxes per pallet
- Deactivate articles that are no longer purchased, without losing their history
Endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /v2/partner/{workspaceID}/references |
Create or update references in bulk |
| DELETE | /v2/partner/{workspaceID}/references |
Soft-delete references in bulk, by code |
Authentication
References use a workspace API token issued by Consentio during onboarding, with the references:write permission. Send it directly on every request; no /login call is needed:
Authorization: Bearer ctio_ws_xxxxxxxxxxxxx
A token without the references:write permission, or used on a different workspace, gets 403 Forbidden. Contact your integration manager to obtain or rotate the token.
Data Model
One reference, several logistics units
On the wire, you send one object per article, and list all its packaging options in logistics_units[].
In Consentio, each logistics unit becomes its own reference, identified by the composite code <code>-<logistics unit code>. For example, article 00027598 with logistics units 01 and 03 creates two references, 00027598-01 and 00027598-03. This composite code is the identifier you use later to delete a reference.
An article sent without any logistics_units creates or changes nothing.
Idempotency
The composite code <code>-<logistics unit code> is the idempotency key:
- A composite code seen for the first time creates a reference
- A composite code that already exists updates that reference
- Logistics units you do not send are left unchanged
Retrying the same request is safe and does not create duplicates.
Fields
| Field | Type | Required | Description |
|---|---|---|---|
code |
string | Yes | Your identifier for the article. Combined with each logistics unit code into the composite code (max 256 characters in total). |
name |
string(256) | Yes | Article name. See Stored name. |
product_kinds |
array | Yes | Category hierarchy, root to leaf. At least one entry. See below. |
attributes |
array | Yes | Must contain at least price_unit. See below. |
logistics_units |
array | No | Packaging options. Each one becomes a reference. Without it, nothing is created or changed. See below. |
status |
enum | No | active or inactive. See Status. |
organic |
boolean | No | Organic-certified article. Defaults to false. |
description |
string | No | Free text. |
deleted_at |
datetime | No | RFC 3339 timestamp. Soft-delete signal. See Soft delete. |
metadata |
object | No | Any key-value pairs for your own traceability. Stored as-is, not interpreted by Consentio. |
group_code |
string | No | Accepted and ignored. |
product_kinds[]
The article's category hierarchy as an ordered array, always root to leaf. Each entry is { "code": "<code>", "name": "<label>" }, and both values must be non-empty.
- The position in the array is the hierarchy level, so always send the same number of levels in the same order.
- Each category is identified by its
codeand its parent. Unknown categories are created automatically with thenameyou send, and if a known category arrives with a differentname, the name is updated. - The reference is linked to the last entry (the leaf).
Example of a 4-level hierarchy:
"product_kinds": [
{ "code": "46", "name": "LEGUMES FRAIS TRAD" },
{ "code": "795", "name": "CONCOMBRE" },
{ "code": "60", "name": "VERT" },
{ "code": "00004593", "name": "CONCOMBRE PIECE" }
]
attributes[]
Each item is { "attribute": "<key>", "value": <string or number> }. Numeric values may be sent as a string ("5.5") or a number (5.5).
| Key | Value | Required | Description |
|---|---|---|---|
price_unit |
enum | Yes | Unit the article is priced in: kg, box, piece or pallet. |
sale_unit |
enum | No | Unit the article is sold in: kg, box, piece or pallet. Defaults to box. |
vat_rate |
number | No | VAT rate, e.g. 5.5. |
net_weight |
number | No | Net weight of one piece, in kg. Not the box weight. |
origin |
text | No | Country of origin, e.g. FR. |
ean13 |
text | No | EAN-13 barcode. Send it as a string to keep leading zeros. |
brand |
text | No | Brand. |
packaging |
text | No | Packaging description. |
size_label |
text | No | Size or caliber. |
production_zone |
text | No | Production zone. |
tag |
text | No | Free tag. |
Rules:
- Any other key is accepted but not stored. This includes
variety,size(usesize_label) andunit_measure_code. - Attributes with an empty value are skipped.
- The list replaces the stored attributes. On update, a stored attribute that is missing from the request is removed from the reference. Always send the complete list.
- Box weight, pieces per box and boxes per pallet belong in
logistics_units[], not here.
logistics_units[]
| Field | Type | Required | Description |
|---|---|---|---|
code |
string(32) | Yes | Your logistics unit code, e.g. 01, 03. Unique within the article. If the same code appears twice, the last one is used. |
box_weight |
string or number | Yes | Weight of one box, in kg, e.g. "4.20" or 4.2. |
pieces_per_box |
string or integer | Yes | Number of pieces in one box, e.g. 12. |
box_per_pallet |
string or integer | No | Average number of boxes per pallet, e.g. 144. |
box_type_code |
string(32) | No | Your code for the physical box type, e.g. CRT, COL, 2PL. Unknown codes are created automatically in the workspace's box types. Omit it when there is no physical container. |
box_type_label |
string | No | Display name of the box type. When sent, it becomes the box type's name, also for an existing box type. When never sent, the name is the code. |
Stored name
Each reference's name is built from the article name, so that the logistics units of one article can be told apart:
price_unit |
Stored name | Example |
|---|---|---|
piece |
<name> - <box_type_code>-<pieces_per_box>piece |
CONCOMBRE PIECE - CRT-12piece |
kg |
<name> - <box_type_code>-<box_weight>kg |
CONCOMBRE PIECE - CRT-4.2kg |
box or pallet |
<name> - <box_type_code> |
CONCOMBRE PIECE - CRT |
Without a box_type_code (or with a code made only of dashes, such as -), the - <box_type_code> part is left out. Numbers are written in their shortest form (4.20 becomes 4.2).
Creating and Updating References
Send a batch of 1 to 500 articles with POST /references. Only JSON is accepted.
Request
POST /v2/partner/{workspaceID}/references
Content-Type: application/json
Authorization: Bearer {token}
{
"references": [
{
"code": "00027598",
"name": "CONCOMBRE PIECE",
"status": "active",
"product_kinds": [
{ "code": "46", "name": "LEGUMES FRAIS TRAD" },
{ "code": "795", "name": "CONCOMBRE" },
{ "code": "60", "name": "VERT" },
{ "code": "00004593", "name": "CONCOMBRE PIECE" }
],
"attributes": [
{ "attribute": "price_unit", "value": "piece" },
{ "attribute": "origin", "value": "FR" },
{ "attribute": "vat_rate", "value": 5.5 },
{ "attribute": "ean13", "value": "3355750001621" }
],
"logistics_units": [
{
"code": "01",
"box_type_code": "CRT",
"box_type_label": "Carton",
"box_weight": "4.20",
"pieces_per_box": 12,
"box_per_pallet": 144
},
{
"code": "03",
"box_type_code": "COL",
"box_weight": "8.40",
"pieces_per_box": 24
}
],
"metadata": { "source": "ERP" }
}
]
}
This request creates or updates two references: 00027598-01, named CONCOMBRE PIECE - CRT-12piece, and 00027598-03, named CONCOMBRE PIECE - COL-24piece.
Response
200 OK with the references that were saved (one per logistics unit sent) and the rows that failed. Trimmed for readability:
{
"references": [
{
"id": 1048213,
"name": "CONCOMBRE PIECE - CRT-12piece",
"buyer_tag": "00027598-01",
"status": "active",
"workspace_id": 123,
"product_kind_id": 52114,
"metadata": { "source": "ERP" },
"created_at": "2026-10-30T09:12:44Z",
"updated_at": "2026-10-30T09:12:44Z"
},
{
"id": 1048214,
"name": "CONCOMBRE PIECE - COL-24piece",
"buyer_tag": "00027598-03",
"status": "active",
"workspace_id": 123,
"product_kind_id": 52114,
"metadata": { "source": "ERP" },
"created_at": "2026-10-30T09:12:44Z",
"updated_at": "2026-10-30T09:12:44Z"
}
],
"errors": []
}
buyer_tag is the composite code. The response only contains the references from the request, not other logistics units already stored for the same article. See the API Reference for every response field.
What an update changes
When a composite code already exists, the reference is updated with the values in the request:
name,description,organic,product_kinds,metadataanddeleted_atare overwritten. A field you leave out is reset (for example, a missingmetadataclears it).attributesare replaced by the list you send.- The box type is replaced by
box_type_code. statusis the only field kept when you leave it out.
Always send the complete article, not only the fields that changed.
Status
- On create: a reference is
inactiveunless you send"status": "active". - On update:
"active"or"inactive"sets the status. Ifstatusis left out, the current status is kept.
Soft delete
deleted_at deactivates the references of the logistics units included in the request:
- A past or current timestamp deactivates them immediately.
- A future timestamp keeps them active until that moment, then they are treated as inactive automatically.
- Leaving
deleted_atout on a later request reactivates them. - An article with
deleted_atwhose composite code does not exist yet is skipped: nothing is created, and it does not appear in the response.
The timestamp must be RFC 3339 (e.g. 2026-11-01T00:00:00Z). Data is never physically deleted, so a reference keeps its ID and history when reactivated.
Partial success
Each article and each logistics unit is checked independently. Rows that fail are left out of references and reported in errors, and the rest of the batch is saved:
{
"references": [ { "id": 1048213, "buyer_tag": "00027598-01", "...": "..." } ],
"errors": [
{
"index": 1,
"code": "00030114",
"logistics_unit_code": "01",
"message": "Attribute 'price_unit' is required and must be one of: kg, box, piece, pallet."
},
{
"index": 2,
"code": "00030200",
"message": "product_kinds is required and must contain at least one entry"
}
]
}
| Field | Description |
|---|---|
code |
code of the failing article. |
logistics_unit_code |
The failing logistics unit, when the error concerns one logistics unit only. |
message |
What went wrong. |
index |
Position of the row. Use code and logistics_unit_code to match an error to your data: index is not always the article's position in your request. |
Common row errors:
| Message | Cause |
|---|---|
product_kinds is required and must contain at least one entry |
product_kinds missing or empty. |
product_kinds entries must have a non-empty code and name |
A category without code or name. |
name exceeds 256 characters |
name too long. |
deleted_at must be RFC3339 |
deleted_at in another date format. |
box_weight is required / pieces_per_box is required |
Missing logistics unit value. |
logistics_unit code exceeds 32 characters |
Logistics unit code too long. |
composite code exceeds 256 characters |
code plus logistics unit code too long. |
Attribute 'price_unit' is required and must be one of: kg, box, piece, pallet. |
price_unit missing or empty. |
Attribute '<key>' must be one of: kg, box, piece, pallet (got '...'). |
Invalid price_unit or sale_unit. |
Attribute '<key>' must be a number, but got '...'. |
Non-numeric value for a numeric attribute or logistics unit field, e.g. vat_rate or box_weight. |
Deleting References
DELETE /references deactivates a list of references immediately, by composite code. It has the same effect as sending deleted_at with the current time.
Request
DELETE /v2/partner/{workspaceID}/references
Content-Type: application/json
Authorization: Bearer {token}
{
"codes": ["00027598-01", "00027598-03"]
}
- Each entry is a composite code
<code>-<logistics unit code>. A bare articlecodeis not recognized. - Send 1 to 500 codes per request. Duplicates are ignored.
- References not listed are left unchanged.
- A reference that already has a
deleted_atkeeps its existing date, and is still reported as deleted. - To reactivate a reference, send it again with
POSTwithoutdeleted_at.
Response
{
"deleted": ["00027598-01"],
"errors": [
{ "index": 1, "code": "00027598-03", "message": "unknown code" }
]
}
Here index is the position of the code in your codes array.
Integration Workflow
sequenceDiagram
participant ERP as Buyer ERP / PIM
participant API as Consentio API
ERP->>API: POST /references (initial load, batches of up to 500)
API-->>ERP: 200 OK (references + errors)
loop On every change, or a scheduled refresh
ERP->>API: POST /references (complete articles)
API-->>ERP: 200 OK (only the logistics units sent are touched)
end
ERP->>API: DELETE /references { codes: ["-"] }
API-->>ERP: 200 OK (deleted + errors)
Error Handling
HTTP Code
Meaning
200
Request processed. Check errors for rows that failed.
400
Empty or malformed JSON body, or a batch outside 1 to 500 items. Nothing is saved.
401
Missing, invalid or revoked token.
403
The token lacks the references:write permission, or belongs to another workspace.
5xx
Unexpected server error. No reference is saved: retry the same request.
Best Practices
-
Always send price_unit: an article without it is rejected.
-
One object per article: list all its packaging options in logistics_units[] instead of sending the article several times.
-
Send complete articles: every update overwrites the reference, including attributes and metadata.
-
Keep codes stable: code, logistics unit codes and box_type_code are the keys that connect your data to Consentio.
-
Keep the category tree consistent: always send the same levels, root to leaf.
-
Check errors on every response: a 200 can still contain rows that failed.
-
Retry safely: requests are idempotent, so resending a batch after a timeout or a 5xx creates no duplicates.