Back to Directory
B

BRIJ Flight API

Other
Production flight API for paid flight search, escrow-backed booking intents, and airline order status lookup. `POST /air/search` accepts either a valid x402 `PAYMENT-SIGNATURE` or a BRIJ gateway sponsored-preview signature. `POST /air/intents`, `POST /air/book`, and `POST /air/refund-requests` are protected by x402. Clients that do not send a valid `PAYMENT-SIGNATURE` header receive `402 Payment Required` with a `PAYMENT-REQUIRED` header and JSON payment requirements body. Action endpoints (POST /air/book, POST /air/refund-requests) take intent_id in the request body — never in the path. The two reads are path-addressed: GET /air/intents/{intent_id} and GET /air/orders/{order_id}.

About BRIJ Flight API

Review the Service identity, supported languages, protocol versions, and source metadata discovered by the directory.

Service details

Service Id
ff71b60d-73c2-44f3-a473-94fb8501eecd
Service origin
https://travel.brij.fi
ListedSeptember 24, 2026, 6:49 PM
Last checkedSeptember 24, 2026, 8:07 PM

Protocol details

SourceOpenAPI

Endpoints

Endpoint details are saved from the OpenAPI document. Authentication requirements and prices are advertised information, not a verified payment or access guarantee. The document may contain additional endpoints.Open OpenAPI document
POST/affiliates

Get your referral code (free, idempotent)

Free. {wallet} → {code, link}. One short code per Solana wallet — share app.brij.fi/?ref=<code> (or pass referrer on POST /air/intents). Referred bookings credit 20% of the platform fee to your wallet — earned when the booking captures (ticket actually issued), vested only once the journey has been flown with no refund issued (pending until then). Statement: GET /affiliates/{code}. Payouts are periodic USDC transfers once the balance clears the minimum.Price unavailable
GET/affiliates/{code}

Read the statement of an affiliate code.

Free, public, self-serve: referred captured bookings, referred vested bookings (flown, unrefunded), accrued (vested), pending (not flown yet), paid, balance (micros USDC) and the share_bps.Price unavailable
GET/air/airlines

Supported airlines (free).

Free, no auth. Aggregate carrier catalog over every fulfiller tier: source "api" is the API-settled (Duffel) catalog, refreshed at most daily; a tier name ("trip.com" = the OTA, "ryanair.com" = ryanair.com) means that fulfiller has actually QUOTED the carrier — observed coverage that grows with searches and understates an OTA's true span. Presence is recognition, not a bookability promise: what is bookable for a route is what POST /air/search returns.Price unavailable
POST/air/book

Pay the escrow and request booking.

The intent id travels in the request body; the resource URL is static. Paid x402 endpoint. The payment challenge is dynamic for the intent: `payTo` is the intent escrow address, `asset` is the expected escrow mint, and `amount` is the expected escrow amount. After x402 settlement, the API records the passenger list (lead first), verifies exact escrow funding on-chain, and enqueues the booking worker. Airline payment and escrow capture happen asynchronously. If booking fails after x402 settlement, retry the same `/book` call; excess funds remain refundable to `refund_wallet`. **Lowcost travel document.** For an intent created on a trip.com: offer, each passenger additionally requires nationality, passport_number, and passport_expiry (YYYY-MM-DD, in the future) — the supplier's booking form demands them. Missing or expired documents are refused BEFORE payment (400 travel_document_required / travel_document_expired). Booking is asynchronous exactly like fastbooking: poll GET /air/intents/{intent_id} until bo…Price unavailableThe operation requires declared authentication; API keys, bearer tokens and other schemes are not evidence of AEP support.
POST/air/intents

Create an escrow-backed booking intent from a selected offer.

Paid x402 endpoint. Send a valid `PAYMENT-SIGNATURE` header to create the intent. Without payment, the API returns `402 Payment Required`. Optional request field referrer: an affiliate code (see POST /affiliates) — the resolved affiliate wallet is frozen on the intent and credited a share of the platform fee once the booking captures and the journey is flown without refund; unknown or self-referring codes are silently ignored. Creates the local booking intent and initializes its Solana escrow. This does not fund the escrow, pay the airline, or issue a ticket. **Lowcost (trip.com: offers).** These book through a browser-driven fulfiller, not an airline API, so a few rules differ. The escrow amount is the offer's own price (fare menus from POST /air/offer-details are bookable: an offer_id like "trip.com:<flight>##<n>@s<search>" locks fare n and the escrow prices at that fare). A fare offer_id is only bookable while its menu is fresh (~30 min) — a stale one returns 409 fare_menu_expired, re-open the fares an…0.10 USDThe operation requires declared authentication; API keys, bearer tokens and other schemes are not evidence of AEP support.
GET/air/intents/{intent_id}

Fetch a booking intent by id.

Price unavailable
POST/air/offer-details

Drill into one offer: fare menu, bag prices, live price.

Paid x402 endpoint: $0.01 USDC for fastbooking: offers, $0.10 USDC for offers from a browser tier (trip.com:, ryanair:) — reading their fare menu costs a real browser session at the supplier — the PAYMENT-REQUIRED challenge always quotes the applicable price. fastbooking: offers — re-fetches the offer from the flight provider and returns it in the same shape as search results, with CURRENT price and expires_at, plus available_services: the purchasable extras (checked bags with weight limits and prices) on that exact fare. fare_options lists EVERY fare of this flight (same aircraft, every brand: Basic, Standard, Flex…), fresh from the supplier and price-ascending, each with its own bookable offer_id — including fares the search left out of its page. Pick the row you want and pass its offer_id to POST /air/intents. browser-tier offers (trip.com:, ryanair:) — return the offer with fare_options filled from the supplier's own fare menu: per fare, the price, cabin, seats_left, and conditions[] (the supplier's ve…0.01 USDThe operation requires declared authentication; API keys, bearer tokens and other schemes are not evidence of AEP support.
GET/air/orders/{order_id}

Fetch airline order status.

Returns the booking reference and order status for ANY fulfiller — a Duffel ticket is read from Duffel, a partner-tier booking (trip.com, ryanair.com) from the marketplace's own record: the supplier reference, the airline PNR the settlement oracle extracted from the confirmation email, and the engaged amount. Paid x402 endpoint at a token $0.01 USDC, and still gated by the X-Customer-Support-Code returned at intent creation. Refusals are never charged: a missing or wrong code (401/403) or an unknown order (404) happens before the payment settles. Note the response distinguishes an unknown order (404) from a wrong code (403).0.01 USDThe operation requires declared authentication; API keys, bearer tokens and other schemes are not evidence of AEP support.
POST/air/refund-requests

Request a manual refund after the intent has been booked.

The intent id travels in the request body; the resource URL is static. Paid x402 endpoint. Send a valid `PAYMENT-SIGNATURE` header to record the refund request. Without payment, the API returns `402 Payment Required`. Records a refund request for a booked intent. The on-chain escrow has already been captured by the time the intent is `booked`, so refunds at this stage are processed manually by an operator. This endpoint persists the request and pages the operator on Telegram; the response is `202 Accepted` and is **not** a guarantee of refund — eligibility and any applicable carrier penalties are reviewed manually. For pre-capture states (`active`), the booking_sync worker handles refunds automatically; this endpoint returns `409 use_automatic_refund` for those. The caller must include the `X-Customer-Support-Code` header matching the code returned at intent creation. For multi-passenger bookings, X-Passenger-Family-Name is the LEAD passenger's family name (position 0 of the /air/book list).0.10 USDThe operation requires declared authentication; API keys, bearer tokens and other schemes are not evidence of AEP support.
POST/air/search

Search live flight offers.

Flight search can be paid with x402 or sponsored by the BRIJ gateway. For direct x402 clients, send a valid `PAYMENT-SIGNATURE` header. Without payment, the API returns `402 Payment Required` carrying the **current** price. For BRIJ Wallet preview calls, the gateway may send `X-BRIJ-SPONSORED-*` headers. Providers should treat that as a BRIJ-sponsored preview, not as a user-paid x402 settlement. Every price in the response is the price a buyer pays: total_amount / total_amount_decimal (offers, fare_options, highlights) are all-in, and max_price filters on that figure. POST /air/intents asks exactly this amount as expected_escrow_amount (a wallet enrolled in a fee-waiving cohort is asked less, never more). Base x402 price is 0.10 USDC. The price is multiplied (up to 50×) by the current search load: completed searches in the last 60 seconds and any upstream rate-limit response in the last 5 minutes. Always read the latest `PAYMENT-REQUIRED` challenge before signing - the price your client saw a moment ago …Price unavailableThe operation requires declared authentication; API keys, bearer tokens and other schemes are not evidence of AEP support.
POST/air/search-updates

Poll a progressive search for offers still arriving (free).

Free companion to POST /air/search — no x402 payment: the search was already paid, and the unguessable `search_id` is the authorization. A search answers within seconds with the fast tiers' offers and `status: enriching` when slower, cheaper fulfiller tiers are still working. Poll this endpoint (respecting `poll_after_ms`, ~5s) until `status: complete`. Each poll returns the full merged-and-deduplicated offer list so far — idempotent reads, safe to retry. The `search_id` expires ~5 minutes after the search; after that this endpoint returns 410 and a new search is needed. The rest of the purchase tunnel (offer-details, intents, book) is unchanged — offers gained here are used exactly like offers from the initial response.Price unavailable
GET/recipes

The recipe catalog with settlement stats

Free. The pinned fulfiller-recipe registry: one entry per supplier domain (discovery facts: flow, kind, regions, cabins, trip types, currency cap, partner, primary, protocol_version) plus settlement-truth stats per domain (orders assigned, booked, captured — ticket actually issued —, refund classes, booking_rate, capture_rate, avg minutes to ticket, per-task run telemetry with an exit-code taxonomy) over last_30d and all_time windows, the pinned recipes_sha, the pinned recipe_spec_sha (the recipe-spec commit whose SDK production runs — an author develops against that public repo, whose main can be ahead of it), and supported_protocol_versions. Infra failures are attributed to the platform, never to a recipe.Price unavailable
GET/recipes/canary/{domain}

Last daily canary run of the recipe you authored.

Free, author-scoped like /recipes/source. Once a day the marketplace runs the same walk-only dry-run a submission gets (search → offer-details → book, no card, Pay never clicked) on every activated recipe, so a supplier site that moved is found before a customer's order. Returns the last run: ran_at, passed (the guichet's own rule: a guest-flow recipe reaches a priced checkout with payReachable:true; an ephemeral-account recipe stops cleanly at the signup wall), a one-line summary, and the full dry_run evidence in the same shape as /recipes/submissions/{id}/evidence. Screenshots of that run at …/canary/{domain}/screenshots/{name}. 404 canary_not_run until the first run. Never cached. Each task carries the Browserbase session_id of its run; fetch the video replay at …/canary/{domain}/replays/{task} (HLS), or watch it in the dashboard session inspector at https://www.browserbase.com/sessions/{session_id}. Every watched version is listed in `runs[]` — production's and each candidate in staging, with its submiss…Price unavailableThe operation requires declared authentication; API keys, bearer tokens and other schemes are not evidence of AEP support.
GET/recipes/canary/{domain}/case/{name}

A case file of the last canary run: debug the DOM offline.

Author-scoped, same session and same ?version= as the screenshots. A screenshot says THAT a walk broke; it never says which selector to write. So every bail also writes, beside the PNG: case.state.json (task, url, phase, exit, the bail message), case.html.gz (the outerHTML at the instant of the failure) and case.payloads.json.gz (the last supplier responses). Names are listed per task in dry_run.tasks[].case. The card, the run's live tokens and the traveller's identity are redacted at the source, by value — the page's structure, the flight and the route survive, so the file stays a usable fixture. Replay it with no session and no network: `node scripts/replay-case.mjs <dir> --selector '…'`. The replayed page is DEAD (no scripts ran, nothing hydrated), which is the correct scope: a broken selector is a pure DOM problem.Price unavailableThe operation requires declared authentication; API keys, bearer tokens and other schemes are not evidence of AEP support.
GET/recipes/canary/{domain}/network/{task}

Network timing of a canary task's browser session.

Author-scoped (same session as /recipes/canary/{domain}). The named task's session in the canary run (production's by default, ?version= for a staged candidate). One line per request: when it started, how long it took, its type, method, host, path, HTTP status and error, plus the page's DOMContentLoaded and load. It shows where a walk spends its time, which screenshots and replays do not: on 2026-09-16 trip.com's search API answered in ~1 s while the page waited ~50 s on trackers and stalled CDN files, and a recipe waiting for "load" waits on the slowest of them. Filtered before it leaves the server: no headers, cookies, request or response bodies, and no URL query or fragment. Only canary and submission dry-run sessions (synthetic travellers, never a card) — never a customer's order.Price unavailableThe operation requires declared authentication; API keys, bearer tokens and other schemes are not evidence of AEP support.
GET/recipes/canary/{domain}/replays/{task}

Video replay of a canary task.

Author-scoped (same session as /recipes/canary/{domain}). Returns the HLS playlist (.m3u8) of the named task's browser session in the LAST canary run — task is one of search, offer-details, book. The playlist's fragmented-MP4 segment URLs are self-authorizing CDN links valid ~6 h; download init.mp4 + segments and concatenate for a plain .mp4 a video-capable model can watch, or re-request this endpoint for fresh URLs.Price unavailableThe operation requires declared authentication; API keys, bearer tokens and other schemes are not evidence of AEP support.
GET/recipes/canary/{domain}/screenshots/{name}

A screenshot of the last canary run.

Author-scoped (same session as /recipes/canary/{domain}). image/png, names as listed in dry_run.tasks[].screenshots. Serves the last run OF THE SELECTED VERSION — production's without ?version=, a staged candidate's with it. Evidence is swept 7 days after its run.Price unavailableThe operation requires declared authentication; API keys, bearer tokens and other schemes are not evidence of AEP support.
GET/recipes/mine

Everything this wallet authored, in one call.

Free, author-scoped. This is the entry point: every other author read needs a domain name or a submission id you must already hold, so without this there was nowhere to ask what you own. Returns each domain whose last ACTIVATED manifest names this wallet — live or RETIRED — with its version and the canary history of every version watched on it, and every submission this wallet ever sent with that submission's own content hash. Comparing the two answers the question a resubmitting author actually has: is what I sent what is live? A retired domain is included and flagged: being taken off sale is a commercial decision and says nothing about who wrote the recipe — a retirement is precisely when its author needs to read why. Never cached.Price unavailableThe operation requires declared authentication; API keys, bearer tokens and other schemes are not evidence of AEP support.
GET/recipes/oracles

Settlement-oracle readiness per tier

Free. Each browser tier's email-settlement oracle state: domain, template label, and ready (true = patterns loaded and the tier can capture; false = bootstrap — sender-only declaration, nothing settles until the first real confirmation email writes the patterns). Fulfiller instances in a paying purchase mode check this at boot.Price unavailable
POST/recipes/session

Open an author session (24h bearer token).

Free. Post the signed challenge; get a bearer token scoped to recipe authorship — an app or fulfiller token cannot be used in its place, and this one cannot be used in theirs. A session is only an authenticated name: ownership is checked per read, against the manifest, at that moment. The signature is the ed25519 signature of the challenge message, BASE64-encoded (standard, padded) — not base58; a base58 signature is refused as invalid_proof.Price unavailable
GET/recipes/session-challenge

Bytes to sign to open an author session.

Free. Returns the exact message to sign with the recipe author's wallet: prefix, wallet and timestamp on three lines. Sign it verbatim — the server compares the canonical message, not a substring of it.Price unavailable
GET/recipes/source/{domain}

Read back the recipe you authored.

Free, and scoped: the session wallet must equal the domain's author.wallet in the manifest that was last ACTIVATED. Returns that recipe.mjs and manifest.yaml plus its version (the content hash) — what you need to submit an update with `supersedes`, and what a submission's own version is compared against. Ownership is re-read on every call, so authorship that moved bites immediately rather than at token expiry. A domain naming no wallet is owned by nobody, not by everybody. A RETIRED domain is still served to its author, with retired:true — retiring says what may be sold, never who wrote it. Response is never cached (private, no-store).Price unavailableThe operation requires declared authentication; API keys, bearer tokens and other schemes are not evidence of AEP support.
POST/recipes/submissions

Submit a fulfiller recipe (paid x402: 1 USDC)

Paid x402 endpoint, 1 USDC all-inclusive. Submit {author_wallet, domain, manifest_yaml, recipe_js, supersedes?}. The paying wallet is the author's durable identity (payout, stake, reputation). Pipeline: static contract review (denylist: no subprocesses, no runtime-built code, no unreviewable encoded blobs; plus the executable contract — protocol_version 1, capabilities naming all three tasks with their input schemas, and author.wallet equal to the submitting wallet) → sandboxed dry-run against the live supplier with runner-collected evidence (a dry run that clicks Pay is refused unconditionally) → structured AI judge (evidence vs manifest vs code; low-confidence pass is not a pass; judge outage escalates to a human). Response is 200 whether queued or rejected — the findings are what the fee buys. An external recipe books with the real card once its commit is reviewed and pinned — what authorizes the card is the pin, not the author. Authoring guide: /docs/recipes; contract: https://github.com/brij-digital/rec…Price unavailable
GET/recipes/submissions/{submission_id}

Read the status of a recipe submission.

Free. Status of a recipe submission: payment_pending (fee not yet settled — never runs; unpaid if the settle fails; a settle that never returns pages the operator for manual review) → pending_dryrun or rejected → judged (pass/fail + findings) → awaiting_activation → activated (registry merge + pin landed). Every response carries next_step naming whose move it is. ACTIVATION POLICY: findings-clean or nothing — open block/warn findings send the ball back to the author (fix and resubmit via supersedes); note-severity findings are observations and gate nothing.Price unavailable
GET/recipes/submissions/{submission_id}/evidence

Submission dry-run evidence

Free. The author's read-back of their own dry-run: per-task outcome (exit code, duration, summary, transcript tail, Browserbase session_id, phases timeline + failure_phase from the recipe's emitPhase calls) as collected by the marketplace's runner, plus the judge's verdict when one exists. Failing tasks may carry screenshots — fetch each at …/evidence/screenshots/{name}. The submission id is the capability: it is returned only to the submitter. Iterate on this, fix, resubmit.Price unavailable
GET/recipes/submissions/{submission_id}/evidence/case/{name}

A case file of your submission's dry run.

Author-scoped, beside the screenshots of the same run: case.state.json, case.html.gz and case.payloads.json.gz, the three files a failing walk leaves so a selector can be fixed OFFLINE. Same redaction and the same replay tool as the canary's. Names are listed per task in the evidence's tasks[].case.Price unavailableThe operation requires declared authentication; API keys, bearer tokens and other schemes are not evidence of AEP support.
GET/recipes/submissions/{submission_id}/evidence/network/{task}

Network timing of a submission dry-run task's browser session.

Scoped to the wallet that submitted (author session). The named task's session in this submission's dry run. One line per request: when it started, how long it took, its type, method, host, path, HTTP status and error, plus the page's DOMContentLoaded and load. It shows where a walk spends its time, which screenshots and replays do not: on 2026-09-16 trip.com's search API answered in ~1 s while the page waited ~50 s on trackers and stalled CDN files, and a recipe waiting for "load" waits on the slowest of them. Filtered before it leaves the server: no headers, cookies, request or response bodies, and no URL query or fragment. Only canary and submission dry-run sessions (synthetic travellers, never a card) — never a customer's order.Price unavailableThe operation requires declared authentication; API keys, bearer tokens and other schemes are not evidence of AEP support.
GET/recipes/submissions/{submission_id}/evidence/screenshots/{name}

Submission failure screenshot

Free. One failure screenshot harvested from the dry-run sandbox before teardown (failing tasks only, bounded, 7-day retention). Names come from the evidence's tasks[].screenshots.Price unavailable