REST API
Ask questions, transform data and keep memory over plain HTTP
The REST API exposes what DataGrout does as resources: answers, transforms, memories, skill runs and background tasks. Requests and responses are JSON with named fields, and errors use HTTP status codes.
It is the integration surface for workflow platforms and for any client that would rather not speak MCP or JSON-RPC.
Base URL
https://gateway.datagrout.ai/api/v1/servers/{server_uuid}
{server_uuid} is your server’s UUID, found on the server detail page. Every endpoint below is relative to this URL.
A client that connected by OAuth can leave the server out. Its token already names the server it was granted, so the same endpoints are also served at:
https://gateway.datagrout.ai/api/v1
with GET /server in place of GET /. An access token belongs to one server and has to use the first form.
The REST API is always available. Unlike JSON-RPC, it needs no enabling step.
Authentication
The same credentials as the rest of the gateway: a bearer access token, OAuth 2.1, or mutual TLS. See Authentication.
curl https://gateway.datagrout.ai/api/v1/servers/YOUR_UUID \
-H "Authorization: Bearer YOUR_TOKEN"
{ "id": "YOUR_UUID", "name": "Sales data" }
GET / reads and costs nothing, which makes it the right check for whether a credential works.
Answers
POST /answers
Ask a question in plain language. DataGrout plans the work, runs it against your connected systems, then checks the result against the question.
| Field | Required | |
|---|---|---|
question |
yes | The question |
inputs |
no | Values the question needs, as an object — see input_required below |
curl -X POST https://gateway.datagrout.ai/api/v1/servers/YOUR_UUID/answers \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"question": "Total open opportunity value by region this quarter"}'
{
"answer": [
{ "region": "North", "total": 184000 },
{ "region": "South", "total": 92500 }
],
"verified": true,
"certificate": { "id": "ctc_...", "url": "https://ctc.datagrout.ai/certs/ctc_..." },
"skill_id": "skill_...",
"skill_expires": false,
"execution_id": "exec_...",
"reference": "rc_...",
"usage": { "credits_charged": 12, "credits_remaining": 988 }
}
verifiedistrueonly when DataGrout’s verifier accepted the answer.- An answer that could not be verified is still a
200. DataGrout answered, honestly:answerisnull, what the plan produced is underunverified_answer, andcaveatsays why it could not be verified. Branch onverifiedrather than treating it as an error. - A confirmed empty answer — two independently planned runs both found nothing — is
verified: truewith an emptyanswerand anote. certificateshows exactly what ran.skill_idre-runs this verified work without planning — see Skill runs. An id beginningskill_does not expire. Whenskill_expiresistrueit is a temporary handle that lasts an hour.
Transforms
POST /transforms
Reshape or aggregate data on DataGrout’s servers. Send the data inline, or name a reference an earlier response returned, so a large result never has to travel back through your client.
| Field | Required | |
|---|---|---|
goal |
yes | What the result should be |
data |
one of | The data, as an array or object |
reference |
one of | A reference from an earlier response |
curl -X POST https://gateway.datagrout.ai/api/v1/servers/YOUR_UUID/transforms \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"goal": "total units by city", "data": [{"city": "Lisbon", "units": 31}, {"city": "Porto", "units": 12}, {"city": "Lisbon", "units": 7}]}'
{
"result": [
{ "city": "Lisbon", "sum_units": 38 },
{ "city": "Porto", "sum_units": 12 }
],
"certificate": { "id": "ctc_...", "url": "https://ctc.datagrout.ai/certs/ctc_..." },
"execution_id": "exec_...",
"reference": "rc_...",
"usage": { "credits_charged": 1, "credits_remaining": 987 }
}
Memories
A memory is a named store of facts that outlives any one request. Recall answers from what was stored and from what follows logically from it.
Store facts
POST /memories/{name}/facts
| Field | Required | |
|---|---|---|
statement |
yes | Facts in plain language |
curl -X POST https://gateway.datagrout.ai/api/v1/servers/YOUR_UUID/memories/warehouses/facts \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"statement": "Warehouse North stocks 40 pallets of item A."}'
Returns 201 Created:
{
"stored": 3,
"facts": [
{ "id": "f1a2b3c4d5e6", "name": "Warehouse North", "type": "entity" }
],
"message": "Remembered 3 facts."
}
Recall
POST /memories/{name}/recall
| Field | Required | |
|---|---|---|
question |
yes | What to look up |
{
"results": [{ "aggregate": "sum", "field": "value", "value": 40 }],
"total": 1,
"has_more": false,
"interpretation": "Count the pallets stocked by Warehouse North.",
"message": "Found 1 matching fact."
}
interpretation is how DataGrout read the question.
Skill runs
POST /skills/{skill_id}/runs
Run previously verified work again, with no planning. {skill_id} is a saved skill’s id or the skill_id an answer returned.
| Field | Required | |
|---|---|---|
inputs |
no | Values the skill takes, as an object |
{
"result": [ ... ],
"certificate": { "id": "ctc_...", "url": "https://ctc.datagrout.ai/certs/ctc_..." },
"execution_id": "exec_...",
"usage": { "credits_charged": 3, "credits_remaining": 984 }
}
An unknown or expired skill is 404.
Background work
Slow work moves to a background task. The request returns 202 Accepted with a Location to poll, in the same form — with or without the server — that the request used:
HTTP/1.1 202 Accepted
Location: /api/v1/servers/YOUR_UUID/tasks/task_...
Retry-After: 2
{ "task_id": "task_...", "status": "running", "location": "/api/v1/servers/YOUR_UUID/tasks/task_..." }
To wait for the result in the same request instead, send Prefer: wait=N (RFC 7240). The request holds for up to N seconds — at most 30 — and returns the finished result if it lands in time, or the 202 if it does not.
Tasks
GET /tasks/{task_id}
{
"id": "task_...",
"status": "completed",
"result": { "answer": [ ... ], "verified": true },
"created_at": "2026-10-08T17:02:11Z",
"updated_at": "2026-10-08T17:02:19Z"
}
status is running, completed, failed or cancelled. A completed task’s result has the same shape as the response the request would have returned directly. A failed task carries an error with the same shape as below.
A task belongs to the server that started it; any other server gets 404.
Idempotency
Send an Idempotency-Key header on any POST to make retrying it safe — a retry after a dropped connection otherwise runs the work, and charges for it, twice.
- A repeat of the same request with the same key returns the first result instead of running again, with
"replayed": trueadded. If the first is still running, the repeat waits for it. - Keys are remembered for 10 minutes, per server.
- A key is 1–128 characters of letters, digits and
_ . : -. - Reusing a key for a different request is refused with
422— a key identifies one request, not a slot.
Errors
Every error has the same body:
{
"error": {
"code": 409,
"type": "loop_detected",
"message": "This identical call ran 4 times with nothing changing in between...",
"details": { "execution_count": 4 }
}
}
type is stable — branch on it. message is written for people.
| Status | type |
Meaning |
|---|---|---|
400 |
The body is not valid JSON | |
401 |
Credentials are missing or were rejected | |
402 |
insufficient_credits |
The account’s balance cannot cover the operation |
403 |
An account problem that new credentials will not fix, such as an unverified email on the account that owns the server | |
403 |
operation_not_enabled |
The server’s tool selection excludes the operation |
404 |
No server has that UUID | |
404 |
not_found |
No such task, or an unknown or expired skill |
409 |
loop_detected |
DataGrout’s loop guard declined an identical repeated request. Vary the input, or raise the thresholds in the server’s Interaction Settings. |
422 |
invalid_request |
A required field is missing or has the wrong type |
422 |
input_required |
The question needs values; details.needed lists them. Send them in inputs. |
422 |
operation_failed |
The operation ran and failed. The message is DataGrout’s own. |
429 |
Too many requests for this server. Wait for the number of seconds in Retry-After. |
|
502 |
upstream_error |
A connected system failed. The request was fine; retrying may succeed. |
The 400, 401, 403 and 404 without a type come from authentication, before the request reaches the API, and carry only code and message. A 429 comes from the gateway’s rate limiter, shared by every transport, and its body is {"error": "rate_limited", "message": "..."}.
Related
- Authentication – All supported auth methods
- JSON-RPC API – Calling DataGrout’s tools directly
- Quick Start – Get started with your first call