Configure order and quote numbers per market
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-.
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 '{}'
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.
| Code | Numbers | First number |
|---|---|---|
order | Orders | ORD-000001 |
delivery | Delivery notes | DEL-000001 |
return | Returns | RET-000001 |
quote | Quotes | ANG-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:
{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:
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.
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:
| App | Setting | Drawn when |
|---|---|---|
| Orders | order_number_range_code | An order is placed |
| Orders | delivery_number_range_code | A delivery note is created by shipping |
| Orders | return_number_range_code | A return is created |
| Quotes | quote_range_code | A 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:
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
422and the errornumber_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-atbecomesQUOTE-AT-, not theANG-AT-you meant. A typo inquote_range_codetherefore does not fail. It starts a new, unwanted sequence. Check a test quote. To find a stray range, list them withGET /v1/quotes/number-ranges. Delete a stray range withDELETE /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
defaultsroutes 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
- Number ranges — every field of an orders range.
- Turn an accepted quote into an order — where the quote number ends up.
- Hand an order to the ERP — the number is the reference your ERP sees.