POST/v1/channel-commissions
Record a channel's cost at a location
A channel's stated cost, in force from a business date. A change is a new version, so the net of orders already taken is not restated; the same channel and date again corrects that version. The rate is the merchant's estimate of what a channel takes, a cost against revenue and never a tender. An owner or a manager.
Parameters
-
Idempotency-Keyheader · stringRequired
A client-generated key making this request safe to retry.
Required on every mutation that creates a resource or moves money. A till on bad wifi cannot tell a lost response from a lost request; without a key its only options are to risk a duplicate sale or to drop a real one. With a key it retries freely: the first request to arrive is processed, and every replay returns that same stored response.
Rules:
- Mint the key once, when the operation is first queued, and reuse it for every retry. A fresh key per attempt defeats the mechanism entirely.
- Keys are scoped per merchant and per operation, and are retained for 30 days — long enough that a till switched off mid-sync can retry with the same key however long it stayed on the shelf.
- Replaying a key with a different request body is a client bug and is rejected
with
422, rather than silently returning the first result — quietly serving the old response would hide a real inconsistency in the caller.
A UUIDv7 or ULID is a good choice.
Request body application/json · ChannelCommissionCreate
channelstring · enumRequired
onlineintegrationeffective_fromstring · dateRequired
location_idstring · uuidRequired
percentagePercentageRequired
A share of a whole, from
"0"to"100", as a percentage in a decimal string with at most four decimal places, as Square'spercentageand Stripe Tax'spercentage_decimalare: UK VAT at 20% is"20", and an 8.875% US sales tax is"8.875".A string because a JSON number is a binary float to most parsers, and a float cannot hold most decimal fractions exactly (RFC 7493 §2.2). Tenvelo computes with a percentage as an integer in parts per million (
"8.875"is 88750), which every four-place percentage is exactly. A client that computes with one should read the digits the same way, never through a float.pattern ^(100(\.0{1,4})?|(0|[1-9][0-9]?)(\.[0-9]{1,4})?)$
Responses
-
201
The version, recorded.
ChannelCommission · application/json
channelstring · enumRequired
onlineintegrationeffective_fromstring · dateRequiredNullable
The business date it first applies, or null for a rate in force since the start.
percentagePercentageRequired
A share of a whole, from
"0"to"100", as a percentage in a decimal string with at most four decimal places, as Square'spercentageand Stripe Tax'spercentage_decimalare: UK VAT at 20% is"20", and an 8.875% US sales tax is"8.875".A string because a JSON number is a binary float to most parsers, and a float cannot hold most decimal fractions exactly (RFC 7493 §2.2). Tenvelo computes with a percentage as an integer in parts per million (
"8.875"is 88750), which every four-place percentage is exactly. A client that computes with one should read the digits the same way, never through a float.pattern ^(100(\.0{1,4})?|(0|[1-9][0-9]?)(\.[0-9]{1,4})?)$
statestring · enumRequired
Where the version stands on the location's business date today.
in_forcescheduledsuperseded
-
400
The request could not be parsed. Malformed JSON, a bad query parameter, or a header that failed to decode, among them an
API-Versionlater than the server speaks or not a date (api-version-unknown, with the server's own inapi_version).Distinct from
422: a400never reached validation, soinvalid_paramsis absent. Retrying without changing the request will fail again, except that a request refused for itsAPI-Versiongoes 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
401after 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
403on a resource the caller may not see, and as404where even confirming existence would leak information. Retrying will not help. -
404
No such resource, or none visible to this caller.
Also returned instead of
403where acknowledging existence would itself leak information — so a404does not prove the id is unused globally, only that it is not yours. -
410
The request names an
API-Versionthis server no longer serves (docs/decisions/0011). Nothing in it was read.api_versionnames 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_paramslists 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-Keyis replayed with a different body, and when device-computed order totals disagree with the server's recomputation. -
429
Rate limit exceeded. Honour
Retry-Afterrather 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; noRateLimitfield is sent, so this response and itsRetry-Afterare the whole of the contract. -
500
Something failed on our side.
trace_ididentifies 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.