Tolls API Guides
Everything you need to integrate toll calculation via API or MCP: authentication, credit billing, errors and limits.
Updated
⚡ Quickstart
- Create an account on the dashboard (dash.tolls.cargoffer.com).
- Top up credits (or redeem a coupon) in the Wallet section.
- Create an API key (starts with
tk_live_; shown only once). - Call
POST /api/tolls/calculate(1 credit per calculation).
# Set TOLLS_API_KEY to your API key; persist a unique key for each calculation.
export TOLLS_API_KEY='YOUR_TOLLS_API_KEY'
curl -X POST https://api.tolls.cargoffer.com/api/tolls/calculate \
-H "Authorization: Bearer $TOLLS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: route-estimate-001" \
-d '{
"origin": {"lat": 40.4168, "lng": -3.7038},
"destination": {"lat": 41.3874, "lng": 2.1686},
"vehicleType": "pesado_1"
}'
Credits are only charged when a calculation is served. If the engine fails, the credit is refunded automatically. A processed key returns 409 and is not charged again, but the API does not replay the original response. Store the response with the key and use a new one for each new calculation.
🔑 Authentication
Authenticate each request with Authorization: Bearer <token>. Use an API key or the dashboard session JWT.
| Method | Use |
|---|---|
tk_live_… (API key) | Agents, MCP, server-to-server. Up to 10 keys per account, revocable from the dashboard. |
| JWT de sesión | The dashboard. POST /api/auth/login → accessToken (2 h) + refreshToken (30 d). |
Verify any token against GET /api/auth/validate: it returns subject, type and scopes. MCP: clients can use the official server at https://mcp.tolls.cargoffer.com/mcp (streamable HTTP, Bearer required on every request).
🧭 Errors
| HTTP | Meaning | What to do |
|---|---|---|
| 400 | Invalid request (coordinates, vehicleType) | Fix the body: origin/destination are {lat,lng} objects; vehicleType ligero|pesado_1|pesado_2 |
| 401 | Missing or invalid token | Regenerate the API key or refresh the JWT |
| 402 | Insufficient balance | Top up credits or redeem a coupon; the engine was not called |
| 404 | Resource or coupon not found | Check the code/path |
| 409 | Idempotency-Key or coupon already used | Generate a new key for a new calculation |
| 429 | Rate limit exceeded (demo) | Wait and retry with backoff |
| 502 | Toll engine unavailable | Retry; the credit was already refunded |
| 503 | Wallet temporarily unavailable | Retry in a few seconds (fail-closed) |
⏱️ Limits & demo
- Separate limits per authentication method, so an anonymous flood cannot block paying clients: API key 200 req/min per key, dashboard session 300 req/min per IP, unauthenticated 50 req/min per IP. When exceeded:
429with aRetry-Afterheader. - Demo
POST /api/demo/calculate: 5 req/min per IP and a shared 500 calculations/day pool (429 when exhausted). No auth, no cost. - Up to 10 active API keys per account; revoking one frees a slot.
GET /api/keys/limitspublishes the current cap. - Each served calculation consumes 1 credit and is logged in
GET /api/history.
🎟️ Coupons
A coupon credits the wallet directly, with no payment: POST /api/wallet/redeem with {"code":"…"}. Each account can redeem each coupon once (409 on repeat); coupons may be limited to one email or N total redemptions.
🧩 Odoo
For Odoo, make the request from a custom module or Python worker authorized for HTTP calls. Keep one Idempotency-Key per logical calculation and reuse it only when retrying that operation:
Server Action context and variables depend on the Odoo version and configuration. This is a generic Python integration pattern; check the official documentation before running code inside Odoo: official Server Actions documentation (Odoo 19)
import os
import requests
# Load a persisted UUID for this calculation; reuse it only for retries.
calculation_id = "persisted-uuid-for-this-calculation"
response = requests.post(
"https://api.tolls.cargoffer.com/api/tolls/calculate",
headers={
"Authorization": f"Bearer {os.environ['TOLLS_API_KEY']}",
"Content-Type": "application/json",
"Idempotency-Key": f"odoo-toll-{calculation_id}",
},
json={
"origin": {"lat": 40.4168, "lng": -3.7038},
"destination": {"lat": 41.3874, "lng": 2.1686},
"vehicleType": "pesado_1",
},
timeout=30,
)
response.raise_for_status()
tolls = response.json()Node.js example (18+):
const apiKey = process.env.TOLLS_API_KEY;
const idempotencyKey = process.env.TOLLS_IDEMPOTENCY_KEY; // Persist per logical calculation.
const response = await fetch('https://api.tolls.cargoffer.com/api/tolls/calculate', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
'Idempotency-Key': idempotencyKey,
},
body: JSON.stringify({
origin: { lat: 40.4168, lng: -3.7038 },
destination: { lat: 41.3874, lng: 2.1686 },
vehicleType: 'pesado_1',
}),
});
if (!response.ok) throw new Error(`Tolls API HTTP ${response.status}`);
const tolls = await response.json();
A processed key returns 409: it is not charged again, but the original response is not replayed. Store the response with the key and use a new key for a new calculation.
📜 Changelog
| Date | Change |
|---|---|
| 2026-10-29 | Wallet coupons (/api/wallet/coupons, /api/wallet/redeem), GET /api/auth/validate, idempotent ledger and Idempotency-Key header on /api/tolls/calculate (409 on reuse). |
| 2026-09-04 | v1: auth, API keys, wallet + Stripe, calculation proxy, history, public demo. |
Frequently asked questions
How many credits does a calculation use?
Each served calculation consumes 1 credit. If the engine fails or rejects the request, the API refunds the credit.
What happens when an Idempotency-Key is reused?
The API returns 409 without a second charge or engine call. It does not replay the original response; store the response with its key.
What coordinate format should I send?
Send origin and destination as objects with decimal latitude and longitude: {lat, lng}. Send the vehicle class in vehicleType.