Layers

POST /v1/projects/:projectId/influencers

Kick off an influencer-create job. Returns 202 with a job envelope.

View as Markdown
POST/v1/projects/:projectId/influencers
Phase 1stableidempotent
Auth
Bearer
Scope
influencers:write

Starts an influencer_create job. The influencer is created in status: "pending" while the persona + portrait render. The response is a job envelope — poll /v1/jobs/:jobId for terminal state, then read the influencer at /v1/influencers/:influencerId.

The body is intentionally tiny. Layers generates the display name, portrait, and persona attributes from the project's brand context plus the optional hints you supply. There is no name field on this request — read it back from GET /v1/influencers/:id after the job lands.

Most partners never need to call this endpoint directly — Layers auto-creates a first influencer in the background as soon as you supply appDescription on POST /v1/projects. Reach for this endpoint when the customer needs additional personas (different gender, ageRange, or prompt vibe) for variation across content batches.

Idempotency

Send an Idempotency-Key header so retries replay the same response. The influencer's id is server-generated and returned in the accept envelope — persist it and map it back to your entity. You cannot supply your own id.

Path parameters

  • projectId
    string (uuid)required
    The project this influencer belongs to.

Body

Every field is optional. When omitted, Layers falls back to the project's defaults — most notably, gender falls back to the project's targetGender so the generated persona reflects the customer's audience.

Body
  • gender
    stringoptional
    Drives persona and rendering. Required for content generation, so always carries a real value on the row. Server default chain when omitted: project's `targetGender` if `female` / `male`, else `female`.
    One of: male, female, non_binary
  • ageRange
    stringoptional
    Canonical age band keyed to the renderer's persona presets. Same enum as the in-product dialog. Defaults to `young_adult` when omitted.
    One of: teen, young_adult, adult, mid_adult, mature, senior
  • prompt
    stringoptional
    Optional free-text hint that steers visual generation — e.g. `70s rocker vibe`, `soft librarian energy`, `barista energy`. 1-60 chars. Omit for a generic persona based on `gender` + `ageRange` alone.

Voice (brandVoice) and language are inherited from the project and not accepted here. To override per persona after creation, use PATCH /v1/influencers/:id.

Request

terminal
curl -X POST https://api.layers.com/v1/projects/{projectId}/influencers \
  -H "Authorization: Bearer $LAYERS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 8d2f1a3e-0b4c-4a11-9f7e-33c0a2c1bd55" \
  -d '{
    "gender": "female",
    "ageRange": "young_adult",
    "prompt": "barista energy, soft morning light"
  }'
create-influencer.ts
const res = await fetch(
  `https://api.layers.com/v1/projects/${projectId}/influencers`,
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.LAYERS_API_KEY}`,
      'Content-Type': 'application/json',
      'Idempotency-Key': crypto.randomUUID(),
    },
    body: JSON.stringify({
      gender: 'female',
      ageRange: 'young_adult',
      prompt: 'barista energy, soft morning light',
    }),
  },
);
const { jobId, influencerId } = await res.json();
create_influencer.py
import os, uuid, httpx

r = httpx.post(
    f"https://api.layers.com/v1/projects/{project_id}/influencers",
    headers={
        "Authorization": f"Bearer {os.environ['LAYERS_API_KEY']}",
        "Content-Type": "application/json",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "gender": "female",
        "ageRange": "young_adult",
        "prompt": "barista energy, soft morning light",
    },
)
job = r.json()

You can also send an empty body — every field is optional. Layers picks gender/age/prompt defaults from the project.

Responses

202Job accepted. The influencer exists with status=pending; poll the job for completion.
{
  "jobId": "job_01HXZ9G7KMV2QX8Y1S5RJW3B7T",
  "kind": "influencer_create",
  "status": "running",
  "influencerId": "inf_4a8e1bc2...",
  "locationUrl": "/v1/jobs/job_01HXZ9G7KMV2QX8Y1S5RJW3B7T"
}
422Validation failed. Sending name, brandVoice, language, or other unknown fields returns 422 — the create surface is intentionally tiny.
{
  "error": {
    "code": "VALIDATION",
    "message": "Invalid body.",
    "requestId": "req_...",
    "details": { "fieldErrors": { "name": ["Unrecognized key — name is server-generated."] } }
  }
}

Notes

Creation is async. The influencer starts in status: "pending", then moves through training to ready. The display name, portrait, and persona attributes are generated server-side; partners cannot supply reference images.

  • Safe to reference immediately. You can use the influencerId in POST /v1/projects/:projectId/content/slideshow-remix the moment this call returns. Content generation waits until the influencer is ready.
  • Status values. pending, training, ready, failed.
  • Idempotency. Set an Idempotency-Key header so a retry replays the same accept envelope. The influencer id is server-generated; you cannot pass your own.

Errors

CodeWhen
VALIDATIONUnknown field (e.g. name, brandVoice, language), enum out of range, or bad JSON.
NOT_FOUNDProject id not in this org.
FORBIDDEN_SCOPEKey lacks influencers:write.

See also

On this page