API referenceTenvelo API
https://api.tenvelo.com The contract, as OpenAPI

Contract 2026-10-10. Every path below carries its own major version.

Payments · Reference

Payouts

What the processor transferred to the merchant's bank, itemised into the sales, refunds and fees that make up each transfer — so a bank line can be traced back to the trading it came from rather than taken on trust.

Operations
4
Base
https://api.tenvelo.com
  1. GET/v1/payouts

    List a merchant's payouts

    No. listPayoutsAuth payouts:read

    Returns the merchant's payouts, newest first, each with the movements that make it up. Paged with the cursor of the previous page.

    Cursor rather than offset for a reason that bites here: payouts arrive at the head of this list, so a second page read by offset would skip the rows the first page was pushed down by. The reader is a bookkeeper matching bank lines, who is exactly who cannot afford a silently missing one.

    Parameters

    • cursorquery · string

      Opaque cursor from a previous page's next_cursor. Omit to start from the beginning.

      Pass it back exactly as received. It is base64 and its structure is not part of this contract — decoding it, or constructing one by hand, will break without notice.

    • limitquery · integer · int32

      Maximum items to return. The server may return fewer, so always drive the loop from has_more rather than from the size of the page you got back.

    Responses

    • 200

      A page of payouts.

      Payout page · application/json
      • has_moreboolean

        Required

        Whether another page may follow.

      • itemsarray of Payout

        Required

        • idstring · uuid

          Required

        • amountMoney

          Required

          What arrived, which is the sum of the items' signed amounts.

          • amountinteger · int64

            Required

            The amount, as a whole number of the currency's minor unit. May be negative to express a refund, discount or credit.

          • currencyCurrency

            Required

            ISO 4217 currency code.

            Determines the minor unit that Money.amount is expressed in. Zero-decimal currencies such as JPY have a minor unit equal to the major unit.

            One of 182 values; see the schema.

        • arrival_datestring · date

          Nullable

          When the processor expects it to arrive, where it has said.

        • by_kindarray of PayoutKindTotal

          Required

          The items summed by kind, signed: what each kind added to the payout or took from it, so they add up to amount. Captures first, then what reduced them; a kind the payout has none of is absent.

          • kindstring · enum

            Required

            capturerefundfeechargebackadjustment

          • amountMoney

            Required

            Signed, so the kinds of a payout add up to its amount.

            • amountinteger · int64

              Required

              The amount, as a whole number of the currency's minor unit. May be negative to express a refund, discount or credit.

            • currencyCurrency

              Required

              ISO 4217 currency code.

              Determines the minor unit that Money.amount is expressed in. Zero-decimal currencies such as JPY have a minor unit equal to the major unit.

              One of 182 values; see the schema.

        • itemsarray of PayoutItem

          Required

          The movements that make up the total, in the order the processor gave them.

          • kindstring · enum

            Required

            capture — a sale settling, which adds to the payout.

            refund — money returned to a cardholder, which takes from it.

            fee — the processor's charge.

            chargeback — a disputed sale clawed back.

            adjustment — anything else the processor moved, carrying its own sign.

            capturerefundfeechargebackadjustment

          • amountMoney

            Required

            The movement's magnitude. For every kind but adjustment this is non-negative and the kind decides the direction; an adjustment carries its own sign.

            • amountinteger · int64

              Required

              The amount, as a whole number of the currency's minor unit. May be negative to express a refund, discount or credit.

            • currencyCurrency

              Required

              ISO 4217 currency code.

              Determines the minor unit that Money.amount is expressed in. Zero-decimal currencies such as JPY have a minor unit equal to the major unit.

              One of 182 values; see the schema.

          • order_idstring · uuid

            Nullable

            The order this movement belongs to, when it is one of ours.

          • payment_idstring · uuid

            Nullable

            The payment this movement settles, when it is one of ours.

          • referencestring

            Required

            The processor's own reference for the movement, for reconciliation.

            ≤ 255 chars

        • payment_account_idstring · uuid

          Required

          The payment account the money was paid out of.

        • settlement_datestring · date

          Nullable

          The processor's settlement day the payout was built from, on the processor's own clock: the takings it pays out. Null for a payout recorded before settlement reports were kept.

        • statusstring · enum

          Required

          pending — the processor has announced it but the money has not moved (Stripe's word).

          paid — it has left the processor. Whether it has cleared the merchant's bank is the bank's business and not something this records.

          failed — it did not go through; the money stays with the processor and is normally carried into a later payout.

          pendingpaidfailed

        • transitionsarray of PayoutTransition

          Every move of the payout's status, oldest first: who said so and when. Present on a payout read by its id.

          • amountMoney

            The amount the processor gave with the move, where it gave one.

            • amountinteger · int64

              Required

              The amount, as a whole number of the currency's minor unit. May be negative to express a refund, discount or credit.

            • currencyCurrency

              Required

              ISO 4217 currency code.

              Determines the minor unit that Money.amount is expressed in. Zero-decimal currencies such as JPY have a minor unit equal to the major unit.

              One of 182 values; see the schema.

          • processor_event_idstring

            The processor's own id for the event, where it gave one.

          • processor_occurred_atstring · date-time

            Nullable

            When the processor says the move happened.

          • received_atstring · date-time

            Required

            When we learned of it.

          • sourcestring · enum

            Required

            processor when the processor said so, merchant when somebody here recorded it, and reconstructed when nobody said so and it was inferred from the status the payout held.

            processormerchantreconstructed

          • statusstring · enum

            Required

            pendingpaidfailed

      • next_cursorstring

        Nullable

        Pass as cursor for the next page, when has_more is true.

    • 400

      The request could not be parsed. Malformed JSON, a bad query parameter, or a header that failed to decode, among them an API-Version later than the server speaks or not a date (api-version-unknown, with the server's own in api_version).

      Distinct from 422: a 400 never reached validation, so invalid_params is absent. Retrying without changing the request will fail again, except that a request refused for its API-Version goes once the server is updated.

    • 401

      No credentials, or credentials that could not be verified — missing bearer token, malformed JWT, expired token, or a signature that failed against the issuer's JWKS.

      Refresh the token and retry once. Repeated 401 after a refresh means the device needs to re-enrol.

    • 403

      Authenticated, but not permitted. The token is valid and its merchant was resolved, but the caller's role does not allow this operation, or the target belongs to a different merchant.

      Cross-tenant access is reported as 403 on a resource the caller may not see, and as 404 where even confirming existence would leak information. Retrying will not help.

    • 410

      The request names an API-Version this server no longer serves (docs/decisions/0011). Nothing in it was read. api_version names the version the server speaks: the client is behind, and sends the request again once it is updated.

    • 422

      The request parsed but failed validation.

      invalid_params lists every failing field with a JSON Pointer, not just the first, so a client can mark up a whole form in one pass rather than making the user fix errors one round trip at a time.

      Also returned when an Idempotency-Key is replayed with a different body, and when device-computed order totals disagree with the server's recomputation.

    • 429

      Rate limit exceeded. Honour Retry-After rather than applying your own backoff.

      A fleet of tills coming back online together after an outage is the realistic cause, which is exactly when uncoordinated client-side retry turns a brief outage into a sustained one. The limit is per process, and per site for what records trade there — a request spends its site's budget, and a till's own smaller one inside it — and per (client, merchant) for everything else; no RateLimit field is sent, so this response and its Retry-After are the whole of the contract.

    • 500

      Something failed on our side. trace_id identifies the request in our tracing — quote it in a support ticket.

      The body never carries internal detail: no SQL, no stack traces, no upstream error strings. Safe to retry with the same Idempotency-Key.

  2. GET/v1/payouts/{id}

    Get one payout

    No. getPayoutAuth payouts:read

    One payout with its itemised movements, so a bookkeeper can trace a single bank line back to the sales, refunds and fees that make it up.

    Parameters

    • idpath · string · uuid

      Required

      UUIDv7 of the resource.

      For resources a device can originate — orders, and their line items — this id is minted by the client before the record is ever synced, which is what makes an offline create idempotent.

    Responses

    • 200

      The payout.

      Payout · application/json
      • idstring · uuid

        Required

      • amountMoney

        Required

        What arrived, which is the sum of the items' signed amounts.

        • amountinteger · int64

          Required

          The amount, as a whole number of the currency's minor unit. May be negative to express a refund, discount or credit.

        • currencyCurrency

          Required

          ISO 4217 currency code.

          Determines the minor unit that Money.amount is expressed in. Zero-decimal currencies such as JPY have a minor unit equal to the major unit.

          One of 182 values; see the schema.

      • arrival_datestring · date

        Nullable

        When the processor expects it to arrive, where it has said.

      • by_kindarray of PayoutKindTotal

        Required

        The items summed by kind, signed: what each kind added to the payout or took from it, so they add up to amount. Captures first, then what reduced them; a kind the payout has none of is absent.

        • kindstring · enum

          Required

          capturerefundfeechargebackadjustment

        • amountMoney

          Required

          Signed, so the kinds of a payout add up to its amount.

          • amountinteger · int64

            Required

            The amount, as a whole number of the currency's minor unit. May be negative to express a refund, discount or credit.

          • currencyCurrency

            Required

            ISO 4217 currency code.

            Determines the minor unit that Money.amount is expressed in. Zero-decimal currencies such as JPY have a minor unit equal to the major unit.

            One of 182 values; see the schema.

      • itemsarray of PayoutItem

        Required

        The movements that make up the total, in the order the processor gave them.

        • kindstring · enum

          Required

          capture — a sale settling, which adds to the payout.

          refund — money returned to a cardholder, which takes from it.

          fee — the processor's charge.

          chargeback — a disputed sale clawed back.

          adjustment — anything else the processor moved, carrying its own sign.

          capturerefundfeechargebackadjustment

        • amountMoney

          Required

          The movement's magnitude. For every kind but adjustment this is non-negative and the kind decides the direction; an adjustment carries its own sign.

          • amountinteger · int64

            Required

            The amount, as a whole number of the currency's minor unit. May be negative to express a refund, discount or credit.

          • currencyCurrency

            Required

            ISO 4217 currency code.

            Determines the minor unit that Money.amount is expressed in. Zero-decimal currencies such as JPY have a minor unit equal to the major unit.

            One of 182 values; see the schema.

        • order_idstring · uuid

          Nullable

          The order this movement belongs to, when it is one of ours.

        • payment_idstring · uuid

          Nullable

          The payment this movement settles, when it is one of ours.

        • referencestring

          Required

          The processor's own reference for the movement, for reconciliation.

          ≤ 255 chars

      • payment_account_idstring · uuid

        Required

        The payment account the money was paid out of.

      • settlement_datestring · date

        Nullable

        The processor's settlement day the payout was built from, on the processor's own clock: the takings it pays out. Null for a payout recorded before settlement reports were kept.

      • statusstring · enum

        Required

        pending — the processor has announced it but the money has not moved (Stripe's word).

        paid — it has left the processor. Whether it has cleared the merchant's bank is the bank's business and not something this records.

        failed — it did not go through; the money stays with the processor and is normally carried into a later payout.

        pendingpaidfailed

      • transitionsarray of PayoutTransition

        Every move of the payout's status, oldest first: who said so and when. Present on a payout read by its id.

        • amountMoney

          The amount the processor gave with the move, where it gave one.

          • amountinteger · int64

            Required

            The amount, as a whole number of the currency's minor unit. May be negative to express a refund, discount or credit.

          • currencyCurrency

            Required

            ISO 4217 currency code.

            Determines the minor unit that Money.amount is expressed in. Zero-decimal currencies such as JPY have a minor unit equal to the major unit.

            One of 182 values; see the schema.

        • processor_event_idstring

          The processor's own id for the event, where it gave one.

        • processor_occurred_atstring · date-time

          Nullable

          When the processor says the move happened.

        • received_atstring · date-time

          Required

          When we learned of it.

        • sourcestring · enum

          Required

          processor when the processor said so, merchant when somebody here recorded it, and reconstructed when nobody said so and it was inferred from the status the payout held.

          processormerchantreconstructed

        • statusstring · enum

          Required

          pendingpaidfailed

    • 400

      The request could not be parsed. Malformed JSON, a bad query parameter, or a header that failed to decode, among them an API-Version later than the server speaks or not a date (api-version-unknown, with the server's own in api_version).

      Distinct from 422: a 400 never reached validation, so invalid_params is absent. Retrying without changing the request will fail again, except that a request refused for its API-Version goes once the server is updated.

    • 401

      No credentials, or credentials that could not be verified — missing bearer token, malformed JWT, expired token, or a signature that failed against the issuer's JWKS.

      Refresh the token and retry once. Repeated 401 after a refresh means the device needs to re-enrol.

    • 403

      Authenticated, but not permitted. The token is valid and its merchant was resolved, but the caller's role does not allow this operation, or the target belongs to a different merchant.

      Cross-tenant access is reported as 403 on a resource the caller may not see, and as 404 where even confirming existence would leak information. Retrying will not help.

    • 404

      No such resource, or none visible to this caller.

      Also returned instead of 403 where acknowledging existence would itself leak information — so a 404 does not prove the id is unused globally, only that it is not yours.

    • 410

      The request names an API-Version this server no longer serves (docs/decisions/0011). Nothing in it was read. api_version names the version the server speaks: the client is behind, and sends the request again once it is updated.

    • 429

      Rate limit exceeded. Honour Retry-After rather than applying your own backoff.

      A fleet of tills coming back online together after an outage is the realistic cause, which is exactly when uncoordinated client-side retry turns a brief outage into a sustained one. The limit is per process, and per site for what records trade there — a request spends its site's budget, and a till's own smaller one inside it — and per (client, merchant) for everything else; no RateLimit field is sent, so this response and its Retry-After are the whole of the contract.

    • 500

      Something failed on our side. trace_id identifies the request in our tracing — quote it in a support ticket.

      The body never carries internal detail: no SQL, no stack traces, no upstream error strings. Safe to retry with the same Idempotency-Key.

  3. GET/v1/payouts/{id}/export

    Export one payout's movements as CSV

    No. getPayoutExportAuth payouts:read

    Streams a payout's itemised movements as CSV, so a bookkeeper can take a single payout into a ledger or an accounts package without re-keying it.

    One row per movement: kind, reference, amount_minor and currency. amount_minor follows the PayoutItem amount: a magnitude whose direction is its kind, except an adjustment, which carries its own sign. Amounts are the currency's minor unit as integers, as everywhere else in this API — a spreadsheet reading them as decimals would reintroduce the rounding this contract exists to avoid.

    Parameters

    • idpath · string · uuid

      Required

      UUIDv7 of the resource.

      For resources a device can originate — orders, and their line items — this id is minted by the client before the record is ever synced, which is what makes an offline create idempotent.

    Responses

    • 200

      The payout's movements, one row each.

    • 400

      The request could not be parsed. Malformed JSON, a bad query parameter, or a header that failed to decode, among them an API-Version later than the server speaks or not a date (api-version-unknown, with the server's own in api_version).

      Distinct from 422: a 400 never reached validation, so invalid_params is absent. Retrying without changing the request will fail again, except that a request refused for its API-Version goes once the server is updated.

    • 401

      No credentials, or credentials that could not be verified — missing bearer token, malformed JWT, expired token, or a signature that failed against the issuer's JWKS.

      Refresh the token and retry once. Repeated 401 after a refresh means the device needs to re-enrol.

    • 403

      Authenticated, but not permitted. The token is valid and its merchant was resolved, but the caller's role does not allow this operation, or the target belongs to a different merchant.

      Cross-tenant access is reported as 403 on a resource the caller may not see, and as 404 where even confirming existence would leak information. Retrying will not help.

    • 404

      No such resource, or none visible to this caller.

      Also returned instead of 403 where acknowledging existence would itself leak information — so a 404 does not prove the id is unused globally, only that it is not yours.

    • 410

      The request names an API-Version this server no longer serves (docs/decisions/0011). Nothing in it was read. api_version names the version the server speaks: the client is behind, and sends the request again once it is updated.

    • 429

      Rate limit exceeded. Honour Retry-After rather than applying your own backoff.

      A fleet of tills coming back online together after an outage is the realistic cause, which is exactly when uncoordinated client-side retry turns a brief outage into a sustained one. The limit is per process, and per site for what records trade there — a request spends its site's budget, and a till's own smaller one inside it — and per (client, merchant) for everything else; no RateLimit field is sent, so this response and its Retry-After are the whole of the contract.

    • 500

      Something failed on our side. trace_id identifies the request in our tracing — quote it in a support ticket.

      The body never carries internal detail: no SQL, no stack traces, no upstream error strings. Safe to retry with the same Idempotency-Key.

  4. POST/v1/settlement-runs/batch-create

    Queue settlement runs

    No. batchCreateSettlementRunsAuth payouts:read

    Queues the ingest of a payment account's settlement for a trading day: the worker reads the processor's report and stores the payout it describes. A batch of one is how a single day is ingested.

    Each item carries its own idempotency_key and stands or falls on its own. The key is load-bearing rather than ceremonial — a run submitted twice would store the day's payout twice, and a merchant would reconcile one bank line against two records of the same money. A replayed key returns the run already queued.

    The work is asynchronous: an item's result names the job, and the job is polled for what became of it.

    Request body application/json · SettlementRunBatchCreateRequest

    • itemsarray of SettlementRunBatchCreateItem

      Required

      ≥ 1 items · ≤ 500 items

      • business_datestring · date

        Required

        The trading day to ingest, in the location's own calendar.

      • idempotency_keyItemIdempotencyKey

        Required

        A key for one item in a batch, minted by the client when it queues the item and reused on every attempt to send it. The item, not the request, is the unit of work: a retried batch returns the stored result for every item already applied under its key. A fresh key per attempt defeats the whole mechanism.

        Scoped per merchant, so keys need only be unique within a merchant's own traffic.

        16–255 chars

      • payment_account_idstring · uuid

        Required

        The payment account whose settlement to ingest. Must be this merchant's own.

    Responses

    • 200

      The batch was processed. results says what happened to each item.

      BatchResponse · application/json
      • resultsarray of BatchResult

        Required

        • idstring · uuid

          The resource the item created or changed. Absent when it failed.

        • idempotency_keystring

          Required

          The item's key, echoed so a result can be matched by key as well as position.

        • problemProblem

          Nullable

          Why the item failed, as an RFC 9457 problem. A 422 means the item is malformed and will never succeed — drop it and show it to staff rather than retrying for ever. A 409 or a 5xx means try again later.

          • typestring · uri

            Required

            A stable URI identifying the problem kind. This is the field to branch on.

            default about:blank

          • api_versionstring

            Present on an api-version-retired or api-version-unknown refusal: the API-Version this server speaks (docs/decisions/0011).

          • detailstring

            A human-readable explanation specific to this occurrence. Safe to show to a developer; never contains internal state, SQL, or stack traces.

          • instancestring · uri-reference

            A URI reference identifying the specific occurrence.

          • invalid_paramsarray of InvalidParam

            Present on 422 responses. Lists every field that failed validation, rather than only the first, so a client can highlight an entire form in one pass.

            • codestring

              Stable machine-readable code for this failure, for clients that localise their own messages rather than showing reason.

            • pointerstring

              Required

              RFC 6901 JSON Pointer to the offending value within the request body. A pointer rather than a field name so that failures nested inside arrays — the third line item of an order — are addressable at all.

            • reasonstring

              Required

              Why this value was rejected, in human-readable form.

          • refusalsarray of Location refusal

            Present on 409 responses to a withdrawal refused by the state it depends on (deleting a location with tills still assigned, open orders, …). Lists every reason at once, each with how much stands in the way, so a client can say what to put right and link to it rather than walking a person through one refusal after another.

            • kindstring

              Required

              A stable refusal kind — devices, open_orders, drawer_sessions, bookings or stock — that a client links to the screen that puts it right. Branch on this field, never on the surrounding prose.

            • countinteger

              Required

              How many things stand in the way of the withdrawal, so a client can repeat the number rather than re-read prose.

              ≥ 1

          • statusinteger

            Required

            The HTTP status code, repeated here so the body is self-contained.

            100–599

          • titlestring

            Required

            A short, human-readable summary of the problem kind.

          • trace_idstring

            The OpenTelemetry trace id for this request. Quote it in a support ticket and the whole request can be pulled up.

        • statusstring · enum

          Required

          created — the item made a new resource. updated — it changed an existing one.

          unchanged — this key was already applied and the original outcome stands. A success, not an error: it is what a client retrying a batch it was unsure landed should expect, and it clears the item from its queue exactly as created does.

          failed — the item was refused, and nothing else in the batch was affected. See problem.

          createdupdatedunchangedfailed

        • versioninteger · int64

          The resource's version after the item. Absent when it failed.

    • 400

      The request could not be parsed. Malformed JSON, a bad query parameter, or a header that failed to decode, among them an API-Version later than the server speaks or not a date (api-version-unknown, with the server's own in api_version).

      Distinct from 422: a 400 never reached validation, so invalid_params is absent. Retrying without changing the request will fail again, except that a request refused for its API-Version goes once the server is updated.

    • 401

      No credentials, or credentials that could not be verified — missing bearer token, malformed JWT, expired token, or a signature that failed against the issuer's JWKS.

      Refresh the token and retry once. Repeated 401 after a refresh means the device needs to re-enrol.

    • 403

      Authenticated, but not permitted. The token is valid and its merchant was resolved, but the caller's role does not allow this operation, or the target belongs to a different merchant.

      Cross-tenant access is reported as 403 on a resource the caller may not see, and as 404 where even confirming existence would leak information. Retrying will not help.

    • 410

      The request names an API-Version this server no longer serves (docs/decisions/0011). Nothing in it was read. api_version names the version the server speaks: the client is behind, and sends the request again once it is updated.

    • 422

      The request parsed but failed validation.

      invalid_params lists every failing field with a JSON Pointer, not just the first, so a client can mark up a whole form in one pass rather than making the user fix errors one round trip at a time.

      Also returned when an Idempotency-Key is replayed with a different body, and when device-computed order totals disagree with the server's recomputation.

    • 429

      Rate limit exceeded. Honour Retry-After rather than applying your own backoff.

      A fleet of tills coming back online together after an outage is the realistic cause, which is exactly when uncoordinated client-side retry turns a brief outage into a sustained one. The limit is per process, and per site for what records trade there — a request spends its site's budget, and a till's own smaller one inside it — and per (client, merchant) for everything else; no RateLimit field is sent, so this response and its Retry-After are the whole of the contract.

    • 500

      Something failed on our side. trace_id identifies the request in our tracing — quote it in a support ticket.

      The body never carries internal detail: no SQL, no stack traces, no upstream error strings. Safe to retry with the same Idempotency-Key.