> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bahnexpress.fi/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> For Bahn Customer API V2 integration work, start with /skill.md and /guides/agent-integration.md on this documentation site. Use /llms.txt to find the guides for the requested workflow.
> The main integration orders your company's transports from existing software. Follow the requested screen, service, or job. Omit ordered_for for own-account orders. Use the platform-ordering guide to choose order ownership and billing for businesses using a platform. Add a customer-facing interface only when requested.
> Use /openapi.yaml for paths, schemas, scopes, and operation preconditions. Use the guides for transport rules and recovery behavior. Load only the relevant pages.
> Use the sandbox for development and tests. Keep credentials on the server. Production orders start real transport work; follow the customer's authorized workflow and account context.
> For required writes, preserve write keys and request data across retries, and use current actions and quoted revisions for order changes. When consuming webhooks, verify and durably store them before acknowledgement.
> Deliver the implemented workflow with test evidence, setup steps, and remaining gaps. Distinguish simulated checks from requests actually verified against Bahn.

# Pickup and delivery times

> Request transport times and show requested windows, agreed windows, ETAs, deadlines, and actual times correctly.

An order contains several kinds of time. Each one answers a different question. Keep them separate in your system and in your user interface.

* `available_from`: When can Bahn first collect the vehicle?
* `requested_windows`: Which times did you request?
* `agreed_window`: Which time range did Bahn agree to?
* `commitment_deadline_at`: What is the latest committed delivery time?
* `eta`: When does Bahn expect to arrive?
* `eta_updated_at`: When did Bahn last update the ETA?
* `actual_at`: When did the stop complete?

A requested window is not a commitment. `agreed_window` is `null` until Bahn agrees a window.

Pickup and delivery each have `requested_windows`, `agreed_window`, `eta`, `eta_updated_at`, and `actual_at`. Only pickup has `available_from`. Only delivery has `commitment_deadline_at`; there is no pickup deadline.

The order list returns only `actual_pickup_at` and `actual_delivery_at` for each order. Read the full order for the other times.

## Request pickup and delivery times

Send the times when you create an order. The same rules apply when you change an order.

* Pickup needs `available_from`, at least one requested window, or both.
* When you send both, each pickup window must start at or after `available_from`.
* Each window needs `start_at`, `end_at`, and an IANA time zone such as `Europe/Helsinki`.
* Send `start_at` and `end_at` as RFC 3339 timestamps with an offset.
* Each window must start before it ends, and it must end in the future.
* The requested windows for one stop must not overlap.
* A delivery window must end after pickup can start.

For a private pickup or an `end_customer` delivery, pickup must start on the next calendar day or later in `Europe/Helsinki`. This is a calendar-day rule, not a 24-hour waiting period.

```json theme={null}
{
  "pickup": {
    "available_from": "2099-08-03T08:00:00+03:00",
    "requested_windows": [
      {
        "start_at": "2099-08-03T08:00:00+03:00",
        "end_at": "2099-08-03T12:00:00+03:00",
        "timezone": "Europe/Helsinki"
      }
    ]
  }
}
```

[Create and find orders](/guides/orders#create-an-order) contains complete requests with these fields.

## Show the current ETA

Read `pickup.eta` and `delivery.eta` from the full order with `GET /v2/orders/{order_id}`. Each ETA is an RFC 3339 timestamp. The order list does not contain ETAs.

Choose the time to show in this order:

1. After the stop completes, show `actual_at`.
2. When `eta` is present, show it as the current estimate. Show `eta_updated_at` near it.
3. When `eta` is `null`, show `agreed_window`.
4. When there is no agreed window, show the requested windows.

`eta` is `null` when Bahn has no reliable estimate, or when the estimate is already in the past. Do not calculate an ETA from a map position, and do not show a window as an ETA.

A live ETA change does not change the order revision. You can keep the revision that you have when only the ETA changes.

## Get notified about time changes

* `com.bahn.order.eta_changed.v1`: An ETA appears or disappears, or changes by 15 minutes or more. Bahn also sends it when the ETA moves outside or back inside the agreed window. Without an agreed window, the delivery commitment deadline is the limit.
* `com.bahn.order.schedule.changed.v1`: A planned pickup or delivery time of the order changed.

Small ETA changes add up from the last ETA that Bahn sent. Read the order before you send a message to a customer or store a new ETA. Read [Webhooks](/guides/webhooks#eta-and-tracking-events) for the event contents.

Read [Live tracking](/guides/tracking-and-inspections) for the vehicle position and the driver's approach to pickup.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.