Number ranges

How orders, delivery notes and returns get their numbers on revenexx — the three seeded ranges, the shape a merchant gives them, and the settings that point at them.

Three things in the orders app carry a number a human reads: the order, the delivery note, and the return. Each is drawn from a counter the merchant shapes.

Base path: https://api.revenexx.com/v1/orders/number-ranges.

The three seeded ranges

Request
curl -X POST https://api.revenexx.com/v1/orders/number-ranges/defaults \
  -H "X-Revenexx-Tenant: <TENANT_SLUG>" \
  -H "X-Revenexx-Api-Key: rvxk_..." \
  -H "Content-Type: application/json" \
  -d '{}'
CodeNumbersSeeded as
orderOrdersORD- + six digits, step 1
deliveryDelivery notes, drawn by POST /v1/orders/{id}/shipDEL- + six digits, step 1
returnReturns, drawn by POST /v1/orders/{id}/returnRET- + six digits, step 1

The app runs this on install, so a fresh tenant needs nothing. Call it by hand after a range was deleted, or to see what a tenant has.

Idempotent: a code that already exists comes back under existing with its format, counter and active flag left as they are, so a merchant who changed the prefix keeps their change. The one thing it writes on an existing range is a standard name the range is missing (title or labels); a name already there is never changed. It answers 200, never 201 — it is a reconcile, not a create.

The shape of a number

text
{prefix}{counter padded to `padding`}{suffix}
FieldMeaning
codeWhich counter this is. Unique per tenant, and the value the settings point at.
titleThe range's name, what a list and a breadcrumb show. The fallback for every locale labels does not carry. The three standard ranges are named "Order numbers", "Delivery note numbers" and "Return numbers". A range of the merchant's own has no name until one is given, and a screen shows its code instead.
labelsThe name per locale, keyed by locale code (de, en, …). Falls back to title for a locale that is missing. The standard ranges carry de and en; a standard range created before names existed gets them on the next seed or draw, and a name already there is never overwritten.
prefixLiteral text in front. ORD- turns counter 123 into ORD-000123. Empty by default.
suffixLiteral text after — a market or year marker. Empty by default, which is what most merchants use.
paddingHow wide the counter is written, zero-padded. 6 makes 123 into 000123; 0 writes the bare number. Widening it later does not renumber what was already drawn.
stepHow far the counter moves per draw. 1 is consecutive; a larger step is what a merchant chooses who does not want their order volume readable off an invoice.
counterThe last number drawn — state, not configuration.
activeWhether numbers may be drawn from this range. true by default. A draw that would use an inactive range is refused with 422 number_range_inactive; it does not fall back to another range. Numbers already issued stay as they are, and seeding the standard ranges never switches an inactive one back on.
position_stepThe gap between the position numbers of a new order: 10 numbers the lines 10, 20, 30, leaving room to slot one in later. Read from the range the market's order_number_range_code names (order by default), and from that range only.
channel_idA label. It does not select the range.
metadataFree-form. The app stores it and reads nothing out of it.

GET /v1/orders/number-ranges filters by exact match on id, code, prefix, suffix, padding, counter, step, position_step, title, active, channel_id, created_at and updated_at. The API reference does not list labels or metadata, so do not filter on them. An unknown filter answers 400 unknown_filter.

counter is state

The next draw is counter + step, and it writes the new value back.

Moving counter forward skips numbers. Setting it below its current value is refused with 422 (counter_rewind), so a number is never issued twice. Setting it is a repair, not a configuration.

Because a draw writes the counter, updated_at on a range changes on every single order.

channel_id does not select the range

A draw finds the range by code alone, and the unique index on (tenant, code) means one code is one range per tenant. So an order on another channel draws from the same range this one names. channel_id is a label on the row, nothing more.

Adding a range

Request
curl -X POST https://api.revenexx.com/v1/orders/number-ranges \
  -H "X-Revenexx-Tenant: <TENANT_SLUG>" \
  -H "X-Revenexx-Api-Key: rvxk_..." \
  -H "Content-Type: application/json" \
  -d '{"code":"order_at","prefix":"AT-","padding":6,"step":1}'

A code that is taken answers 409 rather than creating a second counter under the same name. Creating a range renumbers nothing that already exists.

title, labels and active are optional here, and PUT /v1/orders/number-ranges/{id} takes them too. active defaults to true.

Pointing a setting at it

A new range is only used once a setting names it:

SettingDrawn by
order_number_range_codePOST /v1/orders/place
delivery_number_range_codePOST /v1/orders/{id}/ship
return_number_range_codePOST /v1/orders/{id}/return
A setting naming a code no range carries makes placing an order answer 422. Create the range first, then point the setting at it.

Settings resolve per market, so a per-market number range is a per-market setting pointing at a second range — not a channel_id on the range. See Settings.

Routes

Method and pathWhat it does
POST /v1/orders/number-ranges/defaultsCreates any of the three seeded ranges that is missing. A missing standard range is also recreated by the next draw on its own, so call this to see what a tenant has.
GET /v1/orders/number-ranges/{id}Reads one range by its id, the uuid and not the code. Reading draws no number and does not move counter. To find the id of a code, use GET /v1/orders/number-ranges?code=order. An unknown id answers 404.
PUT /v1/orders/number-ranges/{id}Changes the columns the range accepts: code, prefix, suffix, padding, counter, step, position_step, title, labels, active, channel_id and metadata. The change applies from the next draw only. Moving counter back answers 422 counter_rewind, a code another range holds answers 409, and a body that carries created_at or updated_at answers 400 server_owned_field.
DELETE /v1/orders/number-ranges/{id}Removes a range that has never drawn a number. A range whose counter is above 0 answers 409 range_in_use. Numbers already placed keep their values.

POST /v1/orders/number-ranges adds a range, as shown under Adding a range. code is unique per tenant.

Where to go next

  • Orders — where the order and delivery numbers are drawn.
  • Returns — where the return number is drawn.
  • Settings — the market → tenant → default precedence the range codes resolve through.
Was this page helpful?