Recipient endpoints
Who gets a personalized video, and what comes back for each one.
Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /campaigns/:id/recipients | Add recipients (JSON array) |
| GET | /campaigns/:id/recipients | List recipients (paginated) |
| GET | /recipients/:id | Get single recipient + output |
| DELETE | /campaigns/:id/recipients | Bulk-remove recipients (draft only) |
| DELETE | /recipients/:id | Remove one pending recipient (draft only) |
Max 5,000 recipients per batch. If url is provided, the render includes a PIP screen recording.
The recipient object
Optional fields you never sent are omitted from the response rather than returned as null — check for the key, not for a null value.
| Field | Type | Description |
|---|---|---|
| id | string (uuid) | Recipient id. |
| campaignId | string (uuid) | Owning campaign. |
| firstName | string | Stored as the first whitespace-separated token of what you sent — "Sarah Chen" is stored as "Sarah". |
| company | string | omitted | Carried for your own reporting and exports. |
| title | string | omitted | Carried for your own reporting and exports. |
| language | string | omitted | Language hint for the greeting. |
| pronunciation | string | omitted | Phonetic respelling for the name. |
| url | string | omitted | The page recorded behind the greeting. |
| string | omitted | Correlation key only — Outvo never sends mail to it. Normalized to trimmed lowercase. | |
| externalId | string | omitted | Your own record id, trimmed. Echoed on the recipient.rendered webhook and in exports. |
| customFields | object | omitted | Arbitrary JSON passthrough, echoed verbatim. |
| status | string | pending, queued, processing, completed, failed, degraded or cancelled.Values pendingqueuedprocessingcompletedfaileddegradedcancelled |
| degradationReason | string | omitted | Why a degraded render dropped the screen recording, or why it failed. |
| createdAt | string (ISO 8601) | When the recipient was added. |
Reference
POST/campaigns/:id/recipients
Add recipients to a draft campaign. The body is a JSON array, not an object.
Success: 201 Created
Headers
| Field | Type | Description |
|---|---|---|
| Idempotency-Key | string | Optional, max 255 characters. Strongly recommended: without it a retried batch inserts duplicate rows, and every duplicate row becomes a duplicate billed render at launch. Use a distinct key per campaign — a key is unique across your whole account, not per campaign. See Idempotency. |
Path parameters
| Field | Type | Description |
|---|---|---|
| idreq | string (uuid) | Campaign id, in draft status. |
Body
A JSON array of 1 to 5,000 recipient objects. One bad row rejects the whole batch — nothing is written.
| Field | Type | Description |
|---|---|---|
| firstNamereq | string | 1-100 characters. Only the first word is kept. |
| company | string | Carried through to exports and webhooks. |
| title | string | Carried through to exports. |
| language | string | Language hint for the TTS greeting, e.g. "en", "pt". |
| pronunciation | string | Phonetic respelling used when the name is not pronounced as written. |
| url | string | The page to screen-record. Ignored when the campaign's pipLayout is none; a recipient without one then renders greeting-only. |
| string | Max 254 characters, shaped like an address. Correlation key only — Outvo never sends to it. Surrounding whitespace is tolerated and trimmed. | |
| externalId | string | Max 255 characters. Your own lead/record id. |
| customFields | object | Arbitrary JSON, max 16 KB per recipient. Echoed verbatim in webhooks and JSON exports. |
Request
POST /campaigns/9a8b7c6d-.../recipients
Idempotency-Key: import-2026-08-13-001
[
{
"firstName": "Sarah",
"company": "Acme",
"title": "Head of Growth",
"url": "https://acme.com",
"email": "sarah@acme.com",
"externalId": "lead_8813",
"customFields": { "segment": "enterprise" }
},
{ "firstName": "Mike", "company": "Globex", "url": "https://globex.com" }
]Response
201 Created
{
"data": [
{
"id": "d4c3b2a1-...",
"campaignId": "9a8b7c6d-...",
"firstName": "Sarah",
"company": "Acme",
"title": "Head of Growth",
"url": "https://acme.com",
"email": "sarah@acme.com",
"externalId": "lead_8813",
"customFields": { "segment": "enterprise" },
"status": "pending",
"createdAt": "2026-08-13T12:02:00.000Z"
}
]
}Errors
| Code | HTTP | When |
|---|---|---|
| CAMPAIGN_NOT_FOUND | 404 | Unknown id, not yours, or soft-deleted. |
| CAMPAIGN_NOT_DRAFT | 409 | Recipients can only be added while the campaign is a draft. |
| VALIDATION_ERROR | 400 | Empty array, over 5,000 entries, a missing or oversized firstName, a malformed email, or customFields over 16 KB (the message names the offending index). |
| IDEMPOTENCY_KEY_REUSED | 422 | Same Idempotency-Key, different body. |
GET/campaigns/:id/recipients
List a campaign's recipients with filtering, search and sorting.
Success: 200 OK
Path parameters
| Field | Type | Description |
|---|---|---|
| idreq | string (uuid) | Campaign id. |
Query parameters
| Field | Type | Description |
|---|---|---|
| page | number | Page number, minimum 1. Default 1 |
| per_page | number | Items per page, maximum 100. Default 20 |
| status | string | One of pending, queued, processing, completed, failed, degraded, cancelled. |
| search | string | 1-100 characters. Case-insensitive substring match across firstName, company and url. |
| sort_by | string | Column to sort by. name sorts on firstName. Values namecompanystatuscreated_atDefaultcreated_at |
| sort_order | string | Sort direction. Any value other than asc is treated as desc. Values ascdescDefaultdesc |
Response
200 OK
{
"data": [ /* recipient objects */ ],
"meta": { "page": 1, "perPage": 20, "total": 42, "totalPages": 3 }
}Errors
| Code | HTTP | When |
|---|---|---|
| CAMPAIGN_NOT_FOUND | 404 | Unknown id, not yours, or soft-deleted. |
GET/recipients/:id
Fetch one recipient together with its rendered output, if there is one.
Success: 200 OK
Three response-only fields ride along on top of the recipient object:
thumbnailUrl— the durable/v/{shortId}/thumb.gifproxy that re-signs itself on every request. This is the one to embed in email.nullbefore a render exists.expiresAt— the retention deadline on the rendered asset. Every touch in a sequence has to land before it.nullbefore a render exists.renderOutput— the raw output row, in snake_case, unlike everything else in this response. It is omitted entirely (notnull) until the recipient has rendered. Itsthumbnail_urlandexpires_atare the raw, shorter-lived values — prefer the two top-level fields above.
Path parameters
| Field | Type | Description |
|---|---|---|
| idreq | string (uuid) | Recipient id. |
Response
200 OK
{
"data": {
"id": "d4c3b2a1-...",
"campaignId": "9a8b7c6d-...",
"firstName": "Sarah",
"company": "Acme",
"url": "https://acme.com",
"email": "sarah@acme.com",
"externalId": "lead_8813",
"customFields": { "segment": "enterprise" },
"status": "completed",
"createdAt": "2026-08-13T12:02:00.000Z",
"thumbnailUrl": "https://share.outvo.io/v/abc123/thumb.gif",
"expiresAt": "2026-10-12T12:05:00.000Z",
"renderOutput": {
"id": "...",
"recipient_id": "d4c3b2a1-...",
"video_url": "https://cdn.outvo.io/outputs/....mp4",
"shareable_url": "https://share.outvo.io/v/abc123",
"short_id": "abc123",
"duration_ms": 45000,
"render_time_ms": 180000,
"host": "play",
"vimeo_url": null,
"thumbnail_url": "https://...signed...",
"expires_at": "2026-10-12T12:05:00.000Z",
"created_at": "2026-08-13T12:05:00.000Z"
}
}
}Errors
| Code | HTTP | When |
|---|---|---|
| RECIPIENT_NOT_FOUND | 404 | Unknown id, or it belongs to a campaign you do not own. |
DELETE/campaigns/:id/recipients
Remove every recipient from a draft campaign in one call.
Success: 204 No Content
Path parameters
| Field | Type | Description |
|---|---|---|
| idreq | string (uuid) | Campaign id, in draft status. |
Errors
| Code | HTTP | When |
|---|---|---|
| CAMPAIGN_NOT_FOUND | 404 | Unknown id, not yours, or soft-deleted. |
| CAMPAIGN_NOT_DRAFT | 409 | Recipients can only be deleted while the campaign is a draft. |
DELETE/recipients/:id
Remove a single recipient that has not started rendering.
Success: 204 No Content
Path parameters
| Field | Type | Description |
|---|---|---|
| idreq | string (uuid) | Recipient id. |
Errors
| Code | HTTP | When |
|---|---|---|
| RECIPIENT_NOT_FOUND | 404 | Unknown id, or it belongs to a campaign you do not own. |
| CAMPAIGN_NOT_DRAFT | 409 | The campaign has left draft. |
| RECIPIENT_ALREADY_PROCESSED | 409 | The recipient is past pending — it has been queued, rendered or failed. Records of work already done are not deletable. |
