Subscriptions and bills
- 7 endpoints
Subscriptions are created by completing a checkout (no create route here): a paid subscription is recorded once the customer's on-chain transaction is confirmed (404 until then), a free trial once the customer signed the trial's message at the checkout (status: "trial"). A subscription's uuid is sub@-prefixed and is the same as its checkout session's uuid, for life. The timeline, the webhooks and when to grant access: Subscriptions.
List subscriptions#
GET /subscriptions
Lists the subscriptions of the request's space (with includeMembers, of the organization and its members; with userUuid, of one member), newest first, cancelled ones included unless filtered out. reference: the one with that merchant reference (0 or 1 item).
- Auth
- X-API-Key: any key
- On-Behalf-Of
- Honored: acts in the member's space
- Idempotency-Key
- Ignored
- Success
- 200
request.sh
curl https://api.qbitflow.app/v2/subscriptions \ -H "X-API-Key: $QBITFLOW_API_KEY"# 200: a page of Subscription ({ items, hasMore, nextCursor? })Query parameters
limitintegerOptionalPage size: 20 when absent or ≤ 0, at most 100 (a larger one is clamped, not refused)
cursorstringOptionalThe previous page's nextCursor, verbatim; omit for the first page
max=200customerUuidstringOptionalOnly this customer's
productUuidstringOptionalOnly this product's
createdAfterstringOptionalOnly those created after this instant, excluded (RFC 3339, e.g. 2026-10-04T12:00:00Z, or a date: its midnight UTC)
createdBeforestringOptionalOnly those created before this instant, excluded (RFC 3339 or a date, as createdAfter)
includeMembersbooleanOptionalThe organization's space only (the team, its keys without On-Behalf-Of): its members' too, each naming its member (userUuid). From a member's space → 400 on includeMembers
userUuidstringOptionalThe organization's space only (owners and admins): this member's only, read from the organization's space (a removed member's too). Not with includeMembers (400 on userUuid)
statusstringOptionalOnly those in this status: trial, trialExpired, active, pastDue, paused, stopped or cancelled (cancelled ones are listed too unless filtered out)
max=32One of:
trialStatusTrial is a free trial: nothing was signed on-chain yet.trialExpiredStatusTrialExpired is a trial that ended without its customer confirming it (actionRequired confirmTrial): cancelled after the grace period unless they subscribe.activeStatusActive is a subscription billed on its due dates. Its customer may have something to do (actionRequired: topUpAllowance, raiseMaximum) while it stays active.pastDueStatusPastDue indicates a subscription whose last bill failed: its retries run.pausedStatusPaused is a subscription its customer paused (a message signed with its wallet): QBitFlow doesn't bill it until they resume it. On-chain it is still active. It keeps the period it paid for (currentPeriodEnd).stoppedStatusStopped is a subscription cancelled during its billing period (by its customer, or its merchant's force cancel with immediate=false): never billed again, it still runs until its paid period ends (nextBillingDate), then it is cancelled.cancelledStatusCancelled indicates a cancelled subscription.
referencestringOptionalOnly the one with this reference
referencemax=100
Response
A page: { "items": Subscription[], "hasMore": …, "nextCursor"?: … }. Pass nextCursor back as cursor for the next page; it is absent when hasMore is false.
30 fields of each Subscription
uuidstringIts id (sub@…): the checkout session's
A prefixed id (pay@…, sub@…)
referencestringOptionalThe merchant's reference for the subscription (an order or customer id), set when creating its checkout. Only for the merchant: never in the public reads by uuid (GET /hosted/transactions/:txUuid)
Only for the resource's owner
createdAtstringupdatedAtstringIts last change (a bill, a status, its metadata)
fromstringThe subscriber's wallet: the one it bills. Absent when unknown (a trial not confirmed yet may have none)
tostringThe wallet its bills pay: the merchant's (or, while the organization holds a member's funds, the organization's)
productUuidstringIts product (GET /products/:uuid). Absent without one
subscriptionHashstringThe hash of the transaction that created the subscription on-chain. Absent until then (a trial, until its customer confirms it)
currencyIdintegerThe currency (token) billed, written as the currency object under currency (GET /currencies/:id)
frequencyDurationBilling interval in seconds, as on-chain (e.g. 2592000 = 30 days). Written as a types.Duration in responses and webhooks (e.g. {"value": 1, "unit": "months"})
2 fields of
DurationvalueintegerNumber of units; 0 means no duration
unitstringRequired when value > 0. "months" = 30 days, "years" = 365 days
oneof=seconds minutes hours days weeks months years
statusstringSubscriptionStatus (status) is where it stands: trial, trialExpired, active, pastDue, paused, stopped or cancelled. Every change sends subscription.statusChanged. Don't grant access from it: grant access while now < accessUntil (docs/api/guides/subscriptions.md § Access)
One of:
trialStatusTrial is a free trial: nothing was signed on-chain yet.trialExpiredStatusTrialExpired is a trial that ended without its customer confirming it (actionRequired confirmTrial): cancelled after the grace period unless they subscribe.activeStatusActive is a subscription billed on its due dates. Its customer may have something to do (actionRequired: topUpAllowance, raiseMaximum) while it stays active.pastDueStatusPastDue indicates a subscription whose last bill failed: its retries run.pausedStatusPaused is a subscription its customer paused (a message signed with its wallet): QBitFlow doesn't bill it until they resume it. On-chain it is still active. It keeps the period it paid for (currentPeriodEnd).stoppedStatusStopped is a subscription cancelled during its billing period (by its customer, or its merchant's force cancel with immediate=false): never billed again, it still runs until its paid period ends (nextBillingDate), then it is cancelled.cancelledStatusCancelled indicates a cancelled subscription.
actionRequiredstringOptionalActionRequired is what its customer must do, if anything (absent: nothing): topUpAllowance (the allowance covers about one more bill at most), raiseMaximum (a bill waits for a new maximum per period), confirmTrial (the trial ended unconfirmed). Every change (set, changed or cleared) sends subscription.actionRequiredChanged
One of
topUpAllowanceActionTopUpAllowance: what remains of the allowance covers about one more bill at most (or none). The customer adds periods from their management page.raiseMaximumActionRaiseMaximum: a bill is above the maximum per period the customer signed (the token's price moved); it waits up to 7 days for a new maximum, signed from the management page.confirmTrialActionConfirmTrial: the trial ended unconfirmed (trialExpired); the customer subscribes from their management page before the grace period ends.
currentPeriodEndstringOptionalCurrentPeriodEnd is the paid-through date. A trial: its end. active, paused, stopped: the end of the period paid for (the next bill's due date). pastDue: the start of the unpaid period (past). cancelled: when it was cancelled, or the end of its last paid period if that came first. For access, read accessUntil: an active subscription is renewed a few minutes after this date
accessUntilstringOptionalAccessUntil is when access ends: grant it while now < accessUntil, whatever the status. An active subscription: currentPeriodEnd plus 1 hour, since its renewal is billed within minutes of its due date (a subscriber whose bill is on its way keeps access; a failed bill sets pastDue). Every other status: currentPeriodEnd
priceUsdstringPriceUSD is the price per period the customer subscribed at, in USD (a decimal string, e.g. "9.99"), locked at signup (a free trial's too): every bill charges it and its lines (fees), converted to the token at the bill's price. A later change of the product's price applies to new subscribers only. Never "0"
feesarray of FeeLineFees is what its checkout added to the price, line by line, as the customer saw them (a tax, shipping, the processing fee when the customer pays it), locked at signup with the price: every bill charges priceUsd plus these lines. [] without any
4 fields of each
FeeLinetypestringcustom (the merchant's line) or processingFee (QBitFlow's fee, which the merchant has the customer pay)
One of:
customThe merchant's own line: a tax, shipping, a service feeprocessingFeeQBitFlow's fee, which the merchant has the customer pay (computed by QBitFlow)
labelstringThe line's name, as the checkout shows it ("Processing fee" for the processing fee)
descriptionstringOptionalOptional: more about the line, as the merchant wrote it. Omitted without one
amountUsdstringThe line's amount in USD, a decimal string with at most 2 decimals
allowanceAllowanceOptionalAllowanceStatus (allowance) is what the subscriber's wallet still lets QBitFlow bill, and the most one bill may charge, at today's price. Absent during a free trial (nothing signed yet)
6 fields of
AllowanceamountstringIn the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")
amountMinUnitsstringThe on-chain integer, in the token's smallest unit, a string of digits ("12500000")
amountUsdstringOptionalIts value in USD, a decimal string; absent when no price was known
remainingPeriodsintegerOptionalRemainingPeriods is how many whole bills the allowance still covers at today's price: the allowance divided by a bill (priceUsd and its fees, converted to the token), rounded down, before the network fee each bill adds. 0 is a value: the customer must top it up (actionRequired topUpAllowance) before the next bill. Absent without a price
maxPerPeriodstringOptionalMaxPerPeriod is the most one bill may charge, as the subscriber signed it, in the token's units (maxPerPeriodMinUnits: its on-chain integer): a bill above it waits for a new maximum (actionRequired raiseMaximum). Absent when the subscription has none recorded
maxPerPeriodMinUnitsstringOptional
lastBilledAtstringOptionalWhen it was last billed. Absent before its first bill (a trial)
nextBillingAtstringOptionalWhen its next bill is due (a trial: when it ends; stopped: when it is cancelled). Absent once cancelled
cancelledAtstringOptionalWhen it was cancelled (absent until then, and for subscriptions cancelled before it was recorded)
cancellationReasonstringOptionalWhy it stopped or was cancelled: customer, merchant, billingFailed, trialNotConverted, merchantClosed or inactiveOnChain. Absent while it runs (and for subscriptions cancelled before it was recorded)
One of:
customerCancelledByCustomer: its customer cancelled it (it was stopped until its period ended).merchantCancelledByMerchant: its merchant cancelled it (POST /subscriptions/:uuid/cancel).billingFailedCancelledBillingFailed: its bill's last attempt failed.trialNotConvertedCancelledTrialNotConverted: its trial ended and its customer didn't subscribe within the grace period.merchantClosedCancelledMerchantClosed: its merchant's space closed (a member removed, the organization closed).inactiveOnChainCancelledInactiveOnChain: the contract no longer had it active (cancelled outside QBitFlow).
cancellableAtstringOptionalMinimumCancellationDate (cancellableAt) is the earliest its customer may cancel it (the checkout's minPeriods). Absent when they may cancel it any time
metadatamapOptionalMetadata is the merchant's own key-value data: its checkout session's, copied when the subscription was created, then changed with PATCH /subscriptions/:uuid (merged). {} when none. Only for the merchant (API and webhooks)
Only for the resource's owner
managementPageLinkstringOptionalManagementPageLink is the subscription's page for its customer: where they top up its allowance, raise its maximum, pause, resume or cancel it, and confirm a trial. Share it with them (an email, the merchant's account page)
testbooleanWhether this is test data (testnets) or live data
userUuidstringOptionalMember the resource belongs to; omitted for the organization's own resources
Only for the resource's owner
customerUuidstringOptionalThe customer, when QBitFlow has one (named by the checkout, found by its customerReference, or recorded from the details the customer gave); omitted otherwise
Only for the resource's owner
customerReferencestringOptionalThe merchant's reference of the customer, as the checkout was given it (customerReference), whether or not QBitFlow has a customer with it; omitted otherwise
Only for the resource's owner
customerSummaryOptionalThe customer, named (merchant only, API reads): who paid, even once deleted. Omitted without one
Only for the resource's owner
6 fields of
SummaryuuidstringThe customer (GET /customers/:uuid)
firstNamestringFirst name
lastNamestringOptionalLast name, when given
emailstringEmail, lowercase
referencestringOptionalThe merchant's reference of the customer, when it has one
deletedbooleanThe merchant deleted the customer: it still names its rows, but GET /customers/:uuid answers 404
dunningDunningStatusOptionalDunningStatus (dunning) is where its failing bill's retries stand: the failed attempts, the next one, a wait for a new maximum. Omitted while no bill is failing. API reads only: webhooks tell each failure with subscription.billingFailed
5 fields of
DunningStatusfailedAttemptsintegerThe bill's failed attempts so far (0 while it only waits for a new maximum)
remainingAttemptsintegerAttempts left: after the last one fails, the subscription is cancelled
failingSincestringOptionalThe bill's first failed attempt: when the subscription went past due. Omitted without one
nextAttemptAtstringOptionalWhen the bill is tried again at the latest (sooner once the customer tops up or raises their allowance). Omitted when none is scheduled
awaitingMaximumSincestringOptionalWhile the bill is above the customer's maximum (actionRequired raiseMaximum): when it started waiting for them to raise it. nextAttemptAt is then when the wait ends. Omitted otherwise
Errors
The common codes only, as any route (what each means):
- 400validation_failed
- 401unauthorized
- 403forbidden
- 403policy_disabled
- 404not_found
- 429rate_limit_exceeded
- 500internal
- 504timeout
Get subscription#
GET /subscriptions/
Reads one of the caller's subscriptions (sub@… or its bare uuid), whatever its status (a cancelled one too), with its customer's management page and its allowance at today's price. Another space's, an unknown one or another kind of id → 404.
- Auth
- X-API-Key: any key
- On-Behalf-Of
- Honored: acts in the member's space
- Idempotency-Key
- Ignored
- Success
- 200
request.sh
curl "https://api.qbitflow.app/v2/subscriptions/$SUBSCRIPTION_UUID" \ -H "X-API-Key: $QBITFLOW_API_KEY"# 200: a Subscription// Any status, cancelled included. Before its checkout completes, it is a 404.const subscription = await client. subscriptions. get( subscriptionUuid);console. log( `${subscription. uuid}: ${subscription.status}`);console.log(`paid until ${subscription.currentPeriodEnd ?? '—'}`);subscription = client. subscriptions. get( subscription_uuid) # sub@…, cancelled ones tooprint(subscription.status, "paid until", subscription.current_period_end)subscriptions/main.go
sub, err := client.Subscriptions.Get(ctx, subscriptionUUID, nil) // sub@…, cancelled ones tooif err != nil { log.Fatal(err)}fmt.Println(sub.UUID, sub.Status) // trial, active, pastDue, stopped, cancelled…if sub.CurrentPeriodEnd != nil { fmt.Println("paid until", sub. CurrentPeriodEnd. Format( time. RFC3339))}$subscription = $client->subscriptions->get( $subscriptionUuid); // sub@…printf("%s: %s, paid until %s\n", $subscription->uuid, $subscription->status, $subscription->currentPeriodEnd?->format( 'Y-m-d') ?? '-');has-access.sh
# Grant access while now < accessUntil, whatever the status: a stopped or paused# subscription has paid for its period, an active one keeps an hour while its renewal confirms.curl "https://api.qbitflow.app/v2/subscriptions/$SUBSCRIPTION_UUID" \ -H "X-API-Key: $QBITFLOW_API_KEY"# 200: { "uuid": "sub@…", "status": "active", "currentPeriodEnd": "2026-10-31T12:00:00Z", "accessUntil": "2026-10-31T13:00:00Z", … }// Grant access while now < accessUntil, whatever the status: a stopped or paused// subscription has paid for its period, and an active one keeps it while its renewal is billed.const subscription = await client. subscriptions. get( subscriptionUuid);if (hasAccess(subscription)) { console.log('serve the premium content');} else { console.log('access ended: show the renewal page');}subscription = client. subscriptions. get( subscription_uuid)# now is before access_until, whatever the status: the end of the period paid for, plus an# hour for an active subscription while its renewal is billed.if subscription.has_access(): print("grant access")else: print("no access")subscriptions/main.go
// Access lasts while now < AccessUntil, whatever the status: the end of the period paid// for, plus an hour for an active subscription while its renewal is billed.sub, err := client.Subscriptions.Get(ctx, subscriptionUUID, nil)if err != nil { log.Fatal(err)}if sub.HasAccess(time.Now()) { // now before AccessUntil fmt.Println("access granted until", sub. AccessUntil. Format( time. RFC3339))} else { fmt.Println("no access")}$subscription = $client->subscriptions->get( $subscriptionUuid);// now < accessUntil, whatever the status: the end of the period paid for, plus an hour for an// active subscription while its renewal is billed.if ($subscription->hasAccess()) { echo "Access granted\n";} else { echo "No access: the paid period has ended\n";}Path parameters
uuidstringRequiredIts prefixed uuid (or the bare uuid)
txuuid
Query parameters
includeMembersbooleanOptionalThe organization's space only (the team, its keys without On-Behalf-Of): a member's too, naming its member (userUuid). Read only: acting on it still switches into the member's space or uses On-Behalf-Of. From a member's space → 400 on includeMembers
Response
A Subscription.
30 fields of Subscription
uuidstringIts id (sub@…): the checkout session's
A prefixed id (pay@…, sub@…)
referencestringOptionalThe merchant's reference for the subscription (an order or customer id), set when creating its checkout. Only for the merchant: never in the public reads by uuid (GET /hosted/transactions/:txUuid)
Only for the resource's owner
createdAtstringupdatedAtstringIts last change (a bill, a status, its metadata)
fromstringThe subscriber's wallet: the one it bills. Absent when unknown (a trial not confirmed yet may have none)
tostringThe wallet its bills pay: the merchant's (or, while the organization holds a member's funds, the organization's)
productUuidstringIts product (GET /products/:uuid). Absent without one
subscriptionHashstringThe hash of the transaction that created the subscription on-chain. Absent until then (a trial, until its customer confirms it)
currencyIdintegerThe currency (token) billed, written as the currency object under currency (GET /currencies/:id)
frequencyDurationBilling interval in seconds, as on-chain (e.g. 2592000 = 30 days). Written as a types.Duration in responses and webhooks (e.g. {"value": 1, "unit": "months"})
2 fields of
DurationvalueintegerNumber of units; 0 means no duration
unitstringRequired when value > 0. "months" = 30 days, "years" = 365 days
oneof=seconds minutes hours days weeks months years
statusstringSubscriptionStatus (status) is where it stands: trial, trialExpired, active, pastDue, paused, stopped or cancelled. Every change sends subscription.statusChanged. Don't grant access from it: grant access while now < accessUntil (docs/api/guides/subscriptions.md § Access)
One of:
trialStatusTrial is a free trial: nothing was signed on-chain yet.trialExpiredStatusTrialExpired is a trial that ended without its customer confirming it (actionRequired confirmTrial): cancelled after the grace period unless they subscribe.activeStatusActive is a subscription billed on its due dates. Its customer may have something to do (actionRequired: topUpAllowance, raiseMaximum) while it stays active.pastDueStatusPastDue indicates a subscription whose last bill failed: its retries run.pausedStatusPaused is a subscription its customer paused (a message signed with its wallet): QBitFlow doesn't bill it until they resume it. On-chain it is still active. It keeps the period it paid for (currentPeriodEnd).stoppedStatusStopped is a subscription cancelled during its billing period (by its customer, or its merchant's force cancel with immediate=false): never billed again, it still runs until its paid period ends (nextBillingDate), then it is cancelled.cancelledStatusCancelled indicates a cancelled subscription.
actionRequiredstringOptionalActionRequired is what its customer must do, if anything (absent: nothing): topUpAllowance (the allowance covers about one more bill at most), raiseMaximum (a bill waits for a new maximum per period), confirmTrial (the trial ended unconfirmed). Every change (set, changed or cleared) sends subscription.actionRequiredChanged
One of
topUpAllowanceActionTopUpAllowance: what remains of the allowance covers about one more bill at most (or none). The customer adds periods from their management page.raiseMaximumActionRaiseMaximum: a bill is above the maximum per period the customer signed (the token's price moved); it waits up to 7 days for a new maximum, signed from the management page.confirmTrialActionConfirmTrial: the trial ended unconfirmed (trialExpired); the customer subscribes from their management page before the grace period ends.
currentPeriodEndstringOptionalCurrentPeriodEnd is the paid-through date. A trial: its end. active, paused, stopped: the end of the period paid for (the next bill's due date). pastDue: the start of the unpaid period (past). cancelled: when it was cancelled, or the end of its last paid period if that came first. For access, read accessUntil: an active subscription is renewed a few minutes after this date
accessUntilstringOptionalAccessUntil is when access ends: grant it while now < accessUntil, whatever the status. An active subscription: currentPeriodEnd plus 1 hour, since its renewal is billed within minutes of its due date (a subscriber whose bill is on its way keeps access; a failed bill sets pastDue). Every other status: currentPeriodEnd
priceUsdstringPriceUSD is the price per period the customer subscribed at, in USD (a decimal string, e.g. "9.99"), locked at signup (a free trial's too): every bill charges it and its lines (fees), converted to the token at the bill's price. A later change of the product's price applies to new subscribers only. Never "0"
feesarray of FeeLineFees is what its checkout added to the price, line by line, as the customer saw them (a tax, shipping, the processing fee when the customer pays it), locked at signup with the price: every bill charges priceUsd plus these lines. [] without any
4 fields of each
FeeLinetypestringcustom (the merchant's line) or processingFee (QBitFlow's fee, which the merchant has the customer pay)
One of:
customThe merchant's own line: a tax, shipping, a service feeprocessingFeeQBitFlow's fee, which the merchant has the customer pay (computed by QBitFlow)
labelstringThe line's name, as the checkout shows it ("Processing fee" for the processing fee)
descriptionstringOptionalOptional: more about the line, as the merchant wrote it. Omitted without one
amountUsdstringThe line's amount in USD, a decimal string with at most 2 decimals
allowanceAllowanceOptionalAllowanceStatus (allowance) is what the subscriber's wallet still lets QBitFlow bill, and the most one bill may charge, at today's price. Absent during a free trial (nothing signed yet)
6 fields of
AllowanceamountstringIn the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")
amountMinUnitsstringThe on-chain integer, in the token's smallest unit, a string of digits ("12500000")
amountUsdstringOptionalIts value in USD, a decimal string; absent when no price was known
remainingPeriodsintegerOptionalRemainingPeriods is how many whole bills the allowance still covers at today's price: the allowance divided by a bill (priceUsd and its fees, converted to the token), rounded down, before the network fee each bill adds. 0 is a value: the customer must top it up (actionRequired topUpAllowance) before the next bill. Absent without a price
maxPerPeriodstringOptionalMaxPerPeriod is the most one bill may charge, as the subscriber signed it, in the token's units (maxPerPeriodMinUnits: its on-chain integer): a bill above it waits for a new maximum (actionRequired raiseMaximum). Absent when the subscription has none recorded
maxPerPeriodMinUnitsstringOptional
lastBilledAtstringOptionalWhen it was last billed. Absent before its first bill (a trial)
nextBillingAtstringOptionalWhen its next bill is due (a trial: when it ends; stopped: when it is cancelled). Absent once cancelled
cancelledAtstringOptionalWhen it was cancelled (absent until then, and for subscriptions cancelled before it was recorded)
cancellationReasonstringOptionalWhy it stopped or was cancelled: customer, merchant, billingFailed, trialNotConverted, merchantClosed or inactiveOnChain. Absent while it runs (and for subscriptions cancelled before it was recorded)
One of:
customerCancelledByCustomer: its customer cancelled it (it was stopped until its period ended).merchantCancelledByMerchant: its merchant cancelled it (POST /subscriptions/:uuid/cancel).billingFailedCancelledBillingFailed: its bill's last attempt failed.trialNotConvertedCancelledTrialNotConverted: its trial ended and its customer didn't subscribe within the grace period.merchantClosedCancelledMerchantClosed: its merchant's space closed (a member removed, the organization closed).inactiveOnChainCancelledInactiveOnChain: the contract no longer had it active (cancelled outside QBitFlow).
cancellableAtstringOptionalMinimumCancellationDate (cancellableAt) is the earliest its customer may cancel it (the checkout's minPeriods). Absent when they may cancel it any time
metadatamapOptionalMetadata is the merchant's own key-value data: its checkout session's, copied when the subscription was created, then changed with PATCH /subscriptions/:uuid (merged). {} when none. Only for the merchant (API and webhooks)
Only for the resource's owner
managementPageLinkstringOptionalManagementPageLink is the subscription's page for its customer: where they top up its allowance, raise its maximum, pause, resume or cancel it, and confirm a trial. Share it with them (an email, the merchant's account page)
testbooleanWhether this is test data (testnets) or live data
userUuidstringOptionalMember the resource belongs to; omitted for the organization's own resources
Only for the resource's owner
customerUuidstringOptionalThe customer, when QBitFlow has one (named by the checkout, found by its customerReference, or recorded from the details the customer gave); omitted otherwise
Only for the resource's owner
customerReferencestringOptionalThe merchant's reference of the customer, as the checkout was given it (customerReference), whether or not QBitFlow has a customer with it; omitted otherwise
Only for the resource's owner
customerSummaryOptionalThe customer, named (merchant only, API reads): who paid, even once deleted. Omitted without one
Only for the resource's owner
6 fields of
SummaryuuidstringThe customer (GET /customers/:uuid)
firstNamestringFirst name
lastNamestringOptionalLast name, when given
emailstringEmail, lowercase
referencestringOptionalThe merchant's reference of the customer, when it has one
deletedbooleanThe merchant deleted the customer: it still names its rows, but GET /customers/:uuid answers 404
dunningDunningStatusOptionalDunningStatus (dunning) is where its failing bill's retries stand: the failed attempts, the next one, a wait for a new maximum. Omitted while no bill is failing. API reads only: webhooks tell each failure with subscription.billingFailed
5 fields of
DunningStatusfailedAttemptsintegerThe bill's failed attempts so far (0 while it only waits for a new maximum)
remainingAttemptsintegerAttempts left: after the last one fails, the subscription is cancelled
failingSincestringOptionalThe bill's first failed attempt: when the subscription went past due. Omitted without one
nextAttemptAtstringOptionalWhen the bill is tried again at the latest (sooner once the customer tops up or raises their allowance). Omitted when none is scheduled
awaitingMaximumSincestringOptionalWhile the bill is above the customer's maximum (actionRequired raiseMaximum): when it started waiting for them to raise it. nextAttemptAt is then when the wait ends. Omitted otherwise
Errors
The common codes only, as any route (what each means):
- 400validation_failed
- 401unauthorized
- 403forbidden
- 403policy_disabled
- 404not_found
- 429rate_limit_exceeded
- 500internal
- 504timeout
Update subscription#
PATCH /subscriptions/
Changes a subscription's metadata (merged: a key with "" is deleted, keys not named are kept; at most 50 keys once merged), cancelled ones too, and answers the subscription. Its price, terms and status don't change here. Another space's → 404.
- Auth
- X-API-Key: any keyA member's key needs the members.subscriptions policy
- On-Behalf-Of
- Honored: acts in the member's space
- Idempotency-Key
- Ignored
- Success
- 200
request.sh
curl -X PATCH "https://api.qbitflow.app/v2/subscriptions/$SUBSCRIPTION_UUID" \ -H "X-API-Key: $QBITFLOW_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "metadata": { "plan": "pro" } }'# 200: a Subscription// Metadata is merged into the subscription's: '' deletes a key, keys not named are kept. It// is the only thing a merchant edits on a subscription; webhooks carry it.const updated = await client. subscriptions. update( subscriptionUuid, { metadata: { plan: 'pro', coupon: '' },});console. log( updated. metadata. plan);# Metadata is merged into the subscription's: "" deletes a key, keys not named are kept.# It is the only thing a merchant edits on a subscription; webhooks carry it.updated = client. subscriptions. update( subscription_uuid, metadata={"plan": "pro", "coupon": ""})print( updated. metadata. get( "plan"))subscriptions/main.go
// Metadata is merged into the subscription's: "" deletes a key, keys not named are kept.// It is the only thing a merchant edits on a subscription; webhooks carry it.updated, err := client.Subscriptions.Update(ctx, subscriptionUUID, &qbitflow. UpdateSubscriptionParams{ Metadata: map[string]string{"plan": "pro", "coupon": ""},})if err != nil { log.Fatal(err)}fmt. Println( updated. Metadata["plan"])// Metadata is merged into the subscription's: '' deletes a key, keys not named are kept. It is the// only thing a merchant edits on a subscription; webhooks carry it.$updated = $client->subscriptions->update( $subscriptionUuid, new \QBitFlow\Params\UpdateSubscriptionParams( metadata: ['plan' => 'pro', 'coupon' => ''],));echo ($updated->metadata['plan'] ?? '-') . "\n";Path parameters
uuidstringRequiredThe subscription: sub@… or its bare uuid
txuuid
Body
metadatamapOptionalMetadata is merged into the subscription's (absent: unchanged): a key with a value sets it, a key with "" deletes it, keys not named are kept. At most 50 keys once merged
metadata
Response
A Subscription.
30 fields of Subscription
uuidstringIts id (sub@…): the checkout session's
A prefixed id (pay@…, sub@…)
referencestringOptionalThe merchant's reference for the subscription (an order or customer id), set when creating its checkout. Only for the merchant: never in the public reads by uuid (GET /hosted/transactions/:txUuid)
Only for the resource's owner
createdAtstringupdatedAtstringIts last change (a bill, a status, its metadata)
fromstringThe subscriber's wallet: the one it bills. Absent when unknown (a trial not confirmed yet may have none)
tostringThe wallet its bills pay: the merchant's (or, while the organization holds a member's funds, the organization's)
productUuidstringIts product (GET /products/:uuid). Absent without one
subscriptionHashstringThe hash of the transaction that created the subscription on-chain. Absent until then (a trial, until its customer confirms it)
currencyIdintegerThe currency (token) billed, written as the currency object under currency (GET /currencies/:id)
frequencyDurationBilling interval in seconds, as on-chain (e.g. 2592000 = 30 days). Written as a types.Duration in responses and webhooks (e.g. {"value": 1, "unit": "months"})
2 fields of
DurationvalueintegerNumber of units; 0 means no duration
unitstringRequired when value > 0. "months" = 30 days, "years" = 365 days
oneof=seconds minutes hours days weeks months years
statusstringSubscriptionStatus (status) is where it stands: trial, trialExpired, active, pastDue, paused, stopped or cancelled. Every change sends subscription.statusChanged. Don't grant access from it: grant access while now < accessUntil (docs/api/guides/subscriptions.md § Access)
One of:
trialStatusTrial is a free trial: nothing was signed on-chain yet.trialExpiredStatusTrialExpired is a trial that ended without its customer confirming it (actionRequired confirmTrial): cancelled after the grace period unless they subscribe.activeStatusActive is a subscription billed on its due dates. Its customer may have something to do (actionRequired: topUpAllowance, raiseMaximum) while it stays active.pastDueStatusPastDue indicates a subscription whose last bill failed: its retries run.pausedStatusPaused is a subscription its customer paused (a message signed with its wallet): QBitFlow doesn't bill it until they resume it. On-chain it is still active. It keeps the period it paid for (currentPeriodEnd).stoppedStatusStopped is a subscription cancelled during its billing period (by its customer, or its merchant's force cancel with immediate=false): never billed again, it still runs until its paid period ends (nextBillingDate), then it is cancelled.cancelledStatusCancelled indicates a cancelled subscription.
actionRequiredstringOptionalActionRequired is what its customer must do, if anything (absent: nothing): topUpAllowance (the allowance covers about one more bill at most), raiseMaximum (a bill waits for a new maximum per period), confirmTrial (the trial ended unconfirmed). Every change (set, changed or cleared) sends subscription.actionRequiredChanged
One of
topUpAllowanceActionTopUpAllowance: what remains of the allowance covers about one more bill at most (or none). The customer adds periods from their management page.raiseMaximumActionRaiseMaximum: a bill is above the maximum per period the customer signed (the token's price moved); it waits up to 7 days for a new maximum, signed from the management page.confirmTrialActionConfirmTrial: the trial ended unconfirmed (trialExpired); the customer subscribes from their management page before the grace period ends.
currentPeriodEndstringOptionalCurrentPeriodEnd is the paid-through date. A trial: its end. active, paused, stopped: the end of the period paid for (the next bill's due date). pastDue: the start of the unpaid period (past). cancelled: when it was cancelled, or the end of its last paid period if that came first. For access, read accessUntil: an active subscription is renewed a few minutes after this date
accessUntilstringOptionalAccessUntil is when access ends: grant it while now < accessUntil, whatever the status. An active subscription: currentPeriodEnd plus 1 hour, since its renewal is billed within minutes of its due date (a subscriber whose bill is on its way keeps access; a failed bill sets pastDue). Every other status: currentPeriodEnd
priceUsdstringPriceUSD is the price per period the customer subscribed at, in USD (a decimal string, e.g. "9.99"), locked at signup (a free trial's too): every bill charges it and its lines (fees), converted to the token at the bill's price. A later change of the product's price applies to new subscribers only. Never "0"
feesarray of FeeLineFees is what its checkout added to the price, line by line, as the customer saw them (a tax, shipping, the processing fee when the customer pays it), locked at signup with the price: every bill charges priceUsd plus these lines. [] without any
4 fields of each
FeeLinetypestringcustom (the merchant's line) or processingFee (QBitFlow's fee, which the merchant has the customer pay)
One of:
customThe merchant's own line: a tax, shipping, a service feeprocessingFeeQBitFlow's fee, which the merchant has the customer pay (computed by QBitFlow)
labelstringThe line's name, as the checkout shows it ("Processing fee" for the processing fee)
descriptionstringOptionalOptional: more about the line, as the merchant wrote it. Omitted without one
amountUsdstringThe line's amount in USD, a decimal string with at most 2 decimals
allowanceAllowanceOptionalAllowanceStatus (allowance) is what the subscriber's wallet still lets QBitFlow bill, and the most one bill may charge, at today's price. Absent during a free trial (nothing signed yet)
6 fields of
AllowanceamountstringIn the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")
amountMinUnitsstringThe on-chain integer, in the token's smallest unit, a string of digits ("12500000")
amountUsdstringOptionalIts value in USD, a decimal string; absent when no price was known
remainingPeriodsintegerOptionalRemainingPeriods is how many whole bills the allowance still covers at today's price: the allowance divided by a bill (priceUsd and its fees, converted to the token), rounded down, before the network fee each bill adds. 0 is a value: the customer must top it up (actionRequired topUpAllowance) before the next bill. Absent without a price
maxPerPeriodstringOptionalMaxPerPeriod is the most one bill may charge, as the subscriber signed it, in the token's units (maxPerPeriodMinUnits: its on-chain integer): a bill above it waits for a new maximum (actionRequired raiseMaximum). Absent when the subscription has none recorded
maxPerPeriodMinUnitsstringOptional
lastBilledAtstringOptionalWhen it was last billed. Absent before its first bill (a trial)
nextBillingAtstringOptionalWhen its next bill is due (a trial: when it ends; stopped: when it is cancelled). Absent once cancelled
cancelledAtstringOptionalWhen it was cancelled (absent until then, and for subscriptions cancelled before it was recorded)
cancellationReasonstringOptionalWhy it stopped or was cancelled: customer, merchant, billingFailed, trialNotConverted, merchantClosed or inactiveOnChain. Absent while it runs (and for subscriptions cancelled before it was recorded)
One of:
customerCancelledByCustomer: its customer cancelled it (it was stopped until its period ended).merchantCancelledByMerchant: its merchant cancelled it (POST /subscriptions/:uuid/cancel).billingFailedCancelledBillingFailed: its bill's last attempt failed.trialNotConvertedCancelledTrialNotConverted: its trial ended and its customer didn't subscribe within the grace period.merchantClosedCancelledMerchantClosed: its merchant's space closed (a member removed, the organization closed).inactiveOnChainCancelledInactiveOnChain: the contract no longer had it active (cancelled outside QBitFlow).
cancellableAtstringOptionalMinimumCancellationDate (cancellableAt) is the earliest its customer may cancel it (the checkout's minPeriods). Absent when they may cancel it any time
metadatamapOptionalMetadata is the merchant's own key-value data: its checkout session's, copied when the subscription was created, then changed with PATCH /subscriptions/:uuid (merged). {} when none. Only for the merchant (API and webhooks)
Only for the resource's owner
managementPageLinkstringOptionalManagementPageLink is the subscription's page for its customer: where they top up its allowance, raise its maximum, pause, resume or cancel it, and confirm a trial. Share it with them (an email, the merchant's account page)
testbooleanWhether this is test data (testnets) or live data
userUuidstringOptionalMember the resource belongs to; omitted for the organization's own resources
Only for the resource's owner
customerUuidstringOptionalThe customer, when QBitFlow has one (named by the checkout, found by its customerReference, or recorded from the details the customer gave); omitted otherwise
Only for the resource's owner
customerReferencestringOptionalThe merchant's reference of the customer, as the checkout was given it (customerReference), whether or not QBitFlow has a customer with it; omitted otherwise
Only for the resource's owner
customerSummaryOptionalThe customer, named (merchant only, API reads): who paid, even once deleted. Omitted without one
Only for the resource's owner
6 fields of
SummaryuuidstringThe customer (GET /customers/:uuid)
firstNamestringFirst name
lastNamestringOptionalLast name, when given
emailstringEmail, lowercase
referencestringOptionalThe merchant's reference of the customer, when it has one
deletedbooleanThe merchant deleted the customer: it still names its rows, but GET /customers/:uuid answers 404
dunningDunningStatusOptionalDunningStatus (dunning) is where its failing bill's retries stand: the failed attempts, the next one, a wait for a new maximum. Omitted while no bill is failing. API reads only: webhooks tell each failure with subscription.billingFailed
5 fields of
DunningStatusfailedAttemptsintegerThe bill's failed attempts so far (0 while it only waits for a new maximum)
remainingAttemptsintegerAttempts left: after the last one fails, the subscription is cancelled
failingSincestringOptionalThe bill's first failed attempt: when the subscription went past due. Omitted without one
nextAttemptAtstringOptionalWhen the bill is tried again at the latest (sooner once the customer tops up or raises their allowance). Omitted when none is scheduled
awaitingMaximumSincestringOptionalWhile the bill is above the customer's maximum (actionRequired raiseMaximum): when it started waiting for them to raise it. nextAttemptAt is then when the wait ends. Omitted otherwise
Errors
- 403policy_
disabled a member doing what their organization's policy doesn't allow them, reads of what the policy governs included
Also the common codes, as any route (what each means):
- 400validation_failed
- 401unauthorized
- 403forbidden
- 404not_found
- 429rate_limit_exceeded
- 500internal
- 504timeout
List subscription bills#
GET /subscriptions/
Lists a subscription's bills, newest first: every one, page by page (the hosted page shows the 10 most recent). The subscription is the caller's (sub@… or its bare uuid; cancelled ones too): another space's, an unknown one or another kind of id → 404.
- Auth
- X-API-Key: any key
- On-Behalf-Of
- Honored: acts in the member's space
- Idempotency-Key
- Ignored
- Success
- 200
request.sh
curl "https://api.qbitflow.app/v2/subscriptions/$SUBSCRIPTION_UUID/bills" \ -H "X-API-Key: $QBITFLOW_API_KEY"# 200: a page of Bill ({ items, hasMore, nextCursor? })Path parameters
uuidstringRequiredThe subscription: sub@… or its bare uuid
txuuid
Query parameters
limitintegerOptionalPage size: 20 when absent or ≤ 0, at most 100 (a larger one is clamped, not refused)
cursorstringOptionalThe previous page's nextCursor, verbatim; omit for the first page
max=200
Response
A page: { "items": Bill[], "hasMore": …, "nextCursor"?: … }. Pass nextCursor back as cursor for the next page; it is absent when hasMore is false.
30 fields of each Bill
uuidstringallow read and create, (generated on creation of the payment link)
A prefixed id (pay@…, sub@…)
createdAtstringfromstringtostringamountstringThe amount in the currency's units, exact, a decimal string ("10" for 10 USDC): amountMinUnits / 10^decimals
amountMinUnitsstringThe amount in the currency's smallest unit, a string of digits ("10000000" for 10 USDC, 6 decimals)
amountUsdstringWhat the customer paid, in USD, a decimal string: the price (and, on a one-time payment, the fees its checkout added: fees), which the contracts split between QBitFlow's fee (the referrer's share included), the organization's fee and the merchant (settlement.split). The network fee a customer pays on top (settlement.split.networkFee) is not in it
currencyIdintegerThe currency paid in, written as the currency object under currency (GET /currencies/:id)
txHashstringHash of the transaction in the blockchain
chainstringOptionalThe chain it was paid on: ETH (Ethereum), BASE (Base) or SOL (Solana), the testnet's for a test-mode transfer (currency.test)
explorerUrlstringOptionalIts transaction on the chain's block explorer (the testnet's in test mode). Omitted without a txHash
testbooleanWhether this is test data (testnets) or live data
userUuidstringOptionalMember the resource belongs to; omitted for the organization's own resources
Only for the resource's owner
productNamestringWhat was paid for: the subscription's product name when the bill was paid (the first bill: as it was when the checkout was created). Later product edits don't change it
productDescriptionstringSame, for the product's description. Absent without one
productUuidstringOptionalThe subscription's product
subscriptionUuidstringThe subscription it bills (sub@…)
customerUuidstringOptionalThe customer, when QBitFlow has one (named by the checkout, found by its customerReference, or recorded from the details the customer gave); omitted otherwise
Only for the resource's owner
customerReferencestringOptionalThe merchant's reference of the customer, as the checkout was given it (customerReference), whether or not QBitFlow has a customer with it; omitted otherwise
Only for the resource's owner
customerSummaryOptionalThe customer, named (merchant only, API reads): who paid, even once deleted. Omitted without one
Only for the resource's owner
6 fields of
SummaryuuidstringThe customer (GET /customers/:uuid)
firstNamestringFirst name
lastNamestringOptionalLast name, when given
emailstringEmail, lowercase
referencestringOptionalThe merchant's reference of the customer, when it has one
deletedbooleanThe merchant deleted the customer: it still names its rows, but GET /customers/:uuid answers 404
settlementSettlementOptionalHow it settled: its fees, its split and its transaction (only for the merchant)
Only for the resource's owner
5 fields of
SettlementfeePercentnumberQBitFlow's fee on this payment, in bps (1 % = 100 bps), deducted from the amount paid. Written as feePercent, a percent string
organizationFeeOrganizationFeeOptionalThe organization's fee on its member's payment (a marketplace's commission), paid to the organization's wallet. It is taken from what remains after QBitFlow's fee (contracts SPEC §4.1): organization = (amount - QBitFlow's fee) × its rate, and the member receives the rest. While the organization holds the member's funds, it is withheld from what the organization owes them instead of paid on-chain. Absent: none
2 fields of
OrganizationFeeaddressstringOptionalThe organization's wallet that receives the fee (only for the merchant)
Only for the resource's owner
feePercentnumberThe organization's fee, in bps, taken from what remains after the platform fee. Written as feePercent, a percent string
referralFeeReferralFeeOptionalThe referrer's share of QBitFlow's fee. Absent: none
2 fields of
ReferralFeeaddressstringOptionalThe referrer's wallet that receives the fee (only for the merchant)
Only for the resource's owner
feePercentnumberThe referrer's share of the platform fee, in bps (2000 = 20 % of it). Written as feePercent, a percent string
splitSplitOptionalHow the contracts split what the payer paid. For a payment the organization held for its member, the organization's fee was withheld from the member's share (not paid on-chain): the split is the one a direct payment would have had. Absent on records made before the split was recorded
5 fields of
SplitplatformTokenAmountQBitFlow's fee (the referrer's share not included)
3 fields of
TokenAmountamountstringIn the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")
amountMinUnitsstringThe on-chain integer, in the token's smallest unit, a string of digits ("12500000")
amountUsdstringOptionalIts value in USD, a decimal string; absent when no price was known
organizationTokenAmountOptionalThe organization's fee; absent without one
3 fields of
TokenAmountamountstringIn the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")
amountMinUnitsstringThe on-chain integer, in the token's smallest unit, a string of digits ("12500000")
amountUsdstringOptionalIts value in USD, a decimal string; absent when no price was known
referralTokenAmountOptionalThe referrer's share of QBitFlow's fee; absent without one
3 fields of
TokenAmountamountstringIn the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")
amountMinUnitsstringThe on-chain integer, in the token's smallest unit, a string of digits ("12500000")
amountUsdstringOptionalIts value in USD, a decimal string; absent when no price was known
merchantTokenAmountWhat the merchant received (the member, for a member's payment), net of every fee
3 fields of
TokenAmountamountstringIn the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")
amountMinUnitsstringThe on-chain integer, in the token's smallest unit, a string of digits ("12500000")
amountUsdstringOptionalIts value in USD, a decimal string; absent when no price was known
networkFeeTokenAmountOptionalNetworkFee is the network fee the payer paid on top of the amount, in the payment's currency: the gas refund the contracts took in the token (their GasRefundProcessed event), or, for a payment in a chain's native coin, the fee of the transaction the payer's wallet paid (its gas and, on Base, its L1 data fee), bounded by what QBitFlow prepared. Not the transaction's own cost to its sender (transaction.fee). Absent on records made before it was recorded ("0" once recorded as none)
3 fields of
TokenAmountamountstringIn the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")
amountMinUnitsstringThe on-chain integer, in the token's smallest unit, a string of digits ("12500000")
amountUsdstringOptionalIts value in USD, a decimal string; absent when no price was known
transactionSettlementTransactionOptionalThe transaction that paid it. Absent before its confirmation
6 fields of
SettlementTransactionblockNumberstringThe block (Ethereum, Base) or slot (Solana) it landed in, a string of digits
blockTimestringOptionalWhen its block was produced (UTC); when it was recorded if the chain's node didn't give it. Absent only on records made before it was recorded
gasUsedintegerOptionalThe gas it consumed (Ethereum, Base). Absent on Solana
feeTokenAmountWhat the transaction cost its sender, in the chain's native coin (ETH, SOL; its currency is the payment currency's chain): its gas and, on Base, its L1 data fee. amountUsd at nativePriceUsd
3 fields of
TokenAmountamountstringIn the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")
amountMinUnitsstringThe on-chain integer, in the token's smallest unit, a string of digits ("12500000")
amountUsdstringOptionalIts value in USD, a decimal string; absent when no price was known
l1FeeTokenAmountOptionalThe part of fee that paid Base's L1 data fee, in the native coin. Absent off Base, and on transactions recorded before it was
3 fields of
TokenAmountamountstringIn the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")
amountMinUnitsstringThe on-chain integer, in the token's smallest unit, a string of digits ("12500000")
amountUsdstringOptionalIts value in USD, a decimal string; absent when no price was known
nativePriceUsdstringOptionalNativePriceUSD is the USD price of the chain's native coin when the transaction was recorded (what its fee cost in USD). Absent when the price wasn't available then
periodStartstringOptionalThe start of the period the bill paid: its due date (the subscription's start for its first bill). Omitted on bills recorded before it was
periodEndstringOptionalThe end of the period the bill paid: the subscription's next bill. Paid until then. Omitted on bills recorded before it was
confirmedAtstringOptionalWhen the chain confirmed it: its block's time (settlement.transaction.blockTime). Absent only on records made before it was recorded
paidstringOptionalEverything the customer's wallet sent, in the currency's units (a decimal string): the amount plus the network fee paid on top (settlement.split). A refund's base (refundPercent of it). Only for the merchant; absent when the split wasn't recorded
Only for the resource's owner
paidMinUnitsstringOptionalThe same in the currency's smallest unit, a string of digits. Only for the merchant, like paid
Only for the resource's owner
paidUsdstringOptionalThe same in USD, at the payment's time, a decimal string. Only for the merchant, like paid
Only for the resource's owner
refundRefundSummaryOptionalIts refund (one per transaction); omitted without one. The merchant's own refund appears once its transfer confirmed (approved)
Only for the resource's owner
9 fields of
RefundSummaryuuidstringThe refund (refund@…)
statusstringpending, approved or rejected
One of
pendingapprovedrejectedfailedDeclared, never set: an approval that fails leaves the refund pending, to approve again
initiatedBystringcustomer (a request) or merchant
refundPercentnumberThe share of what the customer paid it sends back, a percent string ("100": all of it). Written as refundPercent
amountstringOptionalWhat it sends back, in the currency's units (a decimal string). Absent when its currency isn't known (a refund requested before refunds named it)
amountMinUnitsstringThe same in the currency's smallest unit (a string of digits)
amountUsdstringOptionalThe same in USD, at the payment's time (a decimal string). Absent when unknown (a refund requested before it was recorded)
createdAtstringWhen it was requested or started
respondedAtstringOptionalWhen it was approved (confirmed) or rejected; absent while pending
refundablebooleanOptionalWhether it can be refunded now
Only for the resource's owner
notRefundableReasonstringOptionalWhy not: refundExists or heldFundsReleased. Omitted when refundable
Only for the resource's owner
Errors
The common codes only, as any route (what each means):
- 400validation_failed
- 401unauthorized
- 403forbidden
- 403policy_disabled
- 404not_found
- 429rate_limit_exceeded
- 500internal
- 504timeout
Get bill#
GET /bills/
Reads one of the caller's subscription bills (sub-hist@… or its bare uuid): another space's, an unknown one or another kind of id → 404.
- Auth
- X-API-Key: any key
- On-Behalf-Of
- Honored: acts in the member's space
- Idempotency-Key
- Ignored
- Success
- 200
request.sh
curl "https://api.qbitflow.app/v2/bills/$BILL_UUID" \ -H "X-API-Key: $QBITFLOW_API_KEY"# 200: a BillPath parameters
uuidstringRequiredIts prefixed uuid (or the bare uuid)
txuuid
Query parameters
includeMembersbooleanOptionalThe organization's space only (the team, its keys without On-Behalf-Of): a member's too, naming its member (userUuid). Read only: acting on it still switches into the member's space or uses On-Behalf-Of. From a member's space → 400 on includeMembers
Response
A Bill.
30 fields of Bill
uuidstringallow read and create, (generated on creation of the payment link)
A prefixed id (pay@…, sub@…)
createdAtstringfromstringtostringamountstringThe amount in the currency's units, exact, a decimal string ("10" for 10 USDC): amountMinUnits / 10^decimals
amountMinUnitsstringThe amount in the currency's smallest unit, a string of digits ("10000000" for 10 USDC, 6 decimals)
amountUsdstringWhat the customer paid, in USD, a decimal string: the price (and, on a one-time payment, the fees its checkout added: fees), which the contracts split between QBitFlow's fee (the referrer's share included), the organization's fee and the merchant (settlement.split). The network fee a customer pays on top (settlement.split.networkFee) is not in it
currencyIdintegerThe currency paid in, written as the currency object under currency (GET /currencies/:id)
txHashstringHash of the transaction in the blockchain
chainstringOptionalThe chain it was paid on: ETH (Ethereum), BASE (Base) or SOL (Solana), the testnet's for a test-mode transfer (currency.test)
explorerUrlstringOptionalIts transaction on the chain's block explorer (the testnet's in test mode). Omitted without a txHash
testbooleanWhether this is test data (testnets) or live data
userUuidstringOptionalMember the resource belongs to; omitted for the organization's own resources
Only for the resource's owner
productNamestringWhat was paid for: the subscription's product name when the bill was paid (the first bill: as it was when the checkout was created). Later product edits don't change it
productDescriptionstringSame, for the product's description. Absent without one
productUuidstringOptionalThe subscription's product
subscriptionUuidstringThe subscription it bills (sub@…)
customerUuidstringOptionalThe customer, when QBitFlow has one (named by the checkout, found by its customerReference, or recorded from the details the customer gave); omitted otherwise
Only for the resource's owner
customerReferencestringOptionalThe merchant's reference of the customer, as the checkout was given it (customerReference), whether or not QBitFlow has a customer with it; omitted otherwise
Only for the resource's owner
customerSummaryOptionalThe customer, named (merchant only, API reads): who paid, even once deleted. Omitted without one
Only for the resource's owner
6 fields of
SummaryuuidstringThe customer (GET /customers/:uuid)
firstNamestringFirst name
lastNamestringOptionalLast name, when given
emailstringEmail, lowercase
referencestringOptionalThe merchant's reference of the customer, when it has one
deletedbooleanThe merchant deleted the customer: it still names its rows, but GET /customers/:uuid answers 404
settlementSettlementOptionalHow it settled: its fees, its split and its transaction (only for the merchant)
Only for the resource's owner
5 fields of
SettlementfeePercentnumberQBitFlow's fee on this payment, in bps (1 % = 100 bps), deducted from the amount paid. Written as feePercent, a percent string
organizationFeeOrganizationFeeOptionalThe organization's fee on its member's payment (a marketplace's commission), paid to the organization's wallet. It is taken from what remains after QBitFlow's fee (contracts SPEC §4.1): organization = (amount - QBitFlow's fee) × its rate, and the member receives the rest. While the organization holds the member's funds, it is withheld from what the organization owes them instead of paid on-chain. Absent: none
2 fields of
OrganizationFeeaddressstringOptionalThe organization's wallet that receives the fee (only for the merchant)
Only for the resource's owner
feePercentnumberThe organization's fee, in bps, taken from what remains after the platform fee. Written as feePercent, a percent string
referralFeeReferralFeeOptionalThe referrer's share of QBitFlow's fee. Absent: none
2 fields of
ReferralFeeaddressstringOptionalThe referrer's wallet that receives the fee (only for the merchant)
Only for the resource's owner
feePercentnumberThe referrer's share of the platform fee, in bps (2000 = 20 % of it). Written as feePercent, a percent string
splitSplitOptionalHow the contracts split what the payer paid. For a payment the organization held for its member, the organization's fee was withheld from the member's share (not paid on-chain): the split is the one a direct payment would have had. Absent on records made before the split was recorded
5 fields of
SplitplatformTokenAmountQBitFlow's fee (the referrer's share not included)
3 fields of
TokenAmountamountstringIn the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")
amountMinUnitsstringThe on-chain integer, in the token's smallest unit, a string of digits ("12500000")
amountUsdstringOptionalIts value in USD, a decimal string; absent when no price was known
organizationTokenAmountOptionalThe organization's fee; absent without one
3 fields of
TokenAmountamountstringIn the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")
amountMinUnitsstringThe on-chain integer, in the token's smallest unit, a string of digits ("12500000")
amountUsdstringOptionalIts value in USD, a decimal string; absent when no price was known
referralTokenAmountOptionalThe referrer's share of QBitFlow's fee; absent without one
3 fields of
TokenAmountamountstringIn the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")
amountMinUnitsstringThe on-chain integer, in the token's smallest unit, a string of digits ("12500000")
amountUsdstringOptionalIts value in USD, a decimal string; absent when no price was known
merchantTokenAmountWhat the merchant received (the member, for a member's payment), net of every fee
3 fields of
TokenAmountamountstringIn the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")
amountMinUnitsstringThe on-chain integer, in the token's smallest unit, a string of digits ("12500000")
amountUsdstringOptionalIts value in USD, a decimal string; absent when no price was known
networkFeeTokenAmountOptionalNetworkFee is the network fee the payer paid on top of the amount, in the payment's currency: the gas refund the contracts took in the token (their GasRefundProcessed event), or, for a payment in a chain's native coin, the fee of the transaction the payer's wallet paid (its gas and, on Base, its L1 data fee), bounded by what QBitFlow prepared. Not the transaction's own cost to its sender (transaction.fee). Absent on records made before it was recorded ("0" once recorded as none)
3 fields of
TokenAmountamountstringIn the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")
amountMinUnitsstringThe on-chain integer, in the token's smallest unit, a string of digits ("12500000")
amountUsdstringOptionalIts value in USD, a decimal string; absent when no price was known
transactionSettlementTransactionOptionalThe transaction that paid it. Absent before its confirmation
6 fields of
SettlementTransactionblockNumberstringThe block (Ethereum, Base) or slot (Solana) it landed in, a string of digits
blockTimestringOptionalWhen its block was produced (UTC); when it was recorded if the chain's node didn't give it. Absent only on records made before it was recorded
gasUsedintegerOptionalThe gas it consumed (Ethereum, Base). Absent on Solana
feeTokenAmountWhat the transaction cost its sender, in the chain's native coin (ETH, SOL; its currency is the payment currency's chain): its gas and, on Base, its L1 data fee. amountUsd at nativePriceUsd
3 fields of
TokenAmountamountstringIn the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")
amountMinUnitsstringThe on-chain integer, in the token's smallest unit, a string of digits ("12500000")
amountUsdstringOptionalIts value in USD, a decimal string; absent when no price was known
l1FeeTokenAmountOptionalThe part of fee that paid Base's L1 data fee, in the native coin. Absent off Base, and on transactions recorded before it was
3 fields of
TokenAmountamountstringIn the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")
amountMinUnitsstringThe on-chain integer, in the token's smallest unit, a string of digits ("12500000")
amountUsdstringOptionalIts value in USD, a decimal string; absent when no price was known
nativePriceUsdstringOptionalNativePriceUSD is the USD price of the chain's native coin when the transaction was recorded (what its fee cost in USD). Absent when the price wasn't available then
periodStartstringOptionalThe start of the period the bill paid: its due date (the subscription's start for its first bill). Omitted on bills recorded before it was
periodEndstringOptionalThe end of the period the bill paid: the subscription's next bill. Paid until then. Omitted on bills recorded before it was
confirmedAtstringOptionalWhen the chain confirmed it: its block's time (settlement.transaction.blockTime). Absent only on records made before it was recorded
paidstringOptionalEverything the customer's wallet sent, in the currency's units (a decimal string): the amount plus the network fee paid on top (settlement.split). A refund's base (refundPercent of it). Only for the merchant; absent when the split wasn't recorded
Only for the resource's owner
paidMinUnitsstringOptionalThe same in the currency's smallest unit, a string of digits. Only for the merchant, like paid
Only for the resource's owner
paidUsdstringOptionalThe same in USD, at the payment's time, a decimal string. Only for the merchant, like paid
Only for the resource's owner
refundRefundSummaryOptionalIts refund (one per transaction); omitted without one. The merchant's own refund appears once its transfer confirmed (approved)
Only for the resource's owner
9 fields of
RefundSummaryuuidstringThe refund (refund@…)
statusstringpending, approved or rejected
One of
pendingapprovedrejectedfailedDeclared, never set: an approval that fails leaves the refund pending, to approve again
initiatedBystringcustomer (a request) or merchant
refundPercentnumberThe share of what the customer paid it sends back, a percent string ("100": all of it). Written as refundPercent
amountstringOptionalWhat it sends back, in the currency's units (a decimal string). Absent when its currency isn't known (a refund requested before refunds named it)
amountMinUnitsstringThe same in the currency's smallest unit (a string of digits)
amountUsdstringOptionalThe same in USD, at the payment's time (a decimal string). Absent when unknown (a refund requested before it was recorded)
createdAtstringWhen it was requested or started
respondedAtstringOptionalWhen it was approved (confirmed) or rejected; absent while pending
refundablebooleanOptionalWhether it can be refunded now
Only for the resource's owner
notRefundableReasonstringOptionalWhy not: refundExists or heldFundsReleased. Omitted when refundable
Only for the resource's owner
Errors
The common codes only, as any route (what each means):
- 400validation_failed
- 401unauthorized
- 403forbidden
- 403policy_disabled
- 404not_found
- 429rate_limit_exceeded
- 500internal
- 504timeout
Cancel subscription#
POST /subscriptions/
Cancels one of the merchant's subscriptions without its subscriber signing (?immediate=true|false), as a bill's last failure does: on-chain (QBitFlow signs the force cancel, so nothing can bill it anymore), else off-chain (a trial, never on-chain; a stopped subscription, cancelled on-chain already; or the force cancel failed). immediate=true (default): cancelled now. immediate=false: stopped now, cancelled at the end of its paid period by its bill. subscription.statusChanged tells each change. It answers the subscription as it is then: 200 once done, 202 while it still confirms on-chain. Another space's subscription (another organization's, another mode's) is not found, whatever its state; a cancelled one: 409 subscription_already_stopped_or_inactive, and so is one stopped already with immediate=false; a trial with immediate=false: 400; a cancellation running: 409 tx_already_sent. A member needs the members.subscriptions policy (403 policy_disabled, checked before the subscription is looked up, so before any 404).
- Auth
- X-API-Key: any keyA member's key needs the members.subscriptions policy
- On-Behalf-Of
- Honored: acts in the member's space
- Idempotency-Key
- Ignored
- Success
- 200 (done), 202 (still confirming on-chain)
request.sh
curl -X POST "https://api.qbitflow.app/v2/subscriptions/$SUBSCRIPTION_UUID/cancel?immediate=false" \ -H "X-API-Key: $QBITFLOW_API_KEY"# 200: a Cancelled (202 while the cancellation is still confirming on-chain)// immediate: false stops it now (never billed again) and cancels it at the end of the period// paid for. A ConflictError when it is already stopped or inactive.const { subscription, pending } = await client. subscriptions. cancel( subscriptionUuid, { immediate: false,});if (pending) { // HTTP 202: the on-chain cancellation is still confirming. It goes on regardless: // subscription.statusChanged tells the end. console.log('cancellation confirming');} else { console.log(`now ${subscription.status}`); // stopped}result = client. subscriptions. cancel( subscription_uuid, immediate=False) # at period endif result.pending: # HTTP 202: still confirming on-chain; subscription.statusChanged tells the end. print("cancellation confirming")else: print("now", result.subscription.status) # stopped, cancelled at the period's endsubscriptions/main.go
// Immediate false: never billed again, cancelled when the paid period ends.res, err := client.Subscriptions.Cancel(ctx, subscriptionUUID, &qbitflow. CancelSubscriptionParams{ Immediate: new(false),})if err != nil { log.Fatal(err) // 409 subscription_already_stopped_or_inactive}if res.Pending { // HTTP 202: the on-chain cancellation is still confirming; subscription.statusChanged // tells the end. fmt.Println("cancellation confirming")} else { fmt.Println("now", res.Subscription.Status) // stopped}$result = $client->subscriptions->cancel( $subscriptionUuid, new \QBitFlow\Params\CancelSubscriptionParams( immediate: false));if ($result->pending) { // HTTP 202: the on-chain cancellation is still confirming; subscription.statusChanged tells the end. echo "Cancellation confirming\n";} else { echo "Now {$result->subscription->status}\n"; // stopped: cancelled at the end of the paid period}Path parameters
uuidstringRequiredThe subscription: sub@… or its bare uuid
txuuid
Query parameters
immediatebooleanOptionalImmediate: true (the default) cancels it now (cancelled); false stops it now (never billed again, its status stopped) and cancels it when its paid period ends (nextBillingAt). A trial is cancelled immediately only (no paid period to finish: false → 400)
Response
A Cancelled.
30 fields of Cancelled
uuidstringIts id (sub@…): the checkout session's
A prefixed id (pay@…, sub@…)
referencestringOptionalThe merchant's reference for the subscription (an order or customer id), set when creating its checkout. Only for the merchant: never in the public reads by uuid (GET /hosted/transactions/:txUuid)
Only for the resource's owner
createdAtstringupdatedAtstringIts last change (a bill, a status, its metadata)
fromstringThe subscriber's wallet: the one it bills. Absent when unknown (a trial not confirmed yet may have none)
tostringThe wallet its bills pay: the merchant's (or, while the organization holds a member's funds, the organization's)
productUuidstringIts product (GET /products/:uuid). Absent without one
subscriptionHashstringThe hash of the transaction that created the subscription on-chain. Absent until then (a trial, until its customer confirms it)
currencyIdintegerThe currency (token) billed, written as the currency object under currency (GET /currencies/:id)
frequencyDurationBilling interval in seconds, as on-chain (e.g. 2592000 = 30 days). Written as a types.Duration in responses and webhooks (e.g. {"value": 1, "unit": "months"})
2 fields of
DurationvalueintegerNumber of units; 0 means no duration
unitstringRequired when value > 0. "months" = 30 days, "years" = 365 days
oneof=seconds minutes hours days weeks months years
statusstringSubscriptionStatus (status) is where it stands: trial, trialExpired, active, pastDue, paused, stopped or cancelled. Every change sends subscription.statusChanged. Don't grant access from it: grant access while now < accessUntil (docs/api/guides/subscriptions.md § Access)
One of:
trialStatusTrial is a free trial: nothing was signed on-chain yet.trialExpiredStatusTrialExpired is a trial that ended without its customer confirming it (actionRequired confirmTrial): cancelled after the grace period unless they subscribe.activeStatusActive is a subscription billed on its due dates. Its customer may have something to do (actionRequired: topUpAllowance, raiseMaximum) while it stays active.pastDueStatusPastDue indicates a subscription whose last bill failed: its retries run.pausedStatusPaused is a subscription its customer paused (a message signed with its wallet): QBitFlow doesn't bill it until they resume it. On-chain it is still active. It keeps the period it paid for (currentPeriodEnd).stoppedStatusStopped is a subscription cancelled during its billing period (by its customer, or its merchant's force cancel with immediate=false): never billed again, it still runs until its paid period ends (nextBillingDate), then it is cancelled.cancelledStatusCancelled indicates a cancelled subscription.
actionRequiredstringOptionalActionRequired is what its customer must do, if anything (absent: nothing): topUpAllowance (the allowance covers about one more bill at most), raiseMaximum (a bill waits for a new maximum per period), confirmTrial (the trial ended unconfirmed). Every change (set, changed or cleared) sends subscription.actionRequiredChanged
One of
topUpAllowanceActionTopUpAllowance: what remains of the allowance covers about one more bill at most (or none). The customer adds periods from their management page.raiseMaximumActionRaiseMaximum: a bill is above the maximum per period the customer signed (the token's price moved); it waits up to 7 days for a new maximum, signed from the management page.confirmTrialActionConfirmTrial: the trial ended unconfirmed (trialExpired); the customer subscribes from their management page before the grace period ends.
currentPeriodEndstringOptionalCurrentPeriodEnd is the paid-through date. A trial: its end. active, paused, stopped: the end of the period paid for (the next bill's due date). pastDue: the start of the unpaid period (past). cancelled: when it was cancelled, or the end of its last paid period if that came first. For access, read accessUntil: an active subscription is renewed a few minutes after this date
accessUntilstringOptionalAccessUntil is when access ends: grant it while now < accessUntil, whatever the status. An active subscription: currentPeriodEnd plus 1 hour, since its renewal is billed within minutes of its due date (a subscriber whose bill is on its way keeps access; a failed bill sets pastDue). Every other status: currentPeriodEnd
priceUsdstringPriceUSD is the price per period the customer subscribed at, in USD (a decimal string, e.g. "9.99"), locked at signup (a free trial's too): every bill charges it and its lines (fees), converted to the token at the bill's price. A later change of the product's price applies to new subscribers only. Never "0"
feesarray of FeeLineFees is what its checkout added to the price, line by line, as the customer saw them (a tax, shipping, the processing fee when the customer pays it), locked at signup with the price: every bill charges priceUsd plus these lines. [] without any
4 fields of each
FeeLinetypestringcustom (the merchant's line) or processingFee (QBitFlow's fee, which the merchant has the customer pay)
One of:
customThe merchant's own line: a tax, shipping, a service feeprocessingFeeQBitFlow's fee, which the merchant has the customer pay (computed by QBitFlow)
labelstringThe line's name, as the checkout shows it ("Processing fee" for the processing fee)
descriptionstringOptionalOptional: more about the line, as the merchant wrote it. Omitted without one
amountUsdstringThe line's amount in USD, a decimal string with at most 2 decimals
allowanceAllowanceOptionalAllowanceStatus (allowance) is what the subscriber's wallet still lets QBitFlow bill, and the most one bill may charge, at today's price. Absent during a free trial (nothing signed yet)
6 fields of
AllowanceamountstringIn the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")
amountMinUnitsstringThe on-chain integer, in the token's smallest unit, a string of digits ("12500000")
amountUsdstringOptionalIts value in USD, a decimal string; absent when no price was known
remainingPeriodsintegerOptionalRemainingPeriods is how many whole bills the allowance still covers at today's price: the allowance divided by a bill (priceUsd and its fees, converted to the token), rounded down, before the network fee each bill adds. 0 is a value: the customer must top it up (actionRequired topUpAllowance) before the next bill. Absent without a price
maxPerPeriodstringOptionalMaxPerPeriod is the most one bill may charge, as the subscriber signed it, in the token's units (maxPerPeriodMinUnits: its on-chain integer): a bill above it waits for a new maximum (actionRequired raiseMaximum). Absent when the subscription has none recorded
maxPerPeriodMinUnitsstringOptional
lastBilledAtstringOptionalWhen it was last billed. Absent before its first bill (a trial)
nextBillingAtstringOptionalWhen its next bill is due (a trial: when it ends; stopped: when it is cancelled). Absent once cancelled
cancelledAtstringOptionalWhen it was cancelled (absent until then, and for subscriptions cancelled before it was recorded)
cancellationReasonstringOptionalWhy it stopped or was cancelled: customer, merchant, billingFailed, trialNotConverted, merchantClosed or inactiveOnChain. Absent while it runs (and for subscriptions cancelled before it was recorded)
One of:
customerCancelledByCustomer: its customer cancelled it (it was stopped until its period ended).merchantCancelledByMerchant: its merchant cancelled it (POST /subscriptions/:uuid/cancel).billingFailedCancelledBillingFailed: its bill's last attempt failed.trialNotConvertedCancelledTrialNotConverted: its trial ended and its customer didn't subscribe within the grace period.merchantClosedCancelledMerchantClosed: its merchant's space closed (a member removed, the organization closed).inactiveOnChainCancelledInactiveOnChain: the contract no longer had it active (cancelled outside QBitFlow).
cancellableAtstringOptionalMinimumCancellationDate (cancellableAt) is the earliest its customer may cancel it (the checkout's minPeriods). Absent when they may cancel it any time
metadatamapOptionalMetadata is the merchant's own key-value data: its checkout session's, copied when the subscription was created, then changed with PATCH /subscriptions/:uuid (merged). {} when none. Only for the merchant (API and webhooks)
Only for the resource's owner
managementPageLinkstringOptionalManagementPageLink is the subscription's page for its customer: where they top up its allowance, raise its maximum, pause, resume or cancel it, and confirm a trial. Share it with them (an email, the merchant's account page)
testbooleanWhether this is test data (testnets) or live data
userUuidstringOptionalMember the resource belongs to; omitted for the organization's own resources
Only for the resource's owner
customerUuidstringOptionalThe customer, when QBitFlow has one (named by the checkout, found by its customerReference, or recorded from the details the customer gave); omitted otherwise
Only for the resource's owner
customerReferencestringOptionalThe merchant's reference of the customer, as the checkout was given it (customerReference), whether or not QBitFlow has a customer with it; omitted otherwise
Only for the resource's owner
customerSummaryOptionalThe customer, named (merchant only, API reads): who paid, even once deleted. Omitted without one
Only for the resource's owner
6 fields of
SummaryuuidstringThe customer (GET /customers/:uuid)
firstNamestringFirst name
lastNamestringOptionalLast name, when given
emailstringEmail, lowercase
referencestringOptionalThe merchant's reference of the customer, when it has one
deletedbooleanThe merchant deleted the customer: it still names its rows, but GET /customers/:uuid answers 404
dunningDunningStatusOptionalDunningStatus (dunning) is where its failing bill's retries stand: the failed attempts, the next one, a wait for a new maximum. Omitted while no bill is failing. API reads only: webhooks tell each failure with subscription.billingFailed
5 fields of
DunningStatusfailedAttemptsintegerThe bill's failed attempts so far (0 while it only waits for a new maximum)
remainingAttemptsintegerAttempts left: after the last one fails, the subscription is cancelled
failingSincestringOptionalThe bill's first failed attempt: when the subscription went past due. Omitted without one
nextAttemptAtstringOptionalWhen the bill is tried again at the latest (sooner once the customer tops up or raises their allowance). Omitted when none is scheduled
awaitingMaximumSincestringOptionalWhile the bill is above the customer's maximum (actionRequired raiseMaximum): when it started waiting for them to raise it. nextAttemptAt is then when the wait ends. Omitted otherwise
Errors
- 403policy_
disabled a member doing what their organization's policy doesn't allow them, reads of what the policy governs included
- 409tx_
already_ sent a transaction sent already: running, or completed (Payments and transactions § Sending a signed transaction)
- 409subscription_
already_ stopped_ or_ inactive cancelling a subscription, raising its allowance or its maximum, once it is
stopped(it ends with its paid period; customer-side routes), a merchant'sPOST /of a stopped one, or a merchant'ssubscriptions/ :uuid/ cancel?immediate=false POST /orsubscriptions/ :uuid/ cancel /test-billof a cancelled one
Also the common codes, as any route (what each means):
- 400validation_failed
- 401unauthorized
- 403forbidden
- 404not_found
- 429rate_limit_exceeded
- 500internal
- 504timeout
Run test bill#
POST /subscriptions/
Bills a test subscription now ("the test bill"): live subscriptions are billed on their due date only (a live one → 400 test_mode_only). It runs the same bill as live, so statuses and webhooks are exactly live's: the bill of its due date, or, on a bill waiting in dunning (or for a new maximum), its next attempt now. It answers once the attempt settled (at most 2 minutes: then where it stands). A paused subscription isn't billed (its bill ends: outcome paused); one not due yet → 409 payment_not_due; a cancelled one → 409 subscription_already_stopped_or_inactive. A member needs the members.subscriptions policy (403 policy_disabled, checked before the subscription is looked up, so before any 404). Another space's → 404.
- Auth
- X-API-Key: any keyA member's key needs the members.subscriptions policy
- On-Behalf-Of
- Honored: acts in the member's space
- Idempotency-Key
- Ignored
- Success
- 200
request.sh
curl -X POST "https://api.qbitflow.app/v2/subscriptions/$SUBSCRIPTION_UUID/test-bill" \ -H "X-API-Key: $QBITFLOW_API_KEY"# 200: a TestBill// Test mode only: runs the next billing now, with live's statuses and webhooks. A ConflictError// payment_not_due before nextBillingAt; a BadRequestError test_mode_only for a live one.const bill = await client. subscriptions. testBill( subscriptionUuid);// billUuid is sub-hist@…: the id its subscription.billed carries once paid.console.log(bill.billUuid, bill.stage, bill.outcome ?? 'running', bill.failureCode ?? '');# Test mode only: run the next bill now instead of on its due date.bill = client. subscriptions. test_bill( subscription_uuid)print(bill.bill_uuid, bill.stage, bill.outcome, bill.failure_code) # sub-hist@…subscriptions/main.go
// Test mode only: runs the next billing now, with live's statuses and webhooks.bill, err := client. Subscriptions. TestBill( ctx, subscriptionUUID)if err != nil { log.Fatal(err) // 409 payment_not_due before NextBillingAt; 400 test_mode_only for a live one}fmt.Println(bill.BillUUID, bill.Stage, bill.Outcome, bill.FailureCode) // sub-hist@…// Test mode only: runs the next billing now instead of on its due date.$bill = $client->subscriptions->testBill( $subscriptionUuid);echo "Bill {$bill->billUuid}: {$bill->stage} " . ($bill->outcome ?? '-') . ' ' . ($bill->failureCode ?? '') . "\n"; // sub-hist@…Path parameters
uuidstringRequiredThe subscription: sub@… or its bare uuid
txuuid
Response
A TestBill.
billUuidstringThe bill (sub-hist@…): the uuid its subscription.billed and GET /bills/:uuid carry once it is paid
stagestringrunning (being charged), waiting (for its next attempt, its due date or a trial's grace period), pending (over the customer's maximum: waiting for a new one) or done
One of
donerunningBeing planned, charged or recordedwaitingWaiting for its next attempt (dunning, or QBitFlow's own failure), its due date, or the end of a trial's grace periodpendingOver the customer's maximum: waiting for their new one
outcomestringOptionalOnce done: paid, notDue (nothing to bill), skipped (below the minimum amount), cancelled or paused. Absent before
One of
paidnotDueNothing to bill: paid already, cancelled, or goneskippedBelow the minimum amount: the period moved on without a chargecancelledThe subscription was cancelled: stopped by its subscriber, its trial over, its merchant closed, inactive on-chain, or its last attempt failedpausedIts customer paused the subscription: not charged (resumed, it is billed by the bill of its new date)
attemptsintegerIts failed attempts so far
nextAttemptAtstringOptionalWhile waiting or pending: when it is tried again at the latest. Absent otherwise
txHashstringOptionalPaid: its charge's transaction. Absent otherwise
failureCodestringOptionalIts last failure's code (errors), e.g. insufficient_allowance. Absent without one
Errors
- 403policy_
disabled a member doing what their organization's policy doesn't allow them, reads of what the policy governs included
- 400test_
mode_ only something only test mode does asked in live mode (a test bill, a faucet, a test payment)
- 409subscription_
already_ stopped_ or_ inactive cancelling a subscription, raising its allowance or its maximum, once it is
stopped(it ends with its paid period; customer-side routes), a merchant'sPOST /of a stopped one, or a merchant'ssubscriptions/ :uuid/ cancel?immediate=false POST /orsubscriptions/ :uuid/ cancel /test-billof a cancelled one - 409payment_
not_ due asked for what the subscription's state refuses
Also the common codes, as any route (what each means):
- 400validation_failed
- 401unauthorized
- 403forbidden
- 404not_found
- 429rate_limit_exceeded
- 500internal
- 504timeout
Edge cases
The contract's rules for these routes: Authentication & roles · Ids · Spaces · Reads & shape · PATCH /subscriptions/:uuid · Cancel (POST /subscriptions/:uuid/cancel) · Test bill (POST /subscriptions/:uuid/test-bill) · Billing · The list · Bills.
Read all 10
Authentication & roles
POST /subscriptions/:uuid/cancelandPOST /subscriptions/:uuid/test-billare under themembers.subscriptionspolicy: a member (session or own key) while it is off →403policy_disabled,details.policy: "members.subscriptions", subscriptions created earlier included (the organization keeps managing them); the team passes, acting in the member's space too. The policy is checked before the subscription is looked up:403first, whatever the uuid (another space's or unknown included); the404ladder applies once the policy passes.PATCHfollows the organization'smembers.subscriptionspolicy, as creating a subscription checkout does: a member's credential while it is off →403policy_disabled.- The two actions change state, so they are
POST; aGETon them →405method_not_allowed(Allow: POST). - The public bill history a customer's page shows is
GET /hosted/subscriptions/:uuid/bills(Hosted subscriptions).
Ids
:uuidis the subscription's id,sub@…, or its bare uuid (the same subscription; answers always name itsub@…). A bill's issub-hist@…or its bare uuid. Another kind of id (pay@…on a subscription route,sub@…on/bills) names nothing →404; a malformed id →400validation_failedonuuid.
Spaces
- Every subscription belongs to one space (spaces): the organization's or a member's, in one mode. Every route works in the request's space: the team (and organization keys) sees the organization's subscriptions, a member (and their keys) their own, and an organization key with
On-Behalf-Of: <userUuid>(or the dashboard switched into the member's space) the member's. - A subscription of another space, of another organization or of the other mode is never listed, and answers
404on every route (the reads,PATCH, the actions): existence is never revealed, never403nor409. - The reads return the caller's subscription whatever its status, a cancelled one too (
status: "cancelled"). The actions (cancel,test-bill) answer409subscription_already_stopped_or_inactivefor a cancelled one of the caller's space (nothing left to do on it);PATCHedits a cancelled one's metadata too. - A subscription doesn't exist while it is a checkout. It exists once its checkout completed (paid on-chain; a free trial: its message signed). Before that, its checkout session's uuid (
sub@…) answers404on every subscription route, and no list has it: read the checkout session you created instead (Checkout sessions:created(withlastAttemptafter a failed try),waitingConfirmation, thencompletedorexpired). - Organization-wide reads (
?includeMembers=trueonGET /subscriptions,GET /subscriptions/:uuidandGET /bills/:uuid): the organization's space (the team, the organization's keys withoutOn-Behalf-Of) reads its members' subscriptions too, in its mode only, each with itsuserUuid: removed members' too, and the personal space an admin kept from before their promotion (as Payments and transactions). From a member's space →400validation_failedonincludeMembers. Read only:PATCH, the actions andGET /subscriptions/:uuid/billsstay in the request's space (a member's subscription:On-Behalf-Ofthat member). - One member's subscriptions (
?userUuid=<member>onGET /subscriptions): that member's only, read from the organization's space by owners and admins, with the rules of Payments and transactions § Spaces (One member's rows).
Reads & shape
GET /subscriptions/:uuidand every item of the list are aSubscription, as everysubscription.*webhook carries it (Webhook events).userUuidis present for a member's subscription and omitted for the organization's own. No organization is named.statusis one oftrial,trialExpired,active,pastDue,paused,stopped,cancelled.actionRequiredsays what its customer must do (topUpAllowance,raiseMaximum;confirmTrialon atrialExpiredone), absent when nothing.stoppedis a subscription cancelled during its paid period (by its customer, or by its merchant withimmediate=false): never billed again, it keeps running untilnextBillingAt, then becomescancelled.paused: its customer paused it; it isn't billed until they resume it. Every status change sendssubscription.statusChanged, every change ofactionRequiredsubscription.actionRequiredChanged.- Access: grant it while now <
accessUntil, whatever the status: Subscriptions § Access.accessUntilis the paid-through date (currentPeriodEnd), plus 1 hour for anactivesubscription, whose renewal is billed within minutes of its due date (§ Billing): a subscriber whose bill is on its way keeps access, a failed bill makes itpastDue(accessUntil=currentPeriodEnd, past). Both are present whenever the subscription has a paid-through date. - The instants:
lastBilledAt(absent before its first bill: a trial),nextBillingAt(a trial: when it ends; stopped: when it is cancelled; absent once cancelled),cancellableAt(the earliest its customer may cancel it, the checkout'sminPeriods; absent when any time),cancelledAt. RFC 3339, UTC. cancellationReason(onstoppedandcancelledsubscriptions):customer,merchant,billingFailed,trialNotConverted,merchantClosedorinactiveOnChain; a stopped subscription keeps the reason it stopped for once cancelled. Absent while it runs.priceUsdis the price the customer subscribed at, never"0": locked at signup, a free trial's too (subscriptions recorded before every path locked it took their product's price, what their next bill would have charged).fees: its checkout's lines, locked with it ([]without any).currency: the token billed, the currency object of naming.subscriptionHash(the on-chain creation's transaction) is absent until it is on-chain (a trial);from(the subscriber's wallet) andproductUuidare absent when unknown.allowance(absent during a free trial: nothing signed yet) is what the subscriber's wallet still lets QBitFlow bill, in the subscription's token:amount/amountMinUnits: what remains of the allowance on-chain;amountUsd: its value at today's price;remainingPeriods: how many whole bills it still covers at today's price (priceUsdand itsfeesconverted to the token, before each bill's network fee), rounded down. The checkout's "N payments" kept room for the network fees, so this count is usually higher: the checkout's figure is an estimate, the least the customer is covered for.0is a value: the customer must top it up before the next bill (actionRequired: "topUpAllowance"comes with a bill that leaves at most about one);maxPerPeriod/maxPerPeriodMinUnits: the most one bill may charge, as the customer signed it; a bill above it waits for a new maximum (raiseMaximum). Absent when none was recorded.- Without a price QBitFlow can trust right now (errors
network_unavailable),amountUsdandremainingPeriodsare absent; the read never fails for it.
managementPageLink: the subscription's page for its customer, where they top up its allowance, raise its maximum, pause, resume or cancel it, and confirm a trial. On every read and in everysubscription.*webhook. Share it with them (an email, the merchant's account page).metadata: the merchant's own key-value data (naming): its checkout session's, copied when the subscription was created (a trial's too, kept when it is confirmed), then edited withPATCH.{}when none. Only for the merchant (API reads and webhooks); bills don't carry it.digestDataand the subscription's stored allowance and maximum in min units are never shown; the product, organization and user relations are absent.
PATCH /subscriptions/:uuid
- The only thing a merchant edits on a subscription: its
metadata. Its price, terms and status are locked (a price change: cancel withimmediate=falseand send a new checkout; § Cancel). Other body fields are ignored. - Merge:
metadataabsent → unchanged; a key with a value sets it; a key with""deletes it; keys not named are kept. There is no "replace everything": send each key with""to clear them. The limits are checked on the merged result: at most 50 keys, keys of 1–40 characters ofA–Z a–z 0–9 _ - . :, values of at most 500 characters without control characters (validators.mdmetadata); otherwise400validation_failedonmetadataormetadata.<key>, and nothing changes. - Two updates at once never lose each other's keys (each reads the stored metadata, its row locked).
- Answers
200with theSubscriptionas written. The nextsubscription.*webhooks carry the new metadata (no event is sent for the edit itself).
Cancel (POST /subscriptions/:uuid/cancel)
?immediate=true|falselets a merchant cancel a subscription without its customer signing. QBitFlow's operator cancels it on-chain now in both cases, so nothing can bill it anymore:immediate=true(the default): cancelled at once (status: "cancelled",cancellationReason: "merchant").immediate=false: stopped now (status: "stopped", never billed again, the customer keeps what they paid for), then cancelled at the end of its paid period (nextBillingAt) by its bill: the normal cancellation process, as when the customer cancels.immediateis a boolean (true/false,1/0); another value →400validation_failedonimmediate.
- Its answer is the subscription, as the cancellation left it (the
Subscriptionabove), with:200once the cancellation is done:cancelled, orstoppedwithimmediate=false. A trial, or a subscription stopped already, ends at once (nothing to send on-chain);202while the on-chain force cancel still confirms (QBitFlow waits up to 15 seconds): the status is unchanged yet, andsubscription.statusChangedtells the end. The cancellation goes on whatever happens to the request.
- The subscription is looked up in the caller's space first: another space's →
404whatever its state, never409. A cancelled one of the caller's space →409subscription_already_stopped_or_inactive(cancelled already: nothing to cancel). - A subscription stopped already:
immediate=false→409subscription_already_stopped_or_inactive;immediate=truecancels it at once (off-chain: it was cancelled on-chain when it stopped), keeping the reason it stopped for. - A free trial (
trial,trialExpired: nothing on-chain, no paid period) is cancelled off-chain at once, without a transaction;immediate=falseon a trial →400validation_failedonimmediate. - If the on-chain force cancel fails, the subscription is cancelled (or stopped) off-chain all the same: it is never billed again either way. Unless the contract has it inactive already, QBitFlow then sends the on-chain force cancel again (every hour) until it lands; its status doesn't change again (no new event).
immediate=falseon a subscription whose bill is due already (pastDue, or waiting for a new maximum) stops it, and its waiting bill cancels it at once.- Each change is announced once:
subscription.statusChangedtocancelled(itsallowanceamount"0",cancelledAt,cancellationReason, nonextBillingAt), or tostoppedthen, at the period's end, tocancelled. A charge already on its way that lands after the cancellation is recorded (subscription.billed, itssubscription.statuscancelled); the subscription stays cancelled. - A cancellation already running for the subscription →
409tx_already_sent. Once it is done, it can be asked again:immediate=false, thenimmediate=true, cancels it at once. - Changing a subscriber's price isn't possible on an existing subscription (its price is locked at signup and the customer signed its maximum): cancel it with
immediate=false(they keep the period they paid for), and send them a new checkout at the new price.
Test bill (POST /subscriptions/:uuid/test-bill)
- The test bill: live subscriptions are billed on their due date; test subscriptions are billed only from here, so integrators bill at their own pace. It runs the same bill as live billing (§ Billing), so statuses and webhooks are exactly live's. A live subscription →
400test_mode_only(every server). - Not due yet (
nextBillingAtin the future) →409payment_not_due, and nothing runs. - When due, it starts the bill of the subscription's
nextBillingAt. If that bill is waiting already (in dunning after a failed attempt, or waiting for a new maximum), it runs the next attempt now instead of waiting (it spends an attempt). The retry schedule still counts from the first failure, and the attempt after it comes at least a day later (§ Billing). - It answers
200with aTestBillonce the attempt settled (at most about 2 minutes; later,stageis stillrunning):billUuid: the bill,sub-hist@…: a paid bill has this id (GET /bills/:uuid,subscription.billed).stage:done,waiting(for its next attempt, or for the end of a trial's grace period),pending(for the customer's new maximum) orrunning.outcome(oncedone; absent before):paid,cancelled(stopped by its subscriber, its trial over, its merchant closed, inactive on-chain, or its last attempt failed),skipped(below the minimum amount: the period moved on without a charge),pausedornotDue.attempts: the bill's failed attempts.nextAttemptAt: whilewaitingorpending, when it tries again at the latest.txHash: the paid charge's transaction.failureCode: the last failure's code (errors), e.g.insufficient_allowance. Each absent without a value.
- A bill that is
doneanswers its end again. A cancelled subscription →409subscription_already_stopped_or_inactive(nothing left to bill); another space's →404.
Billing
QBitFlow bills each subscription from its subscriber's wallet on its nextBillingAt. No funds are held in advance. Each period's bill is charged once: it has its own uuid, and the paid bill has that uuid (sub-hist@<billUuid>).
- When. A live subscription is billed within about 10 minutes of its
nextBillingAt(a sweep every 10 minutes, then the chain confirms the charge), never before it (the contracts refuse an early bill). Its subscriber keeps access meanwhile (accessUntil). A test subscription is billed only when its merchant asks (§ Test bill), with the same statuses and webhooks. - How much. Every bill charges
priceUsdplus the subscription'sfeeslines: the price the customer subscribed at (the checkout's, or the upgrade's for a trial confirmed later) and what its checkout added to it (a tax, shipping, the processing fee: Checkout sessions § Fees), both locked at signup, converted to the token at the bill's day price. The bill'samountUsdis that sum, itsamountthe token amount charged. Changing the product's price applies to new subscribers only (Products). The amount a bill charges never exceeds the maximum per period the subscriber signed (allowance.maxPerPeriod): the contract refuses it, and the bill waits for a new maximum (below). - Paid. The bill is recorded with its fees,
nextBillingAtmoves to the next period, andsubscription.billedis sent:{ "bill": Bill, "subscription": Subscription }, the bill with the period paid (periodStart→periodEnd), the subscription as the bill leaves it. The status goes back toactive; when the remaining allowance is under 1.05 periods,actionRequiredistopUpAllowance. Astoppedsubscription staysstopped. - The customer's wallet can't pay (balance, allowance or approval too low): the subscription becomes
pastDue, andsubscription.billingFailedis sent withattemptandremainingAttempts. The bill is retried 1, 3, 5 and 7 days after its first failed attempt (5 attempts in all), and never less than a day after the previous attempt. The customer raising their allowance (or their maximum) retries it at once; that extra try spends no attempt and sends nothing if it still fails. The subscription stayspastDueuntil the bill is paid. WhenremainingAttemptsis0the subscription is stillpastDuein that event, and its cancellation follows (subscription.statusChangedtocancelled). - Above the customer's maximum (the token's price moved): the subscription keeps its status,
actionRequiredbecomesraiseMaximum(subscription.actionRequiredChanged), andsubscription.billingFailedis sent once withreason: maxAmountExceeded. The bill waits up to 7 days for the customer to approve a new maximum, without spending an attempt. Approving a new maximum retries it at once. After 7 days, the retries above start. - Where a failing bill stands, on a read (
dunning, every subscription read; absent while no bill is failing and once the subscription is cancelled):failedAttemptsandremainingAttempts(after the last one fails, the subscription is cancelled);failingSince: its first failed attempt;nextAttemptAt: when it is tried again at the latest (sooner if the customer tops up or raises their maximum);- while it waits for a new maximum:
awaitingMaximumSince, andnextAttemptAtis when the wait ends (failedAttemptsis0if no attempt failed before). Webhooks don't carry it (subscription.billingFailedtells each failure).
- No longer active on-chain (
subscription_not_active): the subscription is cancelled. - QBitFlow's own failures (a network or a price unavailable, a chain whose clock is behind, a refusal of QBitFlow's own signature or key): retried every 30 minutes. No attempt is spent, no event or email is sent, the status doesn't change, the subscription is never cancelled for them.
- Stopped (by its subscriber, or its merchant with
immediate=false): cancelled when its last paid period ends, keeping itscancellationReason. Free trial:trialExpiredwhen the trial ends, cancelled 7 days later unless the customer confirmed it (trialNotConverted). The customer is emailed when it ends, with the link to confirm it (settingemails.customerTrialEnded). Confirmed, the trial is the paid subscription: the sameuuid,reference,metadataandcreatedAt, nowactiveand billed (subscription.statusChanged, thensubscription.billed; Hosted subscriptions). Merchant closed (the member removed, the organization closed): its subscriptions are cancelled, never charged again. Below the minimum amount: no charge, the next period starts. - Customer emails: each failed attempt emails the customer (why, when it is retried or that the subscription is cancelled, and the management page link); setting
emails.customerBillingFailed. Each paid bill emails the customer its receipt (settingemails.customerReceipts). Test mode never emails; both are sent in the merchant's name.
The list
GET /subscriptionsis paginated (pagination:limit20 by default, at most 100, clamped; the opaquecursorof the previous page'snextCursor): the space's subscriptions, cancelled ones included, newest first bycreatedAtthenuuid. A cursor that isn't anextCursor→400validation_failedoncursor.- Filters (each optional, combined with AND):
status(anystatusvalue; another value →400onstatus, which lists the values),reference(the merchant's reference: 0 or 1 item;referencerules, at most 100),customerUuid,productUuid,createdAfter,createdBefore(RFC 3339 instants or dates, both excluded),includeMembers,userUuid(above). A malformed value →400validation_failedon its name. The formerGET …/reference/:type/:referenceroute is gone: filter the list. - Items are
Subscriptions, asGET /subscriptions/:uuidreturns them.
Bills
GET /subscriptions/:uuid/billslists every bill of one of the caller's subscriptions (a cancelled one's too), paginated (pagination), newest first bycreatedAtthenuuid. A subscription not billed yet →{"items": [], "hasMore": false}.GET /bills/:uuidreads one bill of the caller's space (§ Ids);?includeMembers=truefrom the organization's space reads a member's (read only).- A
Billis what its customer paid for one period:amount/amountMinUnits(the token charged) andamountUsd,currency,productName/productDescription(as they were when it was paid),subscriptionUuid(sub@…),txHashand where to check it, itssettlement(the fees and split, merchant only),paid*andconfirmedAt, itscustomerand itsrefundstate (Payments and transactions § Who paid, § Refund state, § When). Bills carry no merchantmetadata: read the subscription's. - A bill keeps the period it paid:
periodStart(its due date; the subscription's start for its first bill) →periodEnd(its next bill: paid until then), assubscription.billedsays. Absent on bills recorded before (their period can't be recovered).
The codes named here
- 400validation_
failed path/query/body failed binding or validation, or a business rule about one input (a field named in
errors) - 403policy_
disabled a member doing what their organization's policy doesn't allow them, reads of what the policy governs included
- 409tx_
already_ sent a transaction sent already: running, or completed (Payments and transactions § Sending a signed transaction)
- 405method_
not_ allowed the path exists, not with this method; the
Allowheader lists its methods. Answered before any credential is read, like the404of a path no route has - 400test_
mode_ only something only test mode does asked in live mode (a test bill, a faucet, a test payment)
- 409subscription_
already_ stopped_ or_ inactive cancelling a subscription, raising its allowance or its maximum, once it is
stopped(it ends with its paid period; customer-side routes), a merchant'sPOST /of a stopped one, or a merchant'ssubscriptions/ :uuid/ cancel?immediate=false POST /orsubscriptions/ :uuid/ cancel /test-billof a cancelled one - 422insufficient_
allowance granted QBitFlow less than this amount: increase the allowance
- 409subscription_
not_ active asked for what the subscription's state refuses
- 409payment_
not_ due asked for what the subscription's state refuses
- 503network_
unavailable can't act: QBitFlow can't send on the network for now, or has no fresh USD price for the currency (missing, older than 5 minutes, or a stablecoin off its peg by more than 5 %), retry later