Skip to content
Taylorz CRM Developers
Esc
↑↓navigate↵open⌘Jpreview
On this page

Conventions

Idempotency keys, cursor pagination, optimistic revisions and request limits, described as the server actually behaves rather than as guarantees.

Every endpoint shares these. What follows is what the server does, not a guarantee invented for the documentation — where a guarantee is narrower than you might assume, that is said.

Idempotency

Most mutations require an Idempotency-Key header, 8 to 128 characters, and refuse a request without one with 400. The reference names it on each endpoint that takes it — four POST endpoints do not: the two pricing previews, which write nothing, the download link, and POST /api/v1/bootstrap, which installs standard objects and is safe to repeat because an object already installed is left as it is.

Send the same key with the same body and you get the first answer back, replayed, without the work happening twice. Send the same key with a different body and the request is refused with 409 and the reason idempotency_key_reused.

Two limits on that guarantee are worth knowing.

A receipt is scoped to you and to the action, not globally. It is keyed on the user, the action and the key together, so the same key used by a different person, or by you against a different endpoint, is a different command. A key only has to be unique within what you yourself are doing.

A replay answers the body that was stored. If the shape of an endpoint’s response changes in a release, a key issued before it replays the older body. That is inherent to replaying a stored response, and it is why a key belongs to one request rather than being reused across a session.

Pagination

Two kinds of list exist, and they page differently.

Most lists — products, price books, quotes — take limit and after, and answer nextCursor. Pass nextCursor back as after for the next page, and stop when it is null.

Record lists take more: limit, dir, orderBy or view, relatedTo with via, and cursor or after. Four combinations are refused with 400, and they cannot be expressed in a parameter list:

  • via requires relatedTo
  • orderBy cannot be combined with view
  • cursor requires one of orderBy or view
  • after cannot be combined with either — page an ordered query with cursor

A cursor belongs to the ordering that produced it. The ordering — the sort, the direction, the related-record filter, the view — travels inside the cursor. Replay a cursor against a changed view and the request fails loudly with cursor_ordering_mismatch rather than answering a page that looks right and is not. Fetch from the first page again when the ordering changes.

total is counted on the first page only. A request with no after and no cursor answers a count; every later page answers total: null. The count is paid once rather than on every page, so hold the first page’s figure if you want to show one.

Revisions

Records, objects, fields, saved reports and the commercial policy carry a revision. An update sends the revision it was read at, and a stale one is refused with 409.

This is optimistic concurrency rather than a lock: nothing is held while you think. Read, send the revision you read, and handle the refusal by reading again.

Request limits

A request body is limited to 1 MB, except a note image upload, which is 4 MB. The limit is applied before the route runs, so an oversized request is refused with 413 whatever endpoint it was aimed at.

Headers

Header When
Authorization: Bearer <token> Every request to this API
Idempotency-Key Every mutation
Content-Type: application/json Every request with a body

X-CRM-CSRF is not yours to send. The web application uses it for its own cookie-session writes; a bearer-token request does not take that path and does not need it. Credentialed CORS likewise allows the application’s own origin only — it does not apply to a server-to-server call, which sends no browser origin.

Dates and money

Timestamps are ISO 8601 strings in UTC. Date-only fields — a purchase date, a schedule’s start and end — are YYYY-MM-DD.

Money is a decimal string, never a number. Totals, unit prices, tax and discounts arrive as strings like "1234.56" so no value passes through a binary float. Parse them with a decimal library, and do not add them as numbers.

Was this page helpful?