Production Board
GraphQL API
Optional: query and mutate the same data via a single GraphQL endpoint.
GraphQL is optional. The HTTP API and the WebSocket channel already cover every operation - this page is here for services that prefer a single endpoint with a typed query language. POST a query to /xapi2/graphql with the same Bearer token the rest of the API accepts and you get the same data back, in a GraphQL response shape.
Behind the wire it is the same infrastructure the HTTP and WebSocket paths run on - the GraphQL endpoint is a thin layer on top, not a parallel system. Authentication, personal-access-token scopes, per-IP and per-token rate limits, row-level access checks, audit logging, validation rules, plan quotas, and live event fan-out to subscribed WebSocket clients are all the *exact same code paths* the REST endpoints exercise. A bug fix or a new access rule lands across all three at once.
The schema itself is intentionally compact: one Object type whose payload travels as a JSON scalar, plus six roots (object, objects, createObject, updateObject, deleteObject). Field definitions per model live on the matching model page, which stays the source of truth for what each row carries.
How it works
Read
object(type, id) returns one row, objects(type, limit, offset, sort, q, filters, withCount) returns a paginated list. Both honour the same row-level access rules as the HTTP GET paths - rows you can read over REST you can read here, rows you can't see over REST you can't see here.
Mutate
createObject(type, input), updateObject(type, id, input), and deleteObject(type, id) mirror the HTTP POST / PATCH / DELETE routes. Every write runs the same validation rules and plan checks, fans out to the same live WebSocket subscribers, and lands in the same audit log the REST endpoints feed.
Same security envelope
Authentication, scopes, abuse protection, and rate limits behave identically to the HTTP API. There is no "GraphQL bypass" - every root field charges its own rate-limit token and runs through the same access checks a REST call would, so a single document carrying many operations costs the same as the equivalent series of REST calls.
Document limits
A single document is capped at 64 KiB, depth 8, 200 total field selections, 10 root fields per operation, and 50 variables. These bounds keep one query's worst-case cost predictable; comfortably above any realistic dashboard composition.
Security
The GraphQL endpoint runs on the same infrastructure as the HTTP API and the WebSocket channel - it is a thin layer on top, not a parallel system. Authentication, PAT scopes, rate limits, row-level access checks, audit logging, and live event delivery to WebSocket clients all flow through the *exact same code paths*. Anything you can't do over REST you can't do over GraphQL - there is no "GraphQL backdoor." For per-model rules, see the matching model page.
Schema
The schema is intentionally small - one Object shape with a JSON payload plus six root fields. Per-model field shapes live on the matching model page.
scalar JSONtype Object {id: ID!type: String!status: StringownedBy: IDcreatedBy: IDupdatedBy: IDappSlug: Stringdata: JSONcreatedAt: StringupdatedAt: StringisArchived: Boolean}type ObjectList {data: [Object!]!count: Int!hasMore: Boolean!limit: Intoffset: Intsort: StringappSlug: Stringtotal: Int}type DeleteResult { ok: Boolean!, id: ID! }type Query {object (type: String!, id: ID!): Objectobjects (type: String!,limit: Int, offset: Int, after: ID,sort: String, q: String,withCount: Boolean, filters: JSON,): ObjectList!}type Mutation {createObject (type: String!, input: JSON!): Object!updateObject (type: String!, id: ID!, input: JSON!): Object!deleteObject (type: String!, id: ID!): DeleteResult!}
Examples
curl -X POST https://deploysition.cloud/xapi2/graphql \-H "Authorization: Bearer pat_…" \-H "Content-Type: application/json" \-d '{"query": "query($t:String!,$id:ID!){ object(type:$t,id:$id){ id data } }","variables": { "t": "board", "id": "<object-id>" }}'
Limits
A single document is bounded by the values below - comfortably above any realistic dashboard composition. These caps keep one query's worst-case cost predictable.
| Max query size | 64 KiB |
| Max depth | 8 |
| Max selections (total) | 200 |
| Max root fields per op | 10 |
| Max variables | 50 |
| Rate limit per root field | 1 token (same pool as REST) |
Deliberately unsupported
Subscriptions
Live updates ride the WebSocket channel, which enforces the same access layer. A second live surface over GraphQL would just double the security footprint with no new capability.
Fragments
With the compact schema (one `Object` shape, six roots) fragments add no reuse - just extra parser and validation surface. Inline selections cover every realistic shape and are simpler to audit.
Directives
Conditional selections (`@skip` / `@include`) are simpler and safer expressed as a client-side branch when building the query. The server refuses directives so a selection set always reads literally.
Introspection
The full schema is on this page (and on the per-model pages) - a server endpoint that re-publishes the schema is unnecessary, and shipping one would be a reconnaissance surface for attackers.