REST API reference
The current v1 contract, from endpoint discovery to query results and audit events.
Base URL and requests
https://api.recursift.app/v1Send an Authorization bearer header on every data request, and Content-Type: application/json for query creation. Responses are JSON. Unknown fields in query bodies are rejected. The /health route is outside /v1 and requires no key.
Routes
| Method / route | Required scope | Response |
|---|---|---|
| GET /me | Any valid key | key_id, customer_id, scopes |
| GET /agents | agents:read | agents, next_cursor |
| POST /queries | query:write | 202 receipt: id, status, agent_total, deadline |
| GET /queries | query:read | queries, next_cursor |
| GET /queries/{id} | query:read | Job, findings, counts, unanswered_agents |
| POST /queries/{id}/cancel | query:write | Receipt; terminal jobs remain unchanged |
| GET /findings | query:read | findings, coverage, next_cursor |
| GET /audit/events | audit:read | events, next_cursor |
| POST /resource-queries | resources:query | 202: id and status running. The question is not saved. |
| GET /resource-queries/{id} | resources:query | Status and coverage. Result rows are empty. |
| POST /resource-queries/{id}/cancel | resources:query | id and status. A finished query is unchanged. |
Saved questions use /queries and keep the question and answer. Ephemeral resource queries use /resource-queries, require persistence_mode ephemeral, and do not keep the question, parameters, results or idempotency key. customer_history is refused with 422. See Data flows and retention.
Create a query
{
"question": "What operating system are you running?",
"target": {
"agent_ids": [
"YOUR_ENDPOINT_ID"
]
},
"timeout_seconds": 120
}| Field | Constraint |
|---|---|
| question | 1–500 characters after trimming |
| target.agent_ids | 1–100 unique, accessible endpoint IDs |
| timeout_seconds | 5–120 seconds; omitted or 0 defaults to 120 |
| Idempotency-Key header | Optional, at most 128 characters; preserve for retries |
| Request body | At most 16 KiB |
Any unknown or inaccessible target rejects the whole query with 404. An empty target never means the whole fleet. Reusing an idempotency key with a different normalized request returns 409; matching requests return the same job.
Job and finding fields
| Object | Fields |
|---|---|
| Agent | id, hostname, platform, last_seen_at, status |
| Job | id, status, question, created_at, deadline, agent_total, agent_answered, agent_failed, findings, unanswered_agents |
| Finding | agent_id, hostname, status, answer, error, answered_at, query_id |
| Audit event | seq, event_json, prev_hash, hash |
Finding answer, error and answered_at may be null. GET /queries/{id} materializes newly received findings. Answer text is bounded to 4,000 characters; raw evidence rows are not exposed by this API.
Pagination and finding search
List routes accept limit (1–100, default 50) and cursor. Pass the returned next_cursor unchanged and stop when it is empty. Ordering follows stable IDs, not creation time. Audit cursors are sequence numbers.
curl --fail-with-body --get "https://api.recursift.app/v1/findings" \
-H "Authorization: Bearer $RECURSIFT_API_KEY" \
--data-urlencode "search=macOS" \
--data-urlencode "limit=20"Detection routes
| Method / route | Required scope | Purpose |
|---|---|---|
| GET /detection/catalog | detection:read | Atomic catalog metadata; filter by platform or technique |
| GET /detection/bundles | detection:read | Installed rule packs and per-rule attribution |
| POST /detection/bundles | detection:scan | Install a signed rule pack |
| POST /detection/bundles/{id}/activate | detection:scan | Make a pack active for its format |
| DELETE /detection/bundles/{id} | detection:scan | Revoke a pack |
| GET /detection/capabilities | detection:read | Per-endpoint readiness as reported |
| GET /detection/jobs | detection:read | Recent jobs with their endpoint results |
| GET /detection/jobs/{id} | detection:read | One job and its denominator |
| POST /detection/jobs | detection:scan | File scan, path scan or event replay on 1–100 endpoints |
| POST /detection/jobs/{id}/cancel | detection:scan | Cancel a job |
The API checks the scope, records an audit event, and forwards the request with your key, which the console validates again for scope and customer ownership. A missing scope returns 403 naming the required scope. Requests are limited to 256 KiB. An endpoint with no report reads as no detection capability reported, and an unknown result is never shown as no match.
Errors
{
"error": {
"code": "not_found",
"message": "Route not found"
}
}| Status | Meaning |
|---|---|
| 400 | Invalid request, target, cursor or limit |
| 401 | Missing or invalid customer key |
| 403 | Insufficient scope or refused browser origin |
| 404 | Unknown route or resource unavailable to this customer |
| 409 | Idempotency conflict |
| 429 | Key, customer or endpoint limit; honor Retry-After |
| 500 / 503 | Service or storage failure; retain request identifiers |
MCP transport errors do not use this REST JSON envelope. The OpenAPI download describes the REST surface.