Introduction

The Distribution API is the partner-facing channel for pool-based flight distribution. It lets approved partners search flights, re-price offers, hold seats, create orders, and process payments on behalf of their customers.

The API is a two-phase flow:

  1. Mint a session — the partner signs a request with their pre-shared secret (HMAC-SHA256) and exchanges it for a short-lived, scope-limited Bearer token (JWT).

  2. Call endpoints — every other endpoint is called with Authorization: Bearer <token>. The token carries the partner identity, currency, locale, and the granted scopes.

Important: Each airline has their own specific domain. The API endpoints are hosted on airline-specific domains.

Important: All API endpoints are prefixed with /api due to the servlet context path configuration.

To get your partner credentials and airline’s API domain, contact your technical integration manager. Onboarding and operational procedures (secret generation, rotation, kill switches) are covered separately in the partner onboarding guide.

Authentication

Phase 1 — Minting a session

POST /distribution/v1/sessions is the only unauthenticated endpoint. It is protected by an HMAC signature instead of a Bearer token. The partner sends the following headers:

Header Description

X-Partner-Id

The partner’s UUID, as issued during onboarding.

X-Timestamp

Unix epoch seconds at the time of the request. Requests outside the allowed clock-skew window are rejected.

X-Nonce

A unique value per request (UUIDv4 recommended). Replayed nonces are rejected.

X-Signature

Hex-encoded HMAC-SHA256 of the canonical message (see below), keyed with the partner’s pre-shared secret.

The canonical message that is signed is the newline-joined tuple:

<partner_id>\n<timestamp>\n<nonce>\n<sha256_hex(request_body_bytes)>

Example using openssl:

BODY='{"locale":"en_US","currency":"USD","scopes":["search:read","fare:read"]}'
TIMESTAMP=$(date +%s)
NONCE=$(uuidgen | tr 'A-Z' 'a-z')
BODY_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 -hex | awk '{print $2}')
SIGNATURE=$(printf '%s\n%s\n%s\n%s' "$PARTNER_ID" "$TIMESTAMP" "$NONCE" "$BODY_HASH" \
    | openssl dgst -sha256 -hmac "$PARTNER_SECRET" -hex | awk '{print $2}')

Phase 2 — Calling endpoints

All other endpoints require the minted token:

Authorization: Bearer <access_token>

Scopes

The token is granted a subset of scopes — the intersection of what the partner requests and what the partner is entitled to. Each endpoint requires a specific scope; a token missing the required scope receives 403 SCOPE_INSUFFICIENT.

Scope Grants access to

search:read

Flight search

fare:read

Offer re-pricing

order:write

Seat holds and order creation

order:read

Reading order details

payment:write

Creating and confirming payment intents

payment:read

Listing payment methods and reading payment intent status

Airline scoping

Requests may include an optional X-Airline-ID header. When present, it must match the partner’s configured airline; a mismatch is rejected with 403 AUTHZ_FAILED. When absent, the airline is resolved from the partner record.

Error Format

All errors share a consistent JSON envelope:

{
  "code": "SCOPE_INSUFFICIENT",
  "message": "session token does not carry the scope required by this endpoint"
}

Common codes:

HTTP Code Meaning

400

INVALID_REQUEST

Request body or parameters failed validation.

400

INVALID_PAX_COMPOSITION

Passenger composition violates inventory rules.

400

FLOW_KIND_NOT_SUPPORTED

Requested payment flow_kind is not available.

400

PAYMENT_METHOD_NOT_SUPPORTED

Requested payment method is not available.

401

TOKEN_MISSING

Bearer token absent.

401

TOKEN_EXPIRED / TOKEN_REVOKED

Token no longer valid.

403

SCOPE_INSUFFICIENT

Token lacks the scope required by the endpoint.

403

AUTHZ_FAILED

X-Airline-ID did not match the partner’s airline.

403

RETURN_URL_NOT_ALLOWED

Return URL is not HTTPS or its host is not in the partner’s allowed origins.

404

ORDER_NOT_FOUND

Order does not exist or is not owned by the partner.

404

HOLD_NOT_FOUND

Hold does not exist, expired, or is not owned by the partner.

404

PAYMENT_INTENT_NOT_FOUND

No payment intent for the order/intent id.

409

OFFER_SOLD_OUT / SUBQUOTA_EXCEEDED

Capacity is unavailable for the requested composition.

410

OFFER_NOT_AVAILABLE / HOLD_EXPIRED

Offer or hold is no longer available.

429

RATE_LIMIT_EXCEEDED

Per-partner rate limit exceeded.

Rate Limiting

Each partner has a configured requests-per-second limit. Exceeding it returns 429 RATE_LIMIT_EXCEEDED.

Endpoints Summary

Method Endpoint Scope Description

POST

/distribution/v1/sessions

— (HMAC)

Mint a session token

GET

/distribution/v1/ping

any valid token

Bearer-authenticated smoke test

POST

/distribution/v1/search

search:read

Pool-based flight search (one-way)

POST

/distribution/v1/offer

fare:read

Batch re-price pool offers

POST

/distribution/v1/orders/holds

order:write

Hold seats on a pool offer

POST

/distribution/v1/orders

order:write

Create an order from a hold

GET

/distribution/v1/orders/{orderId}

order:read

Get order details

GET

/distribution/v1/orders/{orderId}/payments/methods

payment:read

List available payment methods

POST

/distribution/v1/orders/{orderId}/payments/intents

payment:write

Create a payment intent

GET

/distribution/v1/orders/{orderId}/payments/intents/{intentId}

payment:read

Get payment intent status

POST

/distribution/v1/orders/{orderId}/payments/intents/{intentId}/confirm

payment:write

Confirm a payment intent

API Endpoints

Each request block below lists the headers that endpoint requires. Every endpoint except Mint Session requires the Authorization: Bearer <access_token> header; requests that carry a JSON body also require Content-Type: application/json.

Mint Session

Exchanges an HMAC-signed request for a short-lived Bearer token. See Authentication for the signing scheme.

HTTP Request

POST /distribution/v1/sessions
Content-Type: application/json
X-Partner-Id: <partner_uuid>
X-Timestamp: <unix_epoch_seconds>
X-Nonce: <unique_per_request>
X-Signature: <hmac_sha256_hex>

Request Body

{
  "locale": "en_US",
  "currency": "USD",
  "cart_id": null,
  "scopes": ["search:read", "fare:read", "order:write", "order:read", "payment:write", "payment:read"]
}

Response

{
  "session_id": "5f8d0a2c-1b3e-4c6a-9f1d-2e3b4c5d6e7f",
  "access_token": "<jwt>",
  "expires_at": "2026-07-15T12:30:00Z",
  "token_type": "Bearer",
  "scopes": ["search:read", "fare:read", "order:write", "order:read", "payment:write", "payment:read"]
}

The returned scopes reflect what was actually granted (the intersection of requested and entitled scopes), so the partner can detect any gap.

Ping

A smoke-test endpoint that echoes the caller’s identity from the Bearer token. Useful to confirm the mint → bearer pipeline works.

HTTP Request

GET /distribution/v1/ping
Authorization: Bearer <access_token>

Response

{
  "partner_id": "f8cc4f24-8f68-4ade-9033-ec1402fe1836",
  "session_id": "5f8d0a2c-1b3e-4c6a-9f1d-2e3b4c5d6e7f",
  "server_time": "2026-07-15T12:00:00Z"
}

Search Flights

Pool-based flight search. Requires scope search:read.

Note
This release supports direct, one-way itineraries only. Supplying return_date is rejected with 400 INVALID_REQUEST (round-trip and connections are a later milestone).

HTTP Request

POST /distribution/v1/search
Authorization: Bearer <access_token>
Content-Type: application/json

Request Body

{
  "origin": "ALA",
  "destination": "OSS",
  "outbound_date": "2026-07-15",
  "pax": { "adt": 1, "chd": 0, "inf": 0 },
  "currency": "USD"
}

Response

The call always returns 200 OK; offers may be empty when no pool offers match.

{
  "offers": [
    {
      "offer_id": "22222222-0000-0000-0000-000000000001",
      "fare_frame_id": "ffffffff-0000-0000-0000-000000000001",
      "flight_legs": [
        {
          "flight_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb",
          "flight_number": "FA101",
          "origin": "ALA",
          "destination": "OSS",
          "departure_date": "2026-07-15",
          "arrival_date": "2026-07-15",
          "departure_time": "08:00",
          "arrival_time": "09:00",
          "duration_minutes": 60
        }
      ],
      "pricing": {
        "base_price": 50.00,
        "total_price": 50.00,
        "currency": "USD",
        "adt_count": 1,
        "chd_count": 0,
        "inf_count": 0
      }
    }
  ],
  "currency": "USD"
}

Re-price Offers

Re-checks a batch of pool offers by id. Requires scope fare:read. The whole call returns 200 OK; the partner inspects each entry’s status. Up to 100 ids per call.

HTTP Request

POST /distribution/v1/offer
Authorization: Bearer <access_token>
Content-Type: application/json

Request Body

{ "offer_ids": ["22222222-0000-0000-0000-000000000001"] }

Response

{
  "results": [
    {
      "offer_id": "22222222-0000-0000-0000-000000000001",
      "status": "VALID",
      "current_price": 50.00,
      "currency": null,
      "fare_frame_ids": ["ffffffff-0000-0000-0000-000000000001"]
    }
  ]
}

Per-offer status is one of VALID, NOT_FOUND, or EXPIRED.

Hold Seats

Reserves seats on a pool offer for a configurable time-to-live, giving the partner time to collect passenger details before creating an order. Requires scope order:write.

HTTP Request

POST /distribution/v1/orders/holds
Authorization: Bearer <access_token>
Content-Type: application/json

Request Body

{
  "pool_offer_id": "22222222-0000-0000-0000-000000000001",
  "fare_frame_id": "ffffffff-0000-0000-0000-000000000001",
  "pax": { "adt": 1, "chd": 0, "inf": 0 }
}

Response

201 Created:

{
  "hold_id": "66666666-6666-6666-6666-666666666666",
  "expires_at": "2026-07-15T12:15:00Z"
}

Failure cases: 409 OFFER_SOLD_OUT, 409 SUBQUOTA_EXCEEDED, 410 OFFER_NOT_AVAILABLE, 400 INVALID_PAX_COMPOSITION.

Create Order

Creates an order from a previously obtained hold. Requires scope order:write.

This endpoint is idempotent — callers MUST supply an Idempotency-Key header (UUIDv4 recommended). Repeating the call with the same key returns the original order instead of creating a duplicate.

HTTP Request

POST /distribution/v1/orders
Authorization: Bearer <access_token>
Content-Type: application/json
Idempotency-Key: <uuid>

Request Body

{
  "hold_id": "66666666-6666-6666-6666-666666666666",
  "passengers": [
    {
      "first_name": "John",
      "last_name": "Doe",
      "date_of_birth": "1990-01-01",
      "gender": "MALE",
      "citizenship": "US",
      "document_type": "PASSPORT",
      "document_number": "123456789",
      "document_expiry_date": "2030-01-01"
    }
  ],
  "contact": {
    "name": "John Doe",
    "phone": "+1234567890",
    "email": "john@example.com"
  }
}

Response

201 Created:

{
  "order_id": "77777777-7777-7777-7777-777777777777",
  "status": "CONFIRMED",
  "currency": "USD",
  "total_amount": 50.00,
  "payment_due_date": "2026-07-15T13:00:00Z",
  "payment_intent_id": "99999999-3333-0000-0000-000000000001"
}

Failure cases: 404 HOLD_NOT_FOUND, 410 HOLD_EXPIRED.

Get Order

Returns order details for a partner-owned order. Requires scope order:read. Partners can only read their own orders; an unknown or foreign order returns 404 ORDER_NOT_FOUND.

HTTP Request

GET /distribution/v1/orders/{orderId}
Authorization: Bearer <access_token>

Response

{
  "order_id": "77777777-7777-7777-7777-777777777777",
  "status": "CONFIRMED",
  "currency": "USD",
  "total_amount": 50.00,
  "contact": { "name": "John Doe", "phone": "+1234567890", "email": "john@example.com" },
  "items": [
    {
      "item_id": "0a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9",
      "type": "FLIGHT",
      "status": "CONFIRMED",
      "description": "ALA-OSS 2026-07-15",
      "amount": 50.00,
      "hold_expires_at": null
    }
  ],
  "invoice": {
    "invoice_id": "1a2b3c4d-5e6f-7081-9203-a4b5c6d7e8f9",
    "status": "ISSUED",
    "amount": 50.00,
    "currency": "USD",
    "payment_due_date": "2026-07-15T13:00:00Z",
    "payment_intent_id": "99999999-3333-0000-0000-000000000001",
    "issued_at": "2026-07-15T12:00:00Z",
    "paid_at": null
  }
}

List Payment Methods

Returns the payment methods available for the order. Requires scope payment:read.

HTTP Request

GET /distribution/v1/orders/{orderId}/payments/methods
Authorization: Bearer <access_token>

Response

{
  "methods": [
    { "id": "payler", "name": "Payler", "flow_kinds": ["PSP_NATIVE"] },
    { "id": "freedom", "name": "Freedom", "flow_kinds": ["PSP_NATIVE"] }
  ]
}

Create Payment Intent

Creates a provider-specific payment session on the order’s reserved payment intent. Requires scope payment:write.

The endpoint is idempotent per order: a single payment intent is reserved at order creation, and provider references are get-or-create on that intent, so repeating the call for the same order returns the same reference. No client idempotency key is required.

success_url and cancel_url must use HTTPS and their host must be in the partner’s allowed origins, otherwise 403 RETURN_URL_NOT_ALLOWED is returned. This release supports flow_kind PSP_NATIVE only.

HTTP Request

POST /distribution/v1/orders/{orderId}/payments/intents
Authorization: Bearer <access_token>
Content-Type: application/json

Request Body

{
  "method": "payler",
  "flow_kind": "PSP_NATIVE",
  "success_url": "https://partner.example.com/ok",
  "cancel_url": "https://partner.example.com/cancel",
  "return_locale": "en_US"
}

Response

201 Created:

{
  "intent_id": "99999999-3333-0000-0000-000000000001",
  "status": "CREATED",
  "psp_payload": {
    "url": "https://psp.example.com/pay/abc123",
    "payment_due_date": "2026-07-15T13:00:00Z"
  }
}

The shape of psp_payload depends on the provider.

Get Payment Intent Status

Polls the current status of a payment intent. Requires scope payment:read.

HTTP Request

GET /distribution/v1/orders/{orderId}/payments/intents/{intentId}
Authorization: Bearer <access_token>

Response

{
  "intent_id": "99999999-3333-0000-0000-000000000001",
  "status": "PENDING",
  "has_expired": false,
  "payment_method": "payler",
  "last_transition_at": "2026-07-15T12:05:00Z"
}

Confirm Payment Intent

Server-side confirmation hook, typically called after the buyer completes the provider flow (e.g. post-3DS). Requires scope payment:write. Returns 204 No Content on success.

HTTP Request

POST /distribution/v1/orders/{orderId}/payments/intents/{intentId}/confirm
Authorization: Bearer <access_token>

Machine-readable Contract

The authoritative, always-current request/response schema is published as an OpenAPI document and served by each deployment at /api/swagger-ui/index.html. This page is a narrative companion to that contract.