Skip to content
AtlasVialo
Developers

Put a capacity engine inside your TMS.

One REST API for everything Vialo Fleet does: price an extra load on a route, sync your tours, book cargo and get told about every change. JSON in, JSON out, money in minor units.

Base URL  https://fleet.atlasvialo.com/api/v1

Quick start

  1. 1Create a key

    In Vialo Fleet, open Integrations and create an API key. It is shown once.

  2. 2Ask for a verdict

    Send a route and a few loads to POST /evaluate. Nothing is stored.

  3. 3Sync your tours

    PUT each tour from your TMS by its own reference and read the suggestions.

  4. 4Listen

    Add a webhook endpoint to hear about bookings, pickups and deliveries.

curl -X POST https://fleet.atlasvialo.com/api/v1/evaluate \
  -H "Authorization: Bearer vk_…" \
  -H "Content-Type: application/json" \
  -d @evaluate.json

Authentication

Send the key as a bearer token or in X-Api-Key. A key acts for one company with the permissions of the owner who created it, and stops working when it is revoked.

Every write except /evaluate takes an Idempotency-Key header, so a retry never books twice.

Authorization: Bearer vk_1a2b3c4d_…
X-Api-Key: vk_1a2b3c4d_…
Idempotency-Key: 6f1e2c8a-…

Evaluate a load

The engine on demand: is this extra load good for this vehicle on this route?

POST/evaluate

Up to 100 loads per call. Each answer carries the verdict, the six checks, the full arithmetic, where the load fits among your stops and every ETA. A price of null returns the lowest price that meets your rules.

Request
{
  "currency": "EUR",
  "vehicle": {
    "max_weight_kg": 2000,
    "cost_per_km_minor": 80,
    "cost_per_hour_minor": 4000
  },
  "route": {
    "origin": {
      "lat": 62.3908,
      "lng": 17.3069
    },
    "destination": {
      "lat": 63.8258,
      "lng": 20.263
    },
    "via": [
      {
        "point": {
          "lat": 62.487,
          "lng": 17.326
        },
        "label": "Timrå delivery",
        "service_time_s": 900,
        "load_change": {
          "weight_kg": -1200
        }
      }
    ],
    "depart_from": "2026-10-05T07:00:00+02:00",
    "onboard": {
      "weight_kg": 1500
    }
  },
  "rules": {
    "max_delay_s": 7200,
    "min_profit_minor": 2000
  },
  "loads": [
    {
      "ref": "ORDER-4711",
      "pickup": {
        "lat": 62.6323,
        "lng": 17.9379
      },
      "dropoff": {
        "lat": 63.2909,
        "lng": 18.7153
      },
      "weight_kg": 300,
      "pickup_from": "2026-10-05T08:00:00+02:00",
      "pickup_until": "2026-10-05T14:00:00+02:00",
      "deliver_by": "2026-10-05T18:00:00+02:00",
      "price_minor": 40000
    }
  ]
}
Response
{
  "results": [
    {
      "ref": "ORDER-4711",
      "verdict": "good",
      "blocked_by": null,
      "arithmetic": {
        "detour_distance_m": 8700,
        "detour_duration_s": 2220,
        "extra_cost_minor": 4560,
        "price_minor": 40000,
        "profit_minor": 35440,
        "profit_per_hour_minor": 57470,
        "min_price_minor": 6560,
        "currency": "EUR"
      },
      "insertion": {
        "pickup_after": 1,
        "dropoff_after": 1
      },
      "depart_at": "2026-10-05T05:00:00.000Z",
      "pickup_eta": "2026-10-05T06:12:40.000Z",
      "gates": [
        {
          "gate": "physical",
          "outcome": "pass",
          "detail": {
            "peak_kg": 600,
            "max_kg": 2000
          }
        },
        "…"
      ],
      "stops": [
        {
          "sequence": 0,
          "kind": "origin",
          "eta": "…",
          "load_after_kg": 1500
        },
        "…"
      ]
    }
  ],
  "route": {
    "base_distance_m": 276000,
    "base_duration_s": 11400,
    "via_stops": 1
  }
}

TMS connector

Your tours and vehicles, by your own references.

PUT/tms/vehicles/{ref}

Create or update a vehicle by your reference: capacity, cost per kilometre, cost per hour and equipment.

Request
{
  "label": "Truck 07",
  "max_weight_kg": 24000,
  "cost_per_km_minor": 172,
  "cost_per_hour_minor": 4800,
  "tags": [
    "tail-lift"
  ]
}
Response
{
  "id": "9c1e…",
  "external_ref": "TRUCK-07",
  "created": true
}
PUT/tms/tours/{ref}

Create a tour with its stops, load changes and deadlines. Sending it again replaces it while nothing is booked.

Request
{
  "vehicle_ref": "TRUCK-07",
  "origin": {
    "point": {
      "lat": 62.3908,
      "lng": 17.3069
    },
    "label": "Sundsvall depot"
  },
  "destination": {
    "point": {
      "lat": 63.8258,
      "lng": 20.263
    },
    "label": "Umeå depot"
  },
  "depart_from": "2026-10-05T07:00:00+02:00",
  "onboard": {
    "weight_kg": 1500
  },
  "stops": [
    {
      "point": {
        "lat": 62.487,
        "lng": 17.326
      },
      "label": "Customer A",
      "service_time_s": 900,
      "load_change": {
        "weight_kg": -1000
      },
      "latest_arrival": "2026-10-05T10:00:00+02:00"
    }
  ]
}
Response
{
  "trip_id": "4f2a…",
  "external_ref": "TOUR/2026-10-05/17",
  "replaced": null,
  "base_distance_m": 276000,
  "base_duration_s": 11400
}
GET/tms/tours/{ref}/suggestions

The cargo that fits the tour right now, best profit per hour first, with how likely your team is to accept it.

Response
{
  "trip_id": "4f2a…",
  "external_ref": "TOUR/2026-10-05/17",
  "suggestions": [
    {
      "offer_id": "b71d…",
      "package_id": "0e9c…",
      "pickup_label": "Härnösand",
      "dropoff_label": "Örnsköldsvik",
      "weight_kg": 300,
      "price_minor": 40000,
      "min_price_minor": 6560,
      "profit_minor": 35440,
      "profit_per_hour_minor": 57470,
      "detour_duration_s": 2220,
      "currency": "EUR",
      "acceptance_likelihood": 0.82,
      "insertion": {
        "pickup_after": 1,
        "dropoff_after": 1
      },
      "expires_at": "…"
    }
  ]
}
DELETE/tms/tours/{ref}

Cancel a tour that has no bookings.

Response
{
  "cancelled": true,
  "trip_id": "4f2a…"
}

Offers and booking

Read the ranked offers for a trip, book one, or propose your own price.

GET/trips/{id}/offers

Ranked offers for a trip, plus the loads a rule blocked and the reason.

Response
{
  "offers": [
    {
      "id": "b71d…",
      "package_id": "0e9c…",
      "reward_minor": 40000,
      "profit_minor": 35440,
      "profit_per_hour_minor": 57470,
      "acceptance_likelihood": 0.82,
      "…": "…"
    }
  ],
  "rejects": [
    {
      "package_id": "77aa…",
      "blocked_by": "sla",
      "…": "…"
    }
  ]
}
POST/offers/{id}/accept

Book an offer. Every check runs again against the current plan; a load can only ever be booked once.

Request
{
  "confirm_tracking": true
}
Response
{
  "accepted": true,
  "assignment_id": "a3d0…",
  "trip_id": "4f2a…",
  "package_id": "0e9c…",
  "agreed_price_minor": 40000
}
POST/offers/{id}/quote

Propose your own price on an offer. Below your own rules it is refused before it is sent.

Request
{
  "price_minor": 42000,
  "message": "We pass Härnösand around 08:15.",
  "confirm_tracking": true
}
Response
{
  "conversation_id": "c19f…",
  "quote_id": "q2b8…",
  "profit_minor": 37440,
  "min_quote_minor": 6560
}

External references

Find any vehicle, trip or package by the id your system already uses.

GET/{vehicles|trips|packages}/by-ref/{ref}

Look up the AtlasVialo id behind your reference. URL-encode the reference.

Response
{
  "id": "4f2a…",
  "external_ref": "TOUR/2026-10-05/17"
}

Webhooks

Signed events pushed to your server, retried until they arrive.

POST/webhooks

Add an HTTPS endpoint, optionally for chosen events only. The answer holds the signing secret.

Request
{
  "url": "https://tms.example.com/atlasvialo",
  "events": [
    "booking.created",
    "trip.offers_updated"
  ]
}
Response
{
  "id": "w81c…",
  "url": "https://tms.example.com/atlasvialo",
  "events": [
    "booking.created",
    "trip.offers_updated"
  ],
  "secret": "whsec_…",
  "active": true
}
POST/webhooks/{id}/test

Send one signed test event now and see how your endpoint answered.

Response
{
  "delivered": true,
  "status": 200,
  "error": null
}
GET/webhooks/{id}/deliveries

The latest deliveries with status, attempts and the last error.

Response
{
  "deliveries": [
    {
      "id": "d5e2…",
      "event": "booking.created",
      "status": "delivered",
      "attempts": 1,
      "last_status": 200,
      "created_at": "…"
    }
  ]
}

Metrics and learning

The funnel from trips to bookings and what blocks cargo.

GET/metrics/funnel?from=…&to=…

Trips posted, trips with offers, loads evaluated, viable, booked and delivered, revenue and net profit.

Response
{
  "trips_posted": 42,
  "trips_with_offers": 37,
  "candidates_evaluated": 1180,
  "viable_offers": 214,
  "bookings": 61,
  "delivered": 58,
  "booked_revenue_minor": 1830000,
  "booked_profit_minor": 1412000,
  "rates": {
    "trips_with_offers": 0.881,
    "viable_of_evaluated": 0.181,
    "booked_of_viable": 0.285
  }
}
GET/metrics/insights

Which checks block the most cargo, and how many blocked loads a looser rule would let through.

Response
{
  "blocked": [
    {
      "blocked_by": "sla",
      "reason": "delay_exceeds_max",
      "n": 312
    },
    {
      "blocked_by": "margin",
      "reason": "profit_below_min",
      "n": 140
    }
  ],
  "what_if": [
    {
      "rule": "max_delay_s",
      "change": "+25%",
      "would_pass": 64
    }
  ]
}

Verifying a webhook

X-Vialo-Signature is t=<unix seconds>,v1=<hex HMAC-SHA256 of "t.raw body" with your endpoint secret>. Recompute it, compare in constant time and reject old timestamps. Use the id field to ignore duplicates.

POST https://tms.example.com/atlasvialo
{
  "id": "d5e2…",
  "event": "booking.created",
  "created_at": "2026-10-05T06:01:12.000Z",
  "data": { "trip_id": "4f2a…", "trip_external_ref": "TOUR/2026-10-05/17",
            "package_id": "0e9c…", "agreed_price_minor": 40000, "currency": "EUR" }
}
Node.js
const [t, v1] = header.split(',').map((p) => p.split('=')[1]);
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest();
const ok = crypto.timingSafeEqual(Buffer.from(v1, 'hex'), expected)
  && Math.abs(Date.now() / 1000 - Number(t)) < 300;

Errors

Errors are JSON with an error code: 400 invalid request (with the issues), 401 for a missing or revoked key, 403, 404, 409 for conflicts such as external_ref_exists or tour_has_bookings, and 422 when a route cannot be served.

{
  "error": "tour_has_bookings",
  "trip_id": "4f2a…"
}

Road distances and times come from our own road router. Today it covers Sweden; loads outside it are answered as not routable.