uloggdDevelopers
Developer portaluloggd
Get startedResourcesArcade
Developer portaluloggdOverviewAuthenticationScopesRate limitsErrorsPaginationVersioning

Errors

One shape, always, including on 500.

code is stable and meant to be matched on. message is for a human reading a log and is not a contract: it can be reworded at any time.

{
  "error": {
    "code": "insufficient_scope",
    "message": "This key does not hold library.write.",
    "scope": "library.write"
  }
}
CodeStatusMeans
unauthorized401The request carried no identity: no key, and no session from this origin.
forbidden403The identity is fine and a rule refused anyway: a block, a private account, a closed comment section. When the refusal is one of those, the message says which.
invalid_key401Unknown, revoked or expired. The three are not told apart.
insufficient_scope403The key does not hold the scope, which is named in the body.
not_found404No such resource, or none the owner can see.
invalid_request400A field is missing or not allowed. The message says which.
conflict409That already exists.
rate_limited429Carries retry_after in seconds.
internal500Something failed here. Nothing about the schema is said.
unavailable503Only GET /api/v1/arcade/jwks today, with reason identity_unavailable: the server does not have the keys that sign Arcade identities yet. Try again later.

Other members

Beside code and message, a refusal can carry:

  • reason: a word to act on, finer than the code, on Arcade refusals and on some account ones, such as reauthentication_required and mfa_required. A client that meets one it does not know acts on the code.
  • field: the field refused, with invalid_request.
  • detail: a finer cause, such as too_large on a media_invalid.
  • retry_after: how many seconds to wait, with rate_limited.
  • scope: the scope that was missing, with insufficient_scope.
  • check: the URL check result, when submitting an Arcade release or making it live is refused.
  • second_factor: with reauthentication_required, on the routes that take a code instead of a new sign-in, whether a two-factor code would do.

Arcade's refusals, with each reason, are in the Arcade refusal table.

Where a refusal comes from

Most refusals are the database's, not the handler's. A check constraint, a not-null column, a rate-limit trigger or a row-level policy raises, and that raise is translated into the shape above.

Only refusals raised deliberately carry their message outward. Anything else answers internal with nothing else said, because an unplanned error message is a description of the schema.

404 not_found covers two cases on purpose: the resource does not exist, and the resource exists but the key's owner cannot see it. Telling them apart would be a way to ask whether somebody's private list exists.

Rate limits

Counted per key rather than per account, and reported on every response.

Pagination

One page object, on every collection.

On this page

Other membersWhere a refusal comes from