Guides · Billing & payments

Billing & payments

Two separate flows of money exist in Norba, and this page is only about the second one.

FlowWho pays whomWhere it’s documented
The traveller pays the agencyFare + the agency’s own service fee, settled with the airline by the agency. Carried on payment at POST /v1/orders and settled at POST /v1/orders/{orderID}/commit.Orders Lifecycle
The agency pays NorbaA share of what it sold, plus metered usage, charged to a card on file via Stripe.This page
Who gets charged
Stripe customers, cards, subscriptions and invoices hang off a user, not an agency. A booking made with an agency API key is attributed to that agency’s billing owner — its active owner, else an admin, else finance, deleted accounts excluded — so the bill always lands on someone who can still see it. A developer-portal key bills the user it belongs to directly.

What a booking costs

Fees are ad valorem — a percentage of the airline’s own numbers, never of anything the client sent in the request body — with a floor and a ceiling per line and a cap across a whole order. The full rate card, including the pay-as-you-go and enterprise tiers and the monthly volume ladder, is on the pricing page. Three rules that don’t fit a rate card:

  • The basis is the airline’s price, never the client’s. The booking fee is computed from the base fare Norba snapshotted when the offer was priced (or, failing that, the total the carrier confirmed), and every seat/ancillary fee from the catalogue price Norba itself served for it — nothing else can be the basis of a percentage.
  • Currency conversion is dated, or deferred, never guessed. Fees are defined in EUR; a basis in another currency converts at a dated FX rate and the usage line keeps the carrier’s original figure and the rate used. With no rate available yet, the line is written in the carrier’s currency priced at nothing and marked held — it prices itself the moment a rate is added, and is never converted at a guessed rate or silently dropped.
  • A same-day cancellation gets its fee back. An order cancelled or refunded within 24 hours of booking is credited its Norba fees as a mirror line, net of anything already credited — so cancelling twice does not credit twice.

Standing: what stops a booking

Every metered request, and every order, is checked against the account’s standing first:

SituationData lookupsServicingBooking
Inside the free allowancefree€2.00allowed
Past the allowance, card on filemetered€2.00allowed
Past the allowance, no card402 payment_required€2.00402 payment_method_required (pay-as-you-go); enterprise books without a card
Delinquent (flagged by staff)allowance only, then 402 delinquent€2.00402 delinquent
Paused by staffserved, not billedserved, not billedallowed, not billed

Shopping (search) is never gated and never consumes the allowance. On a deployment with Stripe not configured, nothing is ever gated on a card.

Every gate answers with the same structured body. Most reasons are 402; a plan code the catalogue does not recognise is 403, because no card fixes a configuration problem:

{
  "error":        "payment_method_required",
  "reason":       "payment_method_required",
  "message":      "Your plan requires a payment method on file before an order can be created. Add a card in your dashboard to book.",
  "docs_url":     "/billing",
  "plan_code":    "payg",
  "period_start": "2026-09-01T00:00:00Z",
  "period_end":   "2026-10-01T00:00:00Z"
}
HTTPreasonMeaning
402payment_requiredThe free monthly request allowance is exhausted, and no card is on file to bill past it. The default reason — every other value below is more specific.
402payment_method_requiredThe plan requires a card on file before an order can be created, and none is attached. Add one, then retry the booking — nothing else needs to change.
402delinquentStaff have flagged the account for unpaid invoices. Metered reads still work inside the allowance; booking and anything past it does not, until the open invoices are settled.
402past_dueThe subscription itself is in dunning with Stripe. New charges are refused until it is settled.
403unknown_planThe account's plan code does not match anything in the catalogue. Contact support — this is a configuration problem, not something a card fixes.

A sixth value, plan_disabled, is reserved in the response contract but not currently produced by any check — a plan is retired by removing it from the catalogue, which surfaces as unknown_plan for any account still on it.

Settlement: from usage to invoice to card

One function turns metered usage into an invoice, under a per-account lock so two settlement runs can never draft against the same usage. It runs from two places:

  1. The monthly close — every hour, and at boot, for every period that can no longer grow. It releases any usage line that was held for a missing FX rate and now has one, totals the collectable usage by category, adds the volume-ladder discount and any back-office discount as their own lines, draws down the account’s credit balance, drafts the invoice, claims the usage against it (which is what makes re-running the close a no-op), and charges the default card. A period under €0.50 settles to a zero invoice; a period that nets negative carries the surplus forward as credit instead of issuing a refund.
  2. Removing your last card (DELETE /v1/dev/billing/payment-methods/{id}?charge_outstanding=true) runs the same settlement mid-period: it invoices what is owed so far at the plan rate, claims it, charges the card being removed, and only then detaches it. The month-end close still bills what comes after, and its volume ladder still counts the orders that were settled early.
A Stripe invoice is one attempt at collecting ours
The invoice is Norba’s own row first — its id travels to Stripe in the Stripe invoice’s metadata.norba_invoice_id. The webhook only ever updates that row’s status, number and hosted link; it never touches its period or discount breakdown, and a Stripe invoice this system voided itself is ignored when the same void comes back on the webhook.

A declined card leaves the invoice open with its usage already claimed — nothing is re-billed twice. The card stays on file, a retry runs once a day for up to ten attempts, and adding a new default card triggers an immediate retry rather than waiting for the next scheduled one. From the back office, staff can settle, void, or write off an invoice directly; voiding also voids the corresponding Stripe invoice, so a debt cancelled on Norba’s side does not stay open and payable on Stripe’s.

Managing billing from your account

These routes sit under /v1/dev/billing/* in the developer portal — they use your session cookie, not an X-API-Key, so call them from the dashboard’s own origin rather than a server-to-server integration. A support engineer impersonating your account cannot add, promote, or detach a card on your behalf — every mutating route below is blocked during impersonation.

MethodPathDescription
GET/v1/dev/pricingPublic rate card — no session required. Every plan, its tiers and its volume ladder, with every derived figure (percentage, floor, ceiling) computed server-side.
GET/v1/dev/billing/summaryThe current period: plan, usage by category, whether a card is on file, and BookingBlocked / BookingBlockedReason in the same reason vocabulary as the 402 above.
GET/v1/dev/billing/payment-methodsList saved cards.
POST/v1/dev/billing/setup-intentCreate a Stripe SetupIntent. 503 stripe not configured on a deployment with no Stripe wired.
POST/v1/dev/billing/payment-methodsFinalize attachment of the card confirmed against that SetupIntent — body is { "paymentMethodId": "pm_…" }, never raw card details.
POST/v1/dev/billing/payment-methods/{id}/defaultMake this card the one charged at settlement.
DELETE/v1/dev/billing/payment-methods/{id}Remove a card. Add ?charge_outstanding=true to settle what's owed so far and charge it first — see Settlement above.
GET/v1/dev/billing/invoicesUp to 100 invoices for the current user, each with its Stripe hosted link and PDF once issued.
POST/v1/dev/billing/webhookStripe webhook receiver. Not for integrators to call — listed for completeness.
Cards never reach Norba's servers
Attaching a card is a two-step SetupIntent flow: your frontend confirms the card directly with Stripe.js using the clientSecret from POST /setup-intent, and only the resulting pm_… id — never the card number — is sent to POST /payment-methods.

GET /v1/dev/billing/summary — response

Field names are exactly as below (PascalCase — this response has no snake_case mapping, unlike the rest of the API):

{
  "PlanCode": "payg",
  "PlanName": "Pay as you go",
  "MonthlyFreeRequests": 5000,
  "PeriodStart": "2026-09-01T00:00:00Z",
  "PeriodEnd": "2026-10-01T00:00:00Z",
  "TotalRequests": 812,
  "FreeRequests": 812,
  "BillableRequests": 0,
  "TotalCents": 0,
  "HasPaymentMethod": true,
  "PaymentRequired": false,
  "Orders": 3,
  "OrdersCents": 645,
  "ByCategory": [
    { "Category": "booking", "Label": "Completed booking", "TotalRequests": 3, "BillableRequests": 3, "TotalCents": 645, "Model": "percentage", "RatePercent": "2.50" }
  ],
  "Delinquent": false,
  "RequiresPaymentMethod": true,
  "BookingBlocked": false,
  "BookingBlockedReason": ""
}

Orders/OrdersCents count and total completed bookings only — distinct from BillableRequests, which also includes seats, bags and data calls. BookingBlockedReason is empty until a booking would actually be refused, so a dashboard can show the same warning before an integration ever sees the 402.

Things worth knowing before you integrate

  • Shopping is never billed and never gated — search as much as you want.
  • The booking fee is computed once the airline has confirmed the order, from the airline's own numbers, and runs detached from the request — it can delay when a charge lands in usage_events, but it can never fail or delay the booking itself.
  • Every usage line records where its basis came from (airline_offer, airline_order, airline_catalogue, or client_declared) — useful when a fee looks off and you want to know which number it was computed from.
  • Money is integer millicents throughout the billing system, so a fee amount you read back is exact — there is no floating-point rounding to account for.
  • The monthly volume ladder is a discount applied once at month-end, not a live rate — an order's own usage line never changes after the fact, only the close's own credit line does.
Billing & Payments: Fees, Standing and Settlement | Norba Docs