# B2B travel API for agencies: booking from their own system

> When a large agency wants to book your hotels, tours and transfers from its own system rather than your portal's screens. What an agency booking API should offer, what must stay exactly as it is on the portal, and how an integration project runs.

*Source: https://itravel.cloud/blog/b2b-travel-api-for-agencies · published Nov 1, 2026 · topic: B2B travel*

A B2B travel API lets a large partner agency book your inventory from its own system instead of your portal's screens. In iTravel it is a booking API, `/api/b2b/v1`, that you switch on agency by agency. Each key acts as one of the agency's people, so it sees the same products, prices, currency and credit as the portal, and a test key never books for real.

## When an agency asks to book from its own system

The request tends to come from your biggest partners: an agency with its own website, a consolidator with a mid-office, a group that re-keys every booking you confirm into its own records. Their staff already book with you; what they want is to stop typing it twice.

Saying yes is a commercial decision about one agency, not a change to how you sell. Nothing about the API should give that agency anything the portal would not: not a lower price, not more credit, not a product you keep for other partners. The API is a second door into the same shop. Everything below follows from that one rule.

## What the agency booking API offers

The tenant switches the API on for an agency in Manage, Agencies, the agency, API. Until then every call answers that the API is not enabled for that agency. Once it is on, the agency's developer works from an OpenAPI document downloadable from that page and from the agency's own "My agency" page.

| Call | What the agency's system gets |
|---|---|
| `GET /me` | Who the key acts as, its scopes and mode (live or test), the agency's currency, and which kinds of product it may book |
| `GET /account` and `/account/statement` | Balance, prepaid or credit, what is still available, what is owed and overdue, and the statement with the balance after each line |
| `GET /products` and `/products/{id}` | Your published hotels, tours and transfers this agency may see, per kind: rooms and rate plans, tour options and slots, transfer routes and vehicles. Never a cost or a supplier |
| `POST /search` and `/products/{id}/availability` | Availability and prices for the party, at the agency's own price |
| `POST /quote` | One exact line priced, as the checkout would price it |
| `POST /bookings` | Book or hold the line |
| `POST /bookings/{id}/confirm` or `/release` | Confirm a hold, or let it go |
| `GET /bookings/{id}/cancellation` then `POST /bookings/{id}/cancel` | What cancelling now would charge and refund, then the cancellation |

## What stays exactly as it is on the portal

This is the part to insist on, whoever builds your API. In iTravel, nothing in the API prices or books by itself. Every line goes through the portal's own placement path:

- **The price** comes from the same engine per kind, at the agency's tier, in the agency's currency. The agency never sees the tier or your net, on screen or by machine. (The pricing vocabulary is in [net rates, markup and agent tiers](/blog/net-rates-markup-and-agent-tiers).)
- **The credit** is checked and charged inside the same transaction as on the [one-cart booking engine](/b2b-travel-booking-engine). A booking the account cannot carry is refused and nothing it took is kept. How that account works is covered in [agent credit and prepaid wallets](/blog/agent-credit-and-prepaid-wallets).
- **Visibility** is the same. A product you hide from that agency or its tier answers "no such product", by any route.
- **Holds and requests** follow your settings. A hold lasts the hours you allow and nothing is charged until it is confirmed; a line you sell on request waits for your answer by the deadline you set (see [on request vs instant confirmation](/blog/on-request-vs-instant-confirmation)).
- **Your booking questions** are required through the API exactly as at checkout.
- **Cancellation** follows the policy frozen on the booking. The preview carries a fingerprint, and the cancellation is refused if the fingerprint does not come back or the figures have moved since.
- **Your desk** sees the booking like any other, and the agent gets the same confirmation email.

## Keys, test keys and what protects you

**Each key acts as one person.** A key is bound to the agency and acts as one of its active people, so every rights check is that person's. Suspend the person, or switch the agency's API off, and the key stops working.

**Scopes and limits are yours.** A key carries only the scopes it was given (catalogue, bookings read, bookings write, account) and a requests-per-minute limit that you, the tenant, set per key. The token is shown once. Rotating a key keeps the old one working for 24 hours, so the agency can deploy without an outage; revoking ends it at once.

**A test key never books.** A test key's booking runs the real placement path inside a database transaction that is always rolled back. The answer is real: the price, the refusal, the credit check. But no booking, room, seat, ticket code, money or email survives. What the agency's system was told is kept for 14 days under a TEST reference, so it can confirm, release and cancel it as it would a live one.

**A retry never books twice.** Every write takes an idempotency key. Two identical requests racing each other book once; a retry after a timeout gets the first answer back; the same key with a different body is refused.

**A price nobody saw is never charged.** The agency's system can send the total it showed its user. If the price has moved beyond your tolerance for hotels, or risen at all for tours and transfers, the booking is refused with the new price.

**You can see everything.** A call log on the agency's API page shows each call's route, status, how long it took and the booking it concerned, never a header or a body, and the activity log names the key. A live key that your own staff create needs the right to book on an agency's behalf, and the booking records that member of staff.

## Webhooks: telling the agency's system what changed

An integration that only asks will miss what changes on your side: a request you accept, a hold that lapses, a booking your desk cancels. Booking-status webhooks close that gap. An endpoint belongs to one agency and receives events only about that agency's bookings: `booking.held`, `booking.on_request`, `booking.confirmed`, `booking.cancelled` (with the reason: released, declined, expired, withdrawn or cancelled) and `booking.amended`. They fire whoever made the change, through the API, the portal or your desk.

Each delivery is signed with an HMAC of a timestamp and the raw body, under neutral header names, so the agency's developer sees your brand and never the platform's. A delivery id stays the same on every attempt, so the receiver can discard duplicates. Anything other than a success is retried with growing gaps for up to a day, then marked failed and can be replayed; an endpoint that keeps failing is switched off with the reason. Test bookings are delivered the same way, marked as tests.

## A worked example: an integration project

The figures are illustrative. Agency M sells your tours and transfers on its own website, buys in US dollars on a credit limit of 20,000, and wants bookings to flow straight from its system.

**Week one: switch on and look.** You switch the API on for Agency M and agree who the key acts as: a person you invite for the purpose, "M Integration". Their admin creates a test key with all four scopes. Their developer reads the OpenAPI document and calls `/me`: currency USD, sells tours and transfers. `/products` lists the 60 products Agency M can see; the private tour you keep for another partner is not there.

**Week two: rehearse the failures.** With the test key they quote an airport transfer at 45 USD and book it with that expected total and an idempotency key. They pull the network cable mid-request and retry: the same TEST booking comes back, not a second one. You raise the transfer's price by 10%; their stored 45 is refused with the new figure. They try a group booking of 25,000 USD: refused for insufficient funds, and nothing moved. They hold a tour, confirm it, then preview and cancel it with the fingerprint. Their webhook endpoint receives each event, marked as a test.

**Week three: go live.** Their admin creates a live key and revokes the test key. The first live bookings appear on your desk like any other, and `/account/statement` on their side matches the agency statement on yours.

## What the API does not do

- **Packages** are sold through the portal only; a package through the API is a clean refusal.
- **One line per booking.** A stay, a tour or a transfer; a multi-line cart is a portal feature.
- **Amendments** to dates or names are made in the portal or by your desk; the API confirms, releases and cancels, and its webhook says when a booking was amended.

## Checklist before you switch it on

- **The agency is approved and its account set:** currency, prepaid or credit, tier, visibility (see [sub-agent registration and approval](/blog/sub-agent-registration-and-approval)).
- **A named person the key acts as**, so a suspension or departure has a clear effect.
- **Scopes cut to what the integration needs**, and a per-minute limit you are comfortable with.
- **The test key run through every failure:** unavailable, price changed, insufficient funds, a retry, a cancellation.
- **A webhook endpoint pinged and verifying signatures** before the live key is created.
- **Someone on your side watching the call log** for the first week.
- **The API on the list** if you are still writing [your portal requirements](/blog/b2b-travel-portal-development-requirements).
