CollectsOverviewClaudeZapierMakeToolsPermissionsEventsErrors and limitsAPI referenceVersioningChangelogSupport

Errors and limits

Every REST refusal is a JSON body of exactly { code, message }. Branch on the code; show the message, which says what happened and what to do, and may change. Over MCP a refused tool call is a tool error whose structuredContent holds the same code and message.

Over MCP, a tool error for xero_not_connected or plan_required also carries url: the Collects page where the person fixes it. On MCP 2026-07-28, a host that declares URL elicitation is offered that link once instead, and shows it to the person before opening it.

CodeStatusMeaning
unauthorized401No access token was sent. Sign in (OAuth) and send it as a Bearer token.
invalid_token401The access token is expired, revoked or not for this API. Refresh it, or connect again. A connection ends when its person's password is reset, they are deactivated, or they turn two-step verification on.
insufficient_scope403The connection was not given a permission the call needs. The WWW-Authenticate challenge names every permission it needs; reconnect and grant them.
xero_not_connected403Starting a resolution or creating a webhook subscription needs a live Xero connection. An owner or admin reconnects Xero in Collects. Over MCP the tool error carries the link as url.
plan_required403The organisation's plan does not include this. Only the owner changes the plan, on collects.io; the message carries the link. Over MCP the tool error carries the link as url.
role_required403The person's role in Collects cannot do this, as in the Collects app. The message says which role can.
dunning_disabled422The organisation has Send collection emails switched off, so a resolution would contact nobody. An owner or admin turns it on in Settings.
account_suspended403The organisation is suspended: reads still work, every change is refused until the owner resolves it on collects.io.
connection_read_only403The person who connected the app set the connection to Read only. Reconnect with full access to make changes.
not_found404No such record in this organisation, or not one this person can see. Also returned when the API is switched off for the organisation.
resolution_open409A resolution is already open for this invoice. Follow it instead of starting another.
idempotency_key_reused409This Idempotency-Key was used within 24 hours for a different request. Use a new key for a new request.
not_resolvable422Collects cannot resolve this invoice now (closed, not yet due, or held); the message says which.
invalid_request400An input is not one the Collects screen accepts, the screen does not offer that action in the record's current state, or Collects-Version names a version this API does not serve. The message says which.
rate_limited429The connection's rate limit is spent. Wait the Retry-After seconds, then try again.
method_not_allowed405The path exists but not with this HTTP method.
internal_error500Something went wrong in Collects. Try again later.
confirmation_mismatch409The confirmation token does not match the action as it stands now (what was previewed changed), or is not this person's and connection's. Preview again.
confirmation_expired409The confirmation token is older than ten minutes. Preview again.
confirmation_used409The confirmation token was already used. Preview again.

Rate limits

Each connection (one person's connection of one app) has two buckets: reads, 120 a minute, and writes, 100 a minute, each refilling evenly over the minute. Every response to a signed-in call states its bucket in the IETF RateLimit-Policy and RateLimit headers, for example RateLimit-Policy: "read";q=120;w=60 and RateLimit: "read";r=119;t=1 (r calls left, t seconds until the bucket is full). A refused call answers 429 rate_limited with Retry-After.

Other headers