Metadata
metadata is a flat object of string keys to string values that your company keeps on a shipment request: a cost centre, a purchase order, an eBA process number, whatever your systems key the request by. It is your company’s private data: it comes back in your reads and in the webhooks sent to your company, and nowhere else.
Limits
Section titled “Limits”| Keys | At most 20 |
| A key | 1 to 40 characters from A-Z, a-z, 0-9, _ and - |
| A value | A string of 0 to 500 characters, without control characters such as U+0000 (tab, line feed and carriage return are fine) |
| In all | 8 KB (keys and values, UTF-8) |
Breaking a limit answers 400 with Integration.MetadataInvalid. The key that broke it is named in detail and in args.key, and nothing is written:
{ "title": "Integration.MetadataInvalid", "status": 400, "detail": "Metadata holds at most 20 keys; adding 'plant' makes 21.", "code": "Integration.MetadataInvalid", "args": { "key": "plant" }}Setting it on create
Section titled “Setting it on create”{ "externalReference": "EBA-2026-000123", "metadata": { "costCenter": "LOJ-210", "ebaProcess": "P-7781" }, "...": "the rest of the create body"}The metadata is kept with the request in the same step: a request created with metadata always has it. An empty object, or no metadata at all, means none.
Changing it on update
Section titled “Changing it on update”PATCH /shipment-requests/{id} merges what you send into what is there, in any state of a request your systems created through the API (see Updating and cancelling):
| You send | Result |
|---|---|
"metadata": { "plant": "Gebze" } |
plant is added or changed; the other keys stay. |
"metadata": { "ebaProcess": null } |
ebaProcess is removed; the other keys stay. |
"metadata": null |
Every key is removed. |
No metadata field |
Nothing changes. |
The limits apply to the result: adding a key to a request that already has 20 is refused. Two updates at the same moment are applied one after the other, each to what the other wrote: a key set by one is never lost to the other.
Reading it
Section titled “Reading it”Every read of a request carries metadata, an empty object when there is none:
{ "id": "<request-id>", "externalReference": "EBA-2026-000123", "status": "Published", "metadata": { "costCenter": "LOJ-210", "plant": "Gebze" }, "revision": 1, "...": "the other status fields"}In webhooks
Section titled “In webhooks”A webhook sent to the company that owns the request carries data.metadata when the request has some. It is never sent to anyone else: the carrier’s deliveries of the same deal never contain it. A change of metadata raises request.updated with metadata in changedFields.
Who sees it
Section titled “Who sees it”Only your company, through the API. It is not shown to carriers, the marketplace, the other party of a deal, the Qai assistant, or any other company, and it is not shown in the app today. It is stored with the request under your company’s row-level security.
In the sandbox, metadata works the same way and is deleted with the sandbox request after 30 days.
