Respondent invites API
Respondent invites let your own systems invite known people to an interview. Create a personal link for a customer with their email or account ID attached, send it however you like, and their answers are tied to that customer from the start.
You need a Pro, Business or Enterprise plan and an API key. For keys and authentication, see the API overview.
To read the results, use the Conversations API: each conversation carries its invite and the identities you attached.
These endpoints are older than the rest of the API and keep their original names: they call a project an agent (agentId) and use camelCase parameters.
Create an invite link#
POST /api/v1/respondent-invitesEach request creates one link for one respondent in one project. Diaform also creates the conversation immediately and attaches the identities you send, so the results are linked to that person from the start.
Request body#
| Field | Type | Required | Rules |
|---|---|---|---|
agentId | string | Yes | Project ID (UUID) in the key's organization |
identities | array | Yes | 1 to 10 items, one per type. |
identities[].type | string | Yes | Up to 64 characters: lowercase letters, numbers, _ and -, starting with a letter or number. Uppercase is converted to lowercase. |
identities[].value | string | Yes | 1 to 512 characters after trimming. An email value must be a valid email address and is stored in lowercase. |
organizationId | string | No | If you send it, it must match the key's organization |
send_email | boolean | No | true emails the link to the invite's email identity and sends one reminder after 3 days if the respondent hasn't finished. Default false. |
When you send send_email, the response has an email object: { "status": "sent" }, { "status": "failed" }, or { "status": "skipped", "reason": "..." }. The invite is created in every case. Reasons are no_email, unsubscribed, not_published, no_usage, monthly_limit (your plan's invitation emails for the month are used up), answered, already_sent, in_progress, plan and not_found. See Identifying respondents for what the email contains.
Common types are email, name, identifier, user_id and account_id. Any type that follows the rules works.
Example#
curl -X POST "https://diaform.io/api/v1/respondent-invites" \
-H "Authorization: Bearer $DIAFORM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentId": "b8acb3ab-1f3d-4a7e-a846-b7f493f86b6d",
"identities": [
{ "type": "email", "value": "alex@acme.com" },
{ "type": "account_id", "value": "acct_4821" }
]
}'Response#
{
"organization_id": "4d6ecdb4-8543-4adf-ae65-1f24106d5474",
"agent_id": "b8acb3ab-1f3d-4a7e-a846-b7f493f86b6d",
"agent_name": "Onboarding interviews",
"invite": {
"id": "3a4f49c7-b8f4-4c44-8f74-fd8e4277c3e4",
"created_at": "2026-05-07T12:00:00.123456+00:00",
"status": "pending",
"opened_at": null,
"completed_at": null,
"conversation_id": "0f8d43a2-6df9-49c8-bf5d-fd2b6ec2d9ee",
"identities": [
{
"id": "71138f36-d16a-41f8-8a1f-65b9b8d483e3",
"created_at": "2026-05-07T12:00:00.123456+00:00",
"conversation_id": "0f8d43a2-6df9-49c8-bf5d-fd2b6ec2d9ee",
"source": "owner",
"identity_type": "email",
"value": "alex@acme.com",
"metadata": {}
},
{
"id": "120feee2-a10e-4b9a-af8e-a54d4dd672a1",
"created_at": "2026-05-07T12:00:00.123456+00:00",
"conversation_id": "0f8d43a2-6df9-49c8-bf5d-fd2b6ec2d9ee",
"source": "owner",
"identity_type": "account_id",
"value": "acct_4821",
"metadata": {}
}
],
"url": "https://acme.diaform.io/c/b8acb3ab-1f3d-4a7e-a846-b7f493f86b6d?invite=dia_resp_pCaJHykjUYBKZdkbVUtbyZRMC3yFzm246SK2K8lw"
}
}| Field | Description |
|---|---|
invite.id | Invite ID. Webhooks send it as respondent.invite_id. |
invite.status | pending, opened or completed |
invite.conversation_id | The conversation created for this respondent |
invite.identities | The identities you sent, each with source: "owner" |
invite.url | The link to send. It uses your verified custom domain if you have one. null once the invite is completed. |
How the link behaves#
- Created. The invite is
pendingand its conversation isinvited. - Opened. When the respondent opens the link, both become
opened. The conversation moves tostartedonce they reply. - Resumed. If the respondent leaves and comes back through the same link, they continue the same conversation.
- Ended. When the conversation ends, whether completed or cancelled, the invite becomes
completedand the link stops working.
The project must be published for the link to open. Links work for both Public link and Invite-only links projects. On public projects, the normal link keeps working too. See Identifying respondents.
Older conversations endpoint#
GET /api/v1/respondent-conversationsThis endpoint lists a project's conversations and still works, so existing integrations don't need to change. For new work, use List conversations, which adds filters, transcripts and simpler paging.
| Parameter | Required | Description |
|---|---|---|
agentId | Yes | Project ID |
limit | No | 1 to 100. Default 50. |
status | No | invited, opened, started, completed or cancelled |
cursor | No | nextCursor.created_at from the previous page |
cursorId | No | nextCursor.id from the previous page |
It returns conversations (each with conversation_id, created_at, status, respondent_email, metadata, identities, summary and invite) and nextCursor, an object with created_at and id, or null on the last page. Send those two values back as cursor and cursorId.
URL-encode cursor. Its timestamp contains a +, which must be sent as %2B. A raw + is read as a space and the request fails with 400. curl --data-urlencode and URLSearchParams encode it for you.
Errors#
Errors return JSON with an error message and a code, as in the rest of the API. A 400 also includes details.fieldErrors, which names the field that failed.
{
"error": "Invalid request",
"code": "invalid_request",
"details": {
"formErrors": [],
"fieldErrors": {
"identities": ["Add at least one respondent identity."]
}
}
}| Status | code | Cause |
|---|---|---|
400 | invalid_request | A field or query parameter is missing or invalid |
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, or organizationId doesn't match the key |
404 | not_found | agentId isn't a project in the key's organization |
500 | internal_error | An error occurred on Diaform's side. Try again. |
Limits#
These two endpoints have no rate limit. Each invite takes up to 10 identities (one per type), and each organization can have up to 10 API keys.