API overview
The Diaform API gives your own code and your AI agents access to the feedback you collect: projects, conversations with their AI summaries and full transcripts, and AI reports. Use it to analyze responses with your own agent, feed a data warehouse or trigger automations.
There are two ways to connect, and both use the same API key and return the same data:
- REST API. JSON over HTTPS, for scripts, backends and automation tools.
- MCP server. For AI agents such as Claude Code, Codex or Cursor. The agent gets the API as tools it can call directly. See MCP server.
What you can do#
| Resource | Endpoints | Reference |
|---|---|---|
| Projects | List projects, get one project with its questions | Projects |
| Conversations | List a project's conversations with AI summaries, get one conversation with its transcript | Conversations |
| AI reports | List a project's reports, get one report in full | Reports |
| Respondent invites | Create a personal interview link for a known person | Respondent invites |
To be notified when a conversation ends instead of polling, use webhooks.
Before you start#
You need two things:
- A Pro, Business or Enterprise plan.
- An API key. Owners and admins create keys under Settings → General → API keys.
To create a key, click New API key, give it a name, and click Create key. In the Your new API key dialog, click Copy, then I've saved my key. Diaform shows the key only once.
Keys look like dia_org_live_ followed by 32 characters. They don't expire. Each organization can have up to 10. To turn one off, click Revoke, then Revoke API key. Anything that uses the key stops working immediately.
Authentication#
Send the key as a bearer token on every request:
Authorization: Bearer dia_org_live_...A key can read every project in its organization, whatever the project's access settings. Keep it on your server or in your agent's configuration, and never put it in browser or app code. The examples in these docs read it from a DIAFORM_API_KEY environment variable.
Base URL and conventions#
https://diaform.io/api/v1- Requests and responses are JSON. Field names use
snake_case. - IDs are UUIDs. Timestamps are ISO 8601 strings in UTC.
- A field with no value is
null. It is never left out. - The API calls an interviewer a project, like the dashboard. A project ID is the ID after
/agents/in the project's dashboard URL, and after/c/in its public link. - Conversations started from the dashboard Preview are never returned.
Make your first request#
List your projects:
curl "https://diaform.io/api/v1/projects" \
-H "Authorization: Bearer $DIAFORM_API_KEY"Then fetch the completed conversations of one of them:
curl -G "https://diaform.io/api/v1/projects/b8acb3ab-1f3d-4a7e-a846-b7f493f86b6d/conversations" \
-H "Authorization: Bearer $DIAFORM_API_KEY" \
--data-urlencode "status=completed" \
--data-urlencode "limit=50"Pagination#
List endpoints return the newest items first, in this shape:
{
"data": [],
"has_more": true,
"next_cursor": "WyIyMDI2LTA1LTA3VDEyOjAwOjAwLjEyMzQ1NiswMDowMCIsIjBmOGQ0M2EyIl0"
}| Parameter | Description |
|---|---|
limit | Items per page, 1 to 100. Default 25. |
cursor | The next_cursor of the previous page |
To get the next page, repeat the request with the same filters and cursor set to next_cursor. On the last page, has_more is false and next_cursor is null.
async function listAllConversations(projectId) {
const conversations = [];
let cursor = null;
do {
const url = new URL(
`https://diaform.io/api/v1/projects/${projectId}/conversations`,
);
url.searchParams.set("status", "completed");
url.searchParams.set("limit", "100");
if (cursor) url.searchParams.set("cursor", cursor);
const response = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.DIAFORM_API_KEY}` },
});
if (!response.ok) throw new Error((await response.json()).error);
const page = await response.json();
conversations.push(...page.data);
cursor = page.next_cursor;
} while (cursor);
return conversations;
}Errors#
Errors use standard HTTP status codes and return JSON with a readable error message and a stable code to branch on:
{
"error": "No project with this ID exists in your organization.",
"code": "not_found"
}A 400 also has details, one entry per invalid field:
{
"error": "The request has invalid parameters.",
"code": "invalid_request",
"details": [
{ "field": "limit", "message": "Too big: expected number to be <=100" }
]
}| Status | code | Cause |
|---|---|---|
400 | invalid_request | A parameter is missing or invalid. details names the field. |
401 | unauthorized | The Authorization header is missing, isn't Bearer dia_org_live_..., or the key was revoked |
403 | forbidden | Your organization isn't on Pro, Business or Enterprise |
404 | not_found | The ID doesn't exist in the key's organization |
429 | rate_limited | Too many requests. Wait for the number of seconds in the Retry-After header. |
500 | internal_error | An error occurred on Diaform's side. Try again in a moment. |
An ID from another organization returns 404, the same as one that doesn't exist.
Rate limits#
Each API key can make 120 requests a minute to the read endpoints and the MCP server combined. Over that, requests return 429 with a Retry-After: 60 header. Each page returns up to 100 items.
Give these docs to your agent#
Every docs page has a Copy page button that copies it as Markdown, ready to paste into an AI agent. Each page is also available as plain Markdown, and the whole API reference as one file:
https://diaform.io/api/docs-markdown/apiPoint your agent at that URL, or connect it directly with the MCP server.