Documents

The register of invoices, delivery notes, credit notes and return receipts filed against an order, the document kinds a merchant keeps, and the vocabularies that name them.

The Documents area records the documents a merchant issued against a transaction. A document is an invoice, a delivery note, a credit note or a return receipt, and the register answers one question: which document belongs to which order. The app records what was issued. It does not produce a document and never streams the file itself; it can mint a short-lived link to it. A finance system issues the document, and the register entry points at the file, which lives in the platform's asset storage or on the tenant's own host.

This page is for the developer whose system issues the documents (an ERP or finance system) and files them here, and for a storefront that lists them in the buyer's account.

order is the only owner the API accepts today (as of 2026-10).

Base path: https://api.revenexx.com/v1/documents.

A key needs the documents.* scope (Scopes); uploading the file needs storage. X-Revenexx-Market is optional: on a create it ties the document to that market, and without it the document shows in every market.

What a document record holds

FieldMeaning
entity_type, entity_idThe transaction the document is filed against. entity_type accepts order only (as of 2026-10).
kindA code from the tenant's kinds. See Kinds.
numberThe number a customer quotes, for example on the phone.
sourcestorage or external. storage means storage_asset_id holds the file, an asset of the platform. external means external_url holds a file on the tenant's own host. Each source needs its own field, and the API refuses a record where they do not match. Defaults to storage.
visibilitycustomer or internal. Defaults to customer. See below.
external_idThe identifier from the system that issued the document. A create with an external_id the tenant already holds updates that document.

visibility

  • customer: the buyer may read the document. This is what an account area shows, and the default.
  • internal: only the merchant's side may read it, for example a margin sheet, a supplier confirmation, or a credit note still being argued about.

A call is a storefront one when it is made for a signed-in buyer, or when it sends ?audience=customer. Any other call gets the operator answer, internal documents included. A storefront caller sending ?visibility=internal still gets only customer documents.

A storefront caller is limited to customer documents in the query itself. An internal document is missing from its list and from the list's page total, and it answers 404 at its own address. Set visibility with care: an internal document set to customer is visible to the buyer.

A storefront must ask for the buyer's view. A server that calls with its API key alone gets the operator answer, internal documents included. Send ?audience=customer on every list, read and link call made for a buyer, unless the call already runs on a signed-in buyer's session.

Documents

Every operation below is under /v1/documents/documents.

OperationPathUse
ListGET /v1/documents/documentsThe documents of a transaction. Filter with entity_type and entity_id, kind, or number.
CreatePOST /v1/documents/documentsRecord a document. Returns 201, or 200 when it updated the document its external_id already names. 409 when an overlapping create took the external_id first.
ReadGET /v1/documents/documents/{id}One document.
UpdatePUT /v1/documents/documents/{id}A partial update. A body with no fields is refused.
DeleteDELETE /v1/documents/documents/{id}Removes the register entry only. The file is not deleted, and there is no undo.
LinkPOST /v1/documents/documents/{id}/linkReturns a signed URL to the file's bytes, valid for five minutes.

The list also filters on source, filename, content_type, issued_at, due_date, total_amount, currency, visibility, external_id, storage_asset_id, external_url and the timestamps (exact match only, no ranges), sorts with order (column.asc or column.desc), and pages with limit (default 50, maximum 200) and offset. An unknown query parameter is ignored and the list comes back unfiltered; the filter object in the answer shows what was applied.

Create needs entity_type, entity_id, kind and filename. The kind must be one the tenant keeps, and the refusal lists the valid codes. Retries of a feed are safe: send the same external_id and the document is updated, not copied. A retry that overlaps a create still in flight can answer 409 with code: "unique_violation"; send it again after a short wait.

Delete also leaves the file in place. A feed that still knows the document files it again on its next run, under the same external_id.

Link returns a url, expires_at and expires_in, plus the file's filename, content_type and byte_size; expires_in is 300 seconds. The URL is a bearer credential for the file until it expires, and it cannot be revoked. Each call mints a new URL, so do not cache the answer. The file is never streamed through the API. A storefront caller asking for a link to an internal document gets 404. An external document has no platform file to sign: the link call answers 409 with code: "external_document" and the address in details.external_url. A document whose stored file is gone answers 409 asset_missing, and a storage outage answers 502.

Request: file a document against an order

Request
curl -X POST https://api.revenexx.com/v1/documents/documents \
  -H "X-Revenexx-Tenant: <TENANT_SLUG>" \
  -H "X-Revenexx-Api-Key: rvxk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "entity_type": "order",
    "entity_id": "<ORDER_ID>",
    "kind": "invoice",
    "filename": "invoice-<ORDER_ID>.pdf",
    "external_id": "<YOUR_DOCUMENT_ID>",
    "source": "storage",
    "storage_asset_id": "ast_...",
    "visibility": "customer"
  }'

The system that issues the document uploads the file itself with POST /v1/storage/assets (see Upload and manage files) and records the ast_… id it gets back. On a fresh tenant, list the kinds once first (GET /v1/documents/kinds), which seeds the four standard kinds, so the create finds invoice.

Kinds

Kinds are the document types a merchant keeps, under /v1/documents/kinds. A kind has a code, which every document stores, a title, a tone for the badge colour, optional labels and descriptions, and a position in a select.

OperationPathUse
ListGET /v1/documents/kindsThe tenant's kinds. Filter on code, title, is_default, tone, is_system.
CreatePOST /v1/documents/kindsAdd a kind. Needs code and title. Returns 201.
SeedPOST /v1/documents/kinds/defaultsSeeds the four kinds an order produces. Send {}.
ReadGET /v1/documents/kinds/{id}One kind. Unlike the list, it does not seed.
UpdatePUT /v1/documents/kinds/{id}Edits the words: title, labels, description, tone and position.
DeleteDELETE /v1/documents/kinds/{id}Removes a kind.
Make defaultPOST /v1/documents/kinds/{id}/make-defaultMoves the default flag to this kind in one call.
  • Four seeded kinds: invoice (the default), delivery_note, credit_note and return_receipt. Seeding is idempotent: a kind the tenant already has is left as it is, including a changed title.
  • The list seeds on read. A tenant with no kinds gets the four seeded ones on the first list call, and so does GET /v1/documents/vocabularies/kinds.
  • The code is fixed. A PUT that changes code is refused with 400. To rename a code, create a new kind and move the documents over.
  • Delete rules. A kind that at least one document carries is refused with 409. The last remaining kind is refused with 409 too. Deleting the default hands the flag to the first remaining kind.
  • Exactly one default. PUT with is_default: true also moves the flag, and make-default does the same in a single call.

Vocabularies

Vocabularies are the value sets this app owns, with the title and tone a screen should show for each value. Read them so a screen does not keep its own copy of a list.

OperationPathUse
IndexGET /v1/documents/vocabulariesThe names of the vocabularies, without values.
One vocabularyGET /v1/documents/vocabularies/{name}Every value of one vocabulary.

The two vocabularies are kinds and visibilities. They differ in who owns the set:

  • kinds comes from the merchant's own table (source: 'table'), so a merchant can extend it. is_default marks the fallback.
  • visibilities is fixed by the API (source: 'schema'). A merchant cannot add a value to it.

Both report closed: true: the set is complete at the moment of the read. A value outside the set is stale data, not a missing label.

Was this page helpful?