Appearance
Billing
A contract carries a tenant's limits — one cap per metric and period — and an optional pricingHint; a device hangs off at most one. devices.active counts attached devices rather than activated ones, so an attach past the cap is refused even when nothing on the contract is live. No invoice is stored: it is folded out of the tenant's lifecycle log per call, so one period can be re-billed under a different policy.
The device entry itself belongs to register: attaching and activating write the contract link and activatedAt there, and its derived status follows from both. Each transition also goes out as BILLING_DEVICE_EVENT_CREATE over the websocket.
| POST | Create a contract/api/v1/billing/contracts |
| GET | List contracts/api/v1/billing/contracts |
| GET | Read a contract/api/v1/billing/contracts/:contractId |
| PATCH | Update a contract/api/v1/billing/contracts/:contractId |
| POST | Cancel a contract/api/v1/billing/contracts/:contractId/cancel |
| GET | List a contract's devices/api/v1/billing/contracts/:contractId/devices |
| POST | Attach a device/api/v1/billing/contracts/:contractId/devices |
| DELETE | Detach a device/api/v1/billing/contracts/:contractId/devices/:deviceId |
| POST | Activate a device/api/v1/billing/devices/:deviceId/activate |
| GET | List device lifecycle events/api/v1/billing/device-events |
| GET | Invoice a period/api/v1/billing/invoice |
| GET | List usage records/api/v1/billing/usage |
| GET | Read current usage/api/v1/billing/usage/current |
Create a contract
Creates a contract, live from that moment until it is cancelled — a contract has no term. A tenant may hold several at once, and each limits entry must be unique per metric and period.
POST
/api/v1/billing/contractsBody
| Name | Description |
|---|---|
namestring | Display name of the contract min length 1 · max length 128 |
descriptionstringoptional | Optional description of what this contract includes max length 1024 |
externalContractIdstringoptional | This contract's identifier in an external system, 1–128 characters; stored and returned unchanged and never interpreted by the backend. min length 1 · max length 128 |
tariffArticleNumberstringoptional | Article number of the booked tariff, 1–128 characters; carried along with the contract but never priced from — caps sit in limits, prices in pricingHint.min length 1 · max length 128 |
limitsarray | A usage limit attached to this contract |
metricstring | The metric this limit applies to min length 1 |
quantityinteger | Maximum allowed quantity for this metric in the given period min 0 |
periodenum | Period over which the limit is measured (month, year, lifetime) Possible values: month, year, lifetime |
| enforcement | How this limit is enforced — hard limits block usage when exceeded |
pricingHintobjectoptional | Informational pricing details — not used for enforcement |
modelenum | Pricing model (device-month, device-year, tier) Possible values: device-month, device-year, tier |
tierSizeintegeroptional | Number of units per tier (tier model only) > 0 |
unitPriceMonthstringoptional | Price per unit per month as a decimal string (e.g. 0.50) Pattern: ^\d+\.\d{1,6}$ |
prepaidMonthsintegeroptional | Number of months billed upfront > 0 |
tenantIdstringoptional | Tenant this contract applies to — defaults to the authenticated user's tenant Pattern: ^[0-9A-HJ-NP-Za-km-z]{9,16}$ |
Response
| Name | Description |
|---|---|
resourceIdentifierstring | Unique Coldwave resource name (CRN) that identifies a resource |
Errors
| Status | Description | Body |
|---|---|---|
403 | You do not have permission to perform this action | |
400 | The contract configuration is invalid | code: CONTRACT_INVALID, reason: string |
401 | No valid access token was presented, or the DPoP proof accompanying it was missing, expired or bound to a different key. | error: string |
500 | The request failed for a reason that is not the caller's to fix. Safe to retry. | error: string, message: string, statusCode: 50 |
List contracts
Lists the tenant's contracts, cancelled ones included — status is the only thing that separates them.
GET
/api/v1/billing/contractsResponse
| Name | Description |
|---|---|
resourceIdentifierstring | Unique Coldwave resource name (CRN) that identifies a resource |
namestring | Display name of the contract min length 1 · max length 128 |
descriptionstringoptional | Optional description of what this contract includes max length 1024 |
externalContractIdstringoptional | This contract's identifier in an external system, 1–128 characters; stored and returned unchanged and never interpreted by the backend. min length 1 · max length 128 |
tariffArticleNumberstringoptional | Article number of the booked tariff, 1–128 characters; carried along with the contract but never priced from — caps sit in limits, prices in pricingHint.min length 1 · max length 128 |
limitsarray | A usage limit attached to this contract |
metricstring | The metric this limit applies to min length 1 |
quantityinteger | Maximum allowed quantity for this metric in the given period min 0 |
periodenum | Period over which the limit is measured (month, year, lifetime) Possible values: month, year, lifetime |
| enforcement | How this limit is enforced — hard limits block usage when exceeded |
pricingHintobjectoptional | Informational pricing details — not used for enforcement |
modelenum | Pricing model (device-month, device-year, tier) Possible values: device-month, device-year, tier |
tierSizeintegeroptional | Number of units per tier (tier model only) > 0 |
unitPriceMonthstringoptional | Price per unit per month as a decimal string (e.g. 0.50) Pattern: ^\d+\.\d{1,6}$ |
prepaidMonthsintegeroptional | Number of months billed upfront > 0 |
statusenum | Current status of the contract — active or cancelled Possible values: active, cancelled |
createdAtinteger | Unix timestamp in milliseconds when this resource was created. |
updatedAtinteger | Unix timestamp in milliseconds when this resource was last modified. |
Errors
| Status | Description | Body |
|---|---|---|
403 | You do not have permission to perform this action | |
401 | No valid access token was presented, or the DPoP proof accompanying it was missing, expired or bound to a different key. | error: string |
500 | The request failed for a reason that is not the caller's to fix. Safe to retry. | error: string, message: string, statusCode: 50 |
Read a contract
One contract with its limits and pricing hint; the devices hanging off it are a separate read.
GET
/api/v1/billing/contracts/Yk3pL7rWq2Path Parameters
| Name | Description |
|---|---|
contractIdstring | Resource id: Base58 (9–10 characters) or a 16-character Crockford snowflake. Not an RFC 4122 UUID. Pattern: ^[0-9A-HJ-NP-Za-km-z]{9,16}$ |
Response
| Name | Description |
|---|---|
resourceIdentifierstring | Unique Coldwave resource name (CRN) that identifies a resource |
namestring | Display name of the contract min length 1 · max length 128 |
descriptionstringoptional | Optional description of what this contract includes max length 1024 |
externalContractIdstringoptional | This contract's identifier in an external system, 1–128 characters; stored and returned unchanged and never interpreted by the backend. min length 1 · max length 128 |
tariffArticleNumberstringoptional | Article number of the booked tariff, 1–128 characters; carried along with the contract but never priced from — caps sit in limits, prices in pricingHint.min length 1 · max length 128 |
limitsarray | A usage limit attached to this contract |
metricstring | The metric this limit applies to min length 1 |
quantityinteger | Maximum allowed quantity for this metric in the given period min 0 |
periodenum | Period over which the limit is measured (month, year, lifetime) Possible values: month, year, lifetime |
| enforcement | How this limit is enforced — hard limits block usage when exceeded |
pricingHintobjectoptional | Informational pricing details — not used for enforcement |
modelenum | Pricing model (device-month, device-year, tier) Possible values: device-month, device-year, tier |
tierSizeintegeroptional | Number of units per tier (tier model only) > 0 |
unitPriceMonthstringoptional | Price per unit per month as a decimal string (e.g. 0.50) Pattern: ^\d+\.\d{1,6}$ |
prepaidMonthsintegeroptional | Number of months billed upfront > 0 |
statusenum | Current status of the contract — active or cancelled Possible values: active, cancelled |
createdAtinteger | Unix timestamp in milliseconds when this resource was created. |
updatedAtinteger | Unix timestamp in milliseconds when this resource was last modified. |
Errors
| Status | Description | Body |
|---|---|---|
403 | You do not have permission to perform this action | |
404 | The requested billing resource could not be found | |
400 | The request did not match the schema for this endpoint. The details field carries the specific failures. | error: Validation Error |
401 | No valid access token was presented, or the DPoP proof accompanying it was missing, expired or bound to a different key. | error: string |
500 | The request failed for a reason that is not the caller's to fix. Safe to retry. | error: string, message: string, statusCode: 50 |
Update a contract
Writes the fields the body carries, but replaces limits as a whole — send the complete set, not just the entries that changed. status cannot be written here; cancelling has its own route.
PATCH
/api/v1/billing/contracts/Yk3pL7rWq2Path Parameters
| Name | Description |
|---|---|
contractIdstring | Resource id: Base58 (9–10 characters) or a 16-character Crockford snowflake. Not an RFC 4122 UUID. Pattern: ^[0-9A-HJ-NP-Za-km-z]{9,16}$ |
Body
| Name | Description |
|---|---|
namestringoptional | Display name of the contract min length 1 · max length 128 |
descriptionstringoptional | Optional description of what this contract includes max length 1024 |
externalContractIdstringoptional | This contract's identifier in an external system, 1–128 characters; stored and returned unchanged and never interpreted by the backend. min length 1 · max length 128 |
tariffArticleNumberstringoptional | Article number of the booked tariff, 1–128 characters; carried along with the contract but never priced from — caps sit in limits, prices in pricingHint.min length 1 · max length 128 |
limitsarrayoptional | A usage limit attached to this contract |
metricstring | The metric this limit applies to min length 1 |
quantityinteger | Maximum allowed quantity for this metric in the given period min 0 |
periodenum | Period over which the limit is measured (month, year, lifetime) Possible values: month, year, lifetime |
| enforcement | How this limit is enforced — hard limits block usage when exceeded |
pricingHintobjectoptional | Informational pricing details — not used for enforcement |
modelenum | Pricing model (device-month, device-year, tier) Possible values: device-month, device-year, tier |
tierSizeintegeroptional | Number of units per tier (tier model only) > 0 |
unitPriceMonthstringoptional | Price per unit per month as a decimal string (e.g. 0.50) Pattern: ^\d+\.\d{1,6}$ |
prepaidMonthsintegeroptional | Number of months billed upfront > 0 |
Errors
| Status | Description | Body |
|---|---|---|
403 | You do not have permission to perform this action | |
404 | The requested billing resource could not be found | |
400 | The contract configuration is invalid | code: CONTRACT_INVALID, reason: string |
401 | No valid access token was presented, or the DPoP proof accompanying it was missing, expired or bound to a different key. | error: string |
500 | The request failed for a reason that is not the caller's to fix. Safe to retry. | error: string, message: string, statusCode: 50 |
Cancel a contract
Cancels the contract and ends billing for every device on it — each live device is closed out with a contract-cancelled deactivation and may no longer connect. The devices stay attached, and nothing brings the contract back to active.
POST
/api/v1/billing/contracts/Yk3pL7rWq2/cancelPath Parameters
| Name | Description |
|---|---|
contractIdstring | Resource id: Base58 (9–10 characters) or a 16-character Crockford snowflake. Not an RFC 4122 UUID. Pattern: ^[0-9A-HJ-NP-Za-km-z]{9,16}$ |
Errors
| Status | Description | Body |
|---|---|---|
403 | You do not have permission to perform this action | |
404 | The requested billing resource could not be found | |
400 | The request did not match the schema for this endpoint. The details field carries the specific failures. | error: Validation Error |
401 | No valid access token was presented, or the DPoP proof accompanying it was missing, expired or bound to a different key. | error: string |
500 | The request failed for a reason that is not the caller's to fix. Safe to retry. | error: string, message: string, statusCode: 50 |
List a contract's devices
The CRNs of the devices attached to this contract, activated or not — being attached does not make a device live.
GET
/api/v1/billing/contracts/Yk3pL7rWq2/devicesPath Parameters
| Name | Description |
|---|---|
contractIdstring | Resource id: Base58 (9–10 characters) or a 16-character Crockford snowflake. Not an RFC 4122 UUID. Pattern: ^[0-9A-HJ-NP-Za-km-z]{9,16}$ |
Response
| Name | Description |
|---|
Errors
| Status | Description | Body |
|---|---|---|
403 | You do not have permission to perform this action | |
404 | The requested billing resource could not be found | |
400 | The request did not match the schema for this endpoint. The details field carries the specific failures. | error: Validation Error |
401 | No valid access token was presented, or the DPoP proof accompanying it was missing, expired or bound to a different key. | error: string |
500 | The request failed for a reason that is not the caller's to fix. Safe to retry. | error: string, message: string, statusCode: 50 |
Attach a device
Links the device to the contract without making it live — activation is the customer's own step. A device coming from another contract of the same tenant is swapped over but keeps its activation only if it was still live there, while an unassigned one inherits the contract's tenant and comes back under a new CRN.
POST
/api/v1/billing/contracts/Yk3pL7rWq2/devicesPath Parameters
| Name | Description |
|---|---|
contractIdstring | Resource id: Base58 (9–10 characters) or a 16-character Crockford snowflake. Not an RFC 4122 UUID. Pattern: ^[0-9A-HJ-NP-Za-km-z]{9,16}$ |
Body
| Name | Description |
|---|---|
| deviceId | The device to attach, named by its device id or by the IMEI printed on it. |
Alternative 1string | Resource id: Base58 (9–10 characters) or a 16-character Crockford snowflake. Not an RFC 4122 UUID. Pattern: ^[0-9A-HJ-NP-Za-km-z]{9,16}$ |
Alternative 2string | The device to attach, named by its device id or by the IMEI printed on it. max length 16 Pattern: ^[0-9a-fA-F]+$ |
Response
| Name | Description |
|---|---|
resourceIdentifierstring | Unique Coldwave resource name (CRN) that identifies a resource |
Errors
| Status | Description | Body |
|---|---|---|
403 | You do not have permission to perform this action | |
404 | The requested billing resource could not be found | |
409 | The contract is cancelled and takes no further devices — attach to an active contract instead, as a cancelled one cannot be reopened. | code: CONTRACT_NOT_ACTIVE |
409 | The contract's devices.active limit is reached; it caps attached devices rather than live ones, so detach one or raise the limit before adding another. | code: CONTRACT_DEVICE_LIMIT |
400 | The request did not match the schema for this endpoint. The details field carries the specific failures. | error: Validation Error |
401 | No valid access token was presented, or the DPoP proof accompanying it was missing, expired or bound to a different key. | error: string |
500 | The request failed for a reason that is not the caller's to fix. Safe to retry. | error: string, message: string, statusCode: 50 |
Detach a device
Removes the device from the contract and closes its billing interval. The device keeps its tenant but loses its activation, so attaching it again later needs a fresh one.
DELETE
/api/v1/billing/contracts/Yk3pL7rWq2/devices/Yk3pL7rWq2Path Parameters
| Name | Description |
|---|---|
contractIdstring | Resource id: Base58 (9–10 characters) or a 16-character Crockford snowflake. Not an RFC 4122 UUID. Pattern: ^[0-9A-HJ-NP-Za-km-z]{9,16}$ |
| deviceId | The device to detach, named by its device id or IMEI; it must currently be attached to this contract. |
Alternative 1string | Resource id: Base58 (9–10 characters) or a 16-character Crockford snowflake. Not an RFC 4122 UUID. Pattern: ^[0-9A-HJ-NP-Za-km-z]{9,16}$ |
Alternative 2string | The device to detach, named by its device id or IMEI; it must currently be attached to this contract. max length 16 Pattern: ^[0-9a-fA-F]+$ |
Errors
| Status | Description | Body |
|---|---|---|
403 | You do not have permission to perform this action | |
404 | The requested billing resource could not be found | |
400 | The request did not match the schema for this endpoint. The details field carries the specific failures. | error: Validation Error |
401 | No valid access token was presented, or the DPoP proof accompanying it was missing, expired or bound to a different key. | error: string |
500 | The request failed for a reason that is not the caller's to fix. Safe to retry. | error: string, message: string, statusCode: 50 |
Activate a device
Starts the device's billing and is the last step before it may connect; authorisation is update on the device, so whoever activates never needs access to the contract behind it. A second call is a conflict rather than a no-op, so a double tap in a setup screen cannot move the billing start date.
POST
/api/v1/billing/devices/Yk3pL7rWq2/activatePath Parameters
| Name | Description |
|---|---|
| deviceId | Identifier of the device |
Alternative 1string | Resource id: Base58 (9–10 characters) or a 16-character Crockford snowflake. Not an RFC 4122 UUID. Pattern: ^[0-9A-HJ-NP-Za-km-z]{9,16}$ |
Alternative 2string | Identifier of the device max length 16 Pattern: ^[0-9a-fA-F]+$ |
Errors
| Status | Description | Body |
|---|---|---|
403 | You do not have permission to perform this action | |
404 | The requested billing resource could not be found | |
409 | The device is not on an active contract, so there is nothing to activate | code: DEVICE_NOT_ATTACHED |
409 | The device is already active | code: DEVICE_ALREADY_ACTIVE |
400 | The request did not match the schema for this endpoint. The details field carries the specific failures. | error: Validation Error |
401 | No valid access token was presented, or the DPoP proof accompanying it was missing, expired or bound to a different key. | error: string |
500 | The request failed for a reason that is not the caller's to fix. Safe to retry. | error: string, message: string, statusCode: 50 |
List device lifecycle events
The tenant's append-only activation and deactivation log, oldest first and capped at 1000 entries — the raw input the invoice is folded from. Narrow it with from/to, contractId and deviceId rather than paging: there is no cursor.
GET
/api/v1/billing/device-eventsQuery Parameters
| Name | Description |
|---|---|
contractIdstringoptional | Only events for this contract CRN |
deviceIdstringoptional | Only events for this device CRN |
fromintegeroptional | Only events at or after this Unix timestamp |
tointegeroptional | Only events at or before this Unix timestamp |
Response
| Name | Description |
|---|---|
resourceIdentifierstring | Unique Coldwave resource name (CRN) that identifies a resource |
tenantIdstring | Unique Coldwave resource name (CRN) that identifies a resource |
contractIdstring | Unique Coldwave resource name (CRN) that identifies a resource |
deviceIdstring | Unique Coldwave resource name (CRN) that identifies a resource |
transitionenum | Direction of the change — activated (starts billing) or deactivated (stops billing) Possible values: activated, deactivated |
reasonenum | What caused the change (activated, detached, swapped-in, swapped-out, contract-cancelled) Possible values: activated, detached, swapped-in, swapped-out, contract-cancelled |
timestampinteger | Unix timestamp in milliseconds since epoch. |
Errors
| Status | Description | Body |
|---|---|---|
403 | You do not have permission to perform this action | |
400 | The request did not match the schema for this endpoint. The details field carries the specific failures. | error: Validation Error |
401 | No valid access token was presented, or the DPoP proof accompanying it was missing, expired or bound to a different key. | error: string |
500 | The request failed for a reason that is not the caller's to fix. Safe to retry. | error: string, message: string, statusCode: 50 |
Invoice a period
Folds the lifecycle log into billable device-months for [from, to), one line per contract, device and calendar month, plus a total per contract priced from its pricingHint. policy has no default — whether a partial month costs a whole one is a commercial decision and is asked for on every call.
GET
/api/v1/billing/invoice?from=1756684800000&to=1759276800000&policy=full-monthQuery Parameters
| Name | Description |
|---|---|
frominteger | Start of the period to invoice (UTC ms, inclusive) |
tointeger | End of the period to invoice (UTC ms, exclusive) |
policyenum | full-month charges a whole month for any activity; pro-rated charges the exact fraction of that calendar month; pro-rated-30 charges the monthly rate for a full month and days/30 for a partial one Possible values: full-month, pro-rated, pro-rated-30 |
Response
| Name | Description |
|---|---|
policyenum | The policy the quantities were computed under Possible values: full-month, pro-rated, pro-rated-30 |
frominteger | Start of the invoiced period (UTC ms, inclusive) |
tointeger | End of the invoiced period (UTC ms, exclusive) |
linesarray | One line per contract, device and calendar month with billable time |
contractIdstring | Unique Coldwave resource name (CRN) that identifies a resource |
deviceIdstring | Unique Coldwave resource name (CRN) that identifies a resource |
periodStartinteger | Start of the calendar month, UTC epoch milliseconds. |
periodEndinteger | End of the calendar month, exclusive, UTC epoch milliseconds. |
activeMsinteger | Milliseconds the device was active during this month, clipped to the requested window. min 0 |
quantitynumber | Billable device-months for this line — 1 under full-month, the exact share of the month under pro-rated. Under pro-rated-30 a complete calendar month costs 1 whatever its length, a partial one days/30 capped at 1.min 0 |
totalsarray | Per-contract totals |
contractIdstring | Unique Coldwave resource name (CRN) that identifies a resource |
quantitynumber | Sum of the contract's line quantities. min 0 |
unitPriceMonthstringoptional | Price per device-month taken from the contract's pricing hint; absent when the contract carries none. |
amountMicrosintegeroptional | quantity × unitPriceMonth in integer millionths of a currency unit, absent without a unit price; divide by 1000000 to display.min 0 |
Errors
| Status | Description | Body |
|---|---|---|
403 | You do not have permission to perform this action | |
400 | The requested period covers more lifecycle events than one invoice may fold — narrow the window | code: INVOICE_PERIOD_TOO_LARGE, maxEvents: 50 |
401 | No valid access token was presented, or the DPoP proof accompanying it was missing, expired or bound to a different key. | error: string |
500 | The request failed for a reason that is not the caller's to fix. Safe to retry. | error: string, message: string, statusCode: 50 |
List usage records
The tenant's individual usage records, newest first and capped at 1000; filter with metric, from and to. Nothing is aggregated — granularity does not bucket the result.
GET
/api/v1/billing/usageQuery Parameters
| Name | Description |
|---|---|
metricstringoptional | Filter results by metric name |
fromintegeroptional | Start of the time range (Unix timestamp, inclusive) |
tointegeroptional | End of the time range (Unix timestamp, inclusive) |
granularityoptionalenum | Time bucket size for aggregation (hour, day, month) Possible values: hour, day, month |
Response
| Name | Description |
|---|---|
resourceIdentifierstring | Unique Coldwave resource name (CRN) that identifies a resource |
tenantIdstring | Unique Coldwave resource name (CRN) that identifies a resource |
contractIdstring | Identifier of the contract this usage is billed against min length 1 |
metricstring | Name of the metric that was measured min length 1 |
quantityinteger | Measured quantity min 0 |
timestampinteger | Unix timestamp in milliseconds since epoch. |
metadictionaryoptional | Additional key-value metadata about this usage event |
Errors
| Status | Description | Body |
|---|---|---|
403 | You do not have permission to perform this action | |
400 | The request did not match the schema for this endpoint. The details field carries the specific failures. | error: Validation Error |
401 | No valid access token was presented, or the DPoP proof accompanying it was missing, expired or bound to a different key. | error: string |
500 | The request failed for a reason that is not the caller's to fix. Safe to retry. | error: string, message: string, statusCode: 50 |
Read current usage
One entry per limit of the primary contract — the oldest active one — with what the running period has used so far; an empty array when the tenant holds no active contract. devices.active is counted live from the attached devices, every other metric is summed from usage records.
GET
/api/v1/billing/usage/currentResponse
| Name | Description |
|---|---|
metricstring | Name of the metric |
periodenum | Billing period over which usage is accumulated (month, year, lifetime) Possible values: month, year, lifetime |
quantityinteger | Total usage accumulated in the current period min 0 |
limitinteger | Maximum allowed quantity for this period min 0 |
remaininginteger | Remaining quantity before the limit is reached |
periodStartinteger | Unix timestamp marking the start of the current period |
periodEndintegeroptional | Unix timestamp marking the end of the current period, or null for lifetime limits |
Errors
| Status | Description | Body |
|---|---|---|
403 | You do not have permission to perform this action | |
401 | No valid access token was presented, or the DPoP proof accompanying it was missing, expired or bound to a different key. | error: string |
500 | The request failed for a reason that is not the caller's to fix. Safe to retry. | error: string, message: string, statusCode: 50 |
Events
Published on the tenant's event stream and delivered over the websocket to every subscriber holding read on the resource the message names. resourceIdentifier is the resource the action changed; the payload is the shape below.
| Event | Description |
|---|---|
BILLING_CONTRACT_CANCEL | A contract was cancelled; the payload is empty — the deactivation of each device on it arrives as its own lifecycle event. |
BILLING_CONTRACT_CREATE | A contract was created and is live from that moment; carries the whole entry. |
BILLING_CONTRACT_UPDATE | Fields of a contract were changed; carries only the fields the request sent. |
BILLING_DEVICE_EVENT_CREATE | A device was activated or deactivated on a contract — one entry appended to the log the invoice is folded from. |
BILLING_USAGE_CREATE | A metered request was written to the usage log, billed against the tenant's primary contract. |
Contract cancelled
A contract was cancelled; the payload is empty — the deactivation of each device on it arrives as its own lifecycle event.
EVENT
BILLING_CONTRACT_CANCELPayload
| Name | Description |
|---|
Contract created
A contract was created and is live from that moment; carries the whole entry.
EVENT
BILLING_CONTRACT_CREATEPayload
| Name | Description |
|---|---|
resourceIdentifierstring | Unique Coldwave resource name (CRN) that identifies a resource |
namestring | Display name of the contract min length 1 · max length 128 |
descriptionstringoptional | Optional description of what this contract includes max length 1024 |
externalContractIdstringoptional | This contract's identifier in an external system, 1–128 characters; stored and returned unchanged and never interpreted by the backend. min length 1 · max length 128 |
tariffArticleNumberstringoptional | Article number of the booked tariff, 1–128 characters; carried along with the contract but never priced from — caps sit in limits, prices in pricingHint.min length 1 · max length 128 |
limitsarray | A usage limit attached to this contract |
metricstring | The metric this limit applies to min length 1 |
quantityinteger | Maximum allowed quantity for this metric in the given period min 0 |
periodenum | Period over which the limit is measured (month, year, lifetime) Possible values: month, year, lifetime |
| enforcement | How this limit is enforced — hard limits block usage when exceeded |
pricingHintobjectoptional | Informational pricing details — not used for enforcement |
modelenum | Pricing model (device-month, device-year, tier) Possible values: device-month, device-year, tier |
tierSizeintegeroptional | Number of units per tier (tier model only) > 0 |
unitPriceMonthstringoptional | Price per unit per month as a decimal string (e.g. 0.50) Pattern: ^\d+\.\d{1,6}$ |
prepaidMonthsintegeroptional | Number of months billed upfront > 0 |
statusenum | Current status of the contract — active or cancelled Possible values: active, cancelled |
createdAtinteger | Unix timestamp in milliseconds when this resource was created. |
updatedAtinteger | Unix timestamp in milliseconds when this resource was last modified. |
Contract updated
Fields of a contract were changed; carries only the fields the request sent.
EVENT
BILLING_CONTRACT_UPDATEPayload
| Name | Description |
|---|---|
namestringoptional | Display name of the contract min length 1 · max length 128 |
descriptionstringoptional | Optional description of what this contract includes max length 1024 |
externalContractIdstringoptional | This contract's identifier in an external system, 1–128 characters; stored and returned unchanged and never interpreted by the backend. min length 1 · max length 128 |
tariffArticleNumberstringoptional | Article number of the booked tariff, 1–128 characters; carried along with the contract but never priced from — caps sit in limits, prices in pricingHint.min length 1 · max length 128 |
limitsarrayoptional | A usage limit attached to this contract |
metricstring | The metric this limit applies to min length 1 |
quantityinteger | Maximum allowed quantity for this metric in the given period min 0 |
periodenum | Period over which the limit is measured (month, year, lifetime) Possible values: month, year, lifetime |
| enforcement | How this limit is enforced — hard limits block usage when exceeded |
pricingHintobjectoptional | Informational pricing details — not used for enforcement |
modelenum | Pricing model (device-month, device-year, tier) Possible values: device-month, device-year, tier |
tierSizeintegeroptional | Number of units per tier (tier model only) > 0 |
unitPriceMonthstringoptional | Price per unit per month as a decimal string (e.g. 0.50) Pattern: ^\d+\.\d{1,6}$ |
prepaidMonthsintegeroptional | Number of months billed upfront > 0 |
Device billing state changed
A device was activated or deactivated on a contract — one entry appended to the log the invoice is folded from.
EVENT
BILLING_DEVICE_EVENT_CREATEPayload
| Name | Description |
|---|---|
resourceIdentifierstring | Unique Coldwave resource name (CRN) that identifies a resource |
tenantIdstring | Unique Coldwave resource name (CRN) that identifies a resource |
contractIdstring | Unique Coldwave resource name (CRN) that identifies a resource |
deviceIdstring | Unique Coldwave resource name (CRN) that identifies a resource |
transitionenum | Direction of the change — activated (starts billing) or deactivated (stops billing) Possible values: activated, deactivated |
reasonenum | What caused the change (activated, detached, swapped-in, swapped-out, contract-cancelled) Possible values: activated, detached, swapped-in, swapped-out, contract-cancelled |
timestampinteger | Unix timestamp in milliseconds since epoch. |
Usage recorded
A metered request was written to the usage log, billed against the tenant's primary contract.
EVENT
BILLING_USAGE_CREATEPayload
| Name | Description |
|---|---|
resourceIdentifierstring | Unique Coldwave resource name (CRN) that identifies a resource |
tenantIdstring | Unique Coldwave resource name (CRN) that identifies a resource |
contractIdstring | Identifier of the contract this usage is billed against min length 1 |
metricstring | Name of the metric that was measured min length 1 |
quantityinteger | Measured quantity min 0 |
timestampinteger | Unix timestamp in milliseconds since epoch. |
metadictionaryoptional | Additional key-value metadata about this usage event |