Tolls API
ES EN FR

Guías de la Tolls API

Todo lo necesario para integrar el cálculo de peajes por API o MCP: autenticación, cobro por créditos, errores y límites.

Actualizado

⚡ Quickstart

  1. Crea una cuenta en el dashboard (dash.tolls.cargoffer.com).
  2. Recarga créditos (o canjea un cupón) desde la sección Wallet.
  3. Crea una API key (empieza por tk_live_; se muestra una sola vez).
  4. Llama a POST /api/tolls/calculate (1 crédito por cálculo).
# Define TOLLS_API_KEY con tu clave; usa una clave única y persistente por cálculo.
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"
  }'

Los créditos solo se cobran si el cálculo se sirve. Si el motor falla, el crédito se reembolsa automáticamente. Una clave ya procesada devuelve 409 y no se cobra otra vez, pero la API no repite la respuesta anterior. Guarda la respuesta junto a la clave y usa una nueva para cada cálculo nuevo.

🔑 Autenticación

Autentica cada petición con Authorization: Bearer <token>. Puedes usar una API key o el JWT de sesión del dashboard.

MétodoUso
tk_live_… (API key)Agentes, MCP, integraciones servidor-servidor. Hasta 10 keys por cuenta, revocables desde el dashboard.
JWT de sesiónEl dashboard. POST /api/auth/login → accessToken (2 h) + refreshToken (30 d).

Verifica cualquier token contra GET /api/auth/validate: responde sujeto, tipo y scopes. MCP: los clientes pueden usar el servidor oficial https://mcp.tolls.cargoffer.com/mcp (streamable HTTP, Bearer obligatorio en cada request).

🧭 Errores

HTTPSignificadoQué hacer
400Petición inválida (coordenadas, vehicleType)Corrige el body: origin/destination son objetos {lat,lng}; vehicleType ligero|pesado_1|pesado_2
401Token ausente o inválidoRegenera la API key o refresca el JWT
402Saldo insuficienteRecarga créditos o canjea un cupón; no se llamó al motor
404Recurso o cupón inexistenteRevisa el código/path
409Idempotency-Key o cupón ya usadoGenera una clave nueva para un cálculo nuevo
429Límite de peticiones (demo)Espera y reintenta con backoff
502Motor de peajes no disponibleReintenta; el crédito ya fue reembolsado
503Monedero temporalmente no disponibleReintenta en unos segundos (fail-closed)

⏱️ Límites y demo

  • Límites por separado según cómo te autentiques, para que un atasco anónimo no bloquee a los clientes: API key 200 req/min por clave, sesión del dashboard 300 req/min por IP, sin autenticar 50 req/min por IP. Al superarlos: 429 con cabecera Retry-After.
  • Demo POST /api/demo/calculate: 5 req/min por IP y 500 cálculos/día de fondo compartido (429 al agotarse). Sin auth, sin coste.
  • Hasta 10 API keys activas por cuenta; revocar una libera la plaza. GET /api/keys/limits publica el tope vigente.
  • Cada cálculo servido consume 1 crédito y queda registrado en GET /api/history.

🎟️ Cupones

Un cupón acredita créditos directamente en el monedero, sin pago: POST /api/wallet/redeem con {"code":"…"}. Cada cuenta puede canjear cada cupón una sola vez (409 si repite); los cupones pueden estar limitados a un email o a N redenciones totales.

🧩 Odoo

Para integrar Odoo, realiza la petición desde un módulo personalizado o un worker Python autorizado para llamadas HTTP. Conserva una Idempotency-Key por cálculo lógico y reutilízala solo al reintentar la misma operación:

El contexto y las variables de Server Actions dependen de la versión y configuración de Odoo. Este ejemplo es un patrón Python de integración; verifica la documentación oficial antes de ejecutarlo dentro de Odoo: documentación oficial de Server Actions (Odoo 19)

import os
import requests

# Lee un UUID guardado para este cálculo; reutilízalo solo en reintentos.
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()

Ejemplo Node.js (18+):

const apiKey = process.env.TOLLS_API_KEY;
const idempotencyKey = process.env.TOLLS_IDEMPOTENCY_KEY; // Persistir por cálculo lógico.
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();

Una clave ya procesada devuelve 409: no vuelve a cobrar, pero la respuesta original no se reproduce. Guarda la respuesta junto a la clave y usa otra clave para un cálculo nuevo.

📜 Changelog

FechaCambio
2026-10-29Cupones de monedero (/api/wallet/coupons, /api/wallet/redeem), GET /api/auth/validate, ledger idempotente y cabecera Idempotency-Key en /api/tolls/calculate (409 en reutilización).
2026-09-04v1: auth, API keys, wallet + Stripe, proxy de cálculo, history, demo pública.

Preguntas frecuentes

¿Cuántos créditos consume un cálculo?

Cada cálculo servido consume 1 crédito. Si falla el motor o rechaza la petición, el API reembolsa el crédito.

¿Qué ocurre al reutilizar una Idempotency-Key?

El API devuelve 409, sin un segundo cargo ni otra llamada al motor. No reproduce la respuesta original; guarda la respuesta junto con la clave.

¿Qué formato tienen las coordenadas?

Envía origin y destination como objetos con latitud y longitud decimales: {lat, lng}. El tipo de vehículo se envía en vehicleType.