Asset endpoints

Upload the base video a campaign personalizes.

Endpoints

MethodPathDescription
POST/campaigns/:id/assetsUpload base video (multipart)
POST/campaigns/:id/assets/from-urlImport base video from a URL instead of uploading it
POST/campaigns/:id/assets/from-libraryReuse a video already in your library
GET/campaigns/:id/assetsList campaign assets

Accepted formats: video/mp4, video/webm, video/quicktime (.mov). Max size: 200 MB. Campaign must be in draft status.

A campaign holds one base video. All three ingestion paths upsert: a second call replaces the existing asset and deletes the old file. After ingestion the audio fingerprint is computed inline (1-3 s) and a background transcode starts — POST /campaigns/:id/ready answers 409 CAMPAIGN_NOT_READY until that fingerprint lands.

The asset object

FieldTypeDescription
idstring (uuid)Asset id.
campaignIdstring (uuid)The campaign this base video belongs to.
fileUrlstringStorage URL of the stored object. Not directly playable — use previewUrl from the list endpoint.
mimeTypestringvideo/mp4, video/webm or video/quicktime.
fileSizenumberBytes.
durationMsnumber | omittedFilled in by the background transcode; absent until then.
createdAtstring (ISO 8601)Ingestion timestamp.

Reference

POST/campaigns/:id/assets

Upload the base recording as multipart form data.

Success: 201 Created

Path parameters

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

Request

bash
curl -X POST https://api.outvo.io/campaigns/9a8b7c6d-.../assets \
  -H "Authorization: Bearer pc_live_xxx" \
  -F "file=@base-video.mp4"

Response

json
201 Created

{
  "data": {
    "id": "3c2b1a09-...",
    "campaignId": "9a8b7c6d-...",
    "fileUrl": "https://<storage>/assets/<user>/<campaign>/base.mp4",
    "mimeType": "video/mp4",
    "fileSize": 24117248,
    "createdAt": "2026-08-13T12:01:00.000Z"
  }
}

Errors

CodeHTTPWhen
CAMPAIGN_NOT_FOUND404Unknown id, not yours, or soft-deleted.
CAMPAIGN_NOT_DRAFT409The base video cannot be swapped after the campaign leaves draft.
VALIDATION_ERROR400No file part in the request.
INVALID_FILE_TYPE400Not mp4, webm or quicktime.
FILE_TOO_LARGE400Over 200 MB.

POST/campaigns/:id/assets/from-url

Have Outvo fetch the base recording from a URL. This is the path for tools that cannot post a file — Apify inputs, Clay columns, n8n HTTP nodes, MCP tools.

Success: 201 Created

Headers

FieldTypeDescription
Idempotency-KeystringOptional, max 255 characters. See Idempotency.

Path parameters

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

Body

FieldTypeDescription
urlreqstring1-2048 characters. Must be publicly reachable over http/https; the fetch validates the scheme, resolves and pins DNS, re-checks every redirect hop, caps the download and sniffs the real container type.

Request

json
POST /campaigns/9a8b7c6d-.../assets/from-url
{ "url": "https://your-cdn.com/base-recording.mp4" }

Response

json
201 Created

{ "data": { /* the asset object */ } }

Errors

CodeHTTPWhen
CAMPAIGN_NOT_FOUND404Unknown id, not yours, or soft-deleted.
CAMPAIGN_NOT_DRAFT409The campaign has left draft.
VALIDATION_ERROR400Missing url, an unreachable or disallowed host, a redirect to one, or bytes that are not a supported video container.
FILE_TOO_LARGE400The remote file exceeds 200 MB.
IDEMPOTENCY_KEY_REUSED422Same Idempotency-Key, different body.

POST/campaigns/:id/assets/from-library

Point the campaign at a recording you already have. No bytes move, so this is the cheapest of the three.

Success: 201 Created

placeholderWord on the response reports the word this endpoint wrote onto the campaign, taken from the recording itself — a Studio entry knows for certain which word was spoken on camera. It is null when the campaign's word was left as it was.

Headers

FieldTypeDescription
Idempotency-KeystringOptional, max 255 characters.

Path parameters

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

Body

FieldTypeDescription
libraryEntryIdreqstring (uuid)A library entry id, from GET /video-library or the dashboard's Studio.

Response

json
201 Created

{
  "data": {
    "id": "3c2b1a09-...",
    "campaignId": "9a8b7c6d-...",
    "fileUrl": "https://<storage>/...",
    "mimeType": "video/mp4",
    "fileSize": 24117248,
    "createdAt": "2026-08-13T12:01:00.000Z",
    "libraryEntryId": "7f6e5d4c-...",
    "placeholderWord": "there"
  }
}

Errors

CodeHTTPWhen
CAMPAIGN_NOT_FOUND404Unknown campaign id, not yours, or soft-deleted.
CAMPAIGN_NOT_DRAFT409The campaign has left draft.
NOT_FOUND404Unknown library entry, or not yours.

GET/campaigns/:id/assets

List the campaign's base video with a playable preview link.

Success: 200 OK

previewUrl is signed for one hour and is not part of the stored asset — re-fetch rather than caching it. It is absent if the object could not be signed.

Path parameters

FieldTypeDescription
idreqstring (uuid)Campaign id.

Response

json
200 OK

{
  "data": [
    {
      "id": "3c2b1a09-...",
      "campaignId": "9a8b7c6d-...",
      "fileUrl": "https://<storage>/...",
      "mimeType": "video/mp4",
      "fileSize": 24117248,
      "durationMs": 46000,
      "createdAt": "2026-08-13T12:01:00.000Z",
      "previewUrl": "https://<storage>/...?token=..."
    }
  ]
}

Errors

CodeHTTPWhen
CAMPAIGN_NOT_FOUND404Unknown id, not yours, or soft-deleted.