Recipient endpoints

Who gets a personalized video, and what comes back for each one.

Endpoints

MethodPathDescription
POST/campaigns/:id/recipientsAdd recipients (JSON array)
GET/campaigns/:id/recipientsList recipients (paginated)
GET/recipients/:idGet single recipient + output
DELETE/campaigns/:id/recipientsBulk-remove recipients (draft only)
DELETE/recipients/:idRemove 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.

FieldTypeDescription
idstring (uuid)Recipient id.
campaignIdstring (uuid)Owning campaign.
firstNamestringStored as the first whitespace-separated token of what you sent — "Sarah Chen" is stored as "Sarah".
companystring | omittedCarried for your own reporting and exports.
titlestring | omittedCarried for your own reporting and exports.
languagestring | omittedLanguage hint for the greeting.
pronunciationstring | omittedPhonetic respelling for the name.
urlstring | omittedThe page recorded behind the greeting.
emailstring | omittedCorrelation key only — Outvo never sends mail to it. Normalized to trimmed lowercase.
externalIdstring | omittedYour own record id, trimmed. Echoed on the recipient.rendered webhook and in exports.
customFieldsobject | omittedArbitrary JSON passthrough, echoed verbatim.
statusstringpending, queued, processing, completed, failed, degraded or cancelled.
Valuespendingqueuedprocessingcompletedfaileddegradedcancelled
degradationReasonstring | omittedWhy a degraded render dropped the screen recording, or why it failed.
createdAtstring (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

FieldTypeDescription
Idempotency-KeystringOptional, 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

FieldTypeDescription
idreqstring (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.

FieldTypeDescription
firstNamereqstring1-100 characters. Only the first word is kept.
companystringCarried through to exports and webhooks.
titlestringCarried through to exports.
languagestringLanguage hint for the TTS greeting, e.g. "en", "pt".
pronunciationstringPhonetic respelling used when the name is not pronounced as written.
urlstringThe page to screen-record. Ignored when the campaign's pipLayout is none; a recipient without one then renders greeting-only.
emailstringMax 254 characters, shaped like an address. Correlation key only — Outvo never sends to it. Surrounding whitespace is tolerated and trimmed.
externalIdstringMax 255 characters. Your own lead/record id.
customFieldsobjectArbitrary JSON, max 16 KB per recipient. Echoed verbatim in webhooks and JSON exports.

Request

json
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

json
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

CodeHTTPWhen
CAMPAIGN_NOT_FOUND404Unknown id, not yours, or soft-deleted.
CAMPAIGN_NOT_DRAFT409Recipients can only be added while the campaign is a draft.
VALIDATION_ERROR400Empty 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_REUSED422Same Idempotency-Key, different body.

GET/campaigns/:id/recipients

List a campaign's recipients with filtering, search and sorting.

Success: 200 OK

Path parameters

FieldTypeDescription
idreqstring (uuid)Campaign id.

Query parameters

FieldTypeDescription
pagenumberPage number, minimum 1.
Default1
per_pagenumberItems per page, maximum 100.
Default20
statusstringOne of pending, queued, processing, completed, failed, degraded, cancelled.
searchstring1-100 characters. Case-insensitive substring match across firstName, company and url.
sort_bystringColumn to sort by. name sorts on firstName.
Valuesnamecompanystatuscreated_atDefaultcreated_at
sort_orderstringSort direction. Any value other than asc is treated as desc.
ValuesascdescDefaultdesc

Response

json
200 OK

{
  "data": [ /* recipient objects */ ],
  "meta": { "page": 1, "perPage": 20, "total": 42, "totalPages": 3 }
}

Errors

CodeHTTPWhen
CAMPAIGN_NOT_FOUND404Unknown 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.gif proxy that re-signs itself on every request. This is the one to embed in email. null before a render exists.
  • expiresAt — the retention deadline on the rendered asset. Every touch in a sequence has to land before it. null before a render exists.
  • renderOutput — the raw output row, in snake_case, unlike everything else in this response. It is omitted entirely (not null) until the recipient has rendered. Its thumbnail_url and expires_at are the raw, shorter-lived values — prefer the two top-level fields above.

Path parameters

FieldTypeDescription
idreqstring (uuid)Recipient id.

Response

json
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

CodeHTTPWhen
RECIPIENT_NOT_FOUND404Unknown 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

FieldTypeDescription
idreqstring (uuid)Campaign id, in draft status.

Errors

CodeHTTPWhen
CAMPAIGN_NOT_FOUND404Unknown id, not yours, or soft-deleted.
CAMPAIGN_NOT_DRAFT409Recipients 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

FieldTypeDescription
idreqstring (uuid)Recipient id.

Errors

CodeHTTPWhen
RECIPIENT_NOT_FOUND404Unknown id, or it belongs to a campaign you do not own.
CAMPAIGN_NOT_DRAFT409The campaign has left draft.
RECIPIENT_ALREADY_PROCESSED409The recipient is past pending — it has been queued, rendered or failed. Records of work already done are not deletable.