Configure order and quote numbers per market

Give each market its own order and quote numbers — seed the default ranges, add one range per market, point the market's setting at it, and know what to expect from gaps, simultaneous orders and later changes.

This guide is for the developer or integrator who needs predictable document numbers: an ERP that books by order number, accounting that wants DE- and AT- kept apart, or a quote that reads ANG-000042. By the end you have the default ranges seeded, one range per market for orders and for quotes (delivery notes and returns follow the same pattern), each market pointed at its own, and a clear idea of what a number can and cannot promise.

The Number ranges page describes the orders ranges as a reference. This guide is the order to do it in, and it covers quotes, which have their own ranges.

The short answer: a number comes from a range chosen by a market setting, not from a channel. Add a range with a code of your own, then point the market's setting at that code.

Before you start

  • A tenant with the Orders and Quotes Apps installed, and a back-office API key with access to both. Use a back-office key, not a buyer's.
  • The market codes you sell in (de, at). See Markets.
  • Do this on a staging tenant first. On Orders, a range cannot be rewound or deleted once it has issued a number, and on Quotes a mistake can still leave a stray sequence behind (steps 4 and 5).

1. Seed the default ranges

Seed both Apps yourself and do not rely on the install event. The install event does not always fire for an App installed from the Marketplace (as of 2026-10), so the route is the dependable way, and for quotes it is the only way. Orders also creates a missing range on first use, but only for its own seeded codes (order, delivery, return), never for a code of your own. Here "standard" means the four seeded ranges in the table below, with the prefixes ORD-, DEL-, RET- and ANG-.

Orders
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 '{}'
Quotes
curl -X POST https://api.revenexx.com/v1/quotes/number-ranges/defaults \
  -H "X-Revenexx-Tenant: <TENANT_SLUG>" \
  -H "X-Revenexx-Api-Key: rvxk_..." \
  -H "Content-Type: application/json" \
  -d '{}'

Both are safe to run again. A range that already exists is reported under existing and left exactly as it is, counter included.

CodeNumbersFirst number
orderOrdersORD-000001
deliveryDelivery notesDEL-000001
returnReturnsRET-000001
quoteQuotesANG-000001

2. Know the shape of a number

A number is a literal prefix, the counter padded with zeros to a fixed width, and a literal suffix:

text
{prefix}{counter padded to padding}{suffix}

That is all. There is no token for the year, the month or the market, so AT- is text you type into the prefix, not something the platform fills in. Padding is 0 to 18 digits on an Orders range and 1 to 12 on a Quotes range, and the counter moves by step per number.

Order positions are numbered separately: the lines of an order get 10, 20, 30 and so on, using position_step of the range the market's order_number_range_code names, for example order-at (10 by default). It is read from that range only, so set it on each market's range. A position you send yourself is kept as you sent it, and two lines with the same position in one request are refused with 400.

3. Add a range per market

Ranges are found by code, and a code is unique in the tenant. Two markets therefore need two ranges with two different codes. Put the market in the code so it stays readable:

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}'

Create order-de the same way, with "code":"order-de","prefix":"DE-". For quotes, call /v1/quotes/number-ranges with the same body shape, for example {"code":"quote-at","prefix":"ANG-AT-","padding":6,"step":1}, and quote-de with ANG-DE-. A code that is already taken answers 409 and creates nothing.

Nothing uses the new range yet. Point each market at it in the next step.

A range does not belong to a channel. channel_id on a range is a label and selects nothing, so you cannot give one channel its own numbers. Split by market, or accept one sequence for the tenant.

4. Point each market at its range

The range a market draws from is set by a market-scoped setting:

AppSettingDrawn when
Ordersorder_number_range_codeAn order is placed
Ordersdelivery_number_range_codeA delivery note is created by shipping
Ordersreturn_number_range_codeA return is created
Quotesquote_range_codeA quote is created

Each defaults to the code you seeded in step 1 (order, delivery, return, quote). There is no API route that writes an App's settings (as of 2026-10), so this step is done in Cockpit.

Open App Studio → Installierte Apps → Bestellungen → Einstellungen (installed apps → Orders → settings). The labels below are the German ones. The group numbering holds the three order settings: Nummernkreis für Bestellungen, …für Lieferscheine and …für Retouren. The Quotes App has the same screen with one field, Nummernkreis für Angebote (quote_range_code).

The form first shows the global baseline, which applies to every market. To give one market its own value, switch the market in the bar at the top of Cockpit and enter the code there. The market is the same de or at code you see under /v1/markets. The exact label of the bar's default entry may differ from this description.

A market with no value of its own falls back to the global baseline (the tenant-wide value) and then to the default. So a market you never configure keeps drawing from order and quote, and the default range keeps its own sequence next to the market ones. See Settings for how a value resolves.

Read the result back through the API to confirm it before you place anything:

Request
curl "https://api.revenexx.com/v1/settings/apps/orders?market=at" \
  -H "X-Revenexx-Tenant: <TENANT_SLUG>" \
  -H "X-Revenexx-Api-Key: rvxk_..."

settings.order_number_range_code should read order-at. Do the same with /v1/settings/apps/quotes?market=at and settings.quote_range_code. Then place a test order and create a test quote in each market, and check the prefix.

Create the range first, then change the setting. The two Apps differ when the code is wrong:

  • Orders refuses to place an order with 422 and the error number_range_missing. Nothing is created, and the fix is to create the range.
  • Quotes creates the missing range for you, with your code, uppercased, as the prefix: quote-at becomes QUOTE-AT-, not the ANG-AT- you meant. A typo in quote_range_code therefore does not fail. It starts a new, unwanted sequence. Check a test quote. To find a stray range, list them with GET /v1/quotes/number-ranges. Delete a stray range with DELETE /v1/quotes/number-ranges/<ID>. Quotes does not block this once the range has issued numbers, but then those numbers are gone from the sequence, and a deleted standard code comes back on the next draw. Correct the setting first, then delete the stray range.

5. Know what a number promises

Numbers can have gaps. The counter is written before the order or quote is saved. If saving then fails, that number is gone and the next one is the one after it. Do not build a check that treats a missing number as a missing order. These are order and quote numbers, not invoice numbers: if a law or an auditor wants gap-free invoice numbers, we recommend taking them from your ERP or accounting system, where the invoice is created.

Simultaneous orders can collide on a number. Two orders placed in the same instant can draw the same number. Numbers are unique per tenant, so the second is refused, and Orders then draws again (up to five draws in total, so four redraws) before it gives up. Retries are not a guarantee: if they all fail, a 409 reaches you from POST /v1/orders/place. When that 409 is a taken number, the order was not created and placing it again is reasonable. A 409 can also mean a duplicate external_id, and that one is not retried, so read the error before you place again.

If a call times out with no answer, you do not know whether the order exists. /orders/place takes no idempotency key (as of 2026-10). Send your own reference as customer_order_number and, before you place again, look it up with GET /v1/orders?customer_order_number=<REFERENCE>. The platform does not enforce that this value is unique, so use a reference that is unique on your side. Delivery and return numbers are drawn the same way and are not retried for you. See Idempotency.

A range changes only forward. Update a range with PUT /v1/orders/number-ranges/<ID> (or /v1/quotes/number-ranges/<ID>), sending the fields you change. A new prefix, suffix, padding or step applies from the next number and renumbers nothing already issued. Widening padding leaves ORD-000123 as it is and writes ORD-0000124 next.

Orders also guards the range. Setting counter lower than its current value is refused with 422 (counter_rewind), and a range that has issued a number cannot be deleted: 409, range_in_use. Quotes has neither guard. Lowering a quote range's counter hands out numbers that already exist, and the unique index then refuses them with 409 when you create a quote. Treat a quote counter as forward-only yourself.

You cannot restart a sequence at 1 each year. There is no year token and an Orders counter cannot go down. To start a new series, add a range with a new code, for example order-de-2027, and point the market's setting at it. The old range stays, because on Orders it cannot be deleted once it has issued numbers.

counter is the last number drawn, not configuration. Raising it skips numbers and nothing can undo that. Use it only to continue after a migration, so the first new number sits above the highest number you imported.

Checklist

  • Both defaults routes called, orders and quotes.
  • One range per market for orders and for quotes (and for delivery notes and returns if you number them per market), with the market in the code.
  • Each market's setting points at its range, read back through /v1/settings/apps/....
  • A test order and a test quote per market, with the prefix checked.
  • Nothing downstream treats a gap in the numbers as an error.

Where to go next

Was this page helpful?