Skip to main content

API para parceiros

Envie transfers a um operador por API ou pelo seu agente de IA

Uma API REST e um servidor MCP para cotar, reservar, alterar e cancelar transfers com um operador do TransfersManager. Os preços são os do operador, e é ele quem emite a sua chave no painel dele.

O que é

A API para parceiros permite que uma OTA, uma agência de viagens ou um motor de reservas envie trabalho diretamente a um operador de transporte que usa o TransfersManager. Você não se integra à plataforma em geral: integra-se a um operador, com seus veículos, suas zonas e sua tabela de preços.

  • Você recebe uma cotação com preço assinado. A reserva é cobrada exatamente por esse preço.
  • Você cria, consulta, altera e cancela reservas, e as acompanha com webhooks.
  • A mesma chave serve por REST e por MCP, então um agente de IA pode reservar em seu nome.
  • Nada é cobrado pelo TransfersManager. Você acerta com o operador conforme o acordo de vocês.

Obtenha sua chave

O operador cria um parceiro para você no painel dele (Configurações, Parceiros por API) e entrega duas coisas: sua chave de API (começa com tmp_) e, se quiser receber eventos, um segredo de webhook (começa com whsec_). As duas aparecem uma única vez. Se perder uma, peça ao operador que a renove.

Autenticação e limites

Envie sua chave no cabeçalho X-API-Key ou como token Bearer. A chave é restrita a um operador e a você: só pode ver e alterar as suas próprias reservas.

  • Limite por chave: 120 requisições por minuto por padrão. O operador pode definir entre 10 e 600.
  • Cada resposta traz X-RateLimit-Limit, X-RateLimit-Remaining e X-RateLimit-Reset.
  • Se exceder, você recebe um 429 com o cabeçalho Retry-After. Aguarde esse número de segundos antes de tentar de novo.
curl https://api.transfersmanager.ai/api/v1/partner/me \
  -H "X-API-Key: tmp_your_key_here"

URL base e OpenAPI

Todas as chamadas vão para https://api.transfersmanager.ai e a especificação legível por máquina é publicada como OpenAPI 3:

Endpoints

Todos os caminhos são relativos à URL base.

GET/api/v1/partner/meSeu perfil de parceiro e o operador com quem você trabalha.
GET/api/v1/partner/vehicle-classesAs classes de veículo que o operador oferece, com capacidade de passageiros e bagagem.
POST/api/v1/partner/quotesObtenha opções com preço para um trajeto. Cada opção traz um quote_id assinado.
POST/api/v1/partner/bookingsCrie uma reserva a partir de um quote_id.
GET/api/v1/partner/bookings?external_ref=&limit=&offset=Liste suas reservas. Filtre por external_ref e pagine com limit e offset.
GET/api/v1/partner/bookings/{ref}Consulte uma reserva. A referência pode ser o booking_id, o número de confirmação (TM26-XXXXXX) ou o seu próprio external_ref.
PATCH/api/v1/partner/bookings/{ref}Altere uma reserva até o motorista sair.
GET/api/v1/partner/bookings/{ref}/cancellationVeja quanto custaria cancelar agora, sem cancelar.
POST/api/v1/partner/bookings/{ref}/cancelCancele uma reserva.

Cotar um trajeto

Envie a origem, o destino, o horário e o tamanho do grupo. Latitude e longitude são opcionais: se você as omitir, geocodificamos o endereço.

  • pickup_time é ISO 8601 com fuso (offset). Sem offset, envie também "timezone": "Europe/Madrid" (ou o fuso correspondente). Qualquer outra coisa retorna 422.
  • A partida deve ser pelo menos 30 minutos a partir de agora.
  • Cada opção tem um quote_id assinado. A reserva é cobrada exatamente por esse preço e a cotação vale 24 horas.
  • price_is_approximate indica quando o preço depende de algo que o operador vai confirmar.
Requisição
POST /api/v1/partner/quotes
{
  "pickup": { "address": "Málaga Airport (AGP)", "lat": 36.6749, "lng": -4.4991 },
  "dropoff": { "address": "Hotel Puente Romano, Marbella" },
  "pickup_time": "2026-11-20T10:30:00+01:00",
  "passengers": 3,
  "luggage": 3,
  "child_seats": 1
}
Resposta
{
  "pickup": { "address": "Málaga Airport (AGP)", "lat": 36.6749, "lng": -4.4991 },
  "dropoff": { "address": "Hotel Puente Romano, Marbella", "lat": 36.5016, "lng": -4.9456 },
  "pickup_time": "2026-11-20T10:30:00+01:00",
  "passengers": 3,
  "luggage": 3,
  "distance_km": 54.2,
  "options": [
    {
      "quote_id": "qt_...",
      "vehicle_class": { "code": "minivan", "name": "Minivan", "max_passengers": 6, "max_luggage": 6 },
      "price": 78.0,
      "currency": "EUR",
      "price_is_approximate": false,
      "expires_at": "2026-11-19T10:30:00+01:00"
    }
  ]
}

Criar uma reserva

Envie o quote_id com a sua própria referência e o passageiro. Adicione um cabeçalho Idempotency-Key (qualquer UUID) para que uma nova tentativa após um timeout nunca crie uma segunda reserva.

Requisição
POST /api/v1/partner/bookings
Idempotency-Key: 6f1c2f0e-5b1e-4d0e-9a3b-0c1f7a2d9e11

{
  "quote_id": "qt_...",
  "external_ref": "OTA-123456",
  "passenger": { "name": "Ana García", "phone": "+34600000000", "email": "[email protected]" },
  "pickup_address": "Arrivals hall, Terminal 3",
  "dropoff_address": "Hotel Puente Romano",
  "flight_number": "FR1234",
  "meet_board_name": "GARCÍA",
  "notes": "One child seat needed"
}

O que você pode receber

201
Reserva criada.
200
O external_ref (ou a Idempotency-Key) já foi usado com o mesmo corpo. Você recebe a reserva original com "duplicate": true.
409
O external_ref ou a Idempotency-Key foi reutilizado com um corpo diferente (external_ref_conflict, idempotency_key_conflict), ou a primeira requisição ainda está em andamento (booking_in_progress).
422
invalid_quote, quote_expired, passengers_exceed_quote ou pickup_too_soon.
429
operator_quota_exceeded: o operador atingiu sua cota de reservas da API para parceiros.

Pagamento: nada é cobrado pelo TransfersManager. Você acerta cada reserva com o operador.

O objeto de reserva

Todos os endpoints de reservas retornam a mesma estrutura.

Exemplo
{
  "booking_id": "...",
  "confirmation_number": "TM26-XXXXXX",
  "external_ref": "OTA-123456",
  "status": "confirmed",
  "pickup": { "address": "...", "lat": 36.6749, "lng": -4.4991 },
  "dropoff": { "address": "...", "lat": 36.5016, "lng": -4.9456 },
  "pickup_time": "2026-11-20T10:30:00+01:00",
  "passengers": 3,
  "luggage": 3,
  "child_seats": 1,
  "baby_seats": 0,
  "vehicle_class": { "code": "minivan", "name": "Minivan" },
  "price": 78.0,
  "currency": "EUR",
  "flight_number": "FR1234",
  "passenger": { "name": "Ana García", "phone": "+34600000000", "email": "[email protected]" },
  "driver": { "first_name": "Luis", "phone": "+34611111111" },
  "vehicle": { "brand": "Mercedes", "model": "V-Class", "color": "black", "license_plate": "1234ABC" },
  "cancellation": null,
  "created_at": "2026-11-01T09:12:44Z",
  "duplicate": false
}

Status

  • pending_confirmation
  • confirmed
  • driver_assigned
  • driver_en_route
  • driver_arrived
  • picked_up
  • completed
  • no_show
  • cancelled
  • rejected

Alterar uma reserva

O PATCH altera os dados que não mudam o preço. Enquanto o motorista não saiu, você pode alterar livremente:

  • passenger, flight_number, meet_board_name e notes,
  • pickup_address e dropoff_address (como texto, para o motorista).

Alterar horário, passageiros, bagagem ou cadeirinhas

Cote de novo com a mesma origem e o mesmo destino e envie o novo quote_id no PATCH. A reserva assume esse novo preço.

  • Uma origem ou um destino a mais de 1 km é outro trajeto: você recebe 422 route_changed. Crie uma nova reserva e cancele a anterior.
  • Depois que o motorista saiu, alterar retorna 409 booking_already_started.

Cancelar uma reserva

O cancelamento aplica a política do operador como estava congelada quando você reservou. Chame primeiro GET /cancellation para mostrar ao seu cliente quanto custa e depois POST /cancel.

  • A resposta inclui um objeto cancellation com hours_of_notice, refund_pct, fee_pct, fee_amount, currency, policy_source e cancelled_by.
  • fee_amount é o que o operador pode faturar a você.
  • Repetir o cancelamento retorna o mesmo resultado.
  • Com o passageiro já a bordo, cancelar retorna 409.
POST /api/v1/partner/bookings/{ref}/cancel
{ "reason": "Customer changed plans" }

// Response: the booking, now with
"status": "cancelled",
"cancellation": {
  "hours_of_notice": 30.5,
  "refund_pct": 100,
  "fee_pct": 0,
  "fee_amount": 0.0,
  "currency": "EUR",
  "policy_source": "operator",
  "cancelled_by": "partner"
}

Webhooks

O operador configura no painel a URL do seu webhook (https pública) e entrega o segredo. A partir daí chamamos você a cada mudança nas suas reservas. Os webhooks nunca incluem dados do passageiro.

Eventos

booking.confirmed
O operador confirmou a reserva.
booking.driver_assigned
Um motorista foi atribuído. Inclui driver (first_name, phone) e vehicle.
booking.driver_unassigned
O motorista foi removido; outro virá.
booking.driver_en_route
O motorista está a caminho.
booking.driver_arrived
O motorista está no ponto de embarque.
booking.picked_up
O passageiro já está a bordo.
booking.completed
O trajeto terminou.
booking.no_show
O passageiro não compareceu.
booking.cancelled
Cancelada. Inclui o objeto cancellation e cancelled_by (partner ou operator).
ping
Evento de teste enviado pelo painel do operador.

Corpo

{
  "id": "whd_...",
  "event": "booking.driver_assigned",
  "created_at": "2026-11-20T08:02:11Z",
  "data": {
    "booking_id": "...",
    "confirmation_number": "TM26-XXXXXX",
    "external_ref": "OTA-123456",
    "status": "driver_assigned",
    "occurred_at": "2026-11-20T08:02:10Z",
    "driver": { "first_name": "Luis", "phone": "+34611111111" },
    "vehicle": { "brand": "Mercedes", "model": "V-Class", "color": "black", "license_plate": "1234ABC" }
  }
}

Cabeçalhos

  • X-TM-Event: o nome do evento.
  • X-TM-Delivery-Id: único por entrega. Use para deduplicar.
  • X-TM-Signature: t=<timestamp unix>,v1=<hex>, onde v1 é o HMAC-SHA256 de "<t>.<corpo bruto>" com o seu segredo de webhook.

Verifique cada entrega

  1. Leia o corpo bruto, antes de interpretar o JSON.
  2. Calcule o HMAC-SHA256 de "<t>.<corpo bruto>" e compare com v1 em tempo constante.
  3. Rejeite a requisição se t tiver mais de 5 minutos.
  4. Deduplique por X-TM-Delivery-Id e responda com qualquer 2xx.
Node.js
import crypto from 'node:crypto';

// rawBody: the request body as a string or Buffer, BEFORE JSON.parse.
export function verifyTmSignature(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false; // older than 5 minutes

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${t}.${rawBody}`)
    .digest('hex');

  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1 ?? '');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Python
import hashlib
import hmac
import time


def verify_tm_signature(raw_body: bytes, header: str, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    try:
        t = int(parts["t"])
    except (KeyError, ValueError):
        return False
    if abs(time.time() - t) > 300:  # older than 5 minutes
        return False

    expected = hmac.new(
        secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, parts.get("v1", ""))

Se você não responder com um 2xx, tentamos de novo com espera exponencial: de 30 segundos até 1 hora e depois diariamente. Os eventos de uma mesma reserva são entregues em ordem.

Pelo seu agente de IA (MCP)

O mesmo operador, os mesmos preços e a mesma chave estão disponíveis para agentes de IA por meio de um servidor MCP remoto (Streamable HTTP) em https://mcp.transfersmanager.ai/mcp. Use sua chave como token Bearer.

Claude Code
claude mcp add --transport http transfersmanager https://mcp.transfersmanager.ai/mcp \
  --header "Authorization: Bearer tmp_your_key_here"

Em qualquer outro cliente, adicione um servidor MCP remoto com essa URL e o cabeçalho Authorization: Bearer tmp_...

https://mcp.transfersmanager.ai/mcp

Ferramentas

quote_transfer
Cota um trajeto e retorna opções.
check_availability
Verifica se o operador pode atender um trajeto.
create_booking
Reserva uma opção. Recebe o option_id de quote_transfer, o seu client_reference (usado para idempotência) e passenger_name.
get_booking
Consulta o estado de uma reserva.
cancel_booking
Com confirm=false mostra primeiro a tarifa. Com confirm=true cancela.
list_vehicle_classes
As classes de veículo do operador.

Erros

Os erros compartilham um formato, com um código estável para você tratar e uma mensagem para pessoas:

{ "detail": { "code": "quote_expired", "message": "This quote has expired. Request a new one." } }
401 invalid_key
A chave não foi enviada, está errada ou foi revogada.
402 subscription_required
O plano do operador não inclui a API para parceiros.
404
Não encontrado. É também o que você recebe para uma reserva que não é sua.
409 / 422 / 429
Veja cada endpoint acima.

Você é operador?

Abra o seu painel, vá em Configurações, Parceiros por API e emita uma chave para a primeira OTA que lhe mandar trabalho.

Começar com o TransfersManager