POST /v1/projects/:projectId/content/ugc-remix
Generate a UGC-style influencer reaction video.
POST
/v1/projects/:projectId/content/ugc-remixPhase 1stableidempotent
- Auth
- Bearer
- Scope
- content:write
Starts a content-generate job. Returns 202
with containerIds (single-element array — the partner surface never
fans out variants) and jobId.
UGC remix is the only format with no required creative input — the workflow auto-selects a reaction template, music track, and app-demo source unless the partner pins specific values. The influencer is the on-camera actor; one of socialAccountId (with a wired influencer) or influencerId is required.
Path parameters
projectIdstring (uuid)requiredThe project to generate content for.
Body
Body
socialAccountIdstringoptionalConnected social-account id. Walks the wiring chain to find the influencer voice/language and the layer the container is anchored to.influencerIdstringoptionalExplicit influencer override. Wins over the wired influencer. One of socialAccountId or influencerId is required.hookstringoptionalOptional opening overlay text. 1–2000 chars; line breaks and emoji preserved. Defaults to no overlay.mediaIdstringoptionalPartner-uploaded app-demo source clip. Defaults to the project's first `media_role: "app-demo"` row.
Defaults when fields are omitted
- Reaction template — random pull from our public reaction-video pool.
- Music — random trending track.
- App-demo source — the project's first
media_role: "app-demo"row inmedia_library, unless the partner pinned one viamediaId.
Request
curl -X POST https://api.layers.com/v1/projects/{projectId}/content/ugc-remix \
-H "Authorization: Bearer $LAYERS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"influencerId": "inf_4a8e1bc2..."
}'const res = await fetch(
`https://api.layers.com/v1/projects/${projectId}/content/ugc-remix`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.LAYERS_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({ socialAccountId: 'sac_...' }),
},
);
const { jobId, containerIds } = await res.json();
const [containerId] = containerIds;import os, uuid, httpx
r = httpx.post(
f"https://api.layers.com/v1/projects/{project_id}/content/ugc-remix",
headers={
"Authorization": f"Bearer {os.environ['LAYERS_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={"influencerId": "inf_4a8e1bc2..."},
)
job = r.json()Responses
202Job accepted.
{
"jobId": "job_01HZX3...",
"kind": "content_generate",
"status": "running",
"containerIds": ["cnt_7d18b9a1..."],
"locationUrl": "/v1/jobs/job_01HZX3..."
}422Influencer required, app-demo missing, or wiring resolution failed.
{
"error": {
"code": "VALIDATION",
"message": "MISSING_APP_DEMO: ugc-remix requires an app-demo asset on the project. Upload one via POST /v1/projects/:id/app-media with kind='demo-video' before retrying.",
"requestId": "req_...",
"details": {
"code": "MISSING_APP_DEMO",
"format": "ugc-remix"
}
}
}The top-level error.code is the generic VALIDATION envelope. The specific failure code lives at error.details.code — that's what partners switch on programmatically.
details.code | Cause |
|---|---|
INFLUENCER_REQUIRED | Neither socialAccountId (with wired influencer) nor influencerId provided. |
MISSING_APP_DEMO | mediaId not supplied AND project has no media_role: "app-demo" row. |
ACCOUNT_NOT_WIRED / NO_CONTENT_LAYER_WIRED / INFLUENCER_NOT_WIRED | Wiring chain resolution failed mid-step. |
404`socialAccountId`, `influencerId`, or `mediaId` not on this project.
{ "error": { "code": "NOT_FOUND", "message": "mediaId is not on this project.", "requestId": "req_..." } }Errors
| Code | When |
|---|---|
VALIDATION | Body shape invalid. |
NOT_FOUND | socialAccountId, influencerId, or mediaId not on this project. |
INFLUENCER_REQUIRED | No influencer resolvable from either path. |
MISSING_APP_DEMO | No mediaId AND no app-demo on the project. |
IDEMPOTENCY_CONFLICT | Same Idempotency-Key reused with different body. |
BILLING_EXHAUSTED | Org wallet at zero. |