Skip to main content
Learn these conventions once and every endpoint in the reference becomes predictable.

The basics

All requests go to one base URL, over HTTPS:
https://api.quo.com
Two headers are required on every request, a third on requests with a body:
HeaderValueNotes
AuthorizationYour API keySee Authentication.
Quo-Api-Version2026-03-30Required; there is no default. See Versioning.
Content-Typeapplication/jsonOnly when sending a request body.
Request and response bodies are JSON, UTF-8 encoded.

Response envelopes

Every successful response wraps its payload in data. The shape of data tells you what you got:
A single resource
{
  "data": { "id": "USu3X8sIB9", "...": "..." }
}
A list
{
  "data": [{ "id": "USu3X8sIB9", "...": "..." }],
  "nextCursor": "Y3Vyc29yOjEwMjQ"
}
The envelope keeps metadata like nextCursor out of your data, and it lets responses gain new top-level fields without disturbing your parsing. Which leads to the standing rule: ignore fields you don’t recognize. New optional fields are the one kind of change that ships without a version bump.

Identifiers

Ids are strings with a short resource prefix. A user id starts with US, and other resources follow the same scheme. The prefix makes logs and error messages easier to read. Beyond reading the prefix, treat every id as opaque: don’t parse them, don’t assume a length, and don’t build anything that depends on their internal structure. They are stable identifiers, not data.

Timestamps

All timestamps are ISO 8601 strings in UTC:
"createdAt": "2026-05-19T09:12:45Z"
The API speaks UTC exclusively. If you need local time, convert at display time.

Pagination

Lists are cursor-paginated. Two query parameters in, one field out:
ParameterTypeBehavior
limitinteger, 1–50Items per page. Defaults to 10.
afterstringThe nextCursor from your previous page. Omit it for page one.
Walk pages until nextCursor comes back null:
let cursor = null;
const users = [];

do {
  const url = new URL("https://api.quo.com/users");
  url.searchParams.set("limit", "50");
  if (cursor) url.searchParams.set("after", cursor);

  const res = await fetch(url, {
    headers: {
      Authorization: process.env.QUO_API_KEY,
      "Quo-Api-Version": "2026-03-30",
    },
  });

  const page = await res.json();
  users.push(...page.data);
  cursor = page.nextCursor;
} while (cursor);
Cursors are opaque strings. Pass them back exactly as received, and don’t store them long-term; fetch a fresh page one instead.

When something goes wrong

Failed requests return a structured error that names the field at fault and can include a trace id for support. That contract has its own page.