Skip to main content
Every action on the API follows the same rules, so what you learn from one call holds for all of them. This page lists those rules in one place.

Versions and the beta marker

The version is in the address (/api/v1/) and in the API description (1.0.0-beta). While the API is in beta, every response carries an x-goosy-api: beta header. Beta does not mean anything can change at any moment. A change either adds something — a new action, a new optional argument, a new field in an answer, a new refusal code — or it is deprecated first and removed later (see Deprecations). Write your code to ignore fields it does not know, and a new field will never break it.

Request ids

Send an x-request-id header with your own id for the call, and you get the same id back:
  • Every response carries one, refusals included. If you send none, or one we cannot use, we generate one and send that back instead.
  • An id is 1–128 characters from A–Z a–z 0–9 . _ : -, starting with a letter or a digit.
  • Log it. The id is recorded on the activity trail for the call, so quoting it to support finds your call without a timestamp hunt.

One error shape

Every failure you can read has ok: false, a code and a message at the top level — whether the request was turned away at the door or the action itself declined:
Branch on ok, then on code. The status tells you which layer answered: Refusals at the door also carry the same values in a nested error object. That object is deprecated: read the top-level fields. Every code is listed in Errors and refusals.

Rate-limit headers

Every call that counted against your key’s per-minute allowance tells you where you stand:
Slow down when RateLimit-Remaining gets low, rather than waiting for a 429. A 429 adds Retry-After. Very rarely a response carries none of these three — that means we could not count the call, and we let it through rather than refuse you for our own fault. More in Rate limits.

Retrying safely: Idempotency-Key

A network can fail after an action ran but before you heard back. To retry without doing the work twice, send an Idempotency-Key header with a key you choose, and send the same key when you retry:
  • The retry gets the first result back instead of a second table.
  • A key is 1–200 visible characters with no spaces, and it is honoured for at least 24 hours.
  • The API reference shows the header on every action that takes one, and marks it required on an action that needs a key. On an action that does not, a key is refused with tenant_api.idempotency_unsupported and nothing runs — check whether the first call landed before you retry it.
  • Sending the key in the body as idempotency_key still works, but it is deprecated on the API. If you send both, they must match (tenant_api.idempotency_key_conflict otherwise).
Over MCP there is no header, so assistants keep passing idempotency_key as an argument.

Paging through a list

A list action takes limit and answers next_cursor while there is more. Pass it back as cursor, with the same filters, to get the next page:
When next_cursor is missing, you have the last page. Treat the cursor as an opaque string: do not build or edit one. A cursor used with different filters is refused with invalid_arguments; start again without it. content.status also still answers truncated, which is deprecated in favour of next_cursor. Searches (knowledge.search, library.search) answer their best matches and have no pages; narrow the query instead.

Rotating a key

keys.rotate replaces the key that makes the call with a new one. It takes two calls, like other actions that change who can use something:
  1. Call it with an optional overlap_hours (default 24, at most 168). You get a confirmation_id back and nothing changes yet.
  2. Call it again with the same key, the same overlap_hours and the confirmation_id. The answer carries the new key in key. It is shown once, so store it straight away.
The old key keeps working for overlap_hours, then stops with the ordinary 401. Use the overlap to deploy the new key everywhere the old one was. A confirmation sent with any other key is refused. Keys for a managed site are rotated by the platform and refuse this action. A confirmation is used up the moment it is accepted, so it can never make two new keys. If the rotation then fails (keys.rotate_failed), nothing changed: call again with no confirmation_id to start a fresh one. In the rare case the answer is keys.rotate_successor_not_withdrawn, a new key with the prefix the message names may still be active — revoke it in Settings › API & MCP. Your current key is unchanged.

Deprecations

When something you can send or read is going away, we:
  1. announce it here and in the changelog, with the date it may be removed;
  2. mark every response to a call that uses it — or whose answer carries it, like truncated on content.status — with three headers — Deprecation (when it was deprecated), Sunset (the earliest removal date) and Link (this section);
  3. keep it working for at least 90 days, and never remove it before the sunset date.
Currently deprecated:
Watch for the Deprecation header in your client’s logs. It only appears on calls that use something deprecated, so seeing it is the signal to change that call.