API Conventions
One section captures the rules that apply across every Throttle endpoint: naming, error shapes, pagination, idempotency, and workspace environments. When in doubt, this page wins over the per-endpoint docs.
Field naming: camelCase, single shape
Every request and response field on the wire is camelCase. One name per concept — no aliases, no alternate forms. Type generation from the OpenAPI spec is the intended workflow.
// POST /api/v1/carts request body
{
"applicationId": "e7efb0a6-892e-46b2-97ab-296bb04c5b29",
"currency": "USD"
} // POST /api/v1/carts response
{
"data": {
"id": "9f6e...",
"workspaceId": "6659b411-9cd7-40ac-9a73-1e7801d89f55",
"applicationId": "e7efb0a6-892e-46b2-97ab-296bb04c5b29",
"currency": "USD",
"netN": null,
"lineItems": []
},
"meta": {
"requestId": "..."
}
} applicationId, customerId, lineItems, unitPrice, and externalId; stale snake_case or
store aliases are rejected with a validation error. Generated
[email protected]+ already uses the current shape.
Historical store terminology may still appear in dashboard labels or internal database migrations, but it is not exposed on the public wire.
Errors
Every non-2xx response follows the same envelope:
{
"error": {
"code": "missing_application_id", // stable, machine-readable
"message": "applicationId is required", // human-readable
"details": [ // optional, field-level
{ "field": "applicationId", "code": "required", "message": "Required" }
]
}
} error.code is the contract — use
it for programmatic handling. Reach for error.message only for surfacing
a human-readable string in your UI. Field-level details identify
unrecognized keys and may include a suggestion when the server can
identify the intended canonical camelCase field:
{
"error": {
"code": "validation_error",
"message": "Request body validation failed",
"details": [{
"path": "",
"code": "unrecognized_keys",
"message": "Unsupported field(s): unexpectedField",
"received": ["unexpectedField"]
}]
}
} Typed errors in the SDK
The envelope is published as a named ErrorEnvelope
model, so @usethrottle/api-client exposes it
as a type rather than leaving you to re-parse the body:
import { ApiError } from '@usethrottle/api-client';
import type { ErrorEnvelope } from '@usethrottle/api-client';
try {
await QuotesService.postApiV1QuoteRequests({ formToken, email });
} catch (err) {
if (err instanceof ApiError) {
err.code; // 'too_fast' — stable, branch on this
err.message; // the server's own explanation, not a canned status string
err.status; // 400
err.details; // optional structured context
const body = err.body as ErrorEnvelope; // fully typed
}
} ApiError extends
ThrottleError, so one
catch covers every Throttle SDK.
err.message is the message the server
sent; if a response carries no message, it falls back to a description of
what that status means on this API, and the raw description stays available
as err.statusDescription.
Pagination
List endpoints accept ?limit + ?cursor and return:
{
"data": [...],
"meta": {
"pagination": {
"cursor": "<opaque>", // pass back as ?cursor= to fetch next page
"hasMore": true,
"limit": 25
}
}
}
When hasMore is false, cursor is null. You can use if (cursor) as a "more pages?"
check without re-reading hasMore.
Identifier prefixes
Resource ids are UUID v7 strings. API keys, embed tokens, and session ids carry a short prefix that encodes their type and, for keys, the workspace environment slug:
-
sk_<environment-slug>_*— secret key for one workspace environment. -
pk_<environment-slug>_*— publishable key for one workspace environment. -
sk_live_*andpk_live_*— production environment keys. -
whsec_*— webhook signing secret.
Timestamps and timezones
Every timestamp in an API request or response is ISO 8601 in UTC, always, and is unaffected by any setting. Parse it as an instant and render it wherever you like.
Separately, each application carries a timezone
— an IANA zone name such as America/Los_Angeles.
That setting is what Throttle's own rendered surfaces use: the
dashboard, quote PDFs, and transactional emails. null
means UTC.
timezone from the
application and format the UTC instant yourself.
Like currency and branding, the timezone is stored per workspace environment, so a store can read its test data in one zone and its production data in another.
Quote and order numbers
quoteNumber and
orderNumber are human-facing labels that
count up per application, per workspace environment. Two
applications each issue their own Q-1;
neither ever sees a gap caused by the other.
Both the prefix and the next number are configurable, from the dashboard or over the API:
// PATCH /api/v1/workspaces/{workspaceId}/applications/{applicationId}
{
"timezone": "America/Los_Angeles",
"quoteNumberPrefix": "Q-",
"nextQuoteSeq": 187,
"orderNumberPrefix": "ORD-",
"nextOrderSeq": 536
} -
quoteNumberPrefix/orderNumberPrefix— 1–12 characters of letters, digits,-or_. Defaults areQ-andORD-. -
nextQuoteSeq/nextOrderSeq— the next number to issue. It must be greater than every number this environment has already issued under that prefix; otherwise the request fails with409 starting_number_already_used.
createdAt plus the id
instead, and never infer volume from a number.
Test environments keep their own counters, so QA traffic never consumes a number a live document would otherwise get.