> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.milenow.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.milenow.com/_mcp/server.

# Business rules and FAQ

## Dates and times

* An FTL's `created_on` is its **pickup day** and `expected_on` its **delivery day**. `expected_on` must be the same day or later.
* Stop times are times only, in the web app and in the API (`pickup_at`, `pickup_to`, `deliver_from`, `deliver_to`, as `H:i`). The pickup times use the pickup day and the delivery times use the delivery day. If you send a date in front of the time, it must be that same day.
* Defaults: the pickup window end is the pickup start; the delivery start is the pickup time; the delivery end is the delivery start.
* Each window must end on or after it starts. The delivery must not start before the pickup.
* When you add stops through the API, the pickup window must end in the future, in your company's time zone. An FTL created on its own is not checked against the clock.
* On a round trip, every return pickup must be on or after the latest outbound delivery start.
* Response date-times are in your company's time zone, `Y-m-d H:i:s`, with no offset.

## FTLs

* **Duplicates.** An FTL with the same `shipment_no`, `bl_no` and any of its container numbers already in your account returns 409 with the existing `shipment_id`. Reuse that id. Sending the same new FTL twice in one add-stops request returns 412.
* **Lookups.** Names match without case, for example `maersk`. A product name can be the full name or a part that matches only one product. A wrong name returns the valid values.
* **Unknown fields** return 412 with the list of valid fields.
* **`tracking_no`** is optional and must be unique. Mile generates one when you leave it out.
* **Return-leg FTLs.** On a new FTL inside a `return` stop, port fields are ignored and the container is picked up at the customer.
* **Status.** New FTLs are `Awaiting Approval`. Assigning a driver moves them to `Local Dispatch`.
* **No edits.** The API cannot change or delete an FTL. Removing a stop keeps the FTL.

## Customers and consignees

Send `id` to reuse a customer or consignee. Use the `consigner.id` or `consignee.id` from an FTL response, not `merchant.id`.

For a new party:

* `name` and `address` are required. A new customer (the shipper) also needs `phone`.
* Mile locates the address with Google Maps. That sets the coordinates, locality and sub-locality. When Google finds the address, its coordinates are used, not yours.
* `latitude` and `longitude` are a fallback for addresses Google cannot find. They must be in range: latitude -90 to 90, longitude -180 to 180. If Google cannot find the address and you sent no coordinates, the request fails: send the full address with district and city, or add coordinates.

**Phone numbers.** Send the phone in the correct format for its country, with the country code. Mile matches it against existing customers in your account.

| Phone found, and the name             | Result                                                                                                             |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| Is the same (case and spaces ignored) | The existing customer is used. The address you sent is ignored                                                     |
| Is different                          | 412 `customer.phone ... already belongs to customer "..." (id ...)`. Send that `customer.id`, or a different phone |

## Trips and stops

* Trips are created in the Mile web app. An add-stops request has 1 to 50 stops. Each stop carries one FTL.
* An FTL can be on one trip only. It can be on both legs of the same trip, but only once per leg.
* Adding stops through the API is all or nothing.
* The API checks the whole request first and returns every problem as 412. Conflicts (409) come after validation passes.
* Optimizing starts the route at your company base. Stops are grouped by day, outbound before return, then ordered by distance. Mile optimizes when you click **Optimize and save sequence** in the web app, and after every change made through the API.
* A stop's `tracking_no` is its first container number. Repeats get `-2`, `-3` and so on.
* Multi drop-off trips made in the Mile web app cannot be changed through the API (409).
* See [when a trip can change](/ftl-trips/guides/manage-stops#when-a-trip-can-change).

## Drivers

* Through the API, you assign only your own drivers, to trips you keep. A transporter's driver returns 412. The transporter assigns its drivers to trips outsourced to it.
* A driver is **busy** when they are on another FTL trip that is not Completed and whose `busy_from` to `busy_to` overlaps this trip. Assigning a busy driver returns 409 with the other trip.
* A trip that is not Completed still blocks its driver up to the current time, even after its planned end.
* A trip without an ETA plan clashes with every other trip of the same driver.
* The API cannot remove a driver from a trip. You can change the driver until the trip starts.

## Outsourcing (web)

Trips are outsourced in the Mile web app. See [outsource a trip](/ftl-trips/guides/outsource).

* Outsourcing must be enabled for your company.
* A trip can be outsourced or offered only while it has no driver and has not started.
* While a trip is outsourced you cannot add stops or assign your driver. You can still remove stops and optimize.
* You cannot outsource a trip directly to a transporter who rejected its offer. A new send can still offer it to them.
* Taking a trip back works only until the transporter assigns a driver.
* Moving a trip to another transporter tells the previous one it is no longer theirs.

## Offers

* Transporters get the offer one at a time, in the order the company listed them.
* Each transporter has the **Time to accept** set for the send: 5 minutes to 7 days, 2 hours by default. The offer's `expires_at` and `seconds_left` show the deadline.
* When a transporter rejects, or the time runs out, the next transporter gets the offer. A later transporter is skipped if it has no free truck during the trip, or no rate for one of the trip's zones.
* When nobody is left, the trip is **Not accepted** and stays with the company.
* Without acceptance required, the trip is assigned to its first transporter at once, with no offer to answer.
* A trip with a driver, a started trip, or a trip that already has a waiting offer cannot be offered.
* The company can stop a send. Waiting offers are withdrawn and the transporter is told.
* An offered transporter sees the route, times and containers, but not customer and consignee names and phones until it accepts. See [what you can see](/ftl-trips/guides/transporter#what-you-can-see).
* Accepting outsources the trip to the transporter. Its status stays `null` until the transporter assigns a driver.

## Trip statuses

| `status.id` | Name       | Meaning                                                                                        |
| ----------- | ---------- | ---------------------------------------------------------------------------------------------- |
| `null`      |            | New trip without a driver. Outsourced trips stay `null` until the transporter assigns a driver |
| 1           | Created    | Used by the Mile web app approval flow                                                         |
| 2           | Requested  | Used by the Mile web app approval flow                                                         |
| 3           | UnAssigned | Approved, waiting for a driver                                                                 |
| 4           | Dispatch   | A driver is assigned                                                                           |
| 5           | Outbound   | The driver has started the outbound leg                                                        |
| 6           | Return     | The driver is on the return leg                                                                |
| 7           | Completed  | Done                                                                                           |

A trip can change while its status is `null` or 1 to 4. From 5 on, it has started. The `status_id` filter cannot select `null` trips.

Stop status `1 Scheduled` means the stop has not started. Only scheduled stops can be removed.

## ETA plan

Every trip has an `eta` plan: Mile's estimate of the truck's work, day by day. It is recalculated after every change and when you [optimize](/ftl-trips/guides/manage-stops#optimize-a-trip-web) in the web app. `calculated_at` says when.

**How it is built**

1. The truck leaves your company base, timed to reach the first pickup when its window opens, but not before the shift starts.
2. The shift comes from the driver's vehicle service window, then the driver's shift, then your company shift. Before a driver is assigned, your company shift is used.
3. Each stop adds service time: the driver's service time, or your company's default.
4. Driving and working hours are tracked. When the work runs past the end of the shift, an `Overnight stop` is added and the trip continues the next day.
5. When the delivery day is later than the pickup day, the container goes to your warehouse nearest the pickup (`Gate In`) and leaves it on the delivery day (`Gate Out`). The stop's `staging_warehouse` names it.

**Fields**

| Field                         | Meaning                                                                                             |
| ----------------------------- | --------------------------------------------------------------------------------------------------- |
| `busy_from`, `busy_to`        | Start and end of the truck's work for the whole trip. Used for driver clashes                       |
| `total_km`, `total_drive_min` | Distance and driving time                                                                           |
| `late`                        | `true` when any stop is late                                                                        |
| `days[]`                      | One entry per working day, with its own `busy_from`, `busy_to`, `km`, `drive_min` and `stops`       |
| `days[].stops[].action`       | `Start`, `Continue`, `Pickup`, `Gate In`, `Gate Out`, `Deliver`, `Overnight stop` or `Back at base` |
| `days[].stops[].kind`         | `base`, `carry`, `pickup`, `warehouse`, `delivery`, `rest` or `return`                              |
| `arrive`, `depart`            | Planned arrival and departure                                                                       |
| `window_from`, `window_to`    | The window you asked for                                                                            |
| `days[].stops[].late`         | `true` when the truck leaves after `window_to`                                                      |

A stop's `eta` in `stops[]` is the planned arrival at the consignee.

> **Note**
>
> The plan is an estimate. A window that closes before the shift starts is always late. For example, an 08:00 to 08:30 pickup is late when the shift starts at 09:30.

## FAQ

#### Why is my trip status null?

New trips have no status until a driver is assigned. Assigning your driver, or the transporter assigning theirs, sets status `4 Dispatch`.

#### Why is a stop late when its time is fine?

The plan starts at your shift start. A pickup window that ends before the truck can get there is late. Check the shift times in the Mile web app, or move the window later.

#### How do I pick up on another day?

The day comes from the FTL `created_on`, and the API cannot edit FTLs. Change the date in the Mile web app.

#### How do I move an FTL to another trip?

[Remove the stop](/ftl-trips/guides/manage-stops#remove-a-stop) from the first trip, then [add it](/ftl-trips/guides/manage-stops#add-ftls-to-a-trip) to the other trip with its `shipment_id`.

#### How do I take my driver off a trip?

You cannot through the API. You can only change to another driver, until the trip starts.

#### I created an FTL twice by mistake. What now?

The second call returns 409 with the first `shipment_id`, so nothing is duplicated. Use that id.

#### Why do I get 401 invalid access with a fresh token?

The company token works only on company endpoints, and the transporter token only on `/transporters` endpoints. Check you are using the right one.

#### What happens when an offer expires?

It moves to the next transporter on its own and leaves the expired transporter's offers list. Accepting after `expires_at` fails with `Time to accept has run out`. The company sees the result on the **Trip Offers** page, and in the trip's `offer` through the API.

#### Why are some coordinates strings?

Some come from stored text fields. Parse `latitude` and `longitude` as numbers whether they arrive as numbers or strings.

#### The call succeeded but has warnings. Is it saved?

Yes. A warning means a follow-up step failed, such as dispatching FTLs or calculating ETAs. [Optimize the trip](/ftl-trips/guides/manage-stops#optimize-a-trip-web) in the web app to retry the ETA plan.