Developers
Network API
Sell transfers across the network without contracting operator by operator. Sign up, test with your key this afternoon and go live after a review.
Integrate in a day
Everything you need to test comes with the sign-up. Nobody has to give you access to the test key.
- Sign up in the portal. You get a tmc_test_ key on the spot: keep it, it is only shown once.
- Quote and book with it. No real fleet is involved, nothing is charged and nobody is notified.
- Add your webhook URL in the portal and keep the secret, shown only once.
- Play the scenarios and check your system receives each event.
- Your account waits for review. A person approves it and issues your production key.
curl https://api.transfersmanager.ai/api/v1/channel-api/me \
-H "X-API-Key: tmc_test_..."
curl -X POST https://api.transfersmanager.ai/api/v1/channel-api/quotes \
-H "X-API-Key: tmc_test_..." -H "Content-Type: application/json" \
-d '{"origin_lat":36.68,"origin_lng":-4.49,"destination_lat":36.51,"destination_lng":-4.88,
"pickup_datetime":"2027-03-14T10:30:00+01:00","passengers":2}'With a production key, a booking returns a payment link and enters the network once paid. Quotes only exist where verified fleets offer real supply: check /coverage first. Where there is none, /quotes answers 503 no_operators.
Endpoints
Base: /api/v1/channel-api. Authenticate with the X-API-Key header or Authorization: Bearer. A reference that is not yours answers 404, like one that does not exist.
- GET /me
- Who this key is and whether it is a test key.
- GET /coverage
- Where there is real supply: approximate zones, or the exact answer for a point with lat and lng. It names no fleet.
- POST /quotes
- Price with net, commission and total. A quote lives five minutes and books once.
- POST /bookings
- Idempotent booking by external_ref. Repeating it returns the same booking.
- GET /bookings/{ref}
- One booking by external_ref or booking_id.
- POST /bookings/{ref}/cancel
- Cancel. Unpaid, it closes the link. Paid, it refunds by the operator's cancellation ladder.
- GET /bookings/{ref}/voucher
- Booking data for your own voucher. There is no pass or PDF: those documents name the operator.
- GET /flights/{number}?date=YYYY-MM-DD
- A flight by number and date. 404 flight_not_found when it does not exist and 503 flight_status_unavailable with Retry-After when it cannot be told.
- POST /pickup-suggestion
- When to book the pickup for a flight. It books nothing and holds no price.
- POST /test/bookings/{ref}/scenario
- Test keys only: play a scenario.
- GET /test/bookings/{ref}/events
- Test keys only: what happened to a test booking.
Test keys
A tmc_test_ key answers the same contract from an isolated world: the booking is not a real service and no fleet sees it. The price is a straight-line estimate, flagged is_approximate. The booking is confirmed at once with no payment link. Flights and pickups are invented. driver_assigned first; then the rest.
- driver_assigned
- A driver is assigned.
- driver_delayed
- A delay notice, with delay_minutes. It only exists in the test world: production does not emit this event today.
- driver_position
- An invented car position.
- no_show
- The driver arrives and the passenger does not show up.
- completed
- The service runs to the end.
curl -X POST https://api.transfersmanager.ai/api/v1/channel-api/test/bookings/my-ref-1/scenario \
-H "X-API-Key: tmc_test_..." -H "Content-Type: application/json" \
-d '{"scenario":"driver_assigned"}'Webhooks
One URL per account, set in the portal. Each delivery is signed with your secret, as in the Partner API, and test keys receive them too.
- Verify the signature over the raw body, before parsing the JSON.
- Retries back off from 30 seconds up to one hour, 12 attempts in all. While an attempt fails, your account's next events are held for that pass.
- The portal shows your last 50 events and can resend any of them. The delivery id is kept so you can spot duplicates. They are stored for 30 days.
- Live position: booking.driver_position is optional and off by default. Switch it on in the portal. It is only sent with the car on its way, at the pickup or with the passenger on board, at most every 30 seconds, and it carries only booking ids and coordinates. A position is never retried or resent. A receiver that fails or takes 1.5 seconds or more is paused for 5 minutes.
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);
}Rules
- Minimum notice: checked at quote and at booking, with the autonomous community of the pickup coordinates. If it cannot be told, or the address contradicts them, the strictest minimum applies. It answers 422 lead_time_too_short with the figure and its legal basis. Test keys always apply the strictest.
- An immediate booking (immediate: true) needs a pickup within 24 hours and, with no agreed fleet, at least 60 minutes ahead.
- The contract does not disclose the fleet: not in quotes, not in coverage, not in the voucher.
{
"detail": {
"error": "lead_time_too_short",
"message": "Pickups in Cataluña must be booked at least 15 minutes ahead.",
"minimum_minutes": 15,
"community": "ES-CT",
"community_name": "Cataluña",
"legal_basis": "Decreto-ley 9/2022, ..."
}
}Limits
- Requests
- 120 per minute per key by default; 429 beyond it. Test keys share one limit per account.
- Test keys
- 3 active per account.
- Test bookings
- 1,000 per account and 50 events per booking.
- Sign-ups
- 5 a day per IP.
- Quote
- Lives 5 minutes.
- /coverage
- Up to 1,500 zones of 0.5 degrees; cacheable for 5 minutes.
- Webhook
- 5 seconds per delivery; 3 for a position.
Resources
- Postman collection
- Changelog
- Service status: answers 200 when the API and its database work.