Skip to main content

Authentication and permissions

GraphQL uses the same authentication model as REST. Send an OAuth2/OIDC access token as a Bearer token on both endpoints:

curl -X POST "https://api.tourfold.com/api/v2/graphql" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"query":"{ equipment(first: 1) { edges { node { id created_at } } } }"}'

Replace equipment with a definition slug from your own workspace, and note the shape: a connection must be selected through edges { node { … } }, and page size is first — there is no limit argument.

Use the Authorization Code flow for user-facing applications and Client Credentials for server-to-server integrations. Flows, endpoints and token lifetimes are documented once, for both surfaces, under REST authentication.

Reading requires a write grant​

warning

Querying the GraphQL API requires one of the custom-object write grants:

  • custom-objects:instances:create
  • custom-objects:instances:update
  • custom-objects:instances:delete

There is no custom-objects:instances:read grant, and the authorization check is a single check on the endpoint. A role with read-only access to custom objects therefore cannot use the GraphQL API at all — neither the query endpoint nor the schema endpoint.

If you need a read-only integration today, either grant it one of the write grants above — accepting that this permits writes over REST — or use the REST custom-object endpoints, which do have read-level authorization. A dedicated read grant is planned; until it ships, this is the actual requirement rather than a cleaner one.

Both endpoints are gated identically, so a token that cannot query also cannot download the schema.

Per-type access​

Beyond the endpoint-level grant, individual custom object types can be access-restricted. Those checks run per type, during execution, and they do not fail the query:

SituationResult
A restricted type you may not read, at the rootAn empty connection (edges: [], totalCount: 0)
A restricted type reached through a to-one relationnull
A restricted type reached through a to-many relationAn empty connection
A type that exists only as a child of another recordNever available at the root; only through its parent

The rest of the query still executes and still returns its data. This keeps one inaccessible branch from failing an otherwise valid request, but it has an important consequence: an empty result is not proof of an empty collection. If a type you know to be populated comes back empty, check the role's access to that type before concluding there are no records.

Authentication failures are HTTP errors, not GraphQL errors​

Authentication and authorization are enforced before the GraphQL engine runs, so they surface as HTTP status codes with an RFC 9457 problem document — not as entries in a GraphQL errors array:

StatusMeaning
401No token, an expired token, or an invalid token.
403A valid token whose role lacks a custom-object write grant. The body names the grants that would have satisfied the check.

A 200 response with errors in the body is therefore never an authentication problem — it is something about the query itself. See Errors and limits.

Practical notes​

  • Tokens are workspace-scoped. Use a token issued for the workspace whose data you are querying.
  • Prefer short-lived access tokens with refresh tokens in user-facing applications; never embed a long-lived token in a client application.
  • The GraphQL API is read-only, so a token used only for querying never needs grants beyond the one that unlocks the endpoint — but be aware that grant also permits writes over REST.