GraphQL
The same resources and projections through one GraphQL endpoint.
The Public API also exposes a GraphQL interface as an alternative to REST. It mirrors the same resources and returns the same public projections.
Endpoint & authentication
- Operations:
POST /api/public/v1/graphql - Interactive playground:
GET /api/public/v1/graphiql(GraphiQL, public; no key required to load the UI)
Operations require a bearer token:
Authorization: Bearer strd_<key>In GraphiQL, paste the whole header into the Headers editor to run authenticated
operations, e.g. Authorization: Bearer strd_YOUR_KEY.
Schema & resources
The schema mirrors the REST resources: organizations, contacts, projects,
employees, cvs, tasks, activities, assignments, time_entries, and a search(q)
query. Responses are the same public projections as REST (no team_id, and so on).
Three resources are REST-only, for two different reasons:
filesandattachments, because their uploads and downloads are binary and do not fit GraphQL.trips, which is read-only everywhere and reachable over REST and MCP but has no GraphQL fields at all.
Use the REST endpoints for those three.
Pagination (cursor connections)
List fields use cursor connection pagination:
{
contacts(first: 10, after: "…") {
nodes { id firstName email }
pageInfo { endCursor hasNextPage }
}
}first: page size, max 500 (per-resource defaults match the RESTlimitdefaults).after: an opaque cursor from a previouspageInfo.endCursor; omit for the first page.
Paginate by inspecting pageInfo.hasNextPage; if true, pass pageInfo.endCursor as the
next after and repeat.
Mutations & write scope
Mutations require a write-scoped key. A read-only key attempting a mutation returns
HTTP 200 with a forbidden error:
{
"errors": [
{ "message": "This API key lacks the required scope", "extensions": { "code": "forbidden" } }
]
}mutation CreateContact {
createContact(input: { firstName: "Jane", lastName: "Doe", email: "jane@example.com" }) {
id firstName lastName email createdAt
}
}Errors
GraphQL operations always return HTTP 200; errors appear in the errors array with
the same codes as REST under extensions.code:
{ "errors": [ { "message": "…", "extensions": { "code": "bad_request" } } ] }Abuse limits
- Query depth: maximum nesting level is 10.
- Query complexity: total budget is 10000. List fields cost roughly
min(first, 500) × childComplexity; scalar and object fields costchildComplexity + 1. Wide or deeply nested queries may hit the ceiling. - Page size:
firstis clamped to 1 to 500.