ordered_for is optional. Leave it out and the order behaves exactly like an order from any other customer: your own Bahn account owns it. Add it and Bahn assigns the order to the business you name.
You do not need a second credential to switch between the two. You decide per request.
If your company only ever arranges its own transports, you do not need an ordering channel. Use a standard credential and ignore ordered_for.
Contact support@bahnexpress.com to have your account configured as an ordering channel before you start production testing.
Compare the two requests
Everything else in the request stays the same.- Your own order
- Order for a business on your platform
"ordered_for": null.ordered_for value in the price check and in the order that follows it. A price check for your own account and an order for another business can return different prices.
Name the represented business
Send the official business ID and the country code of the business:FI, DK, DE, and PL.
Bahn ignores spaces, punctuation, and a leading country prefix when it matches the business ID, so FI12345678, 12345678, and 1234567-8 all reach the same business. The country_code must match the country on that business’s Bahn account.
In production the business must already exist as a Bahn customer, and exactly one must match. A request that matches none, or more than one, is rejected with ordered_for_customer_not_found. Ask support@bahnexpress.com to confirm the setup before your first production order for a new business.
The represented business does not need its own API credential or a Bahn portal account, and there is no separate consent or grant step in the API flow.
Check before showing the order flow
CallPOST /v2/represented-business-checks when your platform needs to decide whether to offer Bahn transport for a business. Send the business identity directly:
ordered_for_customer_not_found as not eligible. When no customer matches, the sandbox still returns eligible, so it tests your integration flow rather than production customer existence. More than one matching customer is rejected in both environments.
Understand who owns what
The represented business owns the transport and the commercial order. You own the integration: the customer reference, the stored Bahn order ID, the webhook handling, and the support relationship with Bahn. An ordering channel can read and change every order that belongs to its own Bahn account, regardless of where the order was created. It can also read and change orders it created for another business. It cannot read other orders of a business it represents. If the represented business also has its own Bahn credential, it can read the order you created for it. When you rotate your credential, the replacement keeps access to every order the ordering channel created.Store the context you need
Keep these values together in one record:
The same context appears in webhook events and report rows, so one integration can serve many businesses without mixing their data.
Route webhooks
Configure one webhook endpoint for your ordering channel. Readdata.ordered_for on every order event to decide which business the event belongs to. A null value means the order is your own.
Bahn can also deliver the same event to an active endpoint that the represented business configured. Both deliveries carry the same event ID and order sequence, so each receiver deduplicates normally. Your own orders produce a single delivery, because there is no second business to notify.
Upload files
Upload documents with your ordering-channel credential, then attach the returned file IDs to the order. The represented business can read the attached files. Only your channel can delete its own upload through the API, and only whilecan_delete is true.
Test the flow
The sandbox accepts any business ID in a supported country. When none matches, the sandbox creates an isolated business for that ID, so you can build the flow before any real business exists in Bahn. Cover all four cases before you go live:- One represented-business check, and confirm your product offers the order flow only after
eligible. - One order without
ordered_for, and confirm your product treats it as your own. - Two orders with different
ordered_forvalues, and confirm your storage, webhook routing, and customer-facing views never mix them. - One rejected request, so you handle the problem codes below.