Billing
List My Invoices
Lists invoices across every organization the caller may see, paginated, including invoices for cancelled or soft-deleted orders (design doc §44.2 -- an organization is a shared workspace, so members see each other's invoices). Every row carries its own organization_id, so the listing can be grouped by owner; for one organization's invoices alone, read /organizations/{id}/invoices instead. Each invoice's line items are not included here -- fetch one invoice by id for its lines. Each row carries due_at, the date an unpaid invoice must be paid by -- derived from the invoice's created_at plus the grace period of the subscription it bills, and null for an invoice that bills no subscription. A pending invoice whose due_at has passed is overdue; pending on its own does not say that.
query Parameters
pagePage number (1-indexed). Defaults to 1.
Page number (1-indexed). Defaults to 1.
page_sizeNumber of items per page. Defaults to 20, max 100.
Number of items per page. Defaults to 20, max 100.
List My Invoices › Responses
Invoices
Pagination metadata included in paginated responses
Get My Open Invoices Summary
What the caller still owes across every organization they may see: how many invoices are open (pending, including a postpaid invoice before its due date), how many of those are overdue, and their total. One cheap read for a navigation indicator, so a client does not page the invoice list to decide whether to light a dot. total_cents and currency are null when nothing is open or when the open invoices span several currencies.
Get My Open Invoices Summary › Responses
Open invoices summary
countInvoices issued and not yet paid, including a postpaid invoice before
its due date. 0 means nothing is owed.
overdue_countHow many of count are past their due_at.
currencyISO 4217 code of total_cents; null exactly when total_cents is.
total_centsSum of the open invoices' tax-inclusive totals, in cents. null when
nothing is open, and also when the open invoices span more than one
currency (an amount that adds two currencies is withheld, not guessed).
Get My Invoice
Gets one invoice by id, from any organization the caller may see (design doc §44.2), including its line items and, for a Full-series invoice, the billing-identity snapshot frozen onto it at issue. Each line names the project it was billed for (project_id/project_name), resolved server-side through the line's subscription so a client never needs a lookup per line; both are null for a line that names no subscription. The invoice itself carries due_at, the date an unpaid invoice must be paid by, derived from created_at plus the grace period of the subscription it bills and null when it bills none.
path Parameters
idGet My Invoice › Responses
The invoice
amount_centscreated_atcurrencyidis_syntheticstatusAn invoice's status on the /v2 wire.
Deserialize as well as Serialize, because this is also the closed
vocabulary of listOrganizationInvoices's status filter. Deliberately
one type rather than a separate *Filter enum: the filter's accepted
values and the values a row can come back carrying must be the same set,
and a second enum is a second place for them to disagree. A generated
client sees one schema for both.
tax_amount_centstotal_amount_centsbill_to_addressbill_to_countrybill_to_namebill_to_tax_iddue_atWhen an unpaid invoice must be paid by. Identical in meaning and
derivation to InvoiceSummaryResponse::due_at -- see that field,
including its postpaid settlement exception.
failed_atinvoice_numberpaid_atrefunded_attax_labeltax_rateThe tax rate applied at issue, e.g. 0.21 for 21%. None when no
tax rate was resolved for this invoice. Exactly the stored rate
(numeric(6,4)), never a blurred one: a rate the JSON number cannot
carry exactly fails the response instead (V-0699).
List Organization Invoices
Lists one organization's invoices, paginated, including invoices for cancelled or soft-deleted orders. An organization is a shared workspace, so every member sees the organization's invoices, not only ones they placed themselves (design doc §44.2). Answers 404 for an organization the caller does not belong to, the same way reading that organization directly would. Line items are not included here -- fetch one invoice by id for its lines. Each row carries due_at, derived the same way listMyInvoices describes.
Narrow to one status with status. The filter is applied to the organization's whole invoice set, not to the page: ?status=failed answered with an empty page means this organization has no failed invoice at all, which is the question a past-due banner is really asking. A status that names none of the five invoice statuses is refused with invalid-field-value rather than ignored.
path Parameters
idquery Parameters
pagePage number (1-indexed). Defaults to 1.
Page number (1-indexed). Defaults to 1.
page_sizeNumber of items per page. Defaults to 20, max 100.
Number of items per page. Defaults to 20, max 100.
statusNarrows the page to invoices in one status, e.g. failed.
Optional, and a pure narrowing: omitted, the listing is exactly what
it has always been. status=failed answered with an empty page is
kernel saying this organization has nothing past due -- an answer
over the organization's whole invoice set, not over one page of it.
Typed as a String here and decoded by
[InvoiceStatusResponse::parse_wire] so an unrecognised value can be
refused with this surface's own invalid-field-value 400 rather than
the query extractor's plain-text rejection. The emitted schema is the
closed InvoiceStatusResponse enum regardless, so a generated client
cannot spell one wrong in the first place.
Narrows the page to invoices in one status, e.g. failed.
Optional, and a pure narrowing: omitted, the listing is exactly what
it has always been. status=failed answered with an empty page is
kernel saying this organization has nothing past due -- an answer
over the organization's whole invoice set, not over one page of it.
Typed as a String here and decoded by
[InvoiceStatusResponse::parse_wire] so an unrecognised value can be
refused with this surface's own invalid-field-value 400 rather than
the query extractor's plain-text rejection. The emitted schema is the
closed InvoiceStatusResponse enum regardless, so a generated client
cannot spell one wrong in the first place.
List Organization Invoices › Responses
Invoices
Pagination metadata included in paginated responses
Get Organization Open Invoices Summary
What one organization still owes: its open invoice count, how many are overdue, and their total, as getMyOpenInvoicesSummary describes, scoped to the organization. Every member sees it (an organization is a shared workspace, design doc §44.2). Answers 404 for an organization the caller does not belong to.
path Parameters
idGet Organization Open Invoices Summary › Responses
Open invoices summary
countInvoices issued and not yet paid, including a postpaid invoice before
its due date. 0 means nothing is owed.
overdue_countHow many of count are past their due_at.
currencyISO 4217 code of total_cents; null exactly when total_cents is.
total_centsSum of the open invoices' tax-inclusive totals, in cents. null when
nothing is open, and also when the open invoices span more than one
currency (an amount that adds two currencies is withheld, not guessed).
Checkout
Creates a pending Subscription for one product/plan/region combination, along with the Initial invoice that establishes it. Which hardware profile inside the region runs the workload is resolved server-side; customers never name one. region_id is required for every product except a licence, which is placed nowhere and must omit it. A VPS also requires operating_system_id, chosen from GET /api/v2/catalog/products/{id}/operating-systems. Bills the organization named by organization_id, which requires the Owner or Admin role there (or being its personal owner); omit organization_id to bill the caller's own personal organization. Files the subscription under project_id, which must belong to the buying organization; omit it to use (and, on first purchase, create) that organization's default project. A personal organization's games project belongs to the gaming dashboard and is refused here (403 project-gaming-managed); the gaming dashboard buys through POST /api/v2/checkout/gaming. Creates no Order -- V2 has no order concept at all. Pay the returned invoice through the existing invoice-id-keyed payment endpoints (for example POST /api/v1/invoices/{id}/checkout/mollie); once paid, the subscription activates and provisioning is attempted automatically.
Checkout › Request Body
nameThe customer's name for the service being bought.
The subscription is a purchase of a product and carries no name; this
names the ServiceInstance the initial payment creates -- the VPS or
container the customer actually refers to.
plan_idproduct_idslugThe URL segment addressing that service, unique within its project. Supplied rather than derived here: the console suggests one from the name as the customer types and lets them edit it.
cadence_monthsThe billing term to buy, in months: 1, 3, 6 or 12 (V-0717).
Omitting it buys the monthly term, which is what every client
written before term choice sends. The plan's offered terms, with each
one's price and saving, are its terms on the product plan listing.
A term the plan is not sold at is refused (400), never sold monthly
instead; the subscription renews at the same term and Price.
discount_codeAn optional discount code, exactly as the customer typed it.
A code that cannot be used is refused, not ignored: a customer who entered one and was charged full price anyway has been overcharged as far as they are concerned.
Public ports to open on the service once it runs. Omitting this (or sending none) buys exactly what every earlier request bought.
The customer names only the container port and the protocol(s); Zaroz
assigns the public address and port. Each port is billed by the hour it
is held, monthly in arrears: it is never part of the invoice this
request creates, and the quote lists each one as a line of amount zero
with the estimate in metered_estimate. A port that cannot be opened
right after payment never fails the purchase: it is listed on the
service as pending with the reason, and opened as soon as it can be.
Refused for a product that offers none, past the plan's included ports
when more are not priced, and above 10 per request.
hardware_profile_idThe hardware tier inside region_id to run on, as
RegionHardwareResponse.id from GET /api/v2/catalog/regions
(decision-0347: a customer chooses between a region's tiers, each with
its own Price). Omit it to let kernel place the service on the
cheapest tier that fits. A tier that is not listed under region_id,
or that the listing marks unavailable, is refused exactly as an
unbuyable region is.
operating_system_idThe operating system to install, for a VPS purchase
(ProductFamily::VirtualMachine). Required for that family, and
refused if sent for any other -- nothing else on /v2 installs an
operating system. GET /api/v2/catalog/products/{id}/operating-systems
lists what can be named here.
organization_idWhich organization is buying, and therefore which one the subscription and its invoice bill to.
Optional, and omitting it means "my personal organization" -- the only
thing /v2 checkout could bill before shared organizations became
purchasable. Existing clients that send no organization_id keep the
exact behaviour they had.
Sending one requires the caller to be entitled to spend that organization's money: an Owner or Admin membership, or its personal owner. A Member or Billing role is refused (403), never silently redirected to the caller's own organization. An organization the caller has no relationship to answers 404, the same as an unknown id, so ids cannot be enumerated by their refusal.
project_idWhich project the purchased service belongs to (§34.1's
Organization -> Project -> Subscription -> Resource).
Optional, and omitting it means the buying organization's default
project -- created on first use if it does not exist yet, the same
resolution /v1 order creation has always applied for an organization
with no project concept of its own. This is what makes checkout
reachable without first showing the caller an organization: §11's
invisibility on organization_id is worthless if project_id still
forces one into view.
When supplied, it must name a project the buying organization owns; one belonging to another organization is refused rather than ignored, because a subscription filed under someone else's project misattributes that spend in every read that joins through it.
region_idWhere the service should run. Must be one of the regions
GET /api/v2/console/catalog/regions returns for this
product_id/plan_id: that read applies the same access and capacity
rules checkout does, so anything it lists is buyable and anything it
omits would be refused here.
Required for every purchasable product except a licence
(ProductFamily::License), which is placed nowhere at all and is
refused if this is sent. Omit it entirely for a licence purchase.
The size to buy, when the customer sized it themselves.
Omitting this buys the plan exactly as authored, which is what
every existing client does and what every existing request means. This
is an override, not a replacement: plan_id is still required and
still decides which hardware profile runs the workload and which Price
rates it, because those are placement and money rather than size.
A plan is one convenient point inside the range
products.minimum_*/maximum_* describes, and a customer sizing their
own instance is choosing a different point in the same range. Anything
outside it is refused by Product::admits rather than clamped to fit:
silently resizing somebody's order and then charging them for the
result is the worse of the two answers.
This is also what makes persistent storage opt-in. A product whose
minimum_storage_gb is zero admits a shape with no disk, that shape
prices at zero against the storage rate, and the provisioner builds no
volume for it.
Checkout › Responses
The created subscription and its establishing invoice
available_centsThe establishing invoice, in the same shape GET /billing/invoices/{id}
already returns (InvoiceResponse) -- a client that just checked out
pays this invoice through the existing invoice-id-keyed payment
endpoints (POST /api/v1/invoices/{id}/checkout/mollie or
.../paypal, both of which already resolve by InvoiceId alone).
required_centsZC-1324 org trust S5 (§8.4). See [QuoteResponse::required_cents]'s
own doc comment -- identical meaning, read AFTER this checkout's own
effects (its reservation, if it wrote one) rather than before.
V-0449.04: postpaid disclosure, present only for the OWNER of an
organization with a credit limit. See crate::contracts::standing::PostpaidDisclosureResponse.
Gaming Checkout
The gaming dashboard's checkout: the same purchase as Checkout, filed into the caller's personal organization's games project, which it creates on the first purchase and reuses afterwards. It is the only way anything is added to that project. Sells only products the gaming store sells (any other is 404 like an unknown product). Takes the Checkout body; organization_id may only name the caller's own personal organization and project_id must be omitted, or the purchase is refused with 422 gaming-checkout-target-fixed.
Gaming Checkout › Request Body
nameThe customer's name for the service being bought.
The subscription is a purchase of a product and carries no name; this
names the ServiceInstance the initial payment creates -- the VPS or
container the customer actually refers to.
plan_idproduct_idslugThe URL segment addressing that service, unique within its project. Supplied rather than derived here: the console suggests one from the name as the customer types and lets them edit it.
cadence_monthsThe billing term to buy, in months: 1, 3, 6 or 12 (V-0717).
Omitting it buys the monthly term, which is what every client
written before term choice sends. The plan's offered terms, with each
one's price and saving, are its terms on the product plan listing.
A term the plan is not sold at is refused (400), never sold monthly
instead; the subscription renews at the same term and Price.
discount_codeAn optional discount code, exactly as the customer typed it.
A code that cannot be used is refused, not ignored: a customer who entered one and was charged full price anyway has been overcharged as far as they are concerned.
Public ports to open on the service once it runs. Omitting this (or sending none) buys exactly what every earlier request bought.
The customer names only the container port and the protocol(s); Zaroz
assigns the public address and port. Each port is billed by the hour it
is held, monthly in arrears: it is never part of the invoice this
request creates, and the quote lists each one as a line of amount zero
with the estimate in metered_estimate. A port that cannot be opened
right after payment never fails the purchase: it is listed on the
service as pending with the reason, and opened as soon as it can be.
Refused for a product that offers none, past the plan's included ports
when more are not priced, and above 10 per request.
hardware_profile_idThe hardware tier inside region_id to run on, as
RegionHardwareResponse.id from GET /api/v2/catalog/regions
(decision-0347: a customer chooses between a region's tiers, each with
its own Price). Omit it to let kernel place the service on the
cheapest tier that fits. A tier that is not listed under region_id,
or that the listing marks unavailable, is refused exactly as an
unbuyable region is.
operating_system_idThe operating system to install, for a VPS purchase
(ProductFamily::VirtualMachine). Required for that family, and
refused if sent for any other -- nothing else on /v2 installs an
operating system. GET /api/v2/catalog/products/{id}/operating-systems
lists what can be named here.
organization_idWhich organization is buying, and therefore which one the subscription and its invoice bill to.
Optional, and omitting it means "my personal organization" -- the only
thing /v2 checkout could bill before shared organizations became
purchasable. Existing clients that send no organization_id keep the
exact behaviour they had.
Sending one requires the caller to be entitled to spend that organization's money: an Owner or Admin membership, or its personal owner. A Member or Billing role is refused (403), never silently redirected to the caller's own organization. An organization the caller has no relationship to answers 404, the same as an unknown id, so ids cannot be enumerated by their refusal.
project_idWhich project the purchased service belongs to (§34.1's
Organization -> Project -> Subscription -> Resource).
Optional, and omitting it means the buying organization's default
project -- created on first use if it does not exist yet, the same
resolution /v1 order creation has always applied for an organization
with no project concept of its own. This is what makes checkout
reachable without first showing the caller an organization: §11's
invisibility on organization_id is worthless if project_id still
forces one into view.
When supplied, it must name a project the buying organization owns; one belonging to another organization is refused rather than ignored, because a subscription filed under someone else's project misattributes that spend in every read that joins through it.
region_idWhere the service should run. Must be one of the regions
GET /api/v2/console/catalog/regions returns for this
product_id/plan_id: that read applies the same access and capacity
rules checkout does, so anything it lists is buyable and anything it
omits would be refused here.
Required for every purchasable product except a licence
(ProductFamily::License), which is placed nowhere at all and is
refused if this is sent. Omit it entirely for a licence purchase.
The size to buy, when the customer sized it themselves.
Omitting this buys the plan exactly as authored, which is what
every existing client does and what every existing request means. This
is an override, not a replacement: plan_id is still required and
still decides which hardware profile runs the workload and which Price
rates it, because those are placement and money rather than size.
A plan is one convenient point inside the range
products.minimum_*/maximum_* describes, and a customer sizing their
own instance is choosing a different point in the same range. Anything
outside it is refused by Product::admits rather than clamped to fit:
silently resizing somebody's order and then charging them for the
result is the worse of the two answers.
This is also what makes persistent storage opt-in. A product whose
minimum_storage_gb is zero admits a shape with no disk, that shape
prices at zero against the storage rate, and the provisioner builds no
volume for it.
Gaming Checkout › Responses
The created subscription and its establishing invoice
available_centsThe establishing invoice, in the same shape GET /billing/invoices/{id}
already returns (InvoiceResponse) -- a client that just checked out
pays this invoice through the existing invoice-id-keyed payment
endpoints (POST /api/v1/invoices/{id}/checkout/mollie or
.../paypal, both of which already resolve by InvoiceId alone).
required_centsZC-1324 org trust S5 (§8.4). See [QuoteResponse::required_cents]'s
own doc comment -- identical meaning, read AFTER this checkout's own
effects (its reservation, if it wrote one) rather than before.
V-0449.04: postpaid disclosure, present only for the OWNER of an
organization with a credit limit. See crate::contracts::standing::PostpaidDisclosureResponse.
Quote a checkout
Prices one product/plan/region/size combination without buying it (region_id and operating_system_id follow the same rules as /checkout): the same resolution, the same placement, the same price, the same discount and tax rules POST /checkout would apply, so the total quoted here is the total that endpoint invoices. Writes nothing -- no subscription, no invoice, and a discount code is validated but not redeemed. Send the same body you would send to /checkout, minus name and slug. Refusals match /checkout's exactly, which is the point: a size, region or code that cannot be bought says so here rather than on submit.
Quote a checkout › Request Body
plan_idproduct_idcadence_monthsThe billing term to quote. See [CheckoutRequest::cadence_months]:
omitted is the monthly term, and the quote's total is one whole term.
discount_codeAn optional discount code, exactly as the customer typed it. A code that cannot be used is refused here exactly as it would be at checkout, rather than quietly quoting the undiscounted price.
Public ports to price. See CheckoutRequest::endpoints: the same rules
refuse the same requests here. Each comes back as an informational line
of amount zero, and metered_estimate carries the worst monthly cost.
total_cents never includes them.
hardware_profile_idSee CheckoutRequest::hardware_profile_id's own doc comment.
operating_system_idSee CheckoutRequest::operating_system_id's own doc comment.
organization_idWhich organization would be buying. Decides the reseller price multiplier and the tax jurisdiction, so it is quote input rather than bookkeeping.
project_idWhich project the purchase would be filed under.
Optional, mirroring [CheckoutRequest::project_id]. When supplied it
is still checked against the buying organization -- nothing is
written, but a quote is an answer about a purchase this caller could
make, so naming somebody else's project is refused here too. When
absent, nothing is checked and nothing is resolved: unlike checkout,
a quote never creates the buying organization's default project,
because a quote is documented to write nothing at all.
region_idRequired for every family except a licence. See
CheckoutRequest::region_id's own doc comment.
The size to price. Omitting it quotes the plan as authored.
Quote a checkout › Responses
What the purchase would cost, broken down
amount_centsAfter the discount, in the invoice's own terms: always the net,
pre-tax figure, never the plan's advertised (possibly tax-inclusive)
price. This is the establishing invoice's amount, and the figure the
two can be reconciled on, for both tax behaviours. See this struct's
own doc comment for why it is not a term of the customer-facing
additive chain on an Inclusive plan.
available_centsbalance - required_cents. Compared against required_cents (not
against this purchase's own total) by the caller: kernel does not
know here whether this purchase itself will reserve anything, only
what the organization's standing already looks like.
cadence_labelHow to read the figures aloud, e.g. "per month, billed monthly". Kernel's own words: it is a claim about a billing term, and a client assembling one from a month count would be deciding what that count means.
cadence_monthsThe billing term quoted, in months (1, 3, 6 or 12): the one asked for, or 1 when none was. Every figure below is one whole term (V-0717).
currencyThe currency every figure below is in.
due_now_centsWhat the customer pays now, which is not always total_cents: a
setup fee, a term paid up front or a prorated first period all move it.
Rendered, never derived. Equal to total_cents for every purchase this
endpoint can price today, and a field anyway, because the day it stops
being equal must not be the day a console starts lying.
What is being bought, described the way the invoice will describe it.
Exactly one line today, because a /v2 checkout issues exactly one
invoice line (InvoiceLineItemKind::BaseSubscription). It is a list
rather than a single field so that a setup fee, a term bundle or a
second priced component does not have to reshape the contract, and so
a client renders whatever arrives instead of a fixed row.
required_centsZC-1324 org trust S5 (§8.4): max(organization_floor, already-reserved) -- the buying organization's own standing, not
this purchase's price. Present on every quote, not only a Measurable
one: an unmeasurable product still explains the organization's floor
this way (§8.2's closing paragraph).
The shape that was priced -- the customer's when they sent one, the plan's when they did not. Echoed so a console showing "2 vCPU, 4 GiB, no disk -- EUR 12.40" is reading both halves from the same answer rather than pairing a server price with its own local idea of the size.
subtotal_centsThe sum of lines, before any discount code.
Tax, as data rather than as something to work out.
The client must never derive any of this from a billing country: which
country applies, whether a business is reverse-charged, and what the rate
is on the day are all kernel's decisions, taken by
TaxResolutionService::resolve.
total_centsWhat the customer pays for one billing cycle.
The code that was applied, what it does, and what it took off.
None when no code was sent; a code that was sent and cannot be used
is a refusal, never a silently absent discount.
What the requested public ports can cost, present only when the request
named some. Ports are billed by the hour held, monthly in arrears, so
this is a worst case (every priced port held a whole month), shown
beside the ticket and never added to total_cents or due_now_cents.
V-0449.04: postpaid disclosure, present only for the OWNER of an
organization with a credit limit. See crate::contracts::standing::PostpaidDisclosureResponse.
The organization's own current restriction, if any -- None when its
standing is Good. Kernel supplies the reason and the numbers; the
frontend supplies the words (§8.4).
Pay Invoice With Wallet
Pays one pending invoice from the wallet of the organization it is billed to, and returns the wallet entry the money moved through. Requires an Owner, Admin or Billing role in that organization (or being its personal owner); an API token needs the organizations.wallet.spend grant. A plain Member may read the invoice and may not pay it.
Send an Idempotency-Key header. Paying is the one write in this API where a retry after a timeout can cost the customer real money twice, and the header is what lets a client repeat the identical request safely. It is optional only because a request without one is still better than a customer who cannot pay at all.
A wallet that cannot cover the invoice is a 422 naming the balance and the amount needed, not a 402: nothing was attempted and nothing is owed to a third party. An invoice that is already paid, void or draft is a 422 as well, because the invoice is real and the request is simply not applicable to it. An invoice belonging to somebody else answers exactly as one that does not exist. A card/PayPal settlement or a staff reconciliation already transitioning this invoice answers 409; nothing was charged, and retrying finds either success or the invoice already paid. An active provider checkout also answers 409 because it must be finished or resolved before the wallet can safely own payment.
path Parameters
idPay Invoice With Wallet › Responses
The wallet entry the payment moved through
entry_idStart Mollie Checkout For Invoice
Starts a Mollie payment for one pending invoice and returns the Mollie checkout page to send the customer's browser to. Mollie returns the customer to the invoice on the dashboard when they finish.
Requires an Owner, Admin or Billing role in the organization the invoice is billed to (or being its personal owner); an API token needs the organizations.purchase grant. Paying an invoice by card moves no wallet money, so the wallet grants do not apply.
Starting a checkout pays nothing: the invoice is paid when the provider settles, and is then booked as a card receipt. Calling this again for the same invoice returns the checkout already started. An invoice belonging to an organization the caller cannot see answers exactly as one that does not exist. An invoice that is already paid, failed, refunded or cancelled is a 422, as is one with nothing to pay. A checkout or settlement already in progress is a 409; one active provider must be finished or resolved before choosing another. A 503 means the provider is not available right now.
path Parameters
idStart Mollie Checkout For Invoice › Responses
The Mollie checkout page
checkout_urlThe provider-hosted page the customer completes the payment on. Open it in the browser; the invoice is paid when the provider settles, not when this URL is issued.
Start PayPal Checkout For Invoice
Starts a PayPal order for one pending invoice and returns the PayPal approval page to send the customer's browser to. Once the customer approves, PayPal sends the browser back to GET /api/v2/public/billing/invoices/{id}/checkout/paypal/capture, which captures the payment and redirects to the invoice on the dashboard.
Requires an Owner, Admin or Billing role in the organization the invoice is billed to (or being its personal owner); an API token needs the organizations.purchase grant. Paying an invoice by card moves no wallet money, so the wallet grants do not apply.
Starting a checkout pays nothing: the invoice is paid when the provider settles, and is then booked as a card receipt. Calling this again for the same invoice returns the checkout already started. An invoice belonging to an organization the caller cannot see answers exactly as one that does not exist. An invoice that is already paid, failed, refunded or cancelled is a 422, as is one with nothing to pay. A checkout or settlement already in progress is a 409; one active provider must be finished or resolved before choosing another. A 503 means the provider is not available right now.
path Parameters
idStart PayPal Checkout For Invoice › Responses
The PayPal approval page
checkout_urlThe provider-hosted page the customer completes the payment on. Open it in the browser; the invoice is paid when the provider settles, not when this URL is issued.
Get Organization Wallet
Gets one organization's wallet: its credit balance, one row per currency, in integer cents. The organization owns the wallet (kernel-v2 §19 Q89), so every member of a shared organization reads the same balance -- this is not the caller's personal wallet unless the caller named their personal organization.
Only non-zero balances are returned: the balance is summed from the ledger, so an organization that has never been credited, and one whose movements net to exactly zero, both answer with an empty balances list. Treat a currency that is absent as zero.
The balance is a separate read rather than a field on the organization on purpose. A client's scope layout reads the organization on every request; folding the ledger there would put a sum over every wallet entry in front of every navigation. Ask for the balance on the page that shows money.
No threshold is applied and no "low balance" verdict is served. If one is ever wanted it is kernel's to define, not something a client may work out from this number.
Answers 404 for an organization the caller does not belong to, the same way reading that organization -- or /organizations/{id}/invoices -- would; an organization that does not exist is not distinguishable from one that is not yours.
path Parameters
idGet Organization Wallet › Responses
The organization's wallet balances
One row per currency the organization holds, ordered by currency code.
A currency with no money in it has no row. The balance is summed from the ledger and only non-zero sums come back, so an organization that has never been credited answers with an empty list, and so does one whose movements happen to net to exactly zero. Treat a missing currency as zero; do not treat an empty list as "no wallet".
organization_idThe organization whose wallet this is -- echoed back so a client that fans several of these out cannot mis-file an answer.
List Organization Wallet Entries
Lists one organization's wallet ledger, newest first, paginated the same way the invoice listing is: a { data, pagination } envelope whose total_items counts every entry the filter matches, not the ones on this page. (/v1's /me/wallet/entries answers with a bare array and a timestamp cursor, with no total and no next cursor, so nothing there can build a pager.)
Each row is one signed movement in integer cents: positive credits the organization, negative debits it. Rows carry no running balance, and a client must not add them up to produce one -- a total over a page agrees with the wallet only when the page is the entire ledger, so it would read as correct and be wrong. Read /organizations/{id}/wallet for the balance.
Narrow to one currency with currency (e.g. EUR). A wallet holding two currencies has two independent balances, so an unfiltered page is a history rather than an account. A currency that is not a three-letter uppercase ISO 4217 code is refused with invalid-field-value rather than ignored.
The page is cut on a total order -- time descending, with the entry id breaking ties -- so walking consecutive pages sees each entry exactly once.
Answers 404 for an organization the caller does not belong to, identically to an organization that does not exist.
path Parameters
idquery Parameters
currencyNarrows the page to one currency, e.g. EUR.
Optional, and a pure narrowing: omitted, the page carries every currency the organization has ever moved, newest first. A wallet with two currencies has two independent balances, so a page that mixes them is a history and not an account -- a client showing one balance should ask for that currency by name.
A value that is not three uppercase letters is refused with
invalid-field-value rather than ignored, for the reason
listOrganizationInvoices refuses an unknown status: serving the
unfiltered listing would answer a question the caller did not ask.
Narrows the page to one currency, e.g. EUR.
Optional, and a pure narrowing: omitted, the page carries every currency the organization has ever moved, newest first. A wallet with two currencies has two independent balances, so a page that mixes them is a history and not an account -- a client showing one balance should ask for that currency by name.
A value that is not three uppercase letters is refused with
invalid-field-value rather than ignored, for the reason
listOrganizationInvoices refuses an unknown status: serving the
unfiltered listing would answer a question the caller did not ask.
pagePage number (1-indexed). Defaults to 1.
Page number (1-indexed). Defaults to 1.
page_sizeNumber of items per page. Defaults to 20, max 100.
Number of items per page. Defaults to 20, max 100.
List Organization Wallet Entries › Responses
One page of the organization's wallet ledger
Pagination metadata included in paginated responses
List Subscription Invoices
Lists one subscription's invoices, paginated: every invoice with at least one line item billed against this subscription, newest first. Built for a per-service billing tab that needs one subscription's history rather than an organization's whole one (/organizations/{id}/invoices for that). Each row carries due_at, derived the same way listMyInvoices describes. Line items are not included here -- fetch one invoice by id for its lines.
Answers 404 for a subscription in an organization the caller cannot see, identically to one that does not exist -- the same rule reading that subscription directly already follows.
path Parameters
idquery Parameters
pagePage number (1-indexed). Defaults to 1.
Page number (1-indexed). Defaults to 1.
page_sizeNumber of items per page. Defaults to 20, max 100.
Number of items per page. Defaults to 20, max 100.
List Subscription Invoices › Responses
Invoices
Pagination metadata included in paginated responses
Get My Wallet
Gets the caller's own wallet: their personal organization's credit balance, one row per currency, in integer cents.
Only non-zero balances are returned: the balance is summed from the ledger, so a wallet that has never been credited, and one whose movements net to exactly zero, both answer with an empty balances list. Treat a currency that is absent as zero -- an empty list is a normal 200, not a sign the wallet does not exist.
The organization behind this balance is never named: it is always the caller's own personal organization, resolved from their token. Reading a shared organization's wallet needs GET /api/v2/organizations/{id}/wallet instead.
Get My Wallet › Responses
The caller's own wallet balances
One row per currency the caller's personal organization holds.
A currency with no money in it has no row. An organization that has never been credited, or one whose movements net to exactly zero, answers with an empty list -- this is a normal, fully-formed 200, not a sign that the wallet is missing. Treat an absent currency as zero.
List My Wallet Entries
Lists the caller's own wallet ledger (their personal organization's), newest first, paginated as a { data, pagination } envelope whose total_items counts every entry the filter matches, not only the ones on this page.
Each row is one signed movement in integer cents: positive credits the wallet, negative debits it. Rows carry no running balance; read GET /api/v2/me/wallet for that.
Narrow to one currency with currency (e.g. EUR). A value that is not a three-letter uppercase ISO 4217 code is refused with invalid-field-value rather than ignored.
The organization behind this ledger is never named: it is always the caller's own personal organization. Reading a shared organization's ledger needs GET /api/v2/organizations/{id}/wallet/entries instead.
query Parameters
currencyNarrows the page to one currency, e.g. EUR.
Optional, and a pure narrowing: omitted, the page carries every currency the organization has ever moved, newest first. A wallet with two currencies has two independent balances, so a page that mixes them is a history and not an account -- a client showing one balance should ask for that currency by name.
A value that is not three uppercase letters is refused with
invalid-field-value rather than ignored, for the reason
listOrganizationInvoices refuses an unknown status: serving the
unfiltered listing would answer a question the caller did not ask.
Narrows the page to one currency, e.g. EUR.
Optional, and a pure narrowing: omitted, the page carries every currency the organization has ever moved, newest first. A wallet with two currencies has two independent balances, so a page that mixes them is a history and not an account -- a client showing one balance should ask for that currency by name.
A value that is not three uppercase letters is refused with
invalid-field-value rather than ignored, for the reason
listOrganizationInvoices refuses an unknown status: serving the
unfiltered listing would answer a question the caller did not ask.
pagePage number (1-indexed). Defaults to 1.
Page number (1-indexed). Defaults to 1.
page_sizeNumber of items per page. Defaults to 20, max 100.
Number of items per page. Defaults to 20, max 100.
List My Wallet Entries › Responses
One page of the caller's own wallet ledger
Pagination metadata included in paginated responses
Start My Wallet Top-Up
Starts a top-up of the caller's own wallet: creates a hosted checkout at the chosen provider and returns it. Send the customer to checkout_url.
Nothing is credited by this call. The wallet is credited once, when the provider confirms the payment to the kernel; poll GET /api/v2/me/wallet/top-ups/{id} until status is credited (or a terminal failure). Retrying this request after a timeout starts a second, separate checkout -- neither moves money until it is paid.
amount_cents is one of the fixed EUR presets (2500, 5000, 10000, 25000, 100000); anything else is invalid-field-value. A provider that is not configured, or that does not answer, is a 503 and nothing was charged. The wallet is always the caller's personal organization's and cannot be named. Requires the top-up authority on that wallet; a restricted API token needs organizations.wallet.top_up.
Start My Wallet Top-Up › Request Body
amount_centsInteger euro cents, and one of the fixed presets: 2500, 5000, 10000,
25000 or 100000. Any other amount is refused with
invalid-field-value; top-ups are always EUR.
providerA provider a top-up can be started with. Mollie is deliberately absent: a Mollie top-up has no checkout and no settlement path in the kernel.
Start My Wallet Top-Up › Responses
The new top-up, waiting for payment at checkout_url
amount_centsInteger cents of currency.
created_atcurrencyISO 4217. Always EUR today.
expires_atAfter this the checkout lapses and the top-up becomes expired.
idproviderpaypal or nowpayments. Older rows may say mollie.
Where a top-up is. Only credited means money reached the wallet.
checkout_urlThe provider's hosted page to send the customer to. Present once the
checkout exists; absent while creating and on a failed start.
Get Wallet Top-Up Availability
Says which providers can take a wallet top-up right now. A provider answering false would refuse POST /api/v2/me/wallet/top-ups with a 503; offer only the ones that answer true, and show no top-up option at all when both are false.
Get Wallet Top-Up Availability › Responses
Which providers are configured and enabled
nowpaymentspaypalGet My Wallet Top-Up
Gets one of the caller's own top-ups, to poll while the customer pays. credited is the only status that means money reached the wallet; expired, failed and review_required are terminal too.
A top-up belonging to any other account answers 404, exactly as an id that does not exist does.
path Parameters
idGet My Wallet Top-Up › Responses
The top-up
amount_centsInteger cents of currency.
created_atcurrencyISO 4217. Always EUR today.
expires_atAfter this the checkout lapses and the top-up becomes expired.
idproviderpaypal or nowpayments. Older rows may say mollie.
Where a top-up is. Only credited means money reached the wallet.
checkout_urlThe provider's hosted page to send the customer to. Present once the
checkout exists; absent while creating and on a failed start.
Redeem Wallet Code
Redeems a wallet code into the caller's own wallet and returns the credit it produced.
Send an Idempotency-Key header. Repeating the identical request with the same key answers with the first credit instead of a refusal, so a client that timed out can find out what happened without guessing.
A code credits exactly one wallet, once. Every way a code can fail to redeem -- it does not exist, was already redeemed by anyone, was revoked, has expired, or its group's per-caller limit is reached -- answers with the same invalid-field-value refusal. Distinguishing them would let any signed-in customer walk the wallet-code namespace and learn which codes exist and which giveaway campaigns are still live, the same oracle ZC-0183 closed on the discount-code quote. Requires the top-up authority on the caller's wallet; a restricted API token needs organizations.wallet.top_up.
Redeem Wallet Code › Request Body
codeThe code as the customer typed it. Case and separators are normalised server-side.
Redeem Wallet Code › Responses
The wallet credit the code produced
amount_centscurrencyentry_idThe wallet entry the value landed in.
Get My Wallet Preferences
Gets the caller's own wallet preferences: whether invoices are collected from the wallet automatically, and the wallet's currency. A wallet that has never been configured answers with the defaults (autopay on, EUR).
Get My Wallet Preferences › Responses
The caller's wallet preferences
autopay_enabledWhether invoices are collected from the wallet automatically when they fall due.
preferred_currencyISO 4217. Read-only: wallets are EUR-only, so there is nothing to choose.
Update My Wallet Preferences
Turns automatic invoice collection from the caller's own wallet on or off. autopay_enabled is required and is the only writable field; any other field is refused rather than ignored.
Switching autopay on authorizes the platform to spend the wallet on every renewal, so it needs the spending authority (Owner or Admin), not the top-up authority; a restricted API token needs organizations.wallet.spend.
Update My Wallet Preferences › Request Body
autopay_enabledUpdate My Wallet Preferences › Responses
The saved preferences
autopay_enabledWhether invoices are collected from the wallet automatically when they fall due.
preferred_currencyISO 4217. Read-only: wallets are EUR-only, so there is nothing to choose.
List My Free Trials
Every free trial the caller's storefront offers, each with whether they may claim it right now.
Refused trials are listed, not hidden. A trial the caller has already used comes back with claimable: false and refusal.code = already_claimed, so the product's trial does not silently vanish from the page once it has been taken. The three refusal codes are already_claimed (permanent), capacity_reached (try later) and not_available (the trial is disabled, expired, or not fully configured). Trials belonging to another storefront are absent entirely -- they were never this caller's.
region_is_fixed says the trial pins its own hardware: do not show a region picker for that row, because the claim will ignore whatever it is sent. Writes nothing, and needs no captcha.
List My Free Trials › Responses
The trials this caller's storefront offers, with a verdict on each
Claim A Free Trial
Claims one free trial from GET /api/v2/me/trials. Creates a Subscription in the caller's own personal organization -- the request cannot name an organization -- together with a zero-amount Initial invoice that is settled immediately, which activates the subscription and provisions the service. Nothing is charged and no payment method is required.
auto_renew starts false. A customer who does nothing is never billed: at trial_ends_at the subscription is suspended instead. Turning renewal on through PUT /api/v2/subscriptions/{id}/renewal before that instant is what converts the trial, and billing then starts at trial_ends_at rather than at the claim.
plan, size and price all come from the trial declaration; only name, slug, project_id and region_id are the customer's. project_id may not name a personal organization's games project (403 project-gaming-managed); the gaming dashboard claims through POST /api/v2/me/trials/gaming. region_id is ignored when the trial reports region_is_fixed, and omitting it otherwise places the service server-side.
One trial per product, per account, permanently -- cancelling one does not free the slot. A second attempt answers business-rule-violation with already_claimed. A trial that is not on the caller's storefront answers 404, exactly as an unknown id does. Requires a captcha token when the host has captcha configured for free_trial_claim.
Claim A Free Trial › Request Body
nameThe customer's name for the service they will receive.
slugThe URL segment addressing it, unique within its project. Same rules as checkout's: 3-63 characters of lowercase letters, numbers and single hyphens.
trial_price_idThe id from GET /api/v2/me/trials.
project_idWhich project to file it under. Omit for the personal organization's
default project, created on first use. Must be omitted on
POST /api/v2/me/trials/gaming, which always files into the games
project.
region_idWhere to run it.
Ignored when the trial reports region_is_fixed, and omitting it
otherwise means "anywhere that fits, cheapest first" -- resolved
server-side, never by the client.
Claim A Free Trial › Responses
The running trial subscription and its settled zero invoice
The zero-amount establishing invoice, in the same shape
GET /billing/invoices/{id} returns. Already paid; it is returned so
the customer's invoice list and this response cannot describe the same
row differently.
Claim A Free Trial For Gaming
The gaming dashboard's trial claim: the same claim as Claim A Free Trial, filed into the caller's personal organization's games project, which it creates on first use and reuses afterwards, beside purchases made through POST /api/v2/checkout/gaming. Claims only trials on products the gaming store sells (any other answers 404, exactly as an unknown id does). Takes the same body; project_id must be omitted, or the claim is refused with 422 gaming-checkout-target-fixed. Requires a captcha token when the host has captcha configured for free_trial_claim.
Claim A Free Trial For Gaming › Request Body
nameThe customer's name for the service they will receive.
slugThe URL segment addressing it, unique within its project. Same rules as checkout's: 3-63 characters of lowercase letters, numbers and single hyphens.
trial_price_idThe id from GET /api/v2/me/trials.
project_idWhich project to file it under. Omit for the personal organization's
default project, created on first use. Must be omitted on
POST /api/v2/me/trials/gaming, which always files into the games
project.
region_idWhere to run it.
Ignored when the trial reports region_is_fixed, and omitting it
otherwise means "anywhere that fits, cheapest first" -- resolved
server-side, never by the client.
Claim A Free Trial For Gaming › Responses
The running trial subscription and its settled zero invoice
The zero-amount establishing invoice, in the same shape
GET /billing/invoices/{id} returns. Already paid; it is returned so
the customer's invoice list and this response cannot describe the same
row differently.
Get My Standing
Gets the caller's own standing (ZC-1324 org trust): whether their personal organization is currently restricted, and why, with the full numeric breakdown -- balance, open reservations, floor, required and available cents, credit limit, open receivables. Always the full detail: the caller reading this route is always their own personal organization's owner. The organization behind it is never named, matching every other /api/v2/me route.
Get My Standing › Responses
The caller's own personal organization's current standing
available_centsbalance_centscredit_limit_centscurrencyfloor_centsopen_receivable_centsrequired_centsreserved_centsrestrictedPreview Wallet Code
Returns the face value, currency and expiry of a redeemable wallet code. Requires no authentication: a claim link is opened by somebody who may not have an account yet, and the page has to be able to say what the code is worth before asking them to make one. The code is normalized (trimmed, upper-cased) exactly as redemption normalizes it. A code that is expired, already redeemed, revoked, or simply does not exist is reported as not found, with no way to tell those cases apart. Rate limited more tightly than ordinary reads, because an endpoint that turns a code into a value is an enumeration oracle.
path Parameters
codeThe code as printed, in any case and with or without surrounding whitespace. Normalized server side before lookup, the same way redemption normalizes it, so a preview can never say "valid" about a spelling redemption would then reject.
The code as printed, in any case and with or without surrounding whitespace. Normalized server side before lookup, the same way redemption normalizes it, so a preview can never say "valid" about a spelling redemption would then reject.
Preview Wallet Code › Responses
The code's value and expiry
amount_centsFace value in minor units of currency
currencyexpires_atWhen this code stops being redeemable, or null if it does not expire. A code already past this instant is reported as not found rather than returned with a past date.
Capture PayPal Invoice Checkout
PayPal's return URL for an invoice checkout started with POST /api/v2/billing/invoices/{id}/checkout/paypal. PayPal redirects the customer's browser here with the order id as token once they approve; this captures the payment and redirects the browser to the invoice on the dashboard with payment=success, payment=processing (not settled yet; the invoice updates when it does) or payment=failed.
Unauthenticated by necessity: a provider redirect carries no credentials. It captures only the PayPal order stored on that invoice when the checkout was started, and pays the invoice only when PayPal reports the capture completed. An invoice that does not exist and an order that is not the invoice's own are the same 404. Safe to repeat: a refreshed or replayed return pays the invoice once and redirects to success.
path Parameters
idquery Parameters
tokenThe PayPal order id, as PayPal names it on the return URL.
The PayPal order id, as PayPal names it on the return URL.
Capture PayPal Invoice Checkout › Responses
Redirects the browser to the invoice on the dashboard, with the outcome in payment
