API reference

Webhook events

What QBitFlow posts to your endpoints: every event, its body, its signature and its retries.

A payment confirmed, a subscription billed, a refund answered, a seller joined: QBitFlow posts an event to your endpoints (POST /webhooks/endpoints), each receiving the types it lists, or every type when it lists none. 15 types today.

payment.completed· the body your endpoint receives

{  "createdAt": "2026-10-01T12:00:00Z",  "data": {    "amount": "10",    "reference": "order-1042",    "uuid": "pay@01a0f755-f200-7000-8000-000000000001",    … 25 more fields  },  "id": "evt_82c1e15d-c0d9-584f-a65a-fb4099925273",  "test": false,  "type": "payment.completed",  "version": "v2"}
The contract's example (webhooks.md § Examples)
  1. data: Depends on the type: each event below gives its fields and an example.
  2. id: The same for every endpoint and every retry: deduplicate on it.
  3. test: Whether it happened in test mode.
  4. type: What happened. Answer 2xx to the types you don't handle.

Avoid

Act on the redirect

Reaching your success page proves nothing.

Do

Act on the event

Verify it, deduplicate on its id, answer 2xx fast.

Who receives an event

  • An event happens in one space and mode (spaces). It goes to that space's endpoints that list its type (or list none: every type).
  • A member's events also go to the organization's endpoints with includeMembers: true (same mode), naming the member in the envelope's userUuid. A new organization endpoint includes them by default: a marketplace receives every event of each seller's space, payment.completed, every subscription.* (subscription.actionRequiredChanged included), refund.*, heldFunds.released (the organization triggers a release, the event is the seller's) and checkout.expired, on its own endpoint, with nothing to set up per seller; create it with includeMembers: false to receive the organization's own events only. member.joined and member.removed are the organization's own events: its endpoints get them with or without includeMembers.
  • Test and live never mix: a test-mode event only reaches test-mode endpoints.
  • A member's own endpoints receive nothing while the organization's members.webhooks policy is off for them: each event is logged as skipped (skipped: true in the delivery log), never retried, and not counted against the endpoint's health (Webhooks § Access). Turned back on, the next events are delivered.

The envelope

The envelope

{  "id": "evt_0192f1c2-…",  "type": "payment.completed",  "version": "v2",  "createdAt": "2026-10-01T12:00:00.123Z",  "test": false,  "userUuid": "0192f1c2-7b3a-7c4d-9e5f-6a7b8c9d0e1f",  "data": {}}
Its data depends on the type.
  • idstring

    evt_…: the same for every endpoint and every retry of the event, and if it is ever published again (deduplicate on it)

  • typestring

    One ofpayment.completedsubscription.createdsubscription.billedsubscription.statusChangedsubscription.actionRequiredChangedsubscription.billingFailedsubscription.upcomingBillrefund.requestedrefund.approvedrefund.rejectedmember.joinedmember.removedheldFunds.releasedcheckout.expired

    • webhook.testWebhookTest is the event POST /webhooks/endpoints/:uuid/test sends; no endpoint subscribes to it.
  • versionstring

    v2

    One of:

    • v2V2 is the event envelope: {id, type, version, createdAt, test, userUuid, data}. The only version today: the field exists so that a future payload change is a new version an endpoint opts into.
  • createdAtstring
  • testboolean

    Whether it happened in test mode

  • userUuidstringOptional

    The member whose space it happened in; omitted for the organization's own

  • dataany JSON value

    Depends on the type (GET /webhooks/event-types)

Events

payment.completed#

What it carries, and when

CompletedPayment: the payment as GET /payments/:uuid returns it to its owner (amount + amountMinUnits in the token, amountUsd, priceUsd, fees, productName, productDescription, currency, settlement, paid*, metadata copied from its checkout, chain, explorerUrl: its transaction on the chain's explorer), and its customer page managementPageLink. The refund state (refund, refundable) is API reads only

What to do · One-time payment

Fulfil the order. data.uuid is the checkout session's pay@… (from POST /checkout-sessions/payment), data.reference your reference, data.metadata what you put on the checkout, data.customerUuid the customer.

34 fields of data (CompletedPayment)
  • uuidstring

    allow read and create, (generated on creation of the payment link)

    A prefixed id (pay@…, sub@…)

  • createdAtstring
  • fromstring
  • tostring
  • amountstring

    The amount in the currency's units, exact, a decimal string ("10" for 10 USDC): amountMinUnits / 10^decimals

  • amountMinUnitsstring

    The amount in the currency's smallest unit, a string of digits ("10000000" for 10 USDC, 6 decimals)

  • amountUsdstring

    What 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

  • currencyIdinteger

    The currency paid in, written as the currency object under currency (GET /currencies/:id)

  • txHashstring

    Hash of the transaction in the blockchain

  • chainstringOptional

    The chain it was paid on: ETH (Ethereum), BASE (Base) or SOL (Solana), the testnet's for a test-mode transfer (currency.test)

  • explorerUrlstringOptional

    Its transaction on the chain's block explorer (the testnet's in test mode). Omitted without a txHash

  • testboolean

    Whether this is test data (testnets) or live data

  • userUuidstringOptional

    Member the resource belongs to; omitted for the organization's own resources

    Only for the resource's owner

  • referencestringOptional

    The merchant's reference for the payment (an order or invoice 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

  • priceUsdstringOptional

    PriceUSD is the product's price in USD, as the checkout had it, a decimal string, always present: amountUsd is the price plus the fees' lines. (A payment recorded before checkouts had fees has none stored: its price is its amount, Complete.)

  • feesarray of FeeLine

    What the checkout added to the price, line by line, as the customer saw them (a tax, shipping, the processing fee when the customer paid it); [] without any

    4 fields of each FeeLine
    • typestring

      custom (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 fee
      • processingFeeQBitFlow's fee, which the merchant has the customer pay (computed by QBitFlow)
    • labelstring

      The line's name, as the checkout shows it ("Processing fee" for the processing fee)

    • descriptionstringOptional

      Optional: more about the line, as the merchant wrote it. Omitted without one

    • amountUsdstring

      The line's amount in USD, a decimal string with at most 2 decimals

  • productNamestring

    What was paid for: the checkout's product name, as it was when the checkout was created (the product's, or the inline one). Later product edits don't change it

  • productDescriptionstringOptional

    Same, for the product's description; absent without one

  • metadatamapOptional

    Metadata is the merchant's own data, copied from the checkout session that created the payment (its metadata), read-only. {} without any. Only for the merchant

    Only for the resource's owner

  • productUuidstringOptional

    The product paid for; omitted when the payment has none (an inline product)

  • customerUuidstringOptional

    The 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

  • customerReferencestringOptional

    The 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

  • notestringOptional

    A qbf.cash tipper's message to the handle's owner (only for them); omitted without one

    Only for the resource's owner

  • customerSummaryOptional

    The customer, named (merchant only, API reads): who paid, even once deleted. Omitted without one

    Only for the resource's owner

    6 fields of Summary
    • uuidstring

      The customer (GET /customers/:uuid)

    • firstNamestring

      First name

    • lastNamestringOptional

      Last name, when given

    • emailstring

      Email, lowercase

    • referencestringOptional

      The merchant's reference of the customer, when it has one

    • deletedboolean

      The merchant deleted the customer: it still names its rows, but GET /customers/:uuid answers 404

  • settlementSettlementOptional

    How it settled: its fees, its split and its transaction (only for the merchant)

    Only for the resource's owner

    5 fields of Settlement
    • feePercentnumber

      QBitFlow's fee on this payment, in bps (1 % = 100 bps), deducted from the amount paid. Written as feePercent, a percent string

    • organizationFeeOrganizationFeeOptional

      The 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 OrganizationFee
      • addressstringOptional

        The organization's wallet that receives the fee (only for the merchant)

        Only for the resource's owner

      • feePercentnumber

        The organization's fee, in bps, taken from what remains after the platform fee. Written as feePercent, a percent string

    • referralFeeReferralFeeOptional

      The referrer's share of QBitFlow's fee. Absent: none

      2 fields of ReferralFee
      • addressstringOptional

        The referrer's wallet that receives the fee (only for the merchant)

        Only for the resource's owner

      • feePercentnumber

        The referrer's share of the platform fee, in bps (2000 = 20 % of it). Written as feePercent, a percent string

    • splitSplitOptional

      How 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 Split
      • platformTokenAmount

        QBitFlow's fee (the referrer's share not included)

        3 fields of TokenAmount
        • amountstring

          In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

        • amountMinUnitsstring

          The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

        • amountUsdstringOptional

          Its value in USD, a decimal string; absent when no price was known

      • organizationTokenAmountOptional

        The organization's fee; absent without one

        3 fields of TokenAmount
        • amountstring

          In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

        • amountMinUnitsstring

          The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

        • amountUsdstringOptional

          Its value in USD, a decimal string; absent when no price was known

      • referralTokenAmountOptional

        The referrer's share of QBitFlow's fee; absent without one

        3 fields of TokenAmount
        • amountstring

          In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

        • amountMinUnitsstring

          The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

        • amountUsdstringOptional

          Its value in USD, a decimal string; absent when no price was known

      • merchantTokenAmount

        What the merchant received (the member, for a member's payment), net of every fee

        3 fields of TokenAmount
        • amountstring

          In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

        • amountMinUnitsstring

          The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

        • amountUsdstringOptional

          Its value in USD, a decimal string; absent when no price was known

      • networkFeeTokenAmountOptional

        NetworkFee 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 TokenAmount
        • amountstring

          In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

        • amountMinUnitsstring

          The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

        • amountUsdstringOptional

          Its value in USD, a decimal string; absent when no price was known

    • transactionSettlementTransactionOptional

      The transaction that paid it. Absent before its confirmation

      6 fields of SettlementTransaction
      • blockNumberstring

        The block (Ethereum, Base) or slot (Solana) it landed in, a string of digits

      • blockTimestringOptional

        When 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

      • gasUsedintegerOptional

        The gas it consumed (Ethereum, Base). Absent on Solana

      • feeTokenAmount

        What 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 TokenAmount
        • amountstring

          In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

        • amountMinUnitsstring

          The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

        • amountUsdstringOptional

          Its value in USD, a decimal string; absent when no price was known

      • l1FeeTokenAmountOptional

        The 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 TokenAmount
        • amountstring

          In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

        • amountMinUnitsstring

          The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

        • amountUsdstringOptional

          Its value in USD, a decimal string; absent when no price was known

      • nativePriceUsdstringOptional

        NativePriceUSD 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

  • confirmedAtstringOptional

    When the chain confirmed it: its block's time (settlement.transaction.blockTime). Absent only on records made before it was recorded

  • paidstringOptional

    Everything 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

  • paidMinUnitsstringOptional

    The same in the currency's smallest unit, a string of digits. Only for the merchant, like paid

    Only for the resource's owner

  • paidUsdstringOptional

    The same in USD, at the payment's time, a decimal string. Only for the merchant, like paid

    Only for the resource's owner

  • refundRefundSummaryOptional

    Its 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 RefundSummary
    • uuidstring

      The refund (refund@…)

    • statusstring

      pending, approved or rejected

      One ofpendingapprovedrejected

      • failedDeclared, never set: an approval that fails leaves the refund pending, to approve again
    • initiatedBystring

      customer (a request) or merchant

    • refundPercentnumber

      The share of what the customer paid it sends back, a percent string ("100": all of it). Written as refundPercent

    • amountstringOptional

      What 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)

    • amountMinUnitsstring

      The same in the currency's smallest unit (a string of digits)

    • amountUsdstringOptional

      The same in USD, at the payment's time (a decimal string). Absent when unknown (a refund requested before it was recorded)

    • createdAtstring

      When it was requested or started

    • respondedAtstringOptional

      When it was approved (confirmed) or rejected; absent while pending

  • refundablebooleanOptional

    Whether it can be refunded now

    Only for the resource's owner

  • notRefundableReasonstringOptional

    Why not: refundExists or heldFundsReleased. Omitted when refundable

    Only for the resource's owner

  • checkoutOpenedAtstringOptional

    When its checkout session was created (the payment's uuid is the session's). Omitted for a payment whose uuid doesn't say (made before UUIDv7s)

payment.completed: example body

{  "createdAt": "2026-10-01T12:00:00Z",  "data": {    "amount": "10",    "amountMinUnits": "10000000",    "amountUsd": "10",    "chain": "BASE",    "checkoutOpenedAt": "2026-10-01T12:00:00Z",    "confirmedAt": "2026-10-01T11:59:58Z",    "createdAt": "2026-10-01T12:00:00Z",    "currency": {      "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",      "chain": "BASE",      "code": "BASE:USDC",      "decimals": 6,      "id": 8,      "mainCurrencyId": 3,      "name": "USD Coin",      "symbol": "USDC",      "test": false    },    "customerReference": "crm-5521",    "customerUuid": "01a05cd7-2a00-7d00-8000-0000000000d1",    "explorerUrl": "https://basescan.org/tx/0x1111111111111111111111111111111111111111111111111111111111111111",    "fees": [],    "from": "0xabcdef1234567890abcdef1234567890abcdef12",    "managementPageLink": "https://qbitflow.app/user/019cadfd-8900-7a00-8000-0000000000a1/manage?uuid=pay@01a0f755-f200-7000-8000-000000000001",    "metadata": {      "crm:customer": "C-5521",      "orderId": "order-1042"    },    "paid": "10.0042",    "paidMinUnits": "10004200",    "paidUsd": "10.0042",    "priceUsd": "10",    "productDescription": "Lifetime access to the Pro plan",    "productName": "Pro plan (lifetime)",    "productUuid": "019cadfd-8900-7c00-8000-0000000000c1",    "reference": "order-1042",    "settlement": {      "feePercent": "0.99",      "split": {        "merchant": {          "amount": "9.901",          "amountMinUnits": "9901000",          "amountUsd": "9.901"        },        "networkFee": {          "amount": "0.0042",          "amountMinUnits": "4200",          "amountUsd": "0.0042"        },        "platform": {          "amount": "0.099",          "amountMinUnits": "99000",          "amountUsd": "0.099"        }      },      "transaction": {        "blockNumber": "52468103",        "blockTime": "2026-10-01T11:59:58Z",        "fee": {          "amount": "0.00000114",          "amountMinUnits": "1140000000000",          "amountUsd": "0.005144"        },        "gasUsed": 95000,        "nativePriceUsd": "4512.37"      }    },    "test": false,    "to": "0x1234567890abcdef1234567890abcdef12345678",    "txHash": "0x1111111111111111111111111111111111111111111111111111111111111111",    "uuid": "pay@01a0f755-f200-7000-8000-000000000001"  },  "id": "evt_82c1e15d-c0d9-584f-a65a-fb4099925273",  "test": false,  "type": "payment.completed",  "version": "v2"}
Sent as one line, its keys sorted.
Show all 80 lines

subscription.created#

What it carries, and when

Subscription, as GET /subscriptions/:uuid returns it (its managementPageLink, metadata and allowance included). For a checkout without a trial it is sent once the customer's on-chain transaction is confirmed, with status: "active" (and actionRequired: "topUpAllowance" when the allowance signed covers only that first period: no subscription.actionRequiredChanged at creation, this event tells it) and its first subscription.billed right after; a free trial starts one too, once its customer signed the trial's message (status: "trial", nothing paid yet, no allowance). Sent once per subscription: a trial confirmed later stays the same subscription and sends subscription.statusChanged (trial or trialExpired → active) then its first subscription.billed, never a second subscription.created. The whole timeline: Subscriptions

What to do · Subscription start

status: "active": grant access until currentPeriodEnd (if actionRequired is set, ask the customer to act); store data.uuid (sub@…, the session's id) and managementPageLink. "trial": grant trial access. Its first subscription.billed follows (not for a trial). Subscriptions

29 fields of data (Subscription)
  • uuidstring

    Its id (sub@…): the checkout session's

    A prefixed id (pay@…, sub@…)

  • referencestringOptional

    The 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

  • createdAtstring
  • updatedAtstring

    Its last change (a bill, a status, its metadata)

  • fromstring

    The subscriber's wallet: the one it bills. Absent when unknown (a trial not confirmed yet may have none)

  • tostring

    The wallet its bills pay: the merchant's (or, while the organization holds a member's funds, the organization's)

  • productUuidstring

    Its product (GET /products/:uuid). Absent without one

  • subscriptionHashstring

    The hash of the transaction that created the subscription on-chain. Absent until then (a trial, until its customer confirms it)

  • currencyIdinteger

    The currency (token) billed, written as the currency object under currency (GET /currencies/:id)

  • frequencyDuration

    Billing 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 Duration
    • valueinteger

      Number of units; 0 means no duration

    • unitstring

      Required when value > 0. "months" = 30 days, "years" = 365 days

      oneof=seconds minutes hours days weeks months years

  • statusstring

    SubscriptionStatus (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.
  • actionRequiredstringOptional

    ActionRequired 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.
  • currentPeriodEndstringOptional

    CurrentPeriodEnd 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

  • accessUntilstringOptional

    AccessUntil 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

  • priceUsdstring

    PriceUSD 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 FeeLine

    Fees 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 FeeLine
    • typestring

      custom (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 fee
      • processingFeeQBitFlow's fee, which the merchant has the customer pay (computed by QBitFlow)
    • labelstring

      The line's name, as the checkout shows it ("Processing fee" for the processing fee)

    • descriptionstringOptional

      Optional: more about the line, as the merchant wrote it. Omitted without one

    • amountUsdstring

      The line's amount in USD, a decimal string with at most 2 decimals

  • allowanceAllowanceOptional

    AllowanceStatus (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 Allowance
    • amountstring

      In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

    • amountMinUnitsstring

      The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

    • amountUsdstringOptional

      Its value in USD, a decimal string; absent when no price was known

    • remainingPeriodsintegerOptional

      RemainingPeriods 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

    • maxPerPeriodstringOptional

      MaxPerPeriod 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
  • lastBilledAtstringOptional

    When it was last billed. Absent before its first bill (a trial)

  • nextBillingAtstringOptional

    When its next bill is due (a trial: when it ends; stopped: when it is cancelled). Absent once cancelled

  • cancelledAtstringOptional

    When it was cancelled (absent until then, and for subscriptions cancelled before it was recorded)

  • cancellationReasonstringOptional

    Why 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).
  • cancellableAtstringOptional

    MinimumCancellationDate (cancellableAt) is the earliest its customer may cancel it (the checkout's minPeriods). Absent when they may cancel it any time

  • metadatamapOptional

    Metadata 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

  • testboolean

    Whether this is test data (testnets) or live data

  • userUuidstringOptional

    Member the resource belongs to; omitted for the organization's own resources

    Only for the resource's owner

  • customerUuidstringOptional

    The 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

  • customerReferencestringOptional

    The 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

  • customerSummaryOptional

    The customer, named (merchant only, API reads): who paid, even once deleted. Omitted without one

    Only for the resource's owner

    6 fields of Summary
    • uuidstring

      The customer (GET /customers/:uuid)

    • firstNamestring

      First name

    • lastNamestringOptional

      Last name, when given

    • emailstring

      Email, lowercase

    • referencestringOptional

      The merchant's reference of the customer, when it has one

    • deletedboolean

      The merchant deleted the customer: it still names its rows, but GET /customers/:uuid answers 404

subscription.created: example body

{  "createdAt": "2026-10-01T12:00:00Z",  "data": {    "accessUntil": "2026-10-31T13:00:00Z",    "allowance": {      "amount": "110",      "amountMinUnits": "110000000",      "amountUsd": "110",      "maxPerPeriod": "30",      "maxPerPeriodMinUnits": "30000000",      "remainingPeriods": 11    },    "createdAt": "2026-10-01T12:00:00Z",    "currency": {      "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",      "chain": "BASE",      "code": "BASE:USDC",      "decimals": 6,      "id": 8,      "mainCurrencyId": 3,      "name": "USD Coin",      "symbol": "USDC",      "test": false    },    "currentPeriodEnd": "2026-10-31T12:00:00Z",    "customerReference": "crm-5521",    "customerUuid": "01a05cd7-2a00-7d00-8000-0000000000d1",    "fees": [],    "frequency": {      "unit": "months",      "value": 1    },    "from": "0xabcdef1234567890abcdef1234567890abcdef12",    "lastBilledAt": "2026-10-01T12:00:00Z",    "managementPageLink": "https://qbitflow.app/user/019cadfd-8900-7a00-8000-0000000000a1/manage?uuid=sub@01a0f755-c320-7000-8000-000000000002",    "metadata": {      "crm:account": "A-1042",      "plan": "pro"    },    "nextBillingAt": "2026-10-31T12:00:00Z",    "priceUsd": "10",    "productUuid": "019cadfd-8900-7c00-8000-0000000000c1",    "reference": "customer-7731",    "status": "active",    "subscriptionHash": "0x2222222222222222222222222222222222222222222222222222222222222222",    "test": false,    "to": "0x1234567890abcdef1234567890abcdef12345678",    "updatedAt": "2026-10-01T12:00:00Z",    "uuid": "sub@01a0f755-c320-7000-8000-000000000002"  },  "id": "evt_4fad5fdd-5eed-5d81-a971-ba2ade1e6c20",  "test": false,  "type": "subscription.created",  "version": "v2"}
Sent as one line, its keys sorted.
Show all 55 lines

subscription.billed#

What it carries, and when

SubscriptionBill: `{ "bill": Bill, "subscription": Subscription }`. bill is the bill paid (as GET /bills/:uuid: its currency, chain, explorerUrl, settlement, and the period it pays, periodStart → periodEnd); subscription is the subscription as the bill leaves it (subscription.status: active, or stopped, paused, cancelled if that happened while the charge was on its way; subscription.nextBillingAt: paid until then; subscription.reference, subscription.metadata). A charge already on its way that lands after the subscription was cancelled is recorded and sent too, with subscription.status: "cancelled" (the subscription stays cancelled: no period is granted; refund it if your policy says so)

What to do · Renewal

Record the payment (data.bill.uuid sub-hist@…, data.subscription.uuid, data.subscription.reference, data.subscription.metadata). Access is paid until data.bill.periodEnd, unless data.subscription.status is cancelled (a charge that landed just after a cancellation: record it, grant nothing, refund if your policy says so).

2 fields of data (SubscriptionBill)
  • billBill

    The bill paid

    30 fields of Bill
    • uuidstring

      allow read and create, (generated on creation of the payment link)

      A prefixed id (pay@…, sub@…)

    • createdAtstring
    • fromstring
    • tostring
    • amountstring

      The amount in the currency's units, exact, a decimal string ("10" for 10 USDC): amountMinUnits / 10^decimals

    • amountMinUnitsstring

      The amount in the currency's smallest unit, a string of digits ("10000000" for 10 USDC, 6 decimals)

    • amountUsdstring

      What 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

    • currencyIdinteger

      The currency paid in, written as the currency object under currency (GET /currencies/:id)

    • txHashstring

      Hash of the transaction in the blockchain

    • chainstringOptional

      The chain it was paid on: ETH (Ethereum), BASE (Base) or SOL (Solana), the testnet's for a test-mode transfer (currency.test)

    • explorerUrlstringOptional

      Its transaction on the chain's block explorer (the testnet's in test mode). Omitted without a txHash

    • testboolean

      Whether this is test data (testnets) or live data

    • userUuidstringOptional

      Member the resource belongs to; omitted for the organization's own resources

      Only for the resource's owner

    • productNamestring

      What 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

    • productDescriptionstring

      Same, for the product's description. Absent without one

    • productUuidstringOptional

      The subscription's product

    • subscriptionUuidstring

      The subscription it bills (sub@…)

    • customerUuidstringOptional

      The 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

    • customerReferencestringOptional

      The 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

    • customerSummaryOptional

      The customer, named (merchant only, API reads): who paid, even once deleted. Omitted without one

      Only for the resource's owner

      6 fields of Summary
      • uuidstring

        The customer (GET /customers/:uuid)

      • firstNamestring

        First name

      • lastNamestringOptional

        Last name, when given

      • emailstring

        Email, lowercase

      • referencestringOptional

        The merchant's reference of the customer, when it has one

      • deletedboolean

        The merchant deleted the customer: it still names its rows, but GET /customers/:uuid answers 404

    • settlementSettlementOptional

      How it settled: its fees, its split and its transaction (only for the merchant)

      Only for the resource's owner

      5 fields of Settlement
      • feePercentnumber

        QBitFlow's fee on this payment, in bps (1 % = 100 bps), deducted from the amount paid. Written as feePercent, a percent string

      • organizationFeeOrganizationFeeOptional

        The 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 OrganizationFee
        • addressstringOptional

          The organization's wallet that receives the fee (only for the merchant)

          Only for the resource's owner

        • feePercentnumber

          The organization's fee, in bps, taken from what remains after the platform fee. Written as feePercent, a percent string

      • referralFeeReferralFeeOptional

        The referrer's share of QBitFlow's fee. Absent: none

        2 fields of ReferralFee
        • addressstringOptional

          The referrer's wallet that receives the fee (only for the merchant)

          Only for the resource's owner

        • feePercentnumber

          The referrer's share of the platform fee, in bps (2000 = 20 % of it). Written as feePercent, a percent string

      • splitSplitOptional

        How 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 Split
        • platformTokenAmount

          QBitFlow's fee (the referrer's share not included)

          3 fields of TokenAmount
          • amountstring

            In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

          • amountMinUnitsstring

            The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

          • amountUsdstringOptional

            Its value in USD, a decimal string; absent when no price was known

        • organizationTokenAmountOptional

          The organization's fee; absent without one

          3 fields of TokenAmount
          • amountstring

            In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

          • amountMinUnitsstring

            The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

          • amountUsdstringOptional

            Its value in USD, a decimal string; absent when no price was known

        • referralTokenAmountOptional

          The referrer's share of QBitFlow's fee; absent without one

          3 fields of TokenAmount
          • amountstring

            In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

          • amountMinUnitsstring

            The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

          • amountUsdstringOptional

            Its value in USD, a decimal string; absent when no price was known

        • merchantTokenAmount

          What the merchant received (the member, for a member's payment), net of every fee

          3 fields of TokenAmount
          • amountstring

            In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

          • amountMinUnitsstring

            The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

          • amountUsdstringOptional

            Its value in USD, a decimal string; absent when no price was known

        • networkFeeTokenAmountOptional

          NetworkFee 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 TokenAmount
          • amountstring

            In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

          • amountMinUnitsstring

            The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

          • amountUsdstringOptional

            Its value in USD, a decimal string; absent when no price was known

      • transactionSettlementTransactionOptional

        The transaction that paid it. Absent before its confirmation

        6 fields of SettlementTransaction
        • blockNumberstring

          The block (Ethereum, Base) or slot (Solana) it landed in, a string of digits

        • blockTimestringOptional

          When 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

        • gasUsedintegerOptional

          The gas it consumed (Ethereum, Base). Absent on Solana

        • feeTokenAmount

          What 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 TokenAmount
          • amountstring

            In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

          • amountMinUnitsstring

            The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

          • amountUsdstringOptional

            Its value in USD, a decimal string; absent when no price was known

        • l1FeeTokenAmountOptional

          The 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 TokenAmount
          • amountstring

            In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

          • amountMinUnitsstring

            The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

          • amountUsdstringOptional

            Its value in USD, a decimal string; absent when no price was known

        • nativePriceUsdstringOptional

          NativePriceUSD 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

    • periodStartstringOptional

      The 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

    • periodEndstringOptional

      The end of the period the bill paid: the subscription's next bill. Paid until then. Omitted on bills recorded before it was

    • confirmedAtstringOptional

      When the chain confirmed it: its block's time (settlement.transaction.blockTime). Absent only on records made before it was recorded

    • paidstringOptional

      Everything 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

    • paidMinUnitsstringOptional

      The same in the currency's smallest unit, a string of digits. Only for the merchant, like paid

      Only for the resource's owner

    • paidUsdstringOptional

      The same in USD, at the payment's time, a decimal string. Only for the merchant, like paid

      Only for the resource's owner

    • refundRefundSummaryOptional

      Its 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 RefundSummary
      • uuidstring

        The refund (refund@…)

      • statusstring

        pending, approved or rejected

        One ofpendingapprovedrejected

        • failedDeclared, never set: an approval that fails leaves the refund pending, to approve again
      • initiatedBystring

        customer (a request) or merchant

      • refundPercentnumber

        The share of what the customer paid it sends back, a percent string ("100": all of it). Written as refundPercent

      • amountstringOptional

        What 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)

      • amountMinUnitsstring

        The same in the currency's smallest unit (a string of digits)

      • amountUsdstringOptional

        The same in USD, at the payment's time (a decimal string). Absent when unknown (a refund requested before it was recorded)

      • createdAtstring

        When it was requested or started

      • respondedAtstringOptional

        When it was approved (confirmed) or rejected; absent while pending

    • refundablebooleanOptional

      Whether it can be refunded now

      Only for the resource's owner

    • notRefundableReasonstringOptional

      Why not: refundExists or heldFundsReleased. Omitted when refundable

      Only for the resource's owner

  • subscriptionSubscription

    Its subscription once the bill is paid: active (stopped, paused or cancelled if that happened while the charge was on its way), its next bill and its allowance

    29 fields of Subscription
    • uuidstring

      Its id (sub@…): the checkout session's

      A prefixed id (pay@…, sub@…)

    • referencestringOptional

      The 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

    • createdAtstring
    • updatedAtstring

      Its last change (a bill, a status, its metadata)

    • fromstring

      The subscriber's wallet: the one it bills. Absent when unknown (a trial not confirmed yet may have none)

    • tostring

      The wallet its bills pay: the merchant's (or, while the organization holds a member's funds, the organization's)

    • productUuidstring

      Its product (GET /products/:uuid). Absent without one

    • subscriptionHashstring

      The hash of the transaction that created the subscription on-chain. Absent until then (a trial, until its customer confirms it)

    • currencyIdinteger

      The currency (token) billed, written as the currency object under currency (GET /currencies/:id)

    • frequencyDuration

      Billing 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 Duration
      • valueinteger

        Number of units; 0 means no duration

      • unitstring

        Required when value > 0. "months" = 30 days, "years" = 365 days

        oneof=seconds minutes hours days weeks months years

    • statusstring

      SubscriptionStatus (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.
    • actionRequiredstringOptional

      ActionRequired 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.
    • currentPeriodEndstringOptional

      CurrentPeriodEnd 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

    • accessUntilstringOptional

      AccessUntil 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

    • priceUsdstring

      PriceUSD 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 FeeLine

      Fees 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 FeeLine
      • typestring

        custom (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 fee
        • processingFeeQBitFlow's fee, which the merchant has the customer pay (computed by QBitFlow)
      • labelstring

        The line's name, as the checkout shows it ("Processing fee" for the processing fee)

      • descriptionstringOptional

        Optional: more about the line, as the merchant wrote it. Omitted without one

      • amountUsdstring

        The line's amount in USD, a decimal string with at most 2 decimals

    • allowanceAllowanceOptional

      AllowanceStatus (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 Allowance
      • amountstring

        In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

      • amountMinUnitsstring

        The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

      • amountUsdstringOptional

        Its value in USD, a decimal string; absent when no price was known

      • remainingPeriodsintegerOptional

        RemainingPeriods 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

      • maxPerPeriodstringOptional

        MaxPerPeriod 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
    • lastBilledAtstringOptional

      When it was last billed. Absent before its first bill (a trial)

    • nextBillingAtstringOptional

      When its next bill is due (a trial: when it ends; stopped: when it is cancelled). Absent once cancelled

    • cancelledAtstringOptional

      When it was cancelled (absent until then, and for subscriptions cancelled before it was recorded)

    • cancellationReasonstringOptional

      Why 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).
    • cancellableAtstringOptional

      MinimumCancellationDate (cancellableAt) is the earliest its customer may cancel it (the checkout's minPeriods). Absent when they may cancel it any time

    • metadatamapOptional

      Metadata 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

    • testboolean

      Whether this is test data (testnets) or live data

    • userUuidstringOptional

      Member the resource belongs to; omitted for the organization's own resources

      Only for the resource's owner

    • customerUuidstringOptional

      The 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

    • customerReferencestringOptional

      The 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

    • customerSummaryOptional

      The customer, named (merchant only, API reads): who paid, even once deleted. Omitted without one

      Only for the resource's owner

      6 fields of Summary
      • uuidstring

        The customer (GET /customers/:uuid)

      • firstNamestring

        First name

      • lastNamestringOptional

        Last name, when given

      • emailstring

        Email, lowercase

      • referencestringOptional

        The merchant's reference of the customer, when it has one

      • deletedboolean

        The merchant deleted the customer: it still names its rows, but GET /customers/:uuid answers 404

subscription.billed: example body

{  "createdAt": "2026-10-01T12:00:00Z",  "data": {    "bill": {      "amount": "10",      "amountMinUnits": "10000000",      "amountUsd": "10",      "chain": "BASE",      "confirmedAt": "2026-10-01T11:59:58Z",      "createdAt": "2026-10-01T12:00:00Z",      "currency": {        "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",        "chain": "BASE",        "code": "BASE:USDC",        "decimals": 6,        "id": 8,        "mainCurrencyId": 3,        "name": "USD Coin",        "symbol": "USDC",        "test": false      },      "customerReference": "crm-5521",      "customerUuid": "01a05cd7-2a00-7d00-8000-0000000000d1",      "explorerUrl": "https://basescan.org/tx/0x3333333333333333333333333333333333333333333333333333333333333333",      "from": "0xabcdef1234567890abcdef1234567890abcdef12",      "paid": "10.0042",      "paidMinUnits": "10004200",      "paidUsd": "10.0042",      "periodEnd": "2026-10-31T12:00:00Z",      "periodStart": "2026-10-01T12:00:00Z",      "productDescription": "Monthly access to the Pro plan",      "productName": "Pro plan",      "productUuid": "019cadfd-8900-7c00-8000-0000000000c1",      "settlement": {        "feePercent": "0.99",        "split": {          "merchant": {            "amount": "9.901",            "amountMinUnits": "9901000",            "amountUsd": "9.901"          },          "networkFee": {            "amount": "0.0042",            "amountMinUnits": "4200",            "amountUsd": "0.0042"          },          "platform": {            "amount": "0.099",            "amountMinUnits": "99000",            "amountUsd": "0.099"          }        },        "transaction": {          "blockNumber": "52468103",          "blockTime": "2026-10-01T11:59:58Z",          "fee": {            "amount": "0.00000114",            "amountMinUnits": "1140000000000",            "amountUsd": "0.005144"          },          "gasUsed": 95000,          "nativePriceUsd": "4512.37"        }      },      "subscriptionUuid": "sub@01a0f755-c320-7000-8000-000000000002",      "test": false,      "to": "0x1234567890abcdef1234567890abcdef12345678",      "txHash": "0x3333333333333333333333333333333333333333333333333333333333333333",      "uuid": "sub-hist@01a0f755-f200-7000-8000-000000000005"    },    "subscription": {      "accessUntil": "2026-10-31T13:00:00Z",      "allowance": {        "amount": "110",        "amountMinUnits": "110000000",        "amountUsd": "110",        "maxPerPeriod": "30",        "maxPerPeriodMinUnits": "30000000",        "remainingPeriods": 11      },      "createdAt": "2026-10-01T12:00:00Z",      "currency": {        "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",        "chain": "BASE",        "code": "BASE:USDC",        "decimals": 6,        "id": 8,        "mainCurrencyId": 3,        "name": "USD Coin",        "symbol": "USDC",        "test": false      },      "currentPeriodEnd": "2026-10-31T12:00:00Z",      "customerReference": "crm-5521",      "customerUuid": "01a05cd7-2a00-7d00-8000-0000000000d1",      "fees": [],      "frequency": {        "unit": "months",        "value": 1      },      "from": "0xabcdef1234567890abcdef1234567890abcdef12",      "lastBilledAt": "2026-10-01T12:00:00Z",      "managementPageLink": "https://qbitflow.app/user/019cadfd-8900-7a00-8000-0000000000a1/manage?uuid=sub@01a0f755-c320-7000-8000-000000000002",      "metadata": {        "crm:account": "A-1042",        "plan": "pro"      },      "nextBillingAt": "2026-10-31T12:00:00Z",      "priceUsd": "10",      "productUuid": "019cadfd-8900-7c00-8000-0000000000c1",      "reference": "customer-7731",      "status": "active",      "subscriptionHash": "0x2222222222222222222222222222222222222222222222222222222222222222",      "test": false,      "to": "0x1234567890abcdef1234567890abcdef12345678",      "updatedAt": "2026-10-01T12:00:00Z",      "uuid": "sub@01a0f755-c320-7000-8000-000000000002"    }  },  "id": "evt_bca1239f-e7d7-517c-b004-08aaeee1e76f",  "test": false,  "type": "subscription.billed",  "version": "v2"}
Sent as one line, its keys sorted.
Show all 124 lines

subscription.statusChanged#

What it carries, and when

SubscriptionStatusChange: the Subscription with its new status, and previousStatus. Every status change sends it, once (its id derives from what caused it and from the change's place in the subscription's history: a subscription cancelled, revived and cancelled again sends two → cancelled events, with two ids). A customer's cancellation: active or paused → stopped when it is confirmed, then stopped → cancelled at the end of the paid period (nextBillingAt); a merchant's cancellation (POST /subscriptions/:uuid/cancel): → cancelled at once, or → stopped then → cancelled with immediate=false; the last failed bill, a trial not confirmed in its grace period, a closed merchant: → cancelled. stopped and cancelled carry cancellationReason (customer, merchant, billingFailed, trialNotConverted, merchantClosed, inactiveOnChain; a stopped subscription keeps the reason it stopped for), cancelled also cancelledAt, an allowance of "0" and no nextBillingAt. A free trial confirmed by its customer: trial or trialExpired → active, right before its first subscription.billed (same batch), once (its id derives from the confirming transaction). A pastDue subscription becomes active when its bill is paid (after that subscription.billed), never on a top-up alone. The customer pausing or resuming it (a message signed with their wallet): active → paused, paused → active. What the customer must do (actionRequired) is no status: it changes with subscription.actionRequiredChanged

What to do · Status change

Apply your access rule: grant while now < accessUntil, whatever the status (Subscriptions § Access): stopped and paused keep the period paid for; cancelled: the end (cancellationReason says why).

30 fields of data (SubscriptionStatusChange)
  • uuidstring

    Its id (sub@…): the checkout session's

    A prefixed id (pay@…, sub@…)

  • referencestringOptional

    The 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

  • createdAtstring
  • updatedAtstring

    Its last change (a bill, a status, its metadata)

  • fromstring

    The subscriber's wallet: the one it bills. Absent when unknown (a trial not confirmed yet may have none)

  • tostring

    The wallet its bills pay: the merchant's (or, while the organization holds a member's funds, the organization's)

  • productUuidstring

    Its product (GET /products/:uuid). Absent without one

  • subscriptionHashstring

    The hash of the transaction that created the subscription on-chain. Absent until then (a trial, until its customer confirms it)

  • currencyIdinteger

    The currency (token) billed, written as the currency object under currency (GET /currencies/:id)

  • frequencyDuration

    Billing 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 Duration
    • valueinteger

      Number of units; 0 means no duration

    • unitstring

      Required when value > 0. "months" = 30 days, "years" = 365 days

      oneof=seconds minutes hours days weeks months years

  • statusstring

    SubscriptionStatus (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.
  • actionRequiredstringOptional

    ActionRequired 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.
  • currentPeriodEndstringOptional

    CurrentPeriodEnd 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

  • accessUntilstringOptional

    AccessUntil 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

  • priceUsdstring

    PriceUSD 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 FeeLine

    Fees 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 FeeLine
    • typestring

      custom (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 fee
      • processingFeeQBitFlow's fee, which the merchant has the customer pay (computed by QBitFlow)
    • labelstring

      The line's name, as the checkout shows it ("Processing fee" for the processing fee)

    • descriptionstringOptional

      Optional: more about the line, as the merchant wrote it. Omitted without one

    • amountUsdstring

      The line's amount in USD, a decimal string with at most 2 decimals

  • allowanceAllowanceOptional

    AllowanceStatus (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 Allowance
    • amountstring

      In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

    • amountMinUnitsstring

      The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

    • amountUsdstringOptional

      Its value in USD, a decimal string; absent when no price was known

    • remainingPeriodsintegerOptional

      RemainingPeriods 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

    • maxPerPeriodstringOptional

      MaxPerPeriod 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
  • lastBilledAtstringOptional

    When it was last billed. Absent before its first bill (a trial)

  • nextBillingAtstringOptional

    When its next bill is due (a trial: when it ends; stopped: when it is cancelled). Absent once cancelled

  • cancelledAtstringOptional

    When it was cancelled (absent until then, and for subscriptions cancelled before it was recorded)

  • cancellationReasonstringOptional

    Why 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).
  • cancellableAtstringOptional

    MinimumCancellationDate (cancellableAt) is the earliest its customer may cancel it (the checkout's minPeriods). Absent when they may cancel it any time

  • metadatamapOptional

    Metadata 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

  • testboolean

    Whether this is test data (testnets) or live data

  • userUuidstringOptional

    Member the resource belongs to; omitted for the organization's own resources

    Only for the resource's owner

  • customerUuidstringOptional

    The 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

  • customerReferencestringOptional

    The 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

  • customerSummaryOptional

    The customer, named (merchant only, API reads): who paid, even once deleted. Omitted without one

    Only for the resource's owner

    6 fields of Summary
    • uuidstring

      The customer (GET /customers/:uuid)

    • firstNamestring

      First name

    • lastNamestringOptional

      Last name, when given

    • emailstring

      Email, lowercase

    • referencestringOptional

      The merchant's reference of the customer, when it has one

    • deletedboolean

      The merchant deleted the customer: it still names its rows, but GET /customers/:uuid answers 404

  • previousStatusstring

    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.

subscription.statusChanged: example body

{  "createdAt": "2026-10-01T12:00:00Z",  "data": {    "accessUntil": "2026-10-31T13:00:00Z",    "allowance": {      "amount": "110",      "amountMinUnits": "110000000",      "amountUsd": "110",      "maxPerPeriod": "30",      "maxPerPeriodMinUnits": "30000000",      "remainingPeriods": 11    },    "createdAt": "2026-09-01T12:00:00Z",    "currency": {      "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",      "chain": "BASE",      "code": "BASE:USDC",      "decimals": 6,      "id": 8,      "mainCurrencyId": 3,      "name": "USD Coin",      "symbol": "USDC",      "test": false    },    "currentPeriodEnd": "2026-10-31T12:00:00Z",    "customerReference": "crm-5521",    "customerUuid": "01a05cd7-2a00-7d00-8000-0000000000d1",    "fees": [],    "frequency": {      "unit": "months",      "value": 1    },    "from": "0xabcdef1234567890abcdef1234567890abcdef12",    "lastBilledAt": "2026-10-01T12:00:00Z",    "managementPageLink": "https://qbitflow.app/user/019cadfd-8900-7a00-8000-0000000000a1/manage?uuid=sub@01a05cd7-2a00-7000-8000-000000000003",    "metadata": {      "crm:account": "A-1042",      "plan": "pro"    },    "nextBillingAt": "2026-10-31T12:00:00Z",    "previousStatus": "pastDue",    "priceUsd": "10",    "productUuid": "019cadfd-8900-7c00-8000-0000000000c1",    "reference": "customer-7731",    "status": "active",    "subscriptionHash": "0x2222222222222222222222222222222222222222222222222222222222222222",    "test": false,    "to": "0x1234567890abcdef1234567890abcdef12345678",    "updatedAt": "2026-10-01T12:00:00Z",    "uuid": "sub@01a05cd7-2a00-7000-8000-000000000003"  },  "id": "evt_9c02c80f-4040-582c-a749-f50329b95421",  "test": false,  "type": "subscription.statusChanged",  "version": "v2"}
Sent as one line, its keys sorted.
Show all 56 lines

subscription.actionRequiredChanged#

What it carries, and when

SubscriptionActionChange: the Subscription with its new actionRequired (topUpAllowance, raiseMaximum, confirmTrial; absent: nothing to do anymore), and previousActionRequired (absent: nothing before). The subscription's managementPageLink is where the customer acts. Sent once per change (set, changed or cleared), whether or not the status changes too (then right after its subscription.statusChanged): topUpAllowance when a paid bill leaves the allowance at about one more bill or less, cleared by a top-up; raiseMaximum when a bill above the customer's maximum starts waiting for a new one (the status kept), cleared when a bill is paid; confirmTrial with the trial's trialExpired, cleared when it is confirmed. Any action is cleared when the subscription stops or is cancelled. Not sent at creation (subscription.created carries it)

What to do · Customer action

Ask the customer to act when actionRequired is set (topUpAllowance, raiseMaximum, confirmTrial: managementPageLink is where); absent: nothing left to do (previousActionRequired says what was done).

30 fields of data (SubscriptionActionChange)
  • uuidstring

    Its id (sub@…): the checkout session's

    A prefixed id (pay@…, sub@…)

  • referencestringOptional

    The 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

  • createdAtstring
  • updatedAtstring

    Its last change (a bill, a status, its metadata)

  • fromstring

    The subscriber's wallet: the one it bills. Absent when unknown (a trial not confirmed yet may have none)

  • tostring

    The wallet its bills pay: the merchant's (or, while the organization holds a member's funds, the organization's)

  • productUuidstring

    Its product (GET /products/:uuid). Absent without one

  • subscriptionHashstring

    The hash of the transaction that created the subscription on-chain. Absent until then (a trial, until its customer confirms it)

  • currencyIdinteger

    The currency (token) billed, written as the currency object under currency (GET /currencies/:id)

  • frequencyDuration

    Billing 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 Duration
    • valueinteger

      Number of units; 0 means no duration

    • unitstring

      Required when value > 0. "months" = 30 days, "years" = 365 days

      oneof=seconds minutes hours days weeks months years

  • statusstring

    SubscriptionStatus (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.
  • actionRequiredstringOptional

    ActionRequired 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.
  • currentPeriodEndstringOptional

    CurrentPeriodEnd 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

  • accessUntilstringOptional

    AccessUntil 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

  • priceUsdstring

    PriceUSD 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 FeeLine

    Fees 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 FeeLine
    • typestring

      custom (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 fee
      • processingFeeQBitFlow's fee, which the merchant has the customer pay (computed by QBitFlow)
    • labelstring

      The line's name, as the checkout shows it ("Processing fee" for the processing fee)

    • descriptionstringOptional

      Optional: more about the line, as the merchant wrote it. Omitted without one

    • amountUsdstring

      The line's amount in USD, a decimal string with at most 2 decimals

  • allowanceAllowanceOptional

    AllowanceStatus (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 Allowance
    • amountstring

      In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

    • amountMinUnitsstring

      The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

    • amountUsdstringOptional

      Its value in USD, a decimal string; absent when no price was known

    • remainingPeriodsintegerOptional

      RemainingPeriods 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

    • maxPerPeriodstringOptional

      MaxPerPeriod 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
  • lastBilledAtstringOptional

    When it was last billed. Absent before its first bill (a trial)

  • nextBillingAtstringOptional

    When its next bill is due (a trial: when it ends; stopped: when it is cancelled). Absent once cancelled

  • cancelledAtstringOptional

    When it was cancelled (absent until then, and for subscriptions cancelled before it was recorded)

  • cancellationReasonstringOptional

    Why 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).
  • cancellableAtstringOptional

    MinimumCancellationDate (cancellableAt) is the earliest its customer may cancel it (the checkout's minPeriods). Absent when they may cancel it any time

  • metadatamapOptional

    Metadata 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

  • testboolean

    Whether this is test data (testnets) or live data

  • userUuidstringOptional

    Member the resource belongs to; omitted for the organization's own resources

    Only for the resource's owner

  • customerUuidstringOptional

    The 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

  • customerReferencestringOptional

    The 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

  • customerSummaryOptional

    The customer, named (merchant only, API reads): who paid, even once deleted. Omitted without one

    Only for the resource's owner

    6 fields of Summary
    • uuidstring

      The customer (GET /customers/:uuid)

    • firstNamestring

      First name

    • lastNamestringOptional

      Last name, when given

    • emailstring

      Email, lowercase

    • referencestringOptional

      The merchant's reference of the customer, when it has one

    • deletedboolean

      The merchant deleted the customer: it still names its rows, but GET /customers/:uuid answers 404

  • previousActionRequiredstringOptional

    What its customer had to do before (absent: nothing)

    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.

subscription.actionRequiredChanged: example body

{  "createdAt": "2026-10-01T12:00:00Z",  "data": {    "accessUntil": "2026-10-31T13:00:00Z",    "actionRequired": "topUpAllowance",    "allowance": {      "amount": "1.5",      "amountMinUnits": "1500000",      "amountUsd": "1.5",      "maxPerPeriod": "30",      "maxPerPeriodMinUnits": "30000000",      "remainingPeriods": 0    },    "createdAt": "2026-09-01T12:00:00Z",    "currency": {      "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",      "chain": "BASE",      "code": "BASE:USDC",      "decimals": 6,      "id": 8,      "mainCurrencyId": 3,      "name": "USD Coin",      "symbol": "USDC",      "test": false    },    "currentPeriodEnd": "2026-10-31T12:00:00Z",    "customerReference": "crm-5521",    "customerUuid": "01a05cd7-2a00-7d00-8000-0000000000d1",    "fees": [],    "frequency": {      "unit": "months",      "value": 1    },    "from": "0xabcdef1234567890abcdef1234567890abcdef12",    "lastBilledAt": "2026-10-01T12:00:00Z",    "managementPageLink": "https://qbitflow.app/user/019cadfd-8900-7a00-8000-0000000000a1/manage?uuid=sub@01a05cd7-2a00-7000-8000-000000000003",    "metadata": {      "crm:account": "A-1042",      "plan": "pro"    },    "nextBillingAt": "2026-10-31T12:00:00Z",    "priceUsd": "10",    "productUuid": "019cadfd-8900-7c00-8000-0000000000c1",    "reference": "customer-7731",    "status": "active",    "subscriptionHash": "0x2222222222222222222222222222222222222222222222222222222222222222",    "test": false,    "to": "0x1234567890abcdef1234567890abcdef12345678",    "updatedAt": "2026-10-01T12:00:00Z",    "uuid": "sub@01a05cd7-2a00-7000-8000-000000000003"  },  "id": "evt_b4e9b659-4641-5b64-bf03-ade86da0c456",  "test": false,  "type": "subscription.actionRequiredChanged",  "version": "v2"}
Sent as one line, its keys sorted.
Show all 56 lines

subscription.billingFailed#

What it carries, and when

SubscriptionBillingFailure: the Subscription as the failure leaves it, the bill (billUuid, the sub-hist@… its subscription.billed will have; amountUsd, a decimal string), reason (what the customer does: insufficientBalance: add funds to the wallet; allowanceExhausted: top up the allowance; approvalRevoked: approve the token again; maxAmountExceeded: sign a new maximum; other: the network refused the charge), failureCode (the charge's error code, errors), nextAttemptAt (when it is tried again at the latest; absent after the last attempt), attempt and remainingAttempts (retried 1, 3, 5 and 7 days after the first failed attempt, at least a day apart, and at once when the customer raises their allowance or maximum: such an extra try sends nothing if it fails; at 0 the subscription is still pastDue here and its cancellation follows: subscription.statusChanged to cancelled). The subscription's managementPageLink is where the customer fixes it. Sent once per failed attempt (pastDue), and once when a bill above the customer's maximum starts waiting for a new one (maxAmountExceeded: the status is kept, actionRequired becomes raiseMaximum; no attempt spent: attempt stays the count so far). A status or actionRequired change comes after its billingFailed. A subscription read also carries dunning (where the failing bill stands); webhooks don't. QBitFlow's own failures (a network or a price unavailable, its own signature refused) send nothing (Subscriptions and bills § Billing)

What to do · Failing renewal

Tell the customer what to do (reason; data.managementPageLink fixes it: allowance, approval, maximum; or funds). remainingAttempts: 0: its cancellation follows.

36 fields of data (SubscriptionBillingFailure)
  • uuidstring

    Its id (sub@…): the checkout session's

    A prefixed id (pay@…, sub@…)

  • referencestringOptional

    The 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

  • createdAtstring
  • updatedAtstring

    Its last change (a bill, a status, its metadata)

  • fromstring

    The subscriber's wallet: the one it bills. Absent when unknown (a trial not confirmed yet may have none)

  • tostring

    The wallet its bills pay: the merchant's (or, while the organization holds a member's funds, the organization's)

  • productUuidstring

    Its product (GET /products/:uuid). Absent without one

  • subscriptionHashstring

    The hash of the transaction that created the subscription on-chain. Absent until then (a trial, until its customer confirms it)

  • currencyIdinteger

    The currency (token) billed, written as the currency object under currency (GET /currencies/:id)

  • frequencyDuration

    Billing 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 Duration
    • valueinteger

      Number of units; 0 means no duration

    • unitstring

      Required when value > 0. "months" = 30 days, "years" = 365 days

      oneof=seconds minutes hours days weeks months years

  • statusstring

    SubscriptionStatus (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.
  • actionRequiredstringOptional

    ActionRequired 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.
  • currentPeriodEndstringOptional

    CurrentPeriodEnd 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

  • accessUntilstringOptional

    AccessUntil 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

  • priceUsdstring

    PriceUSD 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 FeeLine

    Fees 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 FeeLine
    • typestring

      custom (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 fee
      • processingFeeQBitFlow's fee, which the merchant has the customer pay (computed by QBitFlow)
    • labelstring

      The line's name, as the checkout shows it ("Processing fee" for the processing fee)

    • descriptionstringOptional

      Optional: more about the line, as the merchant wrote it. Omitted without one

    • amountUsdstring

      The line's amount in USD, a decimal string with at most 2 decimals

  • allowanceAllowanceOptional

    AllowanceStatus (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 Allowance
    • amountstring

      In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

    • amountMinUnitsstring

      The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

    • amountUsdstringOptional

      Its value in USD, a decimal string; absent when no price was known

    • remainingPeriodsintegerOptional

      RemainingPeriods 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

    • maxPerPeriodstringOptional

      MaxPerPeriod 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
  • lastBilledAtstringOptional

    When it was last billed. Absent before its first bill (a trial)

  • nextBillingAtstringOptional

    When its next bill is due (a trial: when it ends; stopped: when it is cancelled). Absent once cancelled

  • cancelledAtstringOptional

    When it was cancelled (absent until then, and for subscriptions cancelled before it was recorded)

  • cancellationReasonstringOptional

    Why 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).
  • cancellableAtstringOptional

    MinimumCancellationDate (cancellableAt) is the earliest its customer may cancel it (the checkout's minPeriods). Absent when they may cancel it any time

  • metadatamapOptional

    Metadata 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

  • testboolean

    Whether this is test data (testnets) or live data

  • userUuidstringOptional

    Member the resource belongs to; omitted for the organization's own resources

    Only for the resource's owner

  • customerUuidstringOptional

    The 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

  • customerReferencestringOptional

    The 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

  • customerSummaryOptional

    The customer, named (merchant only, API reads): who paid, even once deleted. Omitted without one

    Only for the resource's owner

    6 fields of Summary
    • uuidstring

      The customer (GET /customers/:uuid)

    • firstNamestring

      First name

    • lastNamestringOptional

      Last name, when given

    • emailstring

      Email, lowercase

    • referencestringOptional

      The merchant's reference of the customer, when it has one

    • deletedboolean

      The merchant deleted the customer: it still names its rows, but GET /customers/:uuid answers 404

  • reasonstring

    insufficientBalance, allowanceExhausted, approvalRevoked, maxAmountExceeded or other

    One of:

    • insufficientBalanceFailureInsufficientBalance: the customer's wallet doesn't hold the bill; they add funds to it.
    • allowanceExhaustedFailureAllowanceExhausted: what remains of the allowance the customer signed doesn't cover the bill (or it expired); they top it up (the management page).
    • approvalRevokedFailureApprovalRevoked: the customer's wallet revoked the token approval the subscription pulls through; they approve it again (the management page).
    • maxAmountExceededFailureMaxAmountExceeded: the bill, converted at today's price, exceeds the maximum the customer signed; they must sign a new maximum (the management page).
    • otherFailureOther: another refusal of the charge the customer's wallet causes (the amount out of the subscription's limits, a transaction reverted on-chain); retried like the others. QBitFlow's own failures (a network or a price unavailable, its own signature refused) are retried without an event.
  • failureCodestringOptional

    The charge's error code (errors), e.g. insufficient_funds, insufficient_allowance

  • billUuidstring

    The bill that failed: the uuid its subscription.billed has once it is paid (sub-hist@…)

  • amountUsdstring

    The bill, in USD (a decimal string): the price the customer subscribed at and its lines (fees)

  • attemptinteger

    This bill's failed attempts so far, this one included (maxAmountExceeded spends none)

  • remainingAttemptsinteger

    Attempts left: retried 1, 3, 5 and 7 days after the bill's first failed attempt (at least a day apart), and at once when the customer raises their allowance. 0: this was the last; the subscription (still pastDue here) is cancelled next (subscription.statusChanged)

  • nextAttemptAtstringOptional

    When the bill is tried again at the latest (maxAmountExceeded: when it stops waiting for a new maximum); absent after the last attempt

subscription.billingFailed: example body

{  "createdAt": "2026-10-01T12:00:00Z",  "data": {    "accessUntil": "2026-10-01T12:00:00Z",    "allowance": {      "amount": "110",      "amountMinUnits": "110000000",      "amountUsd": "110",      "maxPerPeriod": "30",      "maxPerPeriodMinUnits": "30000000",      "remainingPeriods": 11    },    "amountUsd": "10",    "attempt": 1,    "billUuid": "sub-hist@01a0f755-f200-7000-8000-000000000005",    "createdAt": "2026-09-01T12:00:00Z",    "currency": {      "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",      "chain": "BASE",      "code": "BASE:USDC",      "decimals": 6,      "id": 8,      "mainCurrencyId": 3,      "name": "USD Coin",      "symbol": "USDC",      "test": false    },    "currentPeriodEnd": "2026-10-01T12:00:00Z",    "customerReference": "crm-5521",    "customerUuid": "01a05cd7-2a00-7d00-8000-0000000000d1",    "failureCode": "insufficient_funds",    "fees": [],    "frequency": {      "unit": "months",      "value": 1    },    "from": "0xabcdef1234567890abcdef1234567890abcdef12",    "lastBilledAt": "2026-09-01T12:00:00Z",    "managementPageLink": "https://qbitflow.app/user/019cadfd-8900-7a00-8000-0000000000a1/manage?uuid=sub@01a05cd7-2a00-7000-8000-000000000003",    "metadata": {      "crm:account": "A-1042",      "plan": "pro"    },    "nextAttemptAt": "2026-10-02T12:00:00Z",    "nextBillingAt": "2026-10-01T12:00:00Z",    "priceUsd": "10",    "productUuid": "019cadfd-8900-7c00-8000-0000000000c1",    "reason": "insufficientBalance",    "reference": "customer-7731",    "remainingAttempts": 4,    "status": "pastDue",    "subscriptionHash": "0x2222222222222222222222222222222222222222222222222222222222222222",    "test": false,    "to": "0x1234567890abcdef1234567890abcdef12345678",    "updatedAt": "2026-10-01T12:00:00Z",    "uuid": "sub@01a05cd7-2a00-7000-8000-000000000003"  },  "id": "evt_82950239-430c-595f-ad42-595955b921a0",  "test": false,  "type": "subscription.billingFailed",  "version": "v2"}
Sent as one line, its keys sorted.
Show all 62 lines

subscription.upcomingBill#

What it carries, and when

UpcomingBill: the Subscription, its billingAt (when it is billed; a trial: when it ends), amountUsd (what the bill charges, a decimal string: its priceUsd plus its fees lines), trialEnding (the trial ends then: the customer confirms the subscription to be billed), and whether the customer's wallet can pay it now, both against the bill in the token at today's price: balanceSufficient (absent for a trial, or when it couldn't be checked) and allowanceSufficient (absent for a trial, or without a price). Sent once per bill, subscriptions.upcomingBillDays days before (3 by default, 0: never), checked once a day, for active and trial subscriptions whose period (a trial: the trial) is longer than those days: none for a shorter one (a daily subscription with the 3-day default, a 2-day trial), where it would come right after the previous bill or not at all. Not for a paused one (not billed until resumed), a stopped one (it ends with its paid period) nor a cancelled one. In test mode too (a test bill runs when you trigger it)

What to do · Coming bill

Remind the customer when balanceSufficient or allowanceSufficient is false, or when trialEnding (they confirm the subscription to be billed).

34 fields of data (UpcomingBill)
  • uuidstring

    Its id (sub@…): the checkout session's

    A prefixed id (pay@…, sub@…)

  • referencestringOptional

    The 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

  • createdAtstring
  • updatedAtstring

    Its last change (a bill, a status, its metadata)

  • fromstring

    The subscriber's wallet: the one it bills. Absent when unknown (a trial not confirmed yet may have none)

  • tostring

    The wallet its bills pay: the merchant's (or, while the organization holds a member's funds, the organization's)

  • productUuidstring

    Its product (GET /products/:uuid). Absent without one

  • subscriptionHashstring

    The hash of the transaction that created the subscription on-chain. Absent until then (a trial, until its customer confirms it)

  • currencyIdinteger

    The currency (token) billed, written as the currency object under currency (GET /currencies/:id)

  • frequencyDuration

    Billing 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 Duration
    • valueinteger

      Number of units; 0 means no duration

    • unitstring

      Required when value > 0. "months" = 30 days, "years" = 365 days

      oneof=seconds minutes hours days weeks months years

  • statusstring

    SubscriptionStatus (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.
  • actionRequiredstringOptional

    ActionRequired 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.
  • currentPeriodEndstringOptional

    CurrentPeriodEnd 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

  • accessUntilstringOptional

    AccessUntil 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

  • priceUsdstring

    PriceUSD 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 FeeLine

    Fees 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 FeeLine
    • typestring

      custom (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 fee
      • processingFeeQBitFlow's fee, which the merchant has the customer pay (computed by QBitFlow)
    • labelstring

      The line's name, as the checkout shows it ("Processing fee" for the processing fee)

    • descriptionstringOptional

      Optional: more about the line, as the merchant wrote it. Omitted without one

    • amountUsdstring

      The line's amount in USD, a decimal string with at most 2 decimals

  • allowanceAllowanceOptional

    AllowanceStatus (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 Allowance
    • amountstring

      In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

    • amountMinUnitsstring

      The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

    • amountUsdstringOptional

      Its value in USD, a decimal string; absent when no price was known

    • remainingPeriodsintegerOptional

      RemainingPeriods 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

    • maxPerPeriodstringOptional

      MaxPerPeriod 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
  • lastBilledAtstringOptional

    When it was last billed. Absent before its first bill (a trial)

  • nextBillingAtstringOptional

    When its next bill is due (a trial: when it ends; stopped: when it is cancelled). Absent once cancelled

  • cancelledAtstringOptional

    When it was cancelled (absent until then, and for subscriptions cancelled before it was recorded)

  • cancellationReasonstringOptional

    Why 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).
  • cancellableAtstringOptional

    MinimumCancellationDate (cancellableAt) is the earliest its customer may cancel it (the checkout's minPeriods). Absent when they may cancel it any time

  • metadatamapOptional

    Metadata 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

  • testboolean

    Whether this is test data (testnets) or live data

  • userUuidstringOptional

    Member the resource belongs to; omitted for the organization's own resources

    Only for the resource's owner

  • customerUuidstringOptional

    The 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

  • customerReferencestringOptional

    The 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

  • customerSummaryOptional

    The customer, named (merchant only, API reads): who paid, even once deleted. Omitted without one

    Only for the resource's owner

    6 fields of Summary
    • uuidstring

      The customer (GET /customers/:uuid)

    • firstNamestring

      First name

    • lastNamestringOptional

      Last name, when given

    • emailstring

      Email, lowercase

    • referencestringOptional

      The merchant's reference of the customer, when it has one

    • deletedboolean

      The merchant deleted the customer: it still names its rows, but GET /customers/:uuid answers 404

  • billingAtstring

    When it is billed (for a trial: when the trial ends)

  • amountUsdstring

    The bill, in USD (a decimal string): the price the customer subscribed at and its lines (fees), converted to the token at the billing day's price

  • trialEndingboolean

    The trial ends then: the customer confirms the subscription (its managementPageLink) to be billed

  • balanceSufficientbooleanOptional

    The wallet holds at least the bill at today's price; absent for a trial (its customer chooses the wallet when confirming it), or when it couldn't be checked (price or network unavailable)

  • allowanceSufficientbooleanOptional

    The subscription's remaining allowance covers the bill; absent for a trial (the allowance is signed when confirming), or without a price

subscription.upcomingBill: example body

{  "createdAt": "2026-10-01T12:00:00Z",  "data": {    "accessUntil": "2026-10-04T13:00:00Z",    "allowance": {      "amount": "110",      "amountMinUnits": "110000000",      "amountUsd": "110",      "maxPerPeriod": "30",      "maxPerPeriodMinUnits": "30000000",      "remainingPeriods": 11    },    "allowanceSufficient": true,    "amountUsd": "10",    "balanceSufficient": true,    "billingAt": "2026-10-04T12:00:00Z",    "createdAt": "2026-09-04T12:00:00Z",    "currency": {      "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",      "chain": "BASE",      "code": "BASE:USDC",      "decimals": 6,      "id": 8,      "mainCurrencyId": 3,      "name": "USD Coin",      "symbol": "USDC",      "test": false    },    "currentPeriodEnd": "2026-10-04T12:00:00Z",    "customerReference": "crm-5521",    "customerUuid": "01a05cd7-2a00-7d00-8000-0000000000d1",    "fees": [],    "frequency": {      "unit": "months",      "value": 1    },    "from": "0xabcdef1234567890abcdef1234567890abcdef12",    "lastBilledAt": "2026-09-04T12:00:00Z",    "managementPageLink": "https://qbitflow.app/user/019cadfd-8900-7a00-8000-0000000000a1/manage?uuid=sub@01a06c4a-3e00-7000-8000-000000000004",    "metadata": {      "crm:account": "A-1042",      "plan": "pro"    },    "nextBillingAt": "2026-10-04T12:00:00Z",    "priceUsd": "10",    "productUuid": "019cadfd-8900-7c00-8000-0000000000c1",    "reference": "customer-7731",    "status": "active",    "subscriptionHash": "0x2222222222222222222222222222222222222222222222222222222222222222",    "test": false,    "to": "0x1234567890abcdef1234567890abcdef12345678",    "trialEnding": false,    "updatedAt": "2026-09-04T12:00:00Z",    "uuid": "sub@01a06c4a-3e00-7000-8000-000000000004"  },  "id": "evt_234c641e-08ac-5669-9147-efc5b7b97bd5",  "test": false,  "type": "subscription.upcomingBill",  "version": "v2"}
Sent as one line, its keys sorted.
Show all 60 lines

refund.requested#

What it carries, and when

Refund: a new refund (status: "pending") of a payment (txUuid: "pay@…") or of a subscription's bill (txUuid: "sub-hist@…"). initiatedBy is always customer (a request the merchant answers: a merchant's own refund is announced only by refund.approved). With what the customer paid (paid, paidMinUnits, paidUsd, network fee included) and the share asked (refundPercent, a string: "100" for a customer's request; amount, amountMinUnits, amountUsd; currency). The merchant-only read fields (approval, customer, customerUuid, productName) are API reads only

What to do · Refund

A customer's request (initiatedBy: "customer"): review it (dashboard); a merchant's own refund (initiatedBy: "merchant"): nothing to review. On approved, reverse the order, in full or in part (data.txUuid is the refunded pay@… or sub-hist@…; refundPercent < 100 is a partial refund); rejected changes nothing.

27 fields of data (Refund)
  • uuidstring

    The refund's id (refund@<uuid>)

    A prefixed id (pay@…, sub@…)

  • txUuidstring

    The refunded transaction: a payment (pay@<uuid>) or a subscription's bill (sub-hist@<uuid>)

  • reasonstringOptional

    Why the refund: the customer's reason, or the merchant's when it started the refund (absent when it gave none). Only for the merchant: never in the hosted reads (GET /hosted/transactions/:txUuid/refund, the answer of POST /hosted/refunds)

    Only for the resource's owner

  • statusstring

    pending (a customer's request not answered yet), approved (its transfer confirmed), rejected (the merchant rejected the request). failed is declared but never set: a failed approval leaves the request pending

    One ofpendingapprovedrejected

    • failedDeclared, never set: an approval that fails leaves the refund pending, to approve again
  • createdAtstring

    When the refund was requested (or prepared by the merchant: its intent)

  • merchantMessagestringOptional

    The merchant's message to the customer: why it rejected the request, or a note on a refund it started. Absent when none

  • respondedAtstringOptional

    When the refund was answered: rejected, or its transfer confirmed (approved). Absent while pending

  • testboolean

    Whether this is test data (testnets) or live data

  • userUuidstringOptional

    Member the resource belongs to; omitted for the organization's own resources

    Only for the resource's owner

  • txHashstringOptional

    The refund transfer's transaction, once approved. Absent before

  • initiatedBystring

    Who started the refund: customer (requested by the customer, the merchant answers) or merchant (sent by the merchant without a request: recorded once its transfer confirmed, approved; see POST /refunds/intent)

    One ofcustomermerchant

  • heldbooleanOptional

    Held: the refunded payment was paid to the organization's wallet, which holds the seller's funds (the trust layer): the organization decides the refund and sends it from its wallet; the seller (userUuid) sees it. Once sent, it is a negative line of the seller's held funds. False: the seller (the refund's space) decides it.

    Only for the resource's owner

  • paidstring

    What the customer paid for the refunded transaction: its amount, fees included, and the network fee they paid on top. The base of refundPercent: a full refund sends all of it back. paid and paidMinUnits in the units of currency, paidUsd at the refunded transaction's time (absent when unknown: a request made before it was recorded) In the currency's units, exact (paidMinUnits / 10^decimals), a decimal string

  • paidMinUnitsstring

    In the currency's smallest unit, a string of digits

  • paidUsdstringOptional

    In USD, a decimal string; absent when unknown

  • refundPercentnumber

    The share of what the customer paid that the refund sends back, a percent string (100: all of it). A customer requests 100; the merchant sets it when it approves, or when it starts the refund. Written as refundPercent

  • amountstring

    What the refund sends back: refundPercent of what the customer paid, rounded down to a whole min unit (all of it for a customer's request, the merchant's share for its own refund, and the share sent once approved). amount in the currency's units, amountUsd at the refunded transaction's time (absent when unknown: a request made before it was recorded) In the currency's units, exact (amountMinUnits / 10^decimals), a decimal string

  • amountMinUnitsstring

    In the currency's smallest unit, a string of digits

  • amountUsdstringOptional

    In USD, a decimal string; absent when unknown

  • currencyIdinteger

    The currency paid, and sent back: written as the currency object under currency (GET /currencies/:id)

  • settlementTransferSettlementOptional

    The refund transfer's transaction (its block, what it cost), once approved; absent before

    Only for the resource's owner

    1 field of TransferSettlement
    • transactionSettlementTransactionOptional

      The transfer's transaction. Absent before it was confirmed

      6 fields of SettlementTransaction
      • blockNumberstring

        The block (Ethereum, Base) or slot (Solana) it landed in, a string of digits

      • blockTimestringOptional

        When 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

      • gasUsedintegerOptional

        The gas it consumed (Ethereum, Base). Absent on Solana

      • feeTokenAmount

        What 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 TokenAmount
        • amountstring

          In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

        • amountMinUnitsstring

          The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

        • amountUsdstringOptional

          Its value in USD, a decimal string; absent when no price was known

      • l1FeeTokenAmountOptional

        The 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 TokenAmount
        • amountstring

          In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

        • amountMinUnitsstring

          The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

        • amountUsdstringOptional

          Its value in USD, a decimal string; absent when no price was known

      • nativePriceUsdstringOptional

        NativePriceUSD 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

  • chainstringOptional

    The chain it was paid on: ETH (Ethereum), BASE (Base) or SOL (Solana), the testnet's for a test-mode transfer (currency.test)

  • explorerUrlstringOptional

    Its transaction on the chain's block explorer (the testnet's in test mode). Omitted without a txHash

  • approvalApprovalOptional

    Approval is where the refund transfer stands while the request is pending and it was sent (POST /refunds/:uuid/approve, or the merchant's own refund answering it: POST /refunds): being confirmed, or its last attempt failed. Absent before it is sent, and once the refund is answered (respondedAt). Only for the merchant, in its reads (GET /refunds, GET /refunds/:uuid, GET /hosted/transactions/:txUuid/refund); never in webhooks

    Only for the resource's owner

    3 fields of Approval
    • statusstring

      waitingConfirmation: sent, being confirmed on-chain. created: its last attempt failed (lastAttempt): send it again (POST /refunds/:uuid/approve/intent)

      One of:

      • createdWaiting for the customer: nothing sent yet, or the last attempt failed (lastAttempt)
      • waitingConfirmationA transaction was sent: waiting for the network and its record
      • completedConfirmed and recorded. Final
      • expiredThe checkout expired unpaid (at its expiresAt, or expired by its merchant). Final
    • txHashstringOptional

      The transfer being confirmed, once known

    • lastAttemptAttemptOptional

      With created: why the last attempt failed (its code and message)

      5 fields of Attempt
      • statusstring

        failed

        One of:

        • failedThe attempt's transaction wasn't sent, didn't confirm or failed on the network: nothing was paid
      • codestringOptional

        The error code (errors): what to do about it (insufficient_funds, token_not_approved, tx_not_sent…)

      • messagestringOptional

        The reason, for the customer

      • txHashstringOptional

        The attempt's transaction, if it was sent

      • atstring

        When it failed

  • customerUuidstringOptional

    CustomerUUID is the refunded transaction's customer (a refund has none of its own); absent without one. Only for the merchant, in its reads (never in webhooks)

    Only for the resource's owner

  • productNamestringOptional

    What the refunded transaction paid for (its product's name, as on the payment or bill). Only for the merchant, in its reads (never in webhooks)

    Only for the resource's owner

  • customerSummaryOptional

    The refunded transaction's customer, named (even once deleted); absent without one. Only for the merchant, in its reads (never in webhooks)

    Only for the resource's owner

    6 fields of Summary
    • uuidstring

      The customer (GET /customers/:uuid)

    • firstNamestring

      First name

    • lastNamestringOptional

      Last name, when given

    • emailstring

      Email, lowercase

    • referencestringOptional

      The merchant's reference of the customer, when it has one

    • deletedboolean

      The merchant deleted the customer: it still names its rows, but GET /customers/:uuid answers 404

refund.requested: example body

{  "createdAt": "2026-10-01T12:00:00Z",  "data": {    "amount": "10.0042",    "amountMinUnits": "10004200",    "amountUsd": "10.0042",    "chain": "BASE",    "createdAt": "2026-10-01T12:00:00Z",    "currency": {      "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",      "chain": "BASE",      "code": "BASE:USDC",      "decimals": 6,      "id": 8,      "mainCurrencyId": 3,      "name": "USD Coin",      "symbol": "USDC",      "test": false    },    "held": false,    "initiatedBy": "customer",    "paid": "10.0042",    "paidMinUnits": "10004200",    "paidUsd": "10.0042",    "reason": "I was charged twice for the same order",    "refundPercent": "100",    "status": "pending",    "test": false,    "txUuid": "pay@01a0f755-f200-7000-8000-000000000001",    "uuid": "refund@01a0ec3b-3ba0-7000-8000-000000000006"  },  "id": "evt_ef774d14-7666-58e4-b447-10383579da8e",  "test": false,  "type": "refund.requested",  "version": "v2"}
Sent as one line, its keys sorted.
Show all 36 lines

refund.approved#

What it carries, and when

Refund (status: "approved", txHash, chain, explorerUrl, settlement.transaction, initiatedBy (merchant: the merchant's own refund, the first and only event about it), refundPercent and amount / amountMinUnits / amountUsd: the share actually paid back, which the merchant may have lowered when approving; Refunds § What a refund pays back). It was refund.completed

What to do · Refund

A customer's request (initiatedBy: "customer"): review it (dashboard); a merchant's own refund (initiatedBy: "merchant"): nothing to review. On approved, reverse the order, in full or in part (data.txUuid is the refunded pay@… or sub-hist@…; refundPercent < 100 is a partial refund); rejected changes nothing.

27 fields of data (Refund)
  • uuidstring

    The refund's id (refund@<uuid>)

    A prefixed id (pay@…, sub@…)

  • txUuidstring

    The refunded transaction: a payment (pay@<uuid>) or a subscription's bill (sub-hist@<uuid>)

  • reasonstringOptional

    Why the refund: the customer's reason, or the merchant's when it started the refund (absent when it gave none). Only for the merchant: never in the hosted reads (GET /hosted/transactions/:txUuid/refund, the answer of POST /hosted/refunds)

    Only for the resource's owner

  • statusstring

    pending (a customer's request not answered yet), approved (its transfer confirmed), rejected (the merchant rejected the request). failed is declared but never set: a failed approval leaves the request pending

    One ofpendingapprovedrejected

    • failedDeclared, never set: an approval that fails leaves the refund pending, to approve again
  • createdAtstring

    When the refund was requested (or prepared by the merchant: its intent)

  • merchantMessagestringOptional

    The merchant's message to the customer: why it rejected the request, or a note on a refund it started. Absent when none

  • respondedAtstringOptional

    When the refund was answered: rejected, or its transfer confirmed (approved). Absent while pending

  • testboolean

    Whether this is test data (testnets) or live data

  • userUuidstringOptional

    Member the resource belongs to; omitted for the organization's own resources

    Only for the resource's owner

  • txHashstringOptional

    The refund transfer's transaction, once approved. Absent before

  • initiatedBystring

    Who started the refund: customer (requested by the customer, the merchant answers) or merchant (sent by the merchant without a request: recorded once its transfer confirmed, approved; see POST /refunds/intent)

    One ofcustomermerchant

  • heldbooleanOptional

    Held: the refunded payment was paid to the organization's wallet, which holds the seller's funds (the trust layer): the organization decides the refund and sends it from its wallet; the seller (userUuid) sees it. Once sent, it is a negative line of the seller's held funds. False: the seller (the refund's space) decides it.

    Only for the resource's owner

  • paidstring

    What the customer paid for the refunded transaction: its amount, fees included, and the network fee they paid on top. The base of refundPercent: a full refund sends all of it back. paid and paidMinUnits in the units of currency, paidUsd at the refunded transaction's time (absent when unknown: a request made before it was recorded) In the currency's units, exact (paidMinUnits / 10^decimals), a decimal string

  • paidMinUnitsstring

    In the currency's smallest unit, a string of digits

  • paidUsdstringOptional

    In USD, a decimal string; absent when unknown

  • refundPercentnumber

    The share of what the customer paid that the refund sends back, a percent string (100: all of it). A customer requests 100; the merchant sets it when it approves, or when it starts the refund. Written as refundPercent

  • amountstring

    What the refund sends back: refundPercent of what the customer paid, rounded down to a whole min unit (all of it for a customer's request, the merchant's share for its own refund, and the share sent once approved). amount in the currency's units, amountUsd at the refunded transaction's time (absent when unknown: a request made before it was recorded) In the currency's units, exact (amountMinUnits / 10^decimals), a decimal string

  • amountMinUnitsstring

    In the currency's smallest unit, a string of digits

  • amountUsdstringOptional

    In USD, a decimal string; absent when unknown

  • currencyIdinteger

    The currency paid, and sent back: written as the currency object under currency (GET /currencies/:id)

  • settlementTransferSettlementOptional

    The refund transfer's transaction (its block, what it cost), once approved; absent before

    Only for the resource's owner

    1 field of TransferSettlement
    • transactionSettlementTransactionOptional

      The transfer's transaction. Absent before it was confirmed

      6 fields of SettlementTransaction
      • blockNumberstring

        The block (Ethereum, Base) or slot (Solana) it landed in, a string of digits

      • blockTimestringOptional

        When 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

      • gasUsedintegerOptional

        The gas it consumed (Ethereum, Base). Absent on Solana

      • feeTokenAmount

        What 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 TokenAmount
        • amountstring

          In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

        • amountMinUnitsstring

          The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

        • amountUsdstringOptional

          Its value in USD, a decimal string; absent when no price was known

      • l1FeeTokenAmountOptional

        The 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 TokenAmount
        • amountstring

          In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

        • amountMinUnitsstring

          The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

        • amountUsdstringOptional

          Its value in USD, a decimal string; absent when no price was known

      • nativePriceUsdstringOptional

        NativePriceUSD 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

  • chainstringOptional

    The chain it was paid on: ETH (Ethereum), BASE (Base) or SOL (Solana), the testnet's for a test-mode transfer (currency.test)

  • explorerUrlstringOptional

    Its transaction on the chain's block explorer (the testnet's in test mode). Omitted without a txHash

  • approvalApprovalOptional

    Approval is where the refund transfer stands while the request is pending and it was sent (POST /refunds/:uuid/approve, or the merchant's own refund answering it: POST /refunds): being confirmed, or its last attempt failed. Absent before it is sent, and once the refund is answered (respondedAt). Only for the merchant, in its reads (GET /refunds, GET /refunds/:uuid, GET /hosted/transactions/:txUuid/refund); never in webhooks

    Only for the resource's owner

    3 fields of Approval
    • statusstring

      waitingConfirmation: sent, being confirmed on-chain. created: its last attempt failed (lastAttempt): send it again (POST /refunds/:uuid/approve/intent)

      One of:

      • createdWaiting for the customer: nothing sent yet, or the last attempt failed (lastAttempt)
      • waitingConfirmationA transaction was sent: waiting for the network and its record
      • completedConfirmed and recorded. Final
      • expiredThe checkout expired unpaid (at its expiresAt, or expired by its merchant). Final
    • txHashstringOptional

      The transfer being confirmed, once known

    • lastAttemptAttemptOptional

      With created: why the last attempt failed (its code and message)

      5 fields of Attempt
      • statusstring

        failed

        One of:

        • failedThe attempt's transaction wasn't sent, didn't confirm or failed on the network: nothing was paid
      • codestringOptional

        The error code (errors): what to do about it (insufficient_funds, token_not_approved, tx_not_sent…)

      • messagestringOptional

        The reason, for the customer

      • txHashstringOptional

        The attempt's transaction, if it was sent

      • atstring

        When it failed

  • customerUuidstringOptional

    CustomerUUID is the refunded transaction's customer (a refund has none of its own); absent without one. Only for the merchant, in its reads (never in webhooks)

    Only for the resource's owner

  • productNamestringOptional

    What the refunded transaction paid for (its product's name, as on the payment or bill). Only for the merchant, in its reads (never in webhooks)

    Only for the resource's owner

  • customerSummaryOptional

    The refunded transaction's customer, named (even once deleted); absent without one. Only for the merchant, in its reads (never in webhooks)

    Only for the resource's owner

    6 fields of Summary
    • uuidstring

      The customer (GET /customers/:uuid)

    • firstNamestring

      First name

    • lastNamestringOptional

      Last name, when given

    • emailstring

      Email, lowercase

    • referencestringOptional

      The merchant's reference of the customer, when it has one

    • deletedboolean

      The merchant deleted the customer: it still names its rows, but GET /customers/:uuid answers 404

refund.approved: example body

{  "createdAt": "2026-10-01T12:00:00Z",  "data": {    "amount": "5.0021",    "amountMinUnits": "5002100",    "amountUsd": "5.0021",    "chain": "BASE",    "createdAt": "2026-09-29T08:15:00Z",    "currency": {      "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",      "chain": "BASE",      "code": "BASE:USDC",      "decimals": 6,      "id": 8,      "mainCurrencyId": 3,      "name": "USD Coin",      "symbol": "USDC",      "test": false    },    "explorerUrl": "https://basescan.org/tx/0x4444444444444444444444444444444444444444444444444444444444444444",    "held": false,    "initiatedBy": "customer",    "paid": "10.0042",    "paidMinUnits": "10004200",    "paidUsd": "10.0042",    "reason": "I was charged twice for the same order",    "refundPercent": "50",    "respondedAt": "2026-10-01T12:00:00Z",    "settlement": {      "transaction": {        "blockNumber": "52468103",        "blockTime": "2026-10-01T11:59:58Z",        "fee": {          "amount": "0.00000114",          "amountMinUnits": "1140000000000",          "amountUsd": "0.005144"        },        "gasUsed": 95000,        "nativePriceUsd": "4512.37"      }    },    "status": "approved",    "test": false,    "txHash": "0x4444444444444444444444444444444444444444444444444444444444444444",    "txUuid": "pay@01a0f755-f200-7000-8000-000000000001",    "uuid": "refund@01a0ec3b-3ba0-7000-8000-000000000006"  },  "id": "evt_8f63fc99-3f0c-5e75-8aef-d9b4886a51d7",  "test": false,  "type": "refund.approved",  "version": "v2"}
Sent as one line, its keys sorted.
Show all 52 lines

refund.rejected#

What it carries, and when

Refund (status: "rejected", merchantMessage when one was given, initiatedBy: "customer"): a customer's request rejected by whoever decides it (the merchant, POST /refunds/:uuid/reject; the organization for a payment it held). A decision, final. It was refund.denied

What to do · Refund

A customer's request (initiatedBy: "customer"): review it (dashboard); a merchant's own refund (initiatedBy: "merchant"): nothing to review. On approved, reverse the order, in full or in part (data.txUuid is the refunded pay@… or sub-hist@…; refundPercent < 100 is a partial refund); rejected changes nothing.

27 fields of data (Refund)
  • uuidstring

    The refund's id (refund@<uuid>)

    A prefixed id (pay@…, sub@…)

  • txUuidstring

    The refunded transaction: a payment (pay@<uuid>) or a subscription's bill (sub-hist@<uuid>)

  • reasonstringOptional

    Why the refund: the customer's reason, or the merchant's when it started the refund (absent when it gave none). Only for the merchant: never in the hosted reads (GET /hosted/transactions/:txUuid/refund, the answer of POST /hosted/refunds)

    Only for the resource's owner

  • statusstring

    pending (a customer's request not answered yet), approved (its transfer confirmed), rejected (the merchant rejected the request). failed is declared but never set: a failed approval leaves the request pending

    One ofpendingapprovedrejected

    • failedDeclared, never set: an approval that fails leaves the refund pending, to approve again
  • createdAtstring

    When the refund was requested (or prepared by the merchant: its intent)

  • merchantMessagestringOptional

    The merchant's message to the customer: why it rejected the request, or a note on a refund it started. Absent when none

  • respondedAtstringOptional

    When the refund was answered: rejected, or its transfer confirmed (approved). Absent while pending

  • testboolean

    Whether this is test data (testnets) or live data

  • userUuidstringOptional

    Member the resource belongs to; omitted for the organization's own resources

    Only for the resource's owner

  • txHashstringOptional

    The refund transfer's transaction, once approved. Absent before

  • initiatedBystring

    Who started the refund: customer (requested by the customer, the merchant answers) or merchant (sent by the merchant without a request: recorded once its transfer confirmed, approved; see POST /refunds/intent)

    One ofcustomermerchant

  • heldbooleanOptional

    Held: the refunded payment was paid to the organization's wallet, which holds the seller's funds (the trust layer): the organization decides the refund and sends it from its wallet; the seller (userUuid) sees it. Once sent, it is a negative line of the seller's held funds. False: the seller (the refund's space) decides it.

    Only for the resource's owner

  • paidstring

    What the customer paid for the refunded transaction: its amount, fees included, and the network fee they paid on top. The base of refundPercent: a full refund sends all of it back. paid and paidMinUnits in the units of currency, paidUsd at the refunded transaction's time (absent when unknown: a request made before it was recorded) In the currency's units, exact (paidMinUnits / 10^decimals), a decimal string

  • paidMinUnitsstring

    In the currency's smallest unit, a string of digits

  • paidUsdstringOptional

    In USD, a decimal string; absent when unknown

  • refundPercentnumber

    The share of what the customer paid that the refund sends back, a percent string (100: all of it). A customer requests 100; the merchant sets it when it approves, or when it starts the refund. Written as refundPercent

  • amountstring

    What the refund sends back: refundPercent of what the customer paid, rounded down to a whole min unit (all of it for a customer's request, the merchant's share for its own refund, and the share sent once approved). amount in the currency's units, amountUsd at the refunded transaction's time (absent when unknown: a request made before it was recorded) In the currency's units, exact (amountMinUnits / 10^decimals), a decimal string

  • amountMinUnitsstring

    In the currency's smallest unit, a string of digits

  • amountUsdstringOptional

    In USD, a decimal string; absent when unknown

  • currencyIdinteger

    The currency paid, and sent back: written as the currency object under currency (GET /currencies/:id)

  • settlementTransferSettlementOptional

    The refund transfer's transaction (its block, what it cost), once approved; absent before

    Only for the resource's owner

    1 field of TransferSettlement
    • transactionSettlementTransactionOptional

      The transfer's transaction. Absent before it was confirmed

      6 fields of SettlementTransaction
      • blockNumberstring

        The block (Ethereum, Base) or slot (Solana) it landed in, a string of digits

      • blockTimestringOptional

        When 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

      • gasUsedintegerOptional

        The gas it consumed (Ethereum, Base). Absent on Solana

      • feeTokenAmount

        What 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 TokenAmount
        • amountstring

          In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

        • amountMinUnitsstring

          The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

        • amountUsdstringOptional

          Its value in USD, a decimal string; absent when no price was known

      • l1FeeTokenAmountOptional

        The 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 TokenAmount
        • amountstring

          In the token's units, exact: amountMinUnits / 10^decimals, a decimal string ("12.5")

        • amountMinUnitsstring

          The on-chain integer, in the token's smallest unit, a string of digits ("12500000")

        • amountUsdstringOptional

          Its value in USD, a decimal string; absent when no price was known

      • nativePriceUsdstringOptional

        NativePriceUSD 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

  • chainstringOptional

    The chain it was paid on: ETH (Ethereum), BASE (Base) or SOL (Solana), the testnet's for a test-mode transfer (currency.test)

  • explorerUrlstringOptional

    Its transaction on the chain's block explorer (the testnet's in test mode). Omitted without a txHash

  • approvalApprovalOptional

    Approval is where the refund transfer stands while the request is pending and it was sent (POST /refunds/:uuid/approve, or the merchant's own refund answering it: POST /refunds): being confirmed, or its last attempt failed. Absent before it is sent, and once the refund is answered (respondedAt). Only for the merchant, in its reads (GET /refunds, GET /refunds/:uuid, GET /hosted/transactions/:txUuid/refund); never in webhooks

    Only for the resource's owner

    3 fields of Approval
    • statusstring

      waitingConfirmation: sent, being confirmed on-chain. created: its last attempt failed (lastAttempt): send it again (POST /refunds/:uuid/approve/intent)

      One of:

      • createdWaiting for the customer: nothing sent yet, or the last attempt failed (lastAttempt)
      • waitingConfirmationA transaction was sent: waiting for the network and its record
      • completedConfirmed and recorded. Final
      • expiredThe checkout expired unpaid (at its expiresAt, or expired by its merchant). Final
    • txHashstringOptional

      The transfer being confirmed, once known

    • lastAttemptAttemptOptional

      With created: why the last attempt failed (its code and message)

      5 fields of Attempt
      • statusstring

        failed

        One of:

        • failedThe attempt's transaction wasn't sent, didn't confirm or failed on the network: nothing was paid
      • codestringOptional

        The error code (errors): what to do about it (insufficient_funds, token_not_approved, tx_not_sent…)

      • messagestringOptional

        The reason, for the customer

      • txHashstringOptional

        The attempt's transaction, if it was sent

      • atstring

        When it failed

  • customerUuidstringOptional

    CustomerUUID is the refunded transaction's customer (a refund has none of its own); absent without one. Only for the merchant, in its reads (never in webhooks)

    Only for the resource's owner

  • productNamestringOptional

    What the refunded transaction paid for (its product's name, as on the payment or bill). Only for the merchant, in its reads (never in webhooks)

    Only for the resource's owner

  • customerSummaryOptional

    The refunded transaction's customer, named (even once deleted); absent without one. Only for the merchant, in its reads (never in webhooks)

    Only for the resource's owner

    6 fields of Summary
    • uuidstring

      The customer (GET /customers/:uuid)

    • firstNamestring

      First name

    • lastNamestringOptional

      Last name, when given

    • emailstring

      Email, lowercase

    • referencestringOptional

      The merchant's reference of the customer, when it has one

    • deletedboolean

      The merchant deleted the customer: it still names its rows, but GET /customers/:uuid answers 404

refund.rejected: example body

{  "createdAt": "2026-10-01T12:00:00Z",  "data": {    "amount": "10.0042",    "amountMinUnits": "10004200",    "amountUsd": "10.0042",    "chain": "BASE",    "createdAt": "2026-09-29T08:15:00Z",    "currency": {      "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",      "chain": "BASE",      "code": "BASE:USDC",      "decimals": 6,      "id": 8,      "mainCurrencyId": 3,      "name": "USD Coin",      "symbol": "USDC",      "test": false    },    "held": false,    "initiatedBy": "customer",    "merchantMessage": "Only one payment was received for this order",    "paid": "10.0042",    "paidMinUnits": "10004200",    "paidUsd": "10.0042",    "reason": "I was charged twice for the same order",    "refundPercent": "100",    "respondedAt": "2026-10-01T12:00:00Z",    "status": "rejected",    "test": false,    "txUuid": "pay@01a0f755-f200-7000-8000-000000000001",    "uuid": "refund@01a0ec3b-3ba0-7000-8000-000000000006"  },  "id": "evt_cc682edc-ba17-5d1a-8f4f-a7534c2f4783",  "test": false,  "type": "refund.rejected",  "version": "v2"}
Sent as one line, its keys sorted.
Show all 38 lines

member.joined#

What it carries, and when

JoinedMember: the member (GET /members/:userUuid: firstName, lastName?, spaceUuid, acceptedCurrencies, organizationFeePercent, trustedAt?), in the mode they joined, and invitationUuid, the invitation they accepted (POST /invitations answered it): match the event to it

What to do · Marketplace seller

Mark the seller onboarded and store data.userUuid (for On-Behalf-Of); stop selling for a removed one; reconcile what you paid out (Marketplace).

11 fields of data (JoinedMember)
  • userUuidstring
  • firstNamestring
  • lastNamestring

    Absent when the account has none

  • emailstring
  • testboolean

    The mode

  • organizationFeePercentnumber

    The organization's fee on the member's payments in this mode, in bps. Written as organizationFeePercent ("0": none)

  • trustedAtstringOptional

    When the organization started paying them directly; absent while it holds their funds

  • joinedAtstring

    When they joined in this mode

  • acceptedCurrencyIdsarray of integer

    The currencies (GET /currencies/:id) a checkout of the member can be paid in now: those their wallets accept, always within the organization's (the organization's own while it holds their funds: its wallets receive their payments). Subscriptions take the tokens among them. Empty: a checkout of theirs answers 409 merchant_not_ready. Written as acceptedCurrencies, the currency objects

  • spaceUuidstring

    Their personal space in this mode: what POST /auth/switch-space takes for an owner or admin to act as them

  • invitationUuidstring

    The invitation they accepted (POST /invitations answered its uuid): what matches the event to it

member.joined: example body

{  "createdAt": "2026-10-01T12:00:00Z",  "data": {    "acceptedCurrencies": [      {        "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",        "chain": "BASE",        "code": "BASE:USDC",        "decimals": 6,        "id": 8,        "mainCurrencyId": 3,        "name": "USD Coin",        "symbol": "USDC",        "test": false      }    ],    "email": "alice@example.com",    "firstName": "Alice",    "invitationUuid": "01a0f755-f200-7000-8000-00000000000a",    "joinedAt": "2026-10-01T12:00:00Z",    "lastName": "Martin",    "organizationFeePercent": "5",    "spaceUuid": "019eca82-5680-7b00-8000-0000000000b2",    "test": false,    "userUuid": "019eca82-5680-7b00-8000-0000000000b1"  },  "id": "evt_634bf7f1-010f-5f60-8232-06c30f013f28",  "test": false,  "type": "member.joined",  "version": "v2"}
Sent as one line, its keys sorted.
Show all 31 lines

member.removed#

What it carries, and when

Member (with spaceUuid and acceptedCurrencies), as they were before their removal from that mode

What to do · Marketplace seller

Mark the seller onboarded and store data.userUuid (for On-Behalf-Of); stop selling for a removed one; reconcile what you paid out (Marketplace).

10 fields of data (Member)
  • userUuidstring
  • firstNamestring
  • lastNamestring

    Absent when the account has none

  • emailstring
  • testboolean

    The mode

  • organizationFeePercentnumber

    The organization's fee on the member's payments in this mode, in bps. Written as organizationFeePercent ("0": none)

  • trustedAtstringOptional

    When the organization started paying them directly; absent while it holds their funds

  • joinedAtstring

    When they joined in this mode

  • acceptedCurrencyIdsarray of integer

    The currencies (GET /currencies/:id) a checkout of the member can be paid in now: those their wallets accept, always within the organization's (the organization's own while it holds their funds: its wallets receive their payments). Subscriptions take the tokens among them. Empty: a checkout of theirs answers 409 merchant_not_ready. Written as acceptedCurrencies, the currency objects

  • spaceUuidstring

    Their personal space in this mode: what POST /auth/switch-space takes for an owner or admin to act as them

member.removed: example body

{  "createdAt": "2026-10-01T12:00:00Z",  "data": {    "acceptedCurrencies": [      {        "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",        "chain": "BASE",        "code": "BASE:USDC",        "decimals": 6,        "id": 8,        "mainCurrencyId": 3,        "name": "USD Coin",        "symbol": "USDC",        "test": false      }    ],    "email": "alice@example.com",    "firstName": "Alice",    "joinedAt": "2026-06-15T09:00:00Z",    "lastName": "Martin",    "organizationFeePercent": "5",    "spaceUuid": "019eca82-5680-7b00-8000-0000000000b2",    "test": false,    "trustedAt": "2026-07-01T09:00:00Z",    "userUuid": "019eca82-5680-7b00-8000-0000000000b1"  },  "id": "evt_d32aec3c-d46c-57e3-9292-f8433766df46",  "test": false,  "type": "member.removed",  "version": "v2"}
Sent as one line, its keys sorted.
Show all 31 lines

heldFunds.released#

What it carries, and when

The transfer the member received (amount + amountMinUnits in the token, amountUsd, currency, chain, explorerUrl, settlement.transaction), and ledgers, the lines it settled, oldest first (LedgerEntry, as GET /members/:userUuid/held-funds lists them, Held funds): the payments it paid (positive, type: "payment") and the refunds of them it took off (negative, type: "refund"), each with what it counted for (owed, owedMinUnits, owedUsd). amount is their sum

What to do · Marketplace seller

Mark the seller onboarded and store data.userUuid (for On-Behalf-Of); stop selling for a removed one; reconcile what you paid out (Marketplace).

heldFunds.released: example body

{  "createdAt": "2026-10-01T12:00:00Z",  "data": {    "amount": "98.9068",    "amountMinUnits": "98906800",    "amountUsd": "98.9068",    "chain": "BASE",    "createdAt": "2026-10-01T12:00:00Z",    "currency": {      "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",      "chain": "BASE",      "code": "BASE:USDC",      "decimals": 6,      "id": 8,      "mainCurrencyId": 3,      "name": "USD Coin",      "symbol": "USDC",      "test": false    },    "explorerUrl": "https://basescan.org/tx/0x5555555555555555555555555555555555555555555555555555555555555555",    "from": "0x9876543210fedcba9876543210fedcba98765432",    "ledgers": [      {        "amountUsd": "100",        "createdAt": "2026-09-28T12:00:00Z",        "currency": {          "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",          "chain": "BASE",          "code": "BASE:USDC",          "decimals": 6,          "id": 8,          "mainCurrencyId": 3,          "name": "USD Coin",          "symbol": "USDC",          "test": false        },        "description": "Payment pay@01a0f755-f200-7000-8000-000000000001",        "owed": "99.01",        "owedMinUnits": "99010000",        "owedUsd": "99.01",        "settlement": {          "feePercent": "0.99",          "split": {            "merchant": {              "amount": "99.01",              "amountMinUnits": "99010000",              "amountUsd": "99.01"            },            "networkFee": {              "amount": "0",              "amountMinUnits": "0",              "amountUsd": "0"            },            "platform": {              "amount": "0.99",              "amountMinUnits": "990000",              "amountUsd": "0.99"            }          },          "transaction": {            "blockNumber": "52468103",            "blockTime": "2026-09-28T11:59:58Z",            "fee": {              "amount": "0.00000114",              "amountMinUnits": "1140000000000",              "amountUsd": "0.005144"            },            "gasUsed": 95000,            "nativePriceUsd": "4512.37"          }        },        "txUuid": "pay@01a0f755-f200-7000-8000-000000000001",        "type": "payment"      },      {        "amountUsd": "10",        "createdAt": "2026-09-29T12:00:00Z",        "currency": {          "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",          "chain": "BASE",          "code": "BASE:USDC",          "decimals": 6,          "id": 8,          "mainCurrencyId": 3,          "name": "USD Coin",          "symbol": "USDC",          "test": false        },        "description": "Payment pay@01a0d6d7-c600-7000-8000-00000000000c",        "owed": "9.901",        "owedMinUnits": "9901000",        "owedUsd": "9.901",        "settlement": {          "feePercent": "0.99",          "split": {            "merchant": {              "amount": "9.901",              "amountMinUnits": "9901000",              "amountUsd": "9.901"            },            "networkFee": {              "amount": "0.0042",              "amountMinUnits": "4200",              "amountUsd": "0.0042"            },            "platform": {              "amount": "0.099",              "amountMinUnits": "99000",              "amountUsd": "0.099"            }          },          "transaction": {            "blockNumber": "52468103",            "blockTime": "2026-09-29T11:59:58Z",            "fee": {              "amount": "0.00000114",              "amountMinUnits": "1140000000000",              "amountUsd": "0.005144"            },            "gasUsed": 95000,            "nativePriceUsd": "4512.37"          }        },        "txUuid": "pay@01a0d6d7-c600-7000-8000-00000000000c",        "type": "payment"      },      {        "amountUsd": "10.0042",        "createdAt": "2026-09-30T12:00:00Z",        "currency": {          "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",          "chain": "BASE",          "code": "BASE:USDC",          "decimals": 6,          "id": 8,          "mainCurrencyId": 3,          "name": "USD Coin",          "symbol": "USDC",          "test": false        },        "description": "Refund of payment pay@01a0d6d7-c600-7000-8000-00000000000c",        "owed": "-10.0042",        "owedMinUnits": "-10004200",        "owedUsd": "-10.0042",        "refundedTxUuid": "pay@01a0d6d7-c600-7000-8000-00000000000c",        "txUuid": "refund@01a0ec3b-3ba0-7000-8000-000000000006",        "type": "refund"      }    ],    "received": true,    "settlement": {      "transaction": {        "blockNumber": "52468103",        "blockTime": "2026-10-01T11:59:58Z",        "fee": {          "amount": "0.00000114",          "amountMinUnits": "1140000000000",          "amountUsd": "0.005144"        },        "gasUsed": 95000,        "nativePriceUsd": "4512.37"      }    },    "test": false,    "to": "0x1234567890abcdef1234567890abcdef12345678",    "txHash": "0x5555555555555555555555555555555555555555555555555555555555555555",    "type": "heldFundsRelease",    "userUuid": "019eca82-5680-7b00-8000-0000000000b1",    "uuid": "transfer@01a0f755-f200-7000-8000-000000000007"  },  "id": "evt_620352e2-0285-52e8-adc1-64a9774278c1",  "test": false,  "type": "heldFunds.released",  "userUuid": "019eca82-5680-7b00-8000-0000000000b1",  "version": "v2"}
Sent as one line, its keys sorted.
Show all 176 lines

checkout.expired#

What it carries, and when

The checkout session as GET /hosted/checkout-sessions/:uuid shows it, with your metadata: uuid (pay@…/sub@…, the id the payment or subscription would have had), txType, reference, the product (productUuid, productReference, productName, description, priceUsd), amountUsd (the price and its fees), fees, availableCurrencies (currency objects), successUrl/cancelUrl (your URLs, placeholders filled), organizationName, test, createdAt, expiresAt, metadata (the hosted checkout's own fields, asksCustomerDetails and customerDetails, are not sent). No customer is named (match it on uuid, reference or metadata). Sent once per opened session, at its expiresAt, when it wasn't paid (a payment sent right before is waited for), or when you expire it (POST /checkout-sessions/:uuid/expire: expiresAt is then when you did). A subscription checkout's data carries its terms too (frequency, trialPeriod, minPeriods, upgradingFromTrial). A session opened by a product's link (GET /pay/:productUuid) is watched from its first intent: a visit nobody paid from sends nothing. The session confirming a free trial (upgradingFromTrial: true) sends nothing when it expires: the trial goes on

What to do · Abandoned checkout

Release what the order held (stock, a reservation): it can no longer be paid. A failed attempt (lastAttempt on the session) is not the end: the customer may still pay until it expires.

checkout.expired: example body

{  "createdAt": "2026-10-01T12:00:00Z",  "data": {    "amountUsd": "10",    "availableCurrencies": [      {        "chain": "BASE",        "code": "BASE",        "decimals": 18,        "id": 3,        "name": "Base",        "symbol": "BASE",        "test": false      },      {        "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",        "chain": "BASE",        "code": "BASE:USDC",        "decimals": 6,        "id": 8,        "mainCurrencyId": 3,        "name": "USD Coin",        "symbol": "USDC",        "test": false      }    ],    "cancelUrl": "https://shop.example.com/cart",    "createdAt": "2026-09-30T12:00:00Z",    "description": "Lifetime access to the Pro plan",    "expiresAt": "2026-10-01T12:00:00Z",    "fees": [],    "metadata": {      "crm:customer": "C-5521",      "orderId": "order-1043"    },    "organizationName": "Example Shop",    "priceUsd": "10",    "productName": "Pro plan (lifetime)",    "productUuid": "019cadfd-8900-7c00-8000-0000000000c1",    "reference": "order-1043",    "successUrl": "https://qbitflow.app/user/019cadfd-8900-7a00-8000-0000000000a1/manage?uuid=pay@01a0f22f-9600-7000-8000-000000000008&redirectUrl=https%3A%2F%2Fshop.example.com%2Fthanks",    "test": false,    "txType": "payment",    "uuid": "pay@01a0f22f-9600-7000-8000-000000000008"  },  "id": "evt_63b9afe7-5561-5c7e-b2c5-42b4d58ecba8",  "test": false,  "type": "checkout.expired",  "version": "v2"}
Sent as one line, its keys sorted.
Show all 50 lines

webhook.test#

What it carries, and when

TestData: sent only by POST /webhooks/endpoints/:uuid/test

2 fields of data (TestData)
  • endpointUuidstring
  • messagestring

webhook.test: example body

{  "createdAt": "2026-10-01T12:00:00Z",  "data": {    "endpointUuid": "019cadfd-8900-7e00-8000-0000000000e1",    "message": "A test event from the dashboard: nothing happened"  },  "id": "evt_bd1a913d-188e-5ac5-a2d6-5fd25cd67301",  "test": false,  "type": "webhook.test",  "version": "v2"}
Sent as one line, its keys sorted.

Verify the signature

The SDKs verify every delivery locally, with no call.

Verify and parse a webhook (lower level)
const secret = process.env.QBITFLOW_WEBHOOK_SECRET!;try {	// rawBody: the body exactly as received; signatureHeader: its QBitFlow-Signature header.	const event = webhooks.constructEvent(rawBody, signatureHeader, secret);	if (event.type === 'payment.completed') {		console.log(`fulfil order ${event.data.reference}`); // event.data is a PaymentCompleted	}	return 200; // acknowledge every event, the types you ignore included} catch (err) {	if (err instanceof WebhookSignatureError) {		console.log(`rejected: ${err.reason}`); // e.g. noMatchingSignature		return 400;	}	if (err instanceof ValidationError) {		console.log(`not a v2 event: ${err.message}`);		return 400;	}	throw err;}
try:    event = qbitflow.webhooks.construct_event(        raw_body,  # the raw bytes as received, never re-serialized        signature_header,  # the QBitFlow-Signature header        os.environ["QBITFLOW_WEBHOOK_SECRET"],    )except qbitflow.WebhookSignatureError as exc:    print("rejected:", exc.reason)  # e.g. noMatchingSignature, timestampOutsideTolerance    return 400except qbitflow.ValidationError:    return 400  # not a v2 eventif isinstance(event, qbitflow.PaymentCompletedEvent):  # narrow the union by class    print("fulfil order", event.data.reference)return 200  # acknowledge every other type too, or QBitFlow retries it
func handleWebhook(w http.ResponseWriter, r *http.Request) {	// The raw body, exactly as received: the signature covers its bytes.	rawBody, err := io.ReadAll(http.MaxBytesReader(w, r.Body, 1<<20))	if err != nil {		http.Error(w, "cannot read the body", http.StatusBadRequest)		return	}	event, err := qbitflow.ConstructEvent(rawBody, r.Header.Get(qbitflow.SignatureHeader), os.Getenv("QBITFLOW_WEBHOOK_SECRET"))	var sigErr *qbitflow.WebhookSignatureError	if errors.As(err, &sigErr) {		log.Printf("rejected: %s", sigErr.Reason) // e.g. noMatchingSignature, timestampOutsideTolerance		http.Error(w, "invalid signature", http.StatusBadRequest)		return	}	if err != nil {		http.Error(w, "invalid event", http.StatusBadRequest) // not a v2 event		return	} 	if event.Type == qbitflow.EventTypePaymentCompleted {		payment, err := event.PaymentCompleted()		if err != nil {			http.Error(w, "invalid event", http.StatusBadRequest)			return		}		// Deliveries are at least once: fulfil the order once per event.ID.		log.Printf("event %s: order %q paid", event.ID, payment.Reference)	}	w.WriteHeader(http.StatusOK) // to the types you ignore too}
$rawBody = (string) file_get_contents('php://input'); // the bytes as received: never re-serializetry {	$event = \QBitFlow\Webhooks\Webhook::constructEvent(		$rawBody,		$_SERVER['HTTP_QBITFLOW_SIGNATURE'] ?? '',		(string) getenv('QBITFLOW_WEBHOOK_SECRET'),	);} catch (\QBitFlow\Exceptions\WebhookSignatureException $e) {	http_response_code(400); // $e->reason: noMatchingSignature, timestampOutsideTolerance, …	exit;} catch (\QBitFlow\Exceptions\ValidationException) {	http_response_code(400); // not a v2 event (unsupported payload version), or not JSON	exit;} if ($event instanceof \QBitFlow\Events\PaymentCompletedEvent) {	// Deliveries are at least once: skip an $event->id you already processed.	error_log("fulfil order {$event->data->reference} ({$event->data->uuid})");}http_response_code(200); // to every type, the ignored ones too

Every delivery is a POST with a JSON body (Content-Type: application/json, canonical JSON: sorted keys, no spaces) and these headers:

HeaderValue
QBitFlow-Signaturet=<unix seconds>,v1=<hex HMAC-SHA256(secret, t + "." + body)> (v1 names the signature scheme, not an API version); for 24 hours after a rotation, a second v1= with the previous secret (t=…,v1=<new>,v1=<previous>)
QBitFlow-Event-Idthe event id (evt_…)
QBitFlow-Event-Typethe event type
QBitFlow-Webhook-Versionthe body's version: v2
User-AgentQBitFlow-Webhooks/2

Deliveries come from QBitFlow's cloud workers, without fixed source IP addresses: don't allow-list IPs, verify the signature.

Verify (what the SDK helpers do, no call needed). The receiver holds one secret, the one it was configured with; the header may carry several signatures:

  1. Read the raw body as received (don't re-serialize parsed JSON).
  2. Split QBitFlow-Signature on ,, each part on its first =, trimming spaces: keep t and every v1 value, ignore any other key (a future scheme; never fall back to another one). No t, or no v1 → invalid.
  3. Refuse a t more than 5 minutes from your clock, either way (replays). Each attempt is signed afresh, so a retry carries a new t.
  4. Compute once expected = hex(HMAC-SHA256(secret, t + "." + rawBody)), lowercase.
  5. Compare expected with each v1, in constant time; valid if any matches. Don't stop at the first v1, and don't take the last: during a rotation the receiver's secret may match either.

After a rotation the header is t=…,v1=<new secret's>,v1=<previous secret's> for 24 hours, then t=…,v1=<new secret's> again. A receiver still configured with the previous secret matches the second signature, one already updated matches the first: it switches secrets whenever it likes within the day, without losing a delivery, and there is nothing to configure on its side. Only one previous secret is kept: rotating again within those 24 hours ends the first overlap at once (the header then carries the newest secret and the one just replaced), so a receiver still on the oldest secret starts failing; rotate once, update, then rotate again if needed.

POST /webhooks/verify runs the same check server-side with the endpoint's current secret (whsec_…, from its creation or GET /webhooks/endpoints/:uuid/secret).

Worked example (the SDKs' test vector; core/src/app/events/events_test.go checks it):

raw body{"createdAt":"2026-10-01T12:00:00Z","data":{},"id":"evt_3f1c2d4e-5a6b-5c7d-8e9f-0a1b2c3d4e5f","test":false,"type":"webhook.test","version":"v2"}
t1790856000
new secret → v1whsec_new_secret → 4a158046f55556e922bdec376a917c3ac338ba575b495f825427541a60bd2f4d
previous secret → v1whsec_old_secret → 1cc78a6297ad96dd282d9d0399def50ed42a4e651885ac79f8528194227bc4f5
header during the rotationt=1790856000,v1=4a158046…2f4d,v1=1cc78a62…c4f5 (the two values above, in full)

With the clock at t + 60 s: whsec_new_secret and whsec_old_secret are both valid, any other secret is invalid; at t + 6 min every secret is invalid (too old).

Delivery and retries

  • Any 2xx is delivered. Anything else is retried: another status (a redirect, a 4xx, a 5xx), a timeout or a network error. Answer 2xx to the events you ignore.
  • Live mode: retries come 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours after the failed attempt, then every 12 hours, for 3 days after the first attempt (11 attempts). Test mode: 3 retries in a few hours (5 minutes, 30 minutes, 2 hours after the failed attempt). Then the event is abandoned for that endpoint. These are Stripe's rules.
  • Every attempt uses the endpoint's current URL and secret. Deliveries survive restarts of QBitFlow.
  • One event per request, delivered at least once. The same event can reach an endpoint more than once: an attempt whose 2xx didn't reach QBitFlow (the connection dropped after your server processed it, or it answered after the timeout), a restart on QBitFlow's side between a post and its record, or a manual resend. Deduplicate on id (the QBitFlow-Event-Id header carries it too) and make your handler idempotent.
  • A manual resend (POST /webhooks/events/:id/resend, Webhooks § Event log) that delivers an event ends its automatic retries to that endpoint: the next one sends nothing.
  • Deliveries have a 30-second timeout (connection, headers and body together; the connection must open within 10 seconds, then your answer has the rest of the 30), never follow redirects, and never go to private, loopback or other non-public addresses. Answer fast: store the event, answer 2xx, then process it asynchronously (a queue, a background job); an answer after 30 seconds is a failed attempt, retried even if your server processed it.
  • Order is kept per endpoint within one publication (subscription.created before its first subscription.billed): the next event waits until the previous one is delivered or abandoned. While it waits, the event log shows it pending with waitingFor the earlier event, and no attempt yet (Webhooks § Event log). Across publications there is no order, and nothing waits: a renewal's subscription.billed never waits for an earlier event your endpoint is failing. Use createdAt and the resources' own timestamps.
  • An endpoint disabled (PATCH …/:uuid {"enabled": false}, or by QBitFlow, below) or deleted receives nothing more: a delivery in progress finds it so at its next attempt and stops, for the rest of its events too. Enabled again before that attempt, the delivery goes on (as Stripe does). Events published while it is disabled are never sent to it.

Failing endpoints

  • An endpoint whose every attempt has failed for 24 hours is reported by email to its space's team (each owner and admin; for a member's endpoint, the member), at most once a day while it fails (a UTC calendar day: the check runs hourly, so the next email can come soon after midnight UTC). The setting emails.webhookFailures (on by default) turns these emails off.
  • After 3 days without a successful delivery, the endpoint is disabled: disabledAt is set, an email tells its team (same setting), and it receives nothing more. PATCH /webhooks/endpoints/:uuid {"enabled": true} enables it again; the failures before don't count anymore.
  • A disabled endpoint isn't checked any more: no email about it until it is enabled again (and fails again for a day).
  • Test-mode endpoints are never checked: a test endpoint is usually only reachable while its developer tests, so failing between tests is expected. It is never disabled for failing and no email is sent about it; its failingSince still says how it stands. Each failed delivery is still retried 3 times (§ Delivery and retries).
Handle webhooks
const router = webhooks	.router(process.env.QBITFLOW_WEBHOOK_SECRET!, {		// Every 400 (bad signature or body: event null) and 500 (a handler threw). The router		// answers without the details: log them here.		onError: (event, err) => console.error('webhook rejected:', event?.id, err.message),	})	.on('payment.completed', async (payment, event) => {		await fulfilOrder(payment.reference, event.id); // idempotent: deduplicates on event.id	}); // nodeHandler() reads the raw body itself (at most 1 MiB): mount it before any body parser.const handleWebhook = router.nodeHandler();createServer((req, res) => {	if (req.url === '/webhooks/qbitflow') void handleWebhook(req, res);	else res.writeHead(404).end();}).listen(8080);
router = qbitflow.WebhookRouter(os.environ["QBITFLOW_WEBHOOK_SECRET"])  @router.on("payment.completed")  # the handler gets the typed data, then the eventdef fulfil(data: qbitflow.PaymentCompleted, event: qbitflow.Event) -> None:    # At least once: the same event can arrive twice, deduplicate on event.id.    mark_order_paid(data.reference or data.uuid, event_id=event.id)  app = FastAPI()# Verifies the QBitFlow-Signature header on the raw body, parses the event, runs the handler# for its type, and answers the status QBitFlow expects (200, 400 or 500).app.add_api_route("/webhooks/qbitflow", router.fastapi_endpoint(), methods=["POST"])
var fulfilled sync.Map // stands for your database: deliveries are at least oncerouter := qbitflow.NewWebhookRouter(os.Getenv("QBITFLOW_WEBHOOK_SECRET"),	qbitflow.WithWebhookErrorHandler(func(event *qbitflow.Event, err error) {		log.Printf("webhook refused or failed: %v", err) // event is nil when the body could not be parsed	}),).	OnPaymentCompleted(func(ctx context.Context, payment *qbitflow.PaymentCompleted, event *qbitflow.Event) error {		if _, done := fulfilled.LoadOrStore(event.ID, true); done {			return nil // a retry of an event already handled		}		log.Printf("fulfil order %q: %s USD (%s, %v)", payment.Reference, payment.AmountUSD, payment.UUID, payment.Metadata)		return nil // an error answers 500, and QBitFlow delivers the event again	}).	OnCheckoutExpired(func(ctx context.Context, expired *qbitflow.CheckoutExpired, event *qbitflow.Event) error {		if expired.Payment != nil {			log.Printf("release order %q", expired.Payment.Reference)		}		return nil	}) // The router is an http.Handler: it verifies the QBitFlow-Signature header on the raw body,// parses the event, runs the handlers of its type and answers 200, 400 or 500.http.Handle("POST /webhooks/qbitflow", router)
$router = (new \QBitFlow\Webhooks\WebhookRouter((string) getenv('QBITFLOW_WEBHOOK_SECRET')))	->on(\QBitFlow\Enums\EventType::PAYMENT_COMPLETED, function (		\QBitFlow\Models\PaymentCompleted $payment,		\QBitFlow\Events\Event $event,	): void {		// Deliveries are at least once: skip an $event->id you already processed.		error_log("event {$event->id}: fulfil order {$payment->reference} ({$payment->uuid}), {$payment->amountUsd} USD");	})	->on(\QBitFlow\Enums\EventType::SUBSCRIPTION_STATUS_CHANGED, function (		\QBitFlow\Models\SubscriptionStatusChanged $subscription,	): void {		error_log("{$subscription->uuid} is {$subscription->status}, access: " . ($subscription->hasAccess() ? 'yes' : 'no'));	})	->onError(fn (?\QBitFlow\Events\Event $event, \Throwable $error) => error_log("webhook refused or failed: {$error->getMessage()}")); // Reads php://input and the QBitFlow-Signature header, runs the handler of the event's type, and// answers 200 (ignored types too), 400 (bad signature) or 500 (a handler threw: QBitFlow retries).$router->handleGlobals();