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.
Nesta página
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:
https://api.transfersmanager.ai- https://api.transfersmanager.ai/api/v1/partner/openapi.json
Endpoints
Todos os caminhos são relativos à URL base.
| GET | /api/v1/partner/me | Seu perfil de parceiro e o operador com quem você trabalha. |
| GET | /api/v1/partner/vehicle-classes | As classes de veículo que o operador oferece, com capacidade de passageiros e bagagem. |
| POST | /api/v1/partner/quotes | Obtenha opções com preço para um trajeto. Cada opção traz um quote_id assinado. |
| POST | /api/v1/partner/bookings | Crie 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}/cancellation | Veja quanto custaria cancelar agora, sem cancelar. |
| POST | /api/v1/partner/bookings/{ref}/cancel | Cancele 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.
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
}{
"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.
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.
{
"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_confirmationconfirmeddriver_assigneddriver_en_routedriver_arrivedpicked_upcompletedno_showcancelledrejected
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
- Leia o corpo bruto, antes de interpretar o JSON.
- Calcule o HMAC-SHA256 de "<t>.<corpo bruto>" e compare com v1 em tempo constante.
- Rejeite a requisição se t tiver mais de 5 minutos.
- Deduplique por X-TM-Delivery-Id e responda com qualquer 2xx.
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);
}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 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