Public / partner documentation This version is intended for customers and partners integrating the APIs. Some sections reserved for administrators (user management, tiers, infrastructure details) are not included.

Base URL & Environments

Endpoints are versioned under /api/v1. Always use HTTPS.

Environment Base URL Notes
Production https://flow.icarry.it Connected to real fleet and jobs scheduled by iCarryUP.

In this partner version, links to Swagger and ReDoc are not shown. Access to /api/docs, /api/redoc, and /openapi.json is restricted to superadmins only (other accounts get 403). For partners, the endpoint list on this page is the reference.

Authentication

The API uses JWT tokens with Bearer. Obtain the token by sending credentials to the OAuth2 token endpoint.

POST /api/v1/auth/token
Content-Type: application/x-www-form-urlencoded

grant_type=password&username=OPS_USER&password=********

The returned token must be sent in the header:

Authorization: Bearer <ACCESS_TOKEN>

⚠️ Set server-side token rotation every 24h or use scheduled refresh. For server-to-server integrations, create dedicated technical users (role: system).

Recommended Workflow

  1. Load logistics data (drivers + deliveries) in the format expected by RoutingOptimizationRequest.
  2. Send POST /api/v1/routing/optimize with async_processing=true; receive a job_id.
  3. Monitor status via GET /api/v1/routing/jobs/{job_id} (polling 5-10s) or via WebSocket /ws/jobs.
  4. When status=completed read result, export files or generate maps with dedicated endpoints.
  5. Optional: set webhook_url in the request to receive push callbacks (payload identical to result).

Critical Endpoints

POST /api/v1/routing/optimize

Submits a new optimization. Supports both modern and legacy payloads (automatic conversion of lat/lng to start_lat, etc.).

{
  "async_processing": true,
  "session_id": "tms-session-example-001",
  "webhook_url": "https://your-tms.example.com/webhooks/routing",
  "delivery_points": [
    {
      "tracking_number": "SHIP-EXAMPLE-0001",
      "delivery_company": "Example SpA",
      "address": "Via Esempio 1, Milano",
      "lat": 45.4642,
      "lng": 9.1900,
      "tot_items": 4,
      "priority": 1
    }
  ],
  "drivers": [
    {
      "name": "Driver example 01",
      "start_lat": 45.5342,
      "start_lng": 9.3265,
      "end_lat": 45.5342,
      "end_lng": 9.3265,
      "capacity": 90,
      "vehicle_type": "van_standard",
      "max_distance_km": 220
    }
  ],
  "algorithms": ["standard"],
  "optimization_objective": "time",
  "optimization_params": {
    "max_iterations": 120,
    "time_limit_seconds": 420,
    
    "distance_weight": 0.5,
    "time_weight": 0.3,
    "balance_weight": 0.2
    
  },
  "constraints": {
    "max_route_distance": 250,
    "max_route_duration": 480,
    "max_stops_per_route": 45
  }
}

Immediate response:

{
  "job_id": "6b758bee-65e5-473e-add8-8cecc36f6fda",
  "status": "queued",
  "estimated_completion": "2025-11-26T09:11:37.123Z",
  "result_url": "/api/v1/routing/jobs/6b758bee-65e5-473e-add8-8cecc36f6fda"
}

GEL (locker pickup): if the user does not have the gel_proximity plan feature (tenant with GEL on map + flag on tier/profile), the backend silently strips pickup_point_id from deliveries and gel_cost_to_time_equivalent_minutes_per_eur from optimization_params before optimization.

Which objective to send

The same four choices as the console wizard. The field is optimization_objective on POST /api/v1/routing/optimize. Default is time. An unknown value is treated as time. They exist because one number cannot be the shortest tour, the most punctual tour, and the cheapest tour at once.

Less time time

WhyWithout this, a short-kilometre tour can leave recipients waiting. It penalises waiting and fills time windows.

WhenUse it for SLAs, tight windows, or promised arrival times.

Fewer km distance

WhyUse it when kilometres dominate, not the clock. Waiting is not penalised.

WhenUse it when fuel and tolls dominate, stops are sparse, or time windows are few.

Balanced balanced

WhyA compromise between time and kilometres, without pushing either extreme.

WhenUse it for a mixed fleet or an ordinary day.

Lower cost cost

WhyTurns kilometres and hours into euros using the driver fields hourly_cost, fuel_consumption, prezzo_litro, and costo_pedaggio_km.

WhenUse it for a fleet budget or a cost comparison. budget_max_eur in optimization_params is the cap, not this objective.

This is not balance_options: that splits workload across drivers (similar stop counts, km, or minutes). The weights distance_weight, time_weight, and balance_weight in optimization_params are fine-tuning; for an integration, optimization_objective is enough.

{
  "optimization_objective": "time"
}

Replace time with distance, balanced, or cost. The rest of the optimize request stays as in the example above.

GET /api/v1/routing/jobs/{job_id}

Returns status, progress and, if completed, the full RoutingResultResponse (routes, geometries, global metrics). Results are persisted in shared cache and deleted during scheduled maintenance.

A completed result may include savings_report: estimated savings (km/time/cost) versus a nearest-neighbor baseline on the same job.

GET /api/v1/routing/jobs

Lists the user's latest jobs. Accepts ?limit=50 and ?status=processing/completed... for operational dashboards.

DELETE /api/v1/routing/jobs/{job_id}

Cancels queued or running jobs. Returns 400 if job is already completed.

GET /api/v1/routing/eta

Estimates wall-clock optimization duration (seconds) from similar historical runs (P50/OLS) or the TSP budget. Typical query params: deliveries, drivers, algorithm, routing_provider, has_time_windows, pickup_count, optimization_objective.

GET /api/v1/routing/history

Persistent optimization history for the user/tenant (optionally excludes simulations). Supports q= to search by name or job_id. Useful for dashboards and audit.

GET /api/v1/routing/calendar

Calendar events (planned runs and drafts) for a date range. Powers the console calendar view.

GET /api/v1/routing/status

Enriched health-check (active jobs, version, uptime). Can be used for monitoring systems or readiness probes.

The algorithms_available field is reserved for administrative accounts; for others it is always an empty array.

POST /api/v1/routing/simulate

Starts a what-if simulation (Control Tower) from a baseline job. Returns a simulation_id; the result is an optimization job with is_simulation=true.

{
  "baseline_job_id": "6b758bee-65e5-473e-add8-8cecc36f6fda",
  "baseline_scope": "PLANNED",
  "simulation_mode": "reoptimize",
  "patches": [
    {
      "type": "add_delivery",
      "point_type": "delivery",
      "lat": 41.9028,
      "lng": 12.4964,
      "tracking_number": "NEW-001",
      "pickup_point_id": "HUB-1"
    },
    { "type": "remove_delivery", "tracking_number": "OLD-042" }
  ]
}

Supported patches: add_delivery (also pickups with point_type=pickup and pickup_point_id), remove_delivery, add_driver, remove_driver, delivery_time_window, balance_options, invalidate_matrices. baseline_scope=PLANNED (LIVE not supported in v1).

GET /api/v1/routing/simulations/{simulation_id}

Returns simulation metadata and comparison (baseline vs what-if result).

POST /api/v1/routing/estimate-workload

Estimates workload (minutes/drivers) before choosing the fleet. mode=quick (fast heuristic) or precise (solver with virtual fleet).

GET /api/v1/routing/delivery-execution/summary

Delivery execution KPI (e.g. on-time %) aggregated across the user's recent jobs.

GET /api/v1/routing/jobs/{job_id}/delivery-execution

Per-job delivery execution detail and proof of delivery (POD).

POST /api/v1/routing/manual-assignments

Allows fixing deliveries → driver before launching optimization. Other GET/DELETE /manual-assignments endpoints allow reading/resetting preferences.

POST /api/v1/routing/jobs/{job_id}/generate-real-map

Creates persistent HTML maps (web/static/maps folder) ready for distribution to drivers. GET .../download-map and GET .../export generate maps and Excel respectively.

POST /api/v1/routing/jobs/{job_id}/generate-geometries

Generates road geometries for a completed job (JWT required). Optional If-Match header with routes_version_hash; mismatch returns 409 routes_version_mismatch — retry without the header (geometries come from the stored job).

GET /api/v1/routing/driver-maps

Lists driver maps for the current tenant (JWT required; 401 without token). Superuser without impersonation may see all tenants.

Geocoding (authenticated)

JWT required. POST /optimize does not geocode: numeric lat/lng in the JSON stay mandatory. Call these endpoints first. source is a diagnostic resolver label, not a business contract.

POST /api/v1/geocoding/lookup

Cache only, up to 500 addresses. hit false means not cached (use forward). Does not start an external resolve.

{
  "addresses": ["Via Roma 1, Milano", "Piazza Duomo, Firenze"]
}
{
  "results": [
    { "index": 0, "hit": true, "lat": 45.46, "lng": 9.19, "source": "…", "low_confidence": false, "cache_id": 12 },
    { "index": 1, "hit": false, "lat": null, "lng": null, "source": null, "low_confidence": null, "cache_id": null }
  ]
}

POST /api/v1/geocoding/forward

Resolves up to 10 addresses (raw + query). origin: api (default), wizard, mobile, one_driver, simulator, rubrica. Budget about 25 seconds. status: ok, not_found, pending (retry the same call), error.

{
  "origin": "api",
  "items": [{ "raw": "Via Roma 1, Milano", "query": "Via Roma 1, Milano" }]
}
{
  "results": [
    {
      "status": "ok",
      "lat": 45.46,
      "lng": 9.19,
      "confidence": 90,
      "matched": true,
      "low_confidence": false,
      "source": "…",
      "cache_id": 12
    }
  ]
}

GET /api/geocode_address?address=

Single address, same resolver as forward. 200 with lat/lng; 400 if address is missing; 404 if nothing matched; 503 if the resolve is still pending.

GET /api/reverse_geocode?lat=&lng=

Coordinates to address. 200 { address, source }; 400 if lat or lng is missing; 404 if nothing matched.

Coordinate pins Enterprise

Enterprise plan: store addresses and coordinates. Tenant pins take precedence in lookup/forward. Optional auto-capture from POST /optimize (settings).

POST /api/v1/geocoding/pins

Upsert up to 500 pins. Same role as DELETE and PUT settings: owner, admin, or superuser, plus the geocode_pins feature. A member gets 403 owner_or_admin_required. Each results item has index, status, pin_id, address_key, error.

status: created (new), updated (same address, coordinates moved by at least 5 m), unchanged (move under 5 m), rejected (error: invalid_address, invalid_coordinates, zero_coordinates, swapped_lat_lng, out_of_area), limit_reached (tenant pin cap). summary uses the same counts.

address_key uses the same normalization as lookup and forward: Unicode NFKC, lowercase, collapsed spaces, commas rendered as ", ", leading and trailing spaces and commas removed, 500 characters max. The same string on POST and lookup finds the pin: use the returned value. Civic number, postal code, or extra words change the key; case and double spaces do not.

{
  "items": [
    { "address": "Via Roma 1, 20121 Milano MI", "lat": 45.46, "lng": 9.19, "external_id": "CLI-1" }
  ]
}
{
  "results": [
    { "index": 0, "status": "created", "pin_id": 12, "address_key": "via roma 1, 20121 milano mi", "error": null }
  ],
  "summary": { "created": 1, "updated": 0, "unchanged": 0, "rejected": 0, "limit_reached": 0 }
}

GET /api/v1/geocoding/pins

List pins for anyone with the feature (q from 3 characters, external_id, limit, after_id).

DELETE /api/v1/geocoding/pins/{pin_id}

Delete a pin in your tenant. Owner, admin, or superuser only. Another tenant’s pin returns 404.

GET / PUT /api/v1/geocoding/pins/settings

GET (feature): enabled, auto_capture, count, max_pins. PUT (owner/admin): required body { "auto_capture": true|false }. PUT response: enabled, auto_capture, max_pins.

{ "auto_capture": true }

Excel export (authenticated)

POST /api/v1/routing/export-geocoded-deliveries

Requires Authorization Bearer. Returns an Excel file for the posted payload.

POST /api/v1/routing/export-geocoded-drivers

Requires Authorization Bearer. Returns an Excel file for the posted payload.

POST /api/v1/routing/export-manual-drivers

Requires Authorization Bearer. Returns an Excel file for the posted payload.

POST /api/v1/routing/export-manual-deliveries

Requires Authorization Bearer. Returns an Excel file for the posted payload.

POST /api/v1/routing/export-manual-pickups

Requires Authorization Bearer. Returns an Excel file for the posted payload.

Public / bootstrap endpoints

GET /api/v1/public/features

Returns frontend flags: gel_proximity_enabled, rlp_bryntum_timeline_enabled, and PostHog bootstrap (posthog_enabled, posthog_host, posthog_project_api_key, posthog_session_replay, posthog_unmask_replay). Authorization Bearer is optional: without a token the GEL flag is false; with a valid token, tenant and tier features are evaluated. If PostHog is disabled or incomplete, posthog_enabled is false and the project key is omitted.

GET /api/v1/public/fuel-prices

Fuel and energy averages, no authentication. This is not an iCarry time series: a current snapshot from MIMIT (daily Italy and regional averages) plus plant-level LNG/HVO and monthly public AC/DC charging. For UI and cost estimates, not billing, and with no SLA on third-party sources. It is not the route field prezzo_litro.

Cache-Control public, max-age=1800: honour that TTL when polling. national and regions: diesel, petrol and LPG in €/L, methane in €/kg (regions[].name is the MIMIT region name, e.g. Lombardia). extended.gnl (eur_kg), hvo (eur_l), electric_ac/electric_dc (eur_kwh) with value, unit, updated, source. If every source fails: {"available": false}. The GME PUN is not included. The JSON sample shows shape, not live values.

{
  "available": true,
  "source": "MIMIT",
  "updated": "21/09/2026",
  "national": { "gasolio": 1.623, "benzina": 1.712, "gpl": 0.741, "metano": 1.401 },
  "regions": [{ "name": "Lombardia", "gasolio": 1.631, "benzina": 1.720, "gpl": 0.738, "metano": 1.390 }],
  "extended": {
    "gnl": { "value": 1.452, "unit": "eur_kg", "source": "mimit_impianti" },
    "hvo": { "value": 1.890, "unit": "eur_l", "source": "mimit_impianti" },
    "electric_ac": { "value": 0.62, "unit": "eur_kwh", "source": "adiconsum_mensile" }
  }
}

The GEL pickup-points map endpoint is not part of this public partner documentation. It is covered in internal (superadmin) docs and provided on request when your contract includes locker integration. Contact support for details.

Input data dictionary (Excel/CSV ↔ JSON)

Official template columns map to POST /api/v1/routing/optimize fields. Download blank templates, then send geocoded JSON. Column order is defined in the delivery and driver templates.

Templates: GET /api/v1/templates/delivery-template and GET /api/v1/templates/driver-template (XLSX/CSV). There is no separate pickup download — use the delivery sheet with point_type=pickup.

Excel may omit lat/lng when an address can be geocoded. POST /optimize always requires numeric lat/lng (and start_lat/start_lng for drivers).

POST /api/v1/files/upload is storage only: it does not parse columns or run optimization. Partner API integration is JSON to /optimize.

Deliveries / stops (delivery_points[])

Excel JSON /optimize Required Type Use
tracking_numbertracking_numberyesstring ≤64Stop ID
delivery_addressaddressyes (or lat+lng)stringGeocoding / display
delivery_companydelivery_companyyes in JSON (import default “Cliente”)stringMetadata
lat / lnglat / lngyes in JSON; optional in Excel if address is setdegreesMatrix / geometry
tot_itemstot_itemsno (default 1)int ≥1Capacity (parcels)
prioritypriorityno (default 1)1–10 (1 = highest)Priority / drop protection
estimated_durationestimated_durationnominutesService time at stop
notesnotesnostring ≤500Driver app only
phonephonenoE.164 (+ and digits, e.g. +393331234567)Recipient phone (tel link in app and Control Tower)
preassigned_driverpreassigned_drivernodrivers[].nameLock → mandatory_assignments
point_typepoint_typeno (default delivery)pickup | deliveryPickup before delivery; an unlinked pickup is a standalone stop
pickup_point_idpickup_point_idnostringLinks a delivery to its pickup; omitted pickup = return to the driver's end
time_window_start / endsamenoHH:MMTime windows
volume_m3 / weight_kgsamenofloat ≥0Volume cap (weight → 0.001 m³ if volume missing)

JSON-only (stop): optional id (otherwise tracking_number or a generated UUID).

A point_type=pickup with no linked delivery is not dropped: it is a required stop and the goods return to the driver's end point. The response sets pickup_mode=return_to_depot and adds a warnings row. If it cannot be planned it appears in unassigned_deliveries.

Drivers / vehicles (drivers[])

Excel JSON /optimize Required Notes
namenameyesIdentity
phonephonenoOptional driver phone in E.164
start_address / end_address—start required on importGeocoding → start_* / end_*
lat / lngstart_lat / start_lngyes in JSONStart depot
end_lat / end_lngend_lat / end_lngyes in JSON (default = start)Return depot
capacitycapacityno (default 50)Max parcels (tot_items)
max_volume_m3max_volume_m3noVolume capacity
vehicle_typevehicle_typeno (default car)Full list below: car, van, van_standard, van_maxi, truck, truck_7_5t, truck_12t, truck_18t, truck_32t, artic_44t, bike, scooter, icarry_cargo_bike
routing_profilerouting_profilenoOverride: car, van_standard, van_maxi, truck_7_5t, truck_12t, truck_18t, truck_32t, artic_44t, icarry_cargo_bike
max_distance_kmmax_distance_kmnoMax route km
hourly_cost, fuel_consumption, prezzo_litro, costo_pedaggio_kmsamenoCost / reporting
start_time / end_timesameno (08:00–18:00 if pair set)Shift
break_start / break_endsameas a pairBreak
max_minutesmax_minutesnoMax duration (derivable from shift−break)

JSON-only (driver): optional id, active (default true), max_overtime_minutes (default 0).

Allowed vehicle types (full list)

Send a slug, not a commercial model. Default vehicle_type=car. 4x4 or body variants have no dedicated slug.

API slug Category When to use it
carCar (M1)Standard car.
van AliasVans (N1)Same as van_standard.
van_standardVans (N1)Compact van up to 2.5 t, height about 1.9 m.
van_maxiVans (N1)High-roof van up to 3.5 t, height about 2.7 m.
truck AliasTrucksSame as truck_7_5t.
truck_7_5tTrucksLight urban truck, GVW 7.5 t.
truck_12tTrucksMedium truck, GVW 12 t. Example: Iveco TGM 13.250 4x4 → truck_12t.
truck_18tTrucksHeavy 2-axle truck, GVW 18 t.
truck_32tTrucks4-axle truck, GVW 32 t.
artic_44tArticulatedArticulated 44 t, about 16.5 m.
bike AliasCycle logisticsSame as icarry_cargo_bike.
scooter AliasCar (M1)Same as car.
icarry_cargo_bikeCycle logisticsLast-mile cargo bike.

If set, routing_profile must be a road slug from the table (not a van/truck/bike/scooter alias) and overrides vehicle_type.

Common header aliases (IT/EN)

File import also accepts alternate headers (normalized, accents stripped). Frequent examples:

Main Data Schema

DeliveryPointRequest

Required in JSON: lat/lng, tracking_number (≤64), address, delivery_company. Optional: tot_items (default 1), priority (1–10), estimated_duration (minutes), notes, phone (E.164, e.g. +393331234567), preassigned_driver, point_type (pickup|delivery), pickup_point_id, time_window_start/end (HH:MM), volume_m3, weight_kg, id.

Full Excel↔JSON column map: see “Input data dictionary” above.

DriverRequest

Required in JSON: name, start_lat/lng, end_lat/lng (end defaults to start). Optional: phone (E.164), capacity (default 50), max_volume_m3, vehicle_type (default car; full list in vehicle types table), routing_profile, max_distance_km, costs (hourly_cost, fuel_consumption, prezzo_litro, costo_pedaggio_km), start_time/end_time, break_start/end, max_minutes, active, max_overtime_minutes, id. Excel only: start_address/end_address (geocoded to coordinates).

vehicle_type — full list: car, van, van_standard, van_maxi, truck, truck_7_5t, truck_12t, truck_18t, truck_32t, artic_44t, bike, scooter, icarry_cargo_bike. Default car. Do not send a make/model: an Iveco TGM 13.250 4x4 is truck_12t. Aliases: van → van_standard, truck → truck_7_5t, bike → icarry_cargo_bike, scooter → car.

routing_profile — optional override. Allowed values: car, van_standard, van_maxi, truck_7_5t, truck_12t, truck_18t, truck_32t, artic_44t, icarry_cargo_bike. If omitted it is derived from vehicle_type.

Full Excel↔JSON column map: see “Input data dictionary” above.

OptimizationParamsRequest

Balances distance, time and load via configurable weights. Values depend on your operational priorities.

RoutingConstraintsRequest

Enables hard constraints (max duration, max distance, stops per route). If omitted the engine applies dynamic limits from the drivers.

Completion Webhook

Set webhook_url in the request to receive a signed POST (header X-iCarry-Signature) with the same payload as RoutingResultResponse. If the webhook returns status code < 300 it is considered delivered; otherwise 3 exponential retries (15s, 60s, 180s) are performed.

Realtime via WebSocket

Connect to wss://flow.icarry.it/ws/jobs and join room job_{job_id} via Socket.IO join_room event. You will receive job_progress, job_completed, job_failed events.

Support Tools

Errors & Best Practices

Code Scenario How to react
400 Validation error (Pydantic schema) Read detail for invalid field; apply sanitization on caller side.
401 Token missing or expired (also driver-maps / Excel export without Bearer) Regenerate token and retry the call.
403 algorithms value not allowed (use auto or standard) Use auto or standard, or contact support.
403 Admin/superuser endpoint without privileges Tenant-admin endpoints need is_admin; billing/algorithm mutations and settings need is_superuser.
409 Duplicate job in queue (same session_id) Use unique sessions for parallel jobs.
409 routes_version_mismatch (If-Match / concurrent route edit) Reload the job hash, or retry generate-geometries without If-Match.
503 Dependency not configured (e.g. GEL webhook token missing) Configure the required token/secret in admin settings or env, then retry.
500 Internal error / Routing service unreachable Retry with backoff. Verify status with /api/v1/routing/status.

To reduce response times, set batches between 80 and 120 deliveries per job; beyond 150 stops consider automatic splitting (flag allow_partial_assignments true) and subsequent recomposition.

Support

For advanced features (high-performance engine, priority SLA, dedicated environments) or to enable IP whitelisting on production endpoints, contact [email protected] with your client_id and the public IP to authorize.

🚀 TMS integration guide (Transport Management System)

This guide summarizes how to integrate iCarryUP routing and optimization APIs with your TMS using server-to-server calls (no web UI). For the full Italian walkthrough with extended code samples, switch locale to Italiano.

Quick index

1. Introduction

Use the versioned API under /api/v1. Typical flow: your TMS sends drivers and stops → iCarryUP runs optimization → you poll GET /routing/jobs/{id} or receive a webhook_url callback → import routes back into the TMS.

2. Setup

2.1 Credentials

Request a technical user from [email protected] (roles, IP allowlisting if needed).

2.2 Base URL

Environment Base URL When to use
Production https://flow.icarry.it Live traffic

2.3 Connectivity check

curl -s "https://flow.icarry.it/api/v1/routing/status"

Example fields: status, version (e.g. 2.9.23-1), active_jobs, routing_service_status. No auth required for reachability. The field algorithms_available is reserved for administrative accounts; for others it is always [].

2.4 Public API & GEL map layer

Use GET /api/v1/public/features to see whether GEL proximity features apply to your integration (same as the Public endpoints section above). Bearer is optional; with a valid token, tenant map setting and gel_proximity on profile/tier are applied.

Partner docs: the GEL pickup-points listing endpoint is not included in this public page. It is documented internally (superadmin) and provided on request when your contract covers lockers. Contact [email protected].

On POST /api/v1/routing/optimize, you may set per-stop pickup_point_id and gel_cost_to_time_equivalent_minutes_per_eur in optimization_params. If the token user lacks effective gel_proximity, the API strips these fields silently before solving — validate flags/tier before relying on GEL in optimization.

3. Authentication

OAuth2-style password grant to obtain a JWT, then Authorization: Bearer <token> on API calls.

curl -X POST "https://flow.icarry.it/api/v1/auth/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=password&username=USER&password=SECRET"

Refresh before expiry (often ~24h). Prefer dedicated technical users for server-to-server flows.

4. End-to-end workflow

  1. Map TMS stops/drivers to RoutingOptimizationRequest (see main schema section on this page).
  2. If lat/lng are missing, resolve addresses with POST /api/v1/geocoding/forward (or lookup for cache hits only). POST /optimize still requires numeric coordinates. See Geocoding.
  3. Optional (Enterprise): sync your pins with POST /api/v1/geocoding/pins so later lookups reuse your coordinates. You can also enable auto-capture from POST /optimize via /api/v1/geocoding/pins/settings.
  4. POST /api/v1/routing/optimize with async_processing: true → receive job_id.
  5. Poll GET /api/v1/routing/jobs/{job_id} every 5–10s or subscribe to websocket / webhook.
  6. When status is completed, read result; export maps or Excel via dedicated endpoints if needed.

5. Minimal optimize example

curl -X POST "https://flow.icarry.it/api/v1/routing/optimize" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "async_processing": true,
    "session_id": "tms-2026-04-02-001",
    "delivery_points": [{"tracking_number":"1","delivery_company":"ACME","address":"Via Roma 1, Milano","lat":45.46,"lng":9.19,"tot_items":1,"priority":1}],
    "drivers": [{"name":"Driver 1","start_lat":45.46,"start_lng":9.19,"end_lat":45.46,"end_lng":9.19,"capacity":50,"vehicle_type":"van_standard","max_distance_km":200}],
    "algorithms": ["standard"],
    "optimization_objective": "time"
  }'

Why time, distance, balanced, and cost exist: choice guide.

6. Webhooks

Set webhook_url on the optimize request to receive a POST with the same shape as the completed job result. Optional X-iCarry-Signature verification. Failed deliveries retry with backoff.

7. Best practices

8. Troubleshooting & support

Include job id, HTTP status, redacted payload, and timestamps when writing to [email protected].