Agent API reference
The same authenticated tool set, over two transports. New to this? Start with the BYO-agent guide for how to enable access and mint a token.
Base URL & authentication
All endpoints are served from the canonical host over HTTPS:
https://www.diligio.coEvery request carries a Diligio agent token as a bearer credential. The token resolves to a specific user in a specific workspace; there are no separate API keys or client ids.
Authorization: Bearer dgo_agent_...One optional header is recorded in the audit log as a label and is never used for authorisation:
X-Agent-Identity: claude-opus-4-8/my-botmin(token level, the user's live role), re-resolved on every request. Both the workspace switch and the user's opt-in must be on or the call is refused. See Access levels.REST endpoints
Three endpoints under /api/agent/v1. Tool calls return a uniform envelope: { "ok": true, "result": ... } on success, or { "error", "code" } with an HTTP status on failure.
Self-check
/api/agent/v1/meConfirms the token works and reports the effective level plus the tools it grants. Good for a connection test.
curl -s https://www.diligio.co/api/agent/v1/me \
-H "Authorization: Bearer $TOKEN"{
"ok": true,
"org_id": "8f3c...",
"user_id": "a91b...",
"role": "contributor",
"level": "full",
"agent": "claude-opus-4-8/my-bot",
"tools": ["kb_search", "list_projects", "list_questions",
"get_question", "write_answer", "set_question_status",
"export_project"]
}List tools
/api/agent/v1/toolsReturns the tools available to this token's level, each with its JSON-Schema input. Tools the level does not grant are omitted entirely.
{
"tools": [
{
"name": "kb_search",
"description": "Semantic-search the workspace knowledge base...",
"input_schema": {
"type": "object",
"properties": {
"query": { "type": "string", "description": "Natural-language query..." },
"limit": { "type": "integer", "description": "Max results (1-10, default 5)." }
},
"required": ["query"],
"additionalProperties": false
}
}
]
}Call a tool
/api/agent/v1/tools/{tool}JSON body = the tool's argumentsThe request body is the tool's arguments as a JSON object (send {} for tools that take none). The result is whatever the tool returns, wrapped in the success envelope.
curl -s -X POST https://www.diligio.co/api/agent/v1/tools/kb_search \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"query":"data retention policy","limit":3}'{
"ok": true,
"result": [
{
"id": "0c2e...",
"question": "What is your data retention policy?",
"answer": "Client data is retained for the contract term plus 30 days...",
"category": "Data Governance",
"similarity": 0.81
}
]
}MCP (JSON-RPC)
/api/mcpStreamable HTTP, JSON-RPC 2.0The MCP endpoint is https://www.diligio.co/api/mcp. It is a self-contained MCP server over Streamable HTTP: one JSON response per POST (no server-initiated SSE). The bearer header is the same as REST. See the guide for a Claude Desktop config.
Supported methods: initialize, ping, tools/list, tools/call, and the notifications/* acknowledgements. The default protocol version is 2025-06-18; the server identifies as { "name": "diligio", "version": "1.0.0" }.
POST /api/mcp
Authorization: Bearer dgo_agent_...
{ "jsonrpc": "2.0", "id": 1, "method": "initialize",
"params": { "protocolVersion": "2025-06-18" } }{ "jsonrpc": "2.0", "id": 2, "method": "tools/call",
"params": {
"name": "kb_search",
"arguments": { "query": "encryption at rest" }
} }A tool result comes back as MCP content; tool-level failures are returned as content with isError: true (so the model can read the message) rather than a JSON-RPC error:
{ "jsonrpc": "2.0", "id": 2, "result": {
"content": [ { "type": "text", "text": "[ ... JSON ... ]" } ]
} }GET to /api/mcp returns 405 (there is no SSE stream). Auth failures during initialize return the REST-style { error, code } body with the matching HTTP status.Caching & tokens
The single biggest lever on your input-token bill is your model provider's prompt cache: the system prompt, tool list and recent turns are reused across calls instead of re-priced in full. Provider caching is automatic once a stable prefix is sent, but it only fires when that prefix is byte-identical across calls. Diligio is built to make that true by construction, and to tell you exactly what would break it.
What Diligio guarantees
- Deterministic tool order.
tools/listreturns tools in a fixed registry order on every call. The MCP spec (2026-07-28) calls this a SHOULD because it "improves LLM prompt cache hit rates"; we treat it as a hard rule and a build-time test fails if a reorder lands. - No per-call values in the listing. Tool names, descriptions and schemas never carry a timestamp, a count, a request id or your tenant name. A build-time scan rejects any of these.
- Cache hints on every listing. MCP results carry
ttlMs+cacheScope(private, because the tool set is capability-scoped to your token). REST listing reads (/me,/tools,/resources/read) carry anETagand honourIf-None-Matchwith a bodyless304when nothing changed. - A pinnable manifest. Read
diligio://manifest/<protocol_version>(the version/mestamps) to get a content digest of your exact listing and the change-window policy. Compare the digest on every reconnect: if it changed, your prefix was going to bust anyway, so re-read/toolsnow.
curl -s https://www.diligio.co/api/agent/v1/tools \
-H "Authorization: Bearer $TOKEN" \
-H "If-None-Match: "$ETAG" # from your previous response's ETag header"
# → HTTP 304 with no body when the listing is unchangedProvider knobs you set
Provider caching must be requested at the breakpoints you choose. The exact field is provider-specific:
| Provider | How to cache | TTL |
|---|---|---|
| Anthropic (Claude) | cache_control: { type: "ephemeral" } on the system / tools block you want cached | 5 min default, 1 hour with <code>ttl: "1h"</code> (1h billed at a higher write multiple) |
| OpenAI (GPT-4o/5) | prompt_cache_key to bucket the session + prompt_cache_breakpoint to mark the prefix boundary | session-scoped, automatic on prefix match |
| Google (Gemini) | implicit content cache, no manual breakpoint | automatic on a stable prefix; see Gemini docs for the explicit-cache option |
Beyond our deploys, three client-side changes silently kill your own cache hits. Keep them constant within a session:
tool_choice— changing it between calls invalidates the tool (and on some models, the system) cache.- The thinking config — toggling extended-thinking mode or changing
budget_tokensrewrites the prefix. - The effort / reasoning setting — same effect; pick one and hold it.
The exact-copy rule also means JSON key order must be stable. Some SDKs randomise object key order when serialising tool_use blocks, which silently breaks the prefix hash. If your hit rate is unexpectedly low, serialise tool calls with stable key order.
The 1024-token floor
Most providers only cache prefixes above a minimum size (1,024 tokens on the Claude Sonnet/Opus family; up to 4,096 on some models). The always-on tool core for a full-access token clears this floor. A narrow read-only token (one tool, ~215 tokens) is genuinely below it and is served uncached: that is intentional, because padding a small slice to clear a cache floor would cost more than it saves.
Tool reference
Each Respond tool, its required level, input schema and an example. Of these, a read-only token reaches only kb_search. The Compliance set is summarised by domain below, and /api/agent/v1/tools returns every tool your own token grants with its schema.
kb_search · read-only
Semantic search over the workspace knowledge base of previously answered questions. Use the returned answers as grounding; do not invent facts beyond them.
| Field | Type | Required | Notes |
|---|---|---|---|
query | string | yes | Natural-language query. Max 2,000 characters. |
limit | integer | no | Results to return, 1–10 (default 5). |
// body
{ "query": "Do you support SSO?", "limit": 5 }
// result: array of hits
[ { "id": "...", "question": "...", "answer": "...",
"category": "...", "similarity": 0.78 } ]list_projects · full
Lists the DDQ / questionnaire projects visible to the user. No arguments.
// body
{}
// result
[ { "id": "...", "title": "Acme vendor DDQ", "client": "Acme",
"status": "In Progress", "progress": 42,
"created_at": "2026-06-01T10:00:00Z" } ]list_questions · full
| Field | Type | Required | Notes |
|---|---|---|---|
project_id | string | yes | From list_projects. |
status | string | no | Optional status filter, e.g. "Drafting". |
// body
{ "project_id": "...", "status": "Drafting" }
// result
[ { "id": "...", "number": 12, "category": "Security",
"question": "...", "answer": null, "ai_draft": "...",
"status": "Drafting", "confidence": 0.0,
"answered_by_agent": false, "assignee_id": null,
"due_date": null } ]get_question · full
Fetch a single question in full detail.
// body
{ "question_id": "..." }
// result
{ "id": "...", "project_id": "...", "number": 12,
"category": "Security", "question": "...", "answer": "...",
"ai_draft": "...", "status": "SME Review", "confidence": 0.0,
"answered_by_agent": true, "assignee_id": "...",
"approver_id": "...", "due_date": null }write_answer · full
Write (or overwrite) a question's answer with text the agent composed. The answer is flagged agent-authored and enters the human review workflow.
| Field | Type | Required | Notes |
|---|---|---|---|
question_id | string | yes | — |
answer | string | yes | Answer text. Max 100,000 characters. |
// body
{ "question_id": "...", "answer": "Yes. Diligio supports SAML and OIDC SSO..." }
// result
{ "id": "..." }set_question_status · full
Move a question through the review workflow. The same transition and role rules as a human in this role apply; finalising to Final is admin-only.
| Field | Type | Required | Notes |
|---|---|---|---|
question_id | string | yes | — |
new_status | string | yes | Target status, e.g. "SME Review", "Pending human review", "Final". |
// body
{ "question_id": "...", "new_status": "SME Review" }
// result
{ "id": "...", "status": "SME Review" }export_project · full
Export a completed project to its source format and mark it Completed. A terminal action, reachable only by a full agent whose user is permitted to export.
// body
{ "project_id": "..." }
// result: the export descriptor (filename, format, location)Compliance (GRC) tools
These appear only when the workspace runs Diligio Compliance and the token's user holds a GRC role. Reads need any GRC role; proposing needs a full token held by a grc_admin. Agents propose; a human certifies (tenants can opt in to delegated certification with a status allow-list and daily cap). The set is large, so it is self-documenting: call /api/agent/v1/tools with your token and every tool your token grants comes back with its input schema. The domains, at a glance:
| Domain | Representative tools | Writes |
|---|---|---|
| Worklist & recipes | grc_next_actions, grc_evidence_recipe, grc_list_skills | read-only |
| Posture & frameworks | grc_list_frameworks, grc_list_controls, grc_get_control, grc_list_gaps, grc_propose_status, grc_propose_many, grc_fill_mapped, grc_preview_certify | proposals (human-certified) |
| Evidence | grc_list_evidence, grc_add_evidence, grc_link_evidence, grc_verify_evidence_anchor | direct, hash-anchorable |
| Registers | grc_create_risk / asset / policy / person / device / vendor / subprocessor, attestations, training, checklists | direct planning writes |
| Monitoring | grc_list_connectors, grc_run_checks, grc_record_check_runs (push mode), grc_list_check_history, grc_control_health | verdicts, never posture |
| Reviews & audits | access reviews, internal audits, incidents, audit projects & PBC requests, re-attestation | direct + proposals |
| Trust & questionnaires | Trust Center config, subprocessors, questionnaires, grc_autofill_questionnaire, grc_suggest_answer | drafts (human-approved) |
| Exports | grc_export, grc_verify_export | read-only, digest-signed |
// body
{ "framework": "iso27001", "control_key": "A.8.15",
"status": "implemented", "note": "Centralised logging via...",
"evidence_ids": ["<evidence id>"] }
// result
{ "ok": true, "pending": "implemented" } // awaits human certification// Your runner executes the checks on YOUR infrastructure (credentials
// never reach Diligio) and pushes the verdicts against a credential-less
// connector row that acts as the identity anchor.
// body
{ "connector_id": "<connector id>", "runs": [
{ "check_key": "aws_rds_encryption", "status": "pass",
"resource": "db-prod-1", "ran_at": "2026-07-12T09:30:00Z" }
] }
// result: runs land append-only, stamped source: "self_attested",
// with control evidence auto-captured for passing control-mapped checks.Human-only by design (no tool exists): publishing the Trust Center, deciding trust-access requests, minting auditor share links, and connector credentials. The end-to-end setup, including the full zero-custody loop a prospect's agent can run, is in the Compliance agent quickstart.
Errors
REST errors return { "error": "<message>", "code": "<code>" } with the HTTP status below. In MCP, auth errors surface the same way during initialize; tool failures come back as isError content.
| Status | code | When |
|---|---|---|
| 401 | missing_token | No bearer token on the request. |
| 401 | invalid_token | Malformed, unknown or revoked token. |
| 401 | revoked_token | Token was explicitly revoked. |
| 401 | expired_token | Token passed its expiry. |
| 403 | agent_mode_disabled | The workspace switch is off. |
| 403 | user_unavailable | The token's user is no longer active in the workspace. |
| 403 | user_disabled | The user's per-user switch is off. |
| 403 | forbidden | The tool exceeds this token's level. |
| 400 | invalid_input | Missing/invalid arguments, or a non-JSON body. |
| 405 | method_not_allowed | GET on the MCP endpoint (POST only). |
| 500 | internal_error | Unexpected server error. |
Limits
kb_search: query up to 2,000 characters;limitclamped to 1–10.write_answer: answer up to 100,000 characters.- Tool calls (and MCP requests) run up to 60 seconds; long exports are designed to finish inside that window.
- Tokens act through a short-lived user session that is refreshed transparently; you never manage it.