API
GraphQL
Query and change records with GraphQL, using the same permissions and query object as REST.
Each workspace has one GraphQL endpoint. Every collection gets its own fields, named after the collection. Permissions are the same as REST.
Endpoint
POST /api/v1/workspaces/:workspace/graphql
Send query and optional variables as JSON, with your token.
curl -X POST 'http://127.0.0.1:8080/api/v1/workspaces/YOUR_WORKSPACE_ID/graphql' \
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
--data '{"query":"query ($query: JSON) { records_deals(limit: 20, query: $query) { id title stage amount } }","variables":{"query":{"filters":[{"field":"stage","operator":"eq","value":"Lead"}]}}}' const res = await fetch(`http://127.0.0.1:8080/api/v1/workspaces/${workspaceId}/graphql`, {
method: 'POST',
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
query: `query ($query: JSON) {
records_deals(limit: 20, query: $query) { id title stage amount }
}`,
variables: { query: { filters: [{ field: 'stage', operator: 'eq', value: 'Lead' }] } }
})
});
const { data } = await res.json(); payload, _ := json.Marshal(map[string]any{
"query": `query ($query: JSON) { records_deals(limit: 20, query: $query) { id title stage amount } }`,
"variables": map[string]any{"query": map[string]any{
"filters": []map[string]any{{"field": "stage", "operator": "eq", "value": "Lead"}},
}},
})
req, _ := http.NewRequest("POST", "http://127.0.0.1:8080/api/v1/workspaces/"+workspaceID+"/graphql", bytes.NewReader(payload))
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req) res = requests.post(
f"http://127.0.0.1:8080/api/v1/workspaces/{workspace_id}/graphql",
headers={"Authorization": f"Bearer {token}"},
json={
"query": "query ($query: JSON) { records_deals(limit: 20, query: $query) { id title stage amount } }",
"variables": {"query": {"filters": [{"field": "stage", "operator": "eq", "value": "Lead"}]}},
},
)
data = res.json()["data"] Response:
{
"data": {
"records_deals": [
{
"id": "adb696e5c90257c2c313d635f892a776",
"title": "Warehouse analytics",
"stage": "Lead",
"amount": 7300000
}
]
},
"extensions": { "meta": { "request_id": "...", "response_time": 6.3 } }
} Metadata sits in extensions.meta instead of meta.
Fields per collection
For a collection named deals:
| Field | Type | What it does |
|---|---|---|
records_deals(limit, offset, query) | query | List records. query takes the query object. |
record_deals(id) | query | Get one record. |
create_deals(input) | mutation | Create a record. |
update_deals(id, input) | mutation | Update some fields. |
delete_deals(id) | mutation | Delete a record. |
Joined fields are available as _related when the query has joins.
Mutations
mutation {
create_deals(input: { title: "Cafe loyalty app", stage: "Lead", amount: 4500000 }) {
id
}
} mutation {
update_deals(id: "YOUR_RECORD_ID", input: { stage: "Qualified" }) {
id
stage
}
} Mutations need the same write access as REST. Studio’s API reference shows ready-made queries for each of your collections and a console to run them.