Skip to main content
An order represents one vehicle transport. It always contains one primary vehicle and can also contain one trade-in vehicle. For transports on your own Bahn account, omit ordered_for. Read Quickstart for a complete sandbox creation request. Use the API reference for every request and response field.

Create an order

Supply these facts for a standard order: Use the example that matches your order. Each example is a complete request.
For orders on behalf of other businesses, use the separate Platform ordering guide. A cross-border order needs the primary VIN. A Finland order can use the primary registration number without a VIN. Use customer_note for instructions that apply to the complete order. Use items[].note for instructions about one vehicle. A 201 response means that Bahn accepted the order. Store these fields from the response:
The order also contains the price that Bahn accepted at creation.

Find and read orders

Call GET /v2/orders/{order_id} for the complete current order. Call GET /v2/orders for a page of summaries, ordered from newest to oldest. The list accepts status and an exact customer_reference filter. A customer reference is not unique, so the filter can return several orders.
When has_more is true, send next_cursor as the next request’s cursor, keeping the same filters. Read the full order before making a decision that depends on its current actions or revision.

Order and item status

The order status summarizes all required vehicles. Each vehicle also has its own status. Use the item status when a primary and trade-in vehicle are at different stages. A trade-in order can stay in_transit after Bahn delivers the primary vehicle. Cancellation keeps the last physical item status and actual times.

Keep time facts separate

An ETA is one RFC 3339 timestamp. It is null when no current estimate is available. Use eta_updated_at to show when Bahn last updated the ETA. Do not present a requested or agreed window as an ETA. The pickup needs available_from or one requested window. Requested windows for one stop must not overlap. You can send available_from, requested_windows, or both in the same pickup object. If both are supplied, each pickup window must start at or after available_from. Each window’s start must precede its end, and the end must be in the future. A delivery window must end after pickup can start. For a private pickup or an end-customer delivery, the pickup start must fall on the next day or later in Europe/Helsinki. The same rule applies when changing an order. This is a calendar-day rule, not a 24-hour waiting period. Each window needs start_at, end_at, and an IANA time zone. A requested window is not a commitment.

Vehicle condition and inspection

Each item has a pickup_condition and a delivery_condition. A condition stays null until the handover result is available. Use evidence_file_ids to find related photos in the order file list. Do not infer a condition from an item status or a photo. A full inspection report is a separate resource. Read GET /v2/orders/{order_id}/inspection-report when you need it.

Vehicle and service rules

A trade-in vehicle needs a registration number. Trade-in orders are available only for routes inside Finland. A private pickup cannot have a trade-in vehicle. A private pickup requires a contact name and phone number. An end_customer delivery requires a delivery contact name and phone number. Use standard for the normal pickup check. Use full only when the commercial agreement permits a full inspection.

Choose a document recipient

Ordinary bookings use the customer’s CMR recipient defaults. Set items[].cmr_recipient only when you need a different document recipient. This changes neither the route nor the document handling mode. Read CMR recipients for choices, stale-address protection, and the rules that fix the recipient at creation.

Update an order

Update only when allowed_actions contains update. Send a JSON Merge Patch request with the current revision inside double quotation marks in If-Match. An omitted field keeps its current value. A supplied pickup or delivery object changes only its supplied fields. The file_ids field adds files. It does not remove files. requested_windows replaces the supplied stop’s window list, and an empty list clears it. Clearing pickup windows requires retaining available_from so pickup still has a start time. Use null to clear customer_reference or customer_note. An items update contains one item with role: primary. It replaces the primary vehicle input while leaving the trade-in unchanged. Include its VIN or registration number and all vehicle facts you want to retain. service_options requires both transport_preference and inspection when supplied. The recipient cannot change after creation. This example reads the revision and changes an order-level note. Set BAHN_ORDER_ID to the order you intend to change.
After a timeout, retry the write with the same key, revision, and body. After revision_conflict, read the order again and decide whether the change still applies. A new decision uses a new key. A live ETA change does not change the order revision. You can keep the revision when only the live ETA changes.

Cancel an order

Cancellation stops open production work. Confirm the customer intent before you send a production cancellation.
Cancel only when allowed_actions contains cancel. Send the current revision inside double quotation marks in If-Match and use an idempotency key. This example reads the latest order and cancels only when the action is available. Set BAHN_ORDER_ID to the intended test order.
These shell examples use curl, jq, and uuidgen. Keep the write key when retrying the same request. The cancelled order stays available for reads.