Skip to content

Call the HTTP API

Once your host is running, every registered provider’s actions and entities are HTTP endpoints under /api/providers/. This guide shows the three everyday calls — invoke an action, search an entity, fetch a record by id — with a request and response for each. The examples assume a host on http://localhost:3800 serving a notes provider.

For the exact contract of every route see the HTTP API reference, and for interactive exploration open /api/docs on your running host.

POST /api/providers/:providerId/actions/:actionId. The request body wraps the action’s input under input, plus two optional fields — resumption (to answer a prior interrupt) and items (a conversation-item snapshot). The response is an envelope: status is completed or interrupted, a completed call carries result, and both carry the items the call emitted.

Terminal window
curl -X POST http://localhost:3800/api/providers/notes/actions/create \
-H 'content-type: application/json' \
-d '{ "input": { "title": "Groceries", "body": "Milk, eggs, coffee" } }'

Response:

{
"status": "completed",
"result": {
"id": "note_8f2a",
"title": "Groceries",
"createdAt": "2026-07-21T09:12:00.000Z"
},
"items": []
}

The same call with fetch:

const res = await fetch('http://localhost:3800/api/providers/notes/actions/create', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ input: { title: 'Groceries', body: 'Milk, eggs, coffee' } }),
});
const { status, result } = await res.json();

An action that pauses for a decision responds with status: "interrupted" (a normal 200, not an error) carrying message, data, and an opaque state. Resume by calling again with { input, resumption: { data: <answer>, state } }.

You rarely assemble this envelope by hand: the typed client unwraps result, re-throws an interrupt as a ToolInterrupt, and gives you full type safety against a specific host.

POST /api/providers/:providerId/entities/:entityId. The body wraps the entity’s search input under input, plus optional cursor and limit for pagination. The response is { results, nextCursor? }.

Terminal window
curl -X POST http://localhost:3800/api/providers/notes/entities/note \
-H 'content-type: application/json' \
-d '{ "input": { "query": "coffee" }, "limit": 20 }'

Response:

{
"results": [{ "id": "note_8f2a", "title": "Groceries", "createdAt": "2026-07-21T09:12:00.000Z" }],
"nextCursor": "eyJvZmZzZXQiOjIwfQ=="
}

When nextCursor is present, pass it back as cursor to fetch the next page:

Terminal window
curl -X POST http://localhost:3800/api/providers/notes/entities/note \
-H 'content-type: application/json' \
-d '{ "input": { "query": "coffee" }, "limit": 20, "cursor": "eyJvZmZzZXQiOjIwfQ==" }'

A response with no nextCursor means there are no more pages.

GET /api/providers/:providerId/entities/:entityId/:recordId. Returns the single record, shaped by the entity’s data schema.

Terminal window
curl http://localhost:3800/api/providers/notes/entities/note/note_8f2a

Response:

{
"id": "note_8f2a",
"title": "Groceries",
"body": "Milk, eggs, coffee",
"createdAt": "2026-07-21T09:12:00.000Z"
}

Every request body is validated against the provider’s Zod schema at the boundary. When a request does not match, the host responds 400 with a structured error that names the offending fields — not an opaque message:

{
"error": "Validation Error",
"message": "Request does not match the expected schema — `title`: Required",
"statusCode": 400,
"details": {
"location": "request",
"method": "POST",
"url": "/api/providers/notes/actions/create",
"issues": [{ "path": "title", "message": "Required", "code": "invalid_type" }]
}
}

Read details.issues to see exactly which fields failed and why. The full error shape is in the HTTP API reference.