Skip to navigation

Authentication, rate limits and errors

Log in, send the token, and read every response the same way

Base URL

https://lastmile.milenow.com/index.php

All paths below start with /api/v1/partners.

Company login

Log in with your company API user. This is the existing login endpoint.

curl -X POST "https://lastmile.milenow.com/index.php/api/v1/partners/login" \
-d "user=YOUR_API_USER" \
-d "password=YOUR_PASSWORD"
{
"status": 200,
"msg": "",
"msgs": [],
"data": {
"expiry_time": "2026-10-28 16:11:22",
"error_msg": ""
},
"access_token": "<company-token>"
}
  • The token is in access_token at the top level of the response, not inside data.
  • A token lasts 90 minutes.
  • While your token is still valid, logging in again returns the same token with error_msg set to Your Previous Token Already Exist. Within 90 minutes only one token is allowed. The status is 200. This is not an error.
  • Keep the token and reuse it. Log in again when a call returns 401 Your token expired.

Transporter login

Transporters log in with the email or username and password they use on the Mile web app. Owners and team members can both log in.

curl -X POST "https://lastmile.milenow.com/index.php/api/v1/partners/transporters/login" \
-H "Content-Type: application/json" \
-d '{"user": "dispatch@desertline.example", "password": "YOUR_PASSWORD"}'
{
"status": 200,
"msg": "Logged in successfully",
"msgs": [],
"data": {
"access_token": "<transporter-token>",
"expiry_time": "2026-10-28 16:10:23",
"transporter": { "id": 7019257, "name": "Desert Line Transport" },
"user": { "id": 7019257, "name": "Desert Line Transport", "is_owner": true }
}
}
  • The token is in data.access_token. It lasts 90 minutes and cannot be refreshed. Log in again when it expires.
  • Each login returns a new token. Earlier tokens keep working until they expire.
  • Changing the password ends every token for that login.
  • A team member (is_owner: false) acts for the whole transporter: same offers, trips and drivers.

Send the token

Send the token in the Authorization header:

curl "https://lastmile.milenow.com/index.php/api/v1/partners/shipment-trips" \
-H "Authorization: Bearer $MILE_TOKEN"

You can also send it as an access_token query parameter, or as an access_token field in a JSON body. The examples in these docs use $MILE_TOKEN for the company token and $TRANSPORTER_TOKEN for the transporter token.

The company token works only on company endpoints. The transporter token works only on /api/v1/partners/transporters/.... The wrong token returns 401 invalid access.

Rate limits

Mile sets a limit of requests per minute for your company. Each login has its own counter. The minute starts with your first request and resets 60 seconds later.

FTL trip and transporter endpoints return these headers once the token is accepted:

HeaderMeaning
X-RateLimit-LimitRequests allowed per minute
X-RateLimit-RemainingRequests left in this minute
X-RateLimit-ResetSeconds until the minute resets

When you go over the limit you get 429 with a Retry-After header in seconds. Wait that long, then retry.

{
"status": 429,
"msg": "Rate limit reached: 200 requests per minute. Try again in 42 seconds.",
"msgs": ["Rate limit reached: 200 requests per minute. Try again in 42 seconds."],
"data": []
}

Response envelope

Every response has the same four fields. The HTTP status code and status are always equal.

FieldOn successOn error
status200The error code
msgWhat happenedThe problem, or how many problems there are
msgs[]Every problem, one per item
dataThe result[]

Status codes

CodeMeaningWhat to do
200Done. Trip changes can still carry data.warnings.Read data, and check warnings
400Invalid JSON bodySend valid JSON with Content-Type: application/json
401invalid access, Your token expired., or a bad loginLog in again, or use the right token for the endpoint
403Your partner account is disabled on a transporter endpointAsk the company to enable the transporter account
404The trip or stop is not in your account, or not outsourced or offered to the transporterCheck the id
409The request is valid but conflicts with the current stateRead msg, change the state, then retry
412Validation failedFix every item in msgs, then retry
429Rate limit reachedWait Retry-After seconds
500Something went wrong, please try againRetry later. Contact Mile if it repeats

Validation errors (412)

Mile checks the whole request and returns every problem at once in msgs. Each item says where the problem is, for example stops[1].shipment: containers[0].weight is required (0 or more). Fix them all before you retry.

{
"status": 412,
"msg": "3 problems found, listed in msgs. Fix all of them and send again.",
"msgs": [
"stops[0]: stop has unknown fields shipmentId, pickupAt. Valid fields: leg, shipment_id, shipment, pickup_at, pickup_to, deliver_from, deliver_to",
"stops[0]: send shipment_id or a shipment object",
"stops[1].shipment: customer.id not found. It is a customer id (consigner.id or consignee.id in shipment responses), not merchant.id"
],
"data": []
}

Unknown fields are rejected, and the message lists the valid ones. Field names are snake_case.

Conflicts (409)

A 409 means the request is valid but cannot be done in the trip’s current state. Validation runs first, so you only see a 409 once the request passes validation. Common conflicts:

  • An FTL is already on another trip.
  • An FTL with the same shipment_no, bl_no and container already exists. The message gives its shipment_id.
  • The trip is outsourced, so you cannot add stops or assign your driver.
  • The trip has started.
  • The driver is busy on another trip at the same time.
  • The stop has started, or it is the last stop on the trip.
  • No offer in the request could be accepted or rejected.

The business rules explain each one.

Warnings

Requests that change a trip return data.warnings. A warning means the change was saved but a follow-up step failed, for example ETAs could not be calculated: .... Optimize the trip in the Mile web app to retry.

Formats

  • Request dates: Y-m-d, for example 2026-11-03.
  • Request times: H:i in 24-hour time, for example 09:00.
  • Response date-times: Y-m-d H:i:s in your company’s time zone, with no offset.
  • Coordinates can come back as numbers or numeric strings. Accept both.
  • order_id can be larger than a 32-bit integer. Store ids as 64-bit integers.