Client billing
Generated from contract version 1.0.0 — the same document /v1/api/openapi.json serves.
Prerequisites
Section titled “Prerequisites”- An API key in
x-api-key, or a browser session cookie X-Organization-Idnaming the workspace to act in, unless the session already has an active one- The base URL for your deployment,
https://server.hlix.aiby default
Operations
Section titled “Operations”| Method | Path | Operation |
|---|---|---|
GET |
/v1/api/client-billing/billables |
List the ledger |
POST |
/v1/api/client-billing/billables |
Record a billable |
GET |
/v1/api/client-billing/billables/{id} |
Get one ledger row |
POST |
/v1/api/client-billing/billables/{id}/adjust |
Append a correction to a charge |
POST |
/v1/api/client-billing/billables/{id}/void |
Reverse a charge |
List the ledger
Section titled “List the ledger”GET /v1/api/client-billing/billables · operation ID listClientBillables
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
clientAccountId |
query | no | string | Only this customer’s rows. |
projectId |
query | no | string | Only this project’s rows. A client collaborator asking about a project it cannot reach is REFUSED rather than answered with an empty list — ‘not yours’ must not read as ‘nothing billed yet’. |
X-Organization-Id |
header | no | string | The workspace (organization) to act in. Omitted, the session’s active organization is used. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
ClientBillableView[] | The matching rows, each with the derived netAmountMicros and any void that reverses it. |
400 |
ApiError | The request named no active organization. |
401 |
ApiError | No valid session or API key. |
403 |
ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
404 |
ApiError | A projectId was named that this caller cannot reach. THE ONE PLACE THIS FAMILY REFUSES A READ OUTRIGHT: everywhere else an unreachable row is simply absent, but on a filtered list an empty 200 would make “not yours” indistinguishable from “nothing billed yet”. |
Record a billable
Section titled “Record a billable”POST /v1/api/client-billing/billables · operation ID createClientBillable
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
X-Organization-Id |
header | no | string | The workspace (organization) to act in. Omitted, the session’s active organization is used. |
Request body — application/json, CreateBillable (required)
The contract does not pin this body’s properties.
Responses
| Status | Body | Description |
|---|---|---|
201 |
ClientBillable | The written row. Its price is SNAPSHOTTED here and never recomputed; for agent_usage the tokens and model come from the run, not from the request, so a caller cannot name its own price. |
400 |
ValidationError | ApiError | The JSON body failed schema validation (ValidationError), or the request named no active organization (ApiError). |
401 |
ApiError | No valid session or API key. |
403 |
ApiError | The caller is a client collaborator; the billable ledger is managed by the agency. |
404 |
ApiError | No such client account or project in this workspace. Never a 403 — that would confirm it exists somewhere. |
409 |
ApiError | This source was already billed. A retried request is the usual cause. |
422 |
ApiError | A database constraint refused the row, and the answer names it. Reserved for a rule added after this contract — no request reaching the route trips one today. |
Get one ledger row
Section titled “Get one ledger row”GET /v1/api/client-billing/billables/{id} · operation ID getClientBillable
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id |
path | yes | string | |
X-Organization-Id |
header | no | string | The workspace (organization) to act in. Omitted, the session’s active organization is used. |
Responses
| Status | Body | Description |
|---|---|---|
200 |
ClientBillableView | The row, with its derived net and void. |
400 |
ApiError | The request named no active organization. |
401 |
ApiError | No valid session or API key. |
403 |
ApiError | Authenticated, but not a member of the named organization — or a client collaborator without the required access on this project. |
404 |
ApiError | No such billable this caller may read. |
Append a correction to a charge
Section titled “Append a correction to a charge”POST /v1/api/client-billing/billables/{id}/adjust · operation ID adjustClientBillable
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id |
path | yes | string | |
X-Organization-Id |
header | no | string | The workspace (organization) to act in. Omitted, the session’s active organization is used. |
Request body — application/json, AdjustBillable (required)
| Property | Type | Required | Notes |
|---|---|---|---|
description |
string | yes | min length 1, max length 500 |
note |
string | null | no |
quantity |
number | yes | |
unit |
string | yes | min length 1, max length 50 |
unitPriceMicros |
integer | yes | min 0, max 1000000000000 |
Responses
| Status | Body | Description |
|---|---|---|
201 |
ClientBillable | The correction — a NEW row naming the charge. The charge itself is untouched. |
400 |
ValidationError | ApiError | The JSON body failed validation, or the request named no active organization. |
401 |
ApiError | No valid session or API key. |
403 |
ApiError | The caller is a client collaborator; the billable ledger is managed by the agency. |
404 |
ApiError | No such billable this caller may reach. |
409 |
ApiError | A correction cannot correct another correction, and a voided charge cannot be corrected. The answer names the rule that refused. |
422 |
ApiError | A database constraint refused the row, and the answer names it. Reserved for a rule added after this contract — no request reaching the route trips one today. |
Reverse a charge
Section titled “Reverse a charge”POST /v1/api/client-billing/billables/{id}/void · operation ID voidClientBillable
Parameters
| Name | In | Required | Type | Description |
|---|---|---|---|---|
id |
path | yes | string | |
X-Organization-Id |
header | no | string | The workspace (organization) to act in. Omitted, the session’s active organization is used. |
Request body — application/json, VoidBillable (required)
| Property | Type | Required | Notes |
|---|---|---|---|
reason |
string | yes | min length 1, max length 500 |
Responses
| Status | Body | Description |
|---|---|---|
201 |
ClientBillable | The reversal — a NEW row. Its amount is the charge PLUS every correction already naming it, so a void can never leave a partial credit behind. |
400 |
ValidationError | ApiError | The JSON body failed validation, or the request named no active organization. |
401 |
ApiError | No valid session or API key. |
403 |
ApiError | The caller is a client collaborator; the billable ledger is managed by the agency. |
404 |
ApiError | No such billable this caller may reach. |
409 |
ApiError | It was already voided, or the rule that refuses a void names itself in the answer. A charge can be voided exactly once. |
422 |
ApiError | A database constraint refused the row, and the answer names it. Reserved for a rule added after this contract — no request reaching the route trips one today. |
If a request fails
Section titled “If a request fails”These apply to every operation on this page.
400— the body failed schema validation, or the request named no active organization. AValidationErrorbody carries the failing fields; anApiErrorbody carries onlyerror.401— no valid session or API key.403— authenticated, but not a member of the named organization, or a client collaborator without the required access.404— no such resource in this workspace. Absent and not-visible are deliberately the same answer; do not infer cross-tenant existence from it.409— the expected revision or state is stale, or another operation currently owns the transition.500— the operation failed server-side. Retry only where an idempotency key makes that safe.