commerce
POST /v1/carts/{cartId}/lines
Add a line to a cart.
Authentication
Send an API key as a bearer token. The endpoint checks billing.payment.manage; a key without it is refused with 403, not 404.
This endpoint takes no organisation id. Your key already identifies the organisation it belongs to, and the response is scoped to it.
Try it
Replace anything in angle brackets with your own values, and the key placeholder with a key from your dashboard.
curl -X POST https://api.zinndigital.com/v1/carts/{cartId}/lines \
-H "Authorization: Bearer zdk_live_…" \
-H "Content-Type: application/json" \
-d '{ "kind": <SellableLineKind> }'Signed in? The API console in your dashboard fills in your real organisation id and your own key, and runs the request against the live API so you can see the actual response. Open this endpoint in the API console
Details
Adds a priced line. A hosting_plan line needs a plan_version_id; a domain_register/domain_transfer/domain_renew line needs a domain_name, tld and period_years. The line shape and price are validated + stamped by the server; a bad shape (or a plan version with no price) is a 422.
Parameters
| Name | Type | Required | What it is |
|---|---|---|---|
cartId (path) | Uuid | Yes | The cart's id. |
Request body
| Name | Type | Required | What it is |
|---|---|---|---|
kind | SellableLineKind | Yes | The line kinds a cart will accept today — the write-side subset of LineKind. Narrower than LineKind on purpose. LineKind is what a line can be (a read shape, including… |
domain_name | string | No | Required for a domain line — the FULL name including its extension (example.com, matching tld); a bare label is refused with 422. |
tld | string | No | Required for a domain line. |
period_years | integer | No | Required for a register/transfer/renew line. |
privacy | boolean | No | — |
auto_renew | boolean | No | — |
availability_token | string | No | The signed token from searchDomains for this exact name. Optional: an ordinary name prices from the TLD catalogue without one. It is required to buy a premium name, because… |
auth_code | string | No | The EPP/authorization code issued by the registrar the domain is currently held at. Required for a domain_transfer line — no registry will initiate a transfer without it, so… |
registrar_route | RegistrationRouteKey | No | Which registrar buys the name, on a domain_register or domain_transfer line. Omit it for the organisation's own registrar, which is the behaviour every caller had before this… |
plan_version_id | Uuid | null | No | Required for a hosting_plan line — the immutable plan version to bill. |
extra_code | string | No | The catalogue code of the service an extra line buys. Required for that kind and meaningless on any other. Which addons a cart may buy is validated against… |
aged_domain_id | Uuid | null | No | Required for an aged_domain line (s1014 DOMAINS, docs/1107 §5) — an aged domain on sale (listAgedDomains). Priced at the catalogue's supplier list price, no markup; a USD… |
aged_destination | object | No | Where the bought aged domain goes, asked in the checkout so staff never have to ask: registrar (pushed to your own account there — registrar and account required) or zinn… |
listing_package_id | Uuid | null | No | Required for a marketplace_package line — the package on a live listing being bought. The price is the seller's and is re-read on every basket read, so a client-sent amount… |
interval | BillingInterval | No | The recurrence a price is quoted for. |
region | string | No | The data centre this hosting is bought for — carried onto the resulting subscription and used as the default region for sites created against it (docs/370). Blank or omitted means… |
compute | ComputeLineConfig | No | How one machine on a compute_server cart line is built. The values are not trusted from here: the engine re-resolves the whole order against the live vendor before the line… |
database | DatabaseLineConfig | No | The managed database a managed_database cart line buys (docs/497 §8). The values are not trusted from here: the engine re-resolves the whole purchase through the same… |
Response
| Name | Type | Required | What it is |
|---|---|---|---|
id | Uuid | Yes | UUIDv7 identifier — sortable by creation time (docs/02 §8). |
org_id | Uuid | Yes | UUIDv7 identifier — sortable by creation time (docs/02 §8). |
status | string<open, checking_out, converted, abandoned, expired> | Yes | — |
currency | CurrencyCode | null | No | Fixed when the cart is opened, from CartCreate.currency or the X-Zinn-Currency header, and then locked — every line must match it. Null only on a legacy cart created before… |
product_line | object | No | — |
lines | CartLine[] | Yes | — |
subtotal | MoneyAmount | Yes | A money value — integer minor units + an ISO 4217 code (CLAUDE.md §2.8). |
discount | MoneyAmount | Yes | A money value — integer minor units + an ISO 4217 code (CLAUDE.md §2.8). |
total | MoneyAmount | Yes | A money value — integer minor units + an ISO 4217 code (CLAUDE.md §2.8). |
created_at | string | Yes | — |
updated_at | string | No | — |
Errors this endpoint can return
401 · 403 · 404 · 422 · 429