Creative Studio API
Every Creative Studio action (26) and data model (5): endpoints, scopes, input, preconditions, events and examples.
Describe it, see it, use it — product photos, posts, story videos, voice-overs and music, made by AI.
Module code creative · 26 actions · 5 data models · 8 events. A key or token never does more here than the person behind it can; row-level security applies exactly as in the app. Scopes: creative (everything), creative:read (read only) or creative:<capability>.
Resources
| Resource | Table | List and read | POST | PATCH | DELETE |
|---|---|---|---|---|---|
creative_ads | creative_ad | /api/v1/creative_ads | |||
creative_brand_kits | creative_brand_kit | /api/v1/creative_brand_kits | |||
creative_generations | creative_generation | /api/v1/creative_generations | |||
creative_posts | creative_post | /api/v1/creative_posts | |||
creative_voices | creative_voice | /api/v1/creative_voices |
Lists take limit (up to 200), cursor, order (e.g. -created_at), fields and q; every other query parameter is a column filter: ?status=paid, ?total[gte]=100, ?id[in]=a,b, ?x=null.
Actions
| Action | Needs | What it does |
|---|---|---|
creative.cancel | creative:write | Cancel a generation |
creative.cancel_post | creative:write | Cancel a scheduled post |
creative.clone_voice | creative:write | Clone a voice |
creative.complete_generation | creative:handle | Finish a generation |
creative.compose | creative:write | Set text on a picture, or make a photo reel |
creative.edit_again | creative:write | Edit the video again |
creative.fail_generation | creative:handle | Mark a generation failed |
creative.finish_voice | creative:handle | Finish cloning a voice |
creative.generate | creative:write | Generate an image, video, voice-over or music |
creative.generate_batch | creative:write | Make a batch |
creative.generate_set | creative:write | Product photo studio: make a set |
creative.mark_post | creative:handle | Record a post’s progress |
creative.note_provider_job | creative:handle | Record the provider job |
creative.park_generation | creative:handle | Wait for the owner’s computer |
creative.record_use | creative:write | Record where a result was used |
creative.remove | creative:delete | Remove from history |
creative.remove_ad | creative:delete | Remove from the ad library |
creative.remove_voice | creative:delete | Remove a voice |
creative.resume_provider | creative:write | Check the provider result again |
creative.save_ad | creative:write | Save an ad to the library |
creative.save_brand_kit | creative:write | Save the brand kit |
creative.save_caption | creative:write | Save the post text |
creative.schedule_post | creative:write | Post to social media |
creative.set_favourite | creative:write | Favourite |
creative.start_generation | creative:handle | Start a generation |
creative.vary | creative:write | More like this |
creative.cancel
Cancel a generation — Stops a generation that has not started.
Needs creative:write · removes or cannot be undone
https://app.getera.io/api/v1/creative_generations/{id}/actions/cancel- Canonical address:
POST /api/v1/actions/creative.cancel
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Generation · id of creative_generation |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_queued | Only a generation that has not started can be cancelled. |
What it writes: creative_generation (update) — queued → cancelled
curl -X POST https://app.getera.io/api/v1/creative_generations/CREATIVE_GENERATION_ID/actions/cancel \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": []
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.creativeGenerations.cancel("CREATIVE_GENERATION_ID")era creative_generations cancel CREATIVE_GENERATION_ID{
"name": "era__creative__cancel",
"arguments": {
"id": "CREATIVE_GENERATION_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold creative:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_queued · 429 slow down
creative.cancel_post
Cancel a scheduled post — Stops a post that has not gone out yet.
Needs creative:write · removes or cannot be undone
https://app.getera.io/api/v1/creative_posts/{id}/actions/cancel_post- Canonical address:
POST /api/v1/actions/creative.cancel_post
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Post · id of creative_post |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_scheduled | Only a scheduled post can be cancelled. |
What it writes: creative_post (update) — scheduled → cancelled
curl -X POST https://app.getera.io/api/v1/creative_posts/CREATIVE_POST_ID/actions/cancel_post \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": []
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.creativePosts.cancelPost("CREATIVE_POST_ID")era creative_posts cancel_post CREATIVE_POST_ID{
"name": "era__creative__cancel_post",
"arguments": {
"id": "CREATIVE_POST_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold creative:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_scheduled · 429 slow down
creative.clone_voice
Clone a voice — Makes a voice from your own recordings, for voice-overs and lip sync. Only with the speaker’s consent.
Needs creative:write · reaches outside (sends, publishes or charges) · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/creative_voices/actions/clone_voice- Canonical address:
POST /api/v1/actions/creative.clone_voice
Input
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | yes | Voice name · max 80 |
sampleFileIds | object / array | yes | Recordings · File ids of audio recordings of the voice. |
consent | boolean | yes | I have the speaker’s consent |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
empty_name | The voice needs a name. |
consent_required | A voice is cloned only after the person confirms the speaker agreed. |
needs_samples | At least one recording of the voice. |
Events it publishes: creative.voice_requested
What it writes: creative_voice (insert) — A pending voice, made by the studio right after
curl -X POST https://app.getera.io/api/v1/creative_voices/actions/clone_voice \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"name":"Voice name","sampleFileIds":{},"consent":true}'{
"ok": true,
"result": "…",
"events": [
"creative.voice_requested"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.creativeVoices.cloneVoice({
name: "Voice name",
sampleFileIds: {},
consent: true
})era creative_voices clone_voice --data '{"name":"Voice name","sampleFileIds":{},"consent":true}'{
"name": "era__creative__clone_voice",
"arguments": {
"name": "Voice name",
"sampleFileIds": {},
"consent": true
}
}Errors: 401 no or expired credential · 403 the credential does not hold creative:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 empty_name, consent_required, needs_samples · 429 slow down
creative.complete_generation
Finish a generation — Records the results (files) and the real cost. Machine-only.
Needs creative:handle · changes data
403 machine_only).https://app.getera.io/api/v1/creative_generations/{id}/actions/complete_generation- Canonical address:
POST /api/v1/actions/creative.complete_generation
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Generation · id of creative_generation |
outputs | object / array | yes | Results |
usedModel | string | Model · max 160 | |
provider | string | Provider · max 40 | |
sourceKind | string | Paid by · max 20 | |
costUsd | number | Cost (USD) · min 0 | |
connection | object / array | Whose connection | |
check | object / array | Self-check |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_running | Only a running generation can finish. |
no_outputs | A finished generation has at least one result. |
Events it publishes: creative.generated
What it writes: creative_generation (update) — running → succeeded, with results and cost
Errors: 401 no or expired credential · 403 the credential does not hold creative:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_running, no_outputs · 429 slow down
creative.compose
Set text on a picture, or make a photo reel — Made by Era itself — no AI model, no cost. A layout sets real, correctly spelled text over a picture in the brand’s colours and font with the logo (campaign, new_product, price_tag, announcement, story_cta, menu_card, marketplace_band, quote). A photo reel turns 2–10 photos into a vertical video with gentle motion, the hook, music and an end card.
Needs creative:write · changes data
https://app.getera.io/api/v1/creative_generations/{id}/actions/compose- Canonical address:
POST /api/v1/actions/creative.compose
Input
| Field | Type | Required | Notes |
|---|---|---|---|
kind | string (enum) | yes | What · Allowed values: layout, photo_reel, image_grade |
grade | object / array | Colour adjustments · { exposure:-5..5, contrast:-100..100, saturation:-100..100, hue:-180..180, temperature:-100..100, splitTone:0..1, soften:0..20, vignette:0..1, grain:0..1 }. Local, no model. | |
fileIds | object / array | yes | Pictures · Files ids: one for a layout, 2–10 for a reel. |
layout | object / array | Layout · { code, headline, subline, badge, price, oldPrice, cta, logo } | |
channel | string | Channel · max 40 | |
reel | object / array | Reel · { hook, cta, music: upbeat|calm|cafe|none, voiceGenerationId, perPhotoSec, endCard, captions[] } | |
productId | uuid | Product | |
sourceGenerationId | uuid | Filled from the path {id} · Made from · id of creative_generation |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
unknown_layout | The layout must be one Creative Studio offers. |
needs_image | A layout goes over exactly one picture. |
empty_text | A layout shows a headline, a price or a badge. |
needs_photos | A photo reel needs at least two photos. |
unknown_track | The music must be one of Era’s beds, or none. |
not_found | A voice-over named for the reel must exist here. |
Events it publishes: creative.requested
What it writes: creative_generation (insert) — Queues the layout or reel; Era makes it without a model
curl -X POST https://app.getera.io/api/v1/creative_generations/CREATIVE_GENERATION_ID/actions/compose \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"kind":"layout","fileIds":{}}'{
"ok": true,
"result": "…",
"events": [
"creative.requested"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.creativeGenerations.compose("CREATIVE_GENERATION_ID", {
kind: "layout",
fileIds: {}
})era creative_generations compose CREATIVE_GENERATION_ID --data '{"kind":"layout","fileIds":{}}'{
"name": "era__creative__compose",
"arguments": {
"sourceGenerationId": "CREATIVE_GENERATION_ID",
"kind": "layout",
"fileIds": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold creative:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 unknown_layout, needs_image, empty_text, needs_photos, unknown_track, not_found · 429 slow down
creative.edit_again
Edit the video again — Makes a new version of a finished AI video edit from a note ("shorter intro, no music"); the earlier versions stay.
Needs creative:write · changes data
https://app.getera.io/api/v1/creative_generations/{id}/actions/edit_again- Canonical address:
POST /api/v1/actions/creative.edit_again
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Video edit · id of creative_generation |
note | string | yes | What should change · max 2000 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
empty_note | The note must say what to change. |
not_editable | Only a finished AI video edit (with its clips) can be edited again. |
Events it publishes: creative.requested
What it writes: creative_generation (insert) — A queued video edit: the next version, pointing at the one it changes
curl -X POST https://app.getera.io/api/v1/creative_generations/CREATIVE_GENERATION_ID/actions/edit_again \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"note":"What should change"}'{
"ok": true,
"result": "…",
"events": [
"creative.requested"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.creativeGenerations.editAgain("CREATIVE_GENERATION_ID", {
note: "What should change"
})era creative_generations edit_again CREATIVE_GENERATION_ID \
--note "What should change"{
"name": "era__creative__edit_again",
"arguments": {
"id": "CREATIVE_GENERATION_ID",
"note": "What should change"
}
}Errors: 401 no or expired credential · 403 the credential does not hold creative:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 empty_note, not_editable · 429 slow down
creative.fail_generation
Mark a generation failed — Records why it failed. Machine-only.
Needs creative:handle · changes data
403 machine_only).https://app.getera.io/api/v1/creative_generations/{id}/actions/fail_generation- Canonical address:
POST /api/v1/actions/creative.fail_generation
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Generation · id of creative_generation |
error | string | Why · max 600 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_open | Only a queued or running generation can fail. |
Events it publishes: creative.failed
What it writes: creative_generation (update) — → failed
Errors: 401 no or expired credential · 403 the credential does not hold creative:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_open · 429 slow down
creative.finish_voice
Finish cloning a voice — Records the provider’s voice, or why it failed. Machine-only.
Needs creative:handle · changes data
403 machine_only).https://app.getera.io/api/v1/creative_voices/{id}/actions/finish_voice- Canonical address:
POST /api/v1/actions/creative.finish_voice
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Voice · id of creative_voice |
externalVoiceId | string | Provider voice id · max 120 | |
sourceKind | string (enum) | Account · Allowed values: byok | |
connectionId | uuid | Connection | |
error | string | Why it failed · max 600 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_pending | Only a voice being cloned can finish. |
What it writes: creative_voice (update) — pending → ready / failed
Errors: 401 no or expired credential · 403 the credential does not hold creative:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_pending · 429 slow down
creative.generate
Generate an image, video, voice-over or music — Describe it (or pick a preset like a white-background product shot, an Instagram post, a story video or a menu photo); it is made in the background and lands in Creative Studio and the Files library. The cost is shown first; it runs on your own subscription or key.
Needs creative:write · changes data
https://app.getera.io/api/v1/creative_generations/{id}/actions/generate- Canonical address:
POST /api/v1/actions/creative.generate
Input
| Field | Type | Required | Notes |
|---|---|---|---|
higgsfield | object / array | Studio tool inputs · Open-Higgsfield: {id, values, files}. id is a tool catalogue id, values are model options, files maps typed media field names to Files ids. Requires your MuAPI key. Discover tools with media_make operation catalogue. | |
prompt | string | What to make · max 4000 | |
studio | string (enum) | Studio · The Creative Studio studio it is made in (image, video, cinema, character, lipsync, motion, recast, clip, layers, audio, marketing, product); its history shows it there. · Allowed values: image, video, cinema, character, lipsync, motion, recast, clip, layers, audio, marketing, product | |
kind | string (enum) | Kind · Allowed values: image, video, tts, audio | |
preset | string | Preset · For a person: dance_trend, portrait_avatar, photo_edit, reel, cartoon_me, celebration_card, song_bed. For a business: product_white, product_lifestyle, instagram_post, story_video, product_video, menu_photo, poster, website_banner, voiceover, jingle. Templates that start from a photo need inputFileIds. · max 60 | |
model | string | Model · auto (cheapest that can do it) or a catalogue model id · max 160 | |
aspect | string (enum) | Shape · Allowed values: 1:1, 4:5, 9:16, 16:9, 3:2, 2:3 | |
durationSec | number | Length (seconds) · min 1 · max 30 | |
count | number | How many · min 1 · max 4 | |
voice | string | Voice · max 80 | |
inputFileIds | object / array | Starting photos · File ids of photos to start from (animate or edit). | |
inputVideoIds | object / array | Your clip · File id of your own 4–30 s clip, for motion transfer. | |
resolution | string (enum) | Resolution · Allowed values: 480p, 720p, 1080p | |
inputUrls | object / array | Starting photos on the web · https addresses of photos to start from, e.g. a product page image. | |
inputAudioIds | object / array | Audio · File id of the audio for lip sync or a talking photo. | |
useBrand | boolean | Use the brand kit · Adds the brand name, colours and tone (default: the brand kit’s own setting). | |
harness | string | Editing agent · AI video editor: the coding agent on your computer (claude, codex). · max 40 | |
upscale | number | Upscale · 2× or 4×. · min 2 · max 4 | |
voiceId | uuid | Cloned voice · id of creative_voice | |
editLast | boolean | Edit the last picture · Change the newest picture (sent in chat or made here) instead of drawing a new one: "add X to it", "make this bottle a Fanta". Ignored when starting photos are given. | |
channel | string | Channel · Where it goes — instagram_post, instagram_story, reels, whatsapp_status, google_business, facebook_ad, trendyol, hepsiburada, amazon, website_hero … — sets the shape. · max 40 | |
productId | uuid | Product · The shop product it is made for. | |
sourceGenerationId | uuid | Filled from the path {id} · Made from · The result this one varies or edits. · id of creative_generation |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
empty_prompt | There must be a description or a preset. |
unknown_preset | The preset must be one Creative Studio offers. |
needs_image | A preset that animates a photo needs the photo; editing the last picture needs one. |
no_source | Your own plan or key must be able to make this kind. |
budget_exceeded | The workspace AI budget must have room. |
voice_not_ready | A cloned voice must be ready before it speaks. |
Events it publishes: creative.requested
What it writes: creative_generation (insert) — Queues the generation with its estimate
curl -X POST https://app.getera.io/api/v1/creative_generations/CREATIVE_GENERATION_ID/actions/generate \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"higgsfield":{}}'{
"ok": true,
"result": "…",
"events": [
"creative.requested"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.creativeGenerations.generate("CREATIVE_GENERATION_ID", {
higgsfield: {}
})era creative_generations generate CREATIVE_GENERATION_ID --data '{"higgsfield":{}}'{
"name": "era__creative__generate",
"arguments": {
"sourceGenerationId": "CREATIVE_GENERATION_ID",
"higgsfield": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold creative:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 empty_prompt, unknown_preset, needs_image, no_source, budget_exceeded, voice_not_ready · 429 slow down
creative.generate_batch
Make a batch — One template for many products, photos or lines at once (up to 20): "these 12 products on white", "a story for every dish on the menu". Each one is its own generation in one batch.
Needs creative:write · changes data
https://app.getera.io/api/v1/creative_generations/actions/generate_batch- Canonical address:
POST /api/v1/actions/creative.generate_batch
Input
| Field | Type | Required | Notes |
|---|---|---|---|
preset | string | Template · max 60 | |
prompt | string | For all of them · max 2000 | |
items | object / array | yes | Lines · A list of { prompt?, inputFileIds?, productId?, label? } — one picture each. |
channel | string | Channel · max 40 | |
model | string | Model · max 160 | |
aspect | string (enum) | Shape · Allowed values: 1:1, 4:5, 9:16, 16:9, 3:2, 2:3 | |
useBrand | boolean | Use the brand kit | |
label | string | Name · max 120 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
empty_batch | A batch has at least one line. |
batch_too_big | A batch holds at most 20. |
unknown_preset | The template must be one Creative Studio offers. |
empty_prompt | Each line needs words or a template. |
needs_image | A template that starts from a photo needs the photo on every line. |
no_source | Your own plan or key must be able to make it. |
budget_exceeded | The workspace AI budget must have room. |
Events it publishes: creative.requested
What it writes: creative_generation (insert) — Queues one generation per line, sharing a batch
curl -X POST https://app.getera.io/api/v1/creative_generations/actions/generate_batch \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"items":{}}'{
"ok": true,
"result": "…",
"events": [
"creative.requested"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.creativeGenerations.generateBatch({
items: {}
})era creative_generations generate_batch --data '{"items":{}}'{
"name": "era__creative__generate_batch",
"arguments": {
"items": {}
}
}Errors: 401 no or expired credential · 403 the credential does not hold creative:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 empty_batch, batch_too_big, unknown_preset, empty_prompt, needs_image, no_source, budget_exceeded · 429 slow down
creative.generate_set
Product photo studio: make a set — One photo of a product becomes a set of shots, each made separately so the product stays the product: clean (on white), angles (front · three-quarter · side · back), lifestyle (three scenes for your sector) or listing (main on white · angle · detail · in use, cut for Trendyol or Hepsiburada).
Needs creative:write · changes data
https://app.getera.io/api/v1/creative_generations/actions/generate_set- Canonical address:
POST /api/v1/actions/creative.generate_set
Input
| Field | Type | Required | Notes |
|---|---|---|---|
set | string | yes | Set · clean (on white) · angles (front, three-quarter, side, back) · lifestyle (three scenes) · listing (main, angle, detail, in use). · max 20 |
inputFileIds | object / array | Product photo · File id(s) of the product photo (1–2). | |
inputUrls | object / array | Product photo on the web · https address of a product photo. | |
productId | uuid | Product · The shop product the set is for. | |
product | string | Product name · max 200 | |
note | string | Anything else · max 600 | |
channel | string | Channel · trendyol, hepsiburada, amazon, n11, instagram_post … (a listing set defaults to trendyol). · max 40 | |
sector | string | Sector · food, cafe, bakery, beauty, barber, fashion, jewellery, cosmetics, home, health, realestate, ecommerce — the scenes it suits. · max 40 | |
model | string | Model · max 160 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
unknown_set | The set must be one the product studio offers. |
needs_image | The product studio starts from a photo of the product. |
no_source | Your own plan or key must be able to make it. |
budget_exceeded | The workspace AI budget must have room. |
Events it publishes: creative.requested
What it writes: creative_generation (insert) — Queues one generation per shot, sharing a batch
curl -X POST https://app.getera.io/api/v1/creative_generations/actions/generate_set \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"set":"Set"}'{
"ok": true,
"result": "…",
"events": [
"creative.requested"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.creativeGenerations.generateSet({
set: "Set"
})era creative_generations generate_set \
--set "Set"{
"name": "era__creative__generate_set",
"arguments": {
"set": "Set"
}
}Errors: 401 no or expired credential · 403 the credential does not hold creative:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 unknown_set, needs_image, no_source, budget_exceeded · 429 slow down
creative.mark_post
Record a post’s progress — scheduled → posting → posted / failed. Machine-only.
Needs creative:handle · changes data
403 machine_only).https://app.getera.io/api/v1/creative_posts/{id}/actions/mark_post- Canonical address:
POST /api/v1/actions/creative.mark_post
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Post · id of creative_post |
status | string (enum) | yes | Status · Allowed values: posting, posted, failed |
externalId | string | Platform id · max 200 | |
externalUrl | string | Link · max 600 | |
error | string | Why it failed · max 600 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_open | A post moves only forward: scheduled → posting → posted or failed. |
Events it publishes: creative.posted, creative.post_failed
What it writes: creative_post (update) — Moves the post forward; creative_generation (update) — Adds "used on <channel>"
Errors: 401 no or expired credential · 403 the credential does not hold creative:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_open · 429 slow down
creative.note_provider_job
Record the provider job — Persists a MuAPI request before polling so recovery never submits it twice. Machine-only.
Needs creative:handle · changes data
403 machine_only).https://app.getera.io/api/v1/creative_generations/{id}/actions/note_provider_job- Canonical address:
POST /api/v1/actions/creative.note_provider_job
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Generation · id of creative_generation |
requestId | string | yes | Provider request · max 200 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_running | Only a running studio tool can record its provider job. |
What it writes: creative_generation (update) — Saves the provider request id
Errors: 401 no or expired credential · 403 the credential does not hold creative:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_running · 429 slow down
creative.park_generation
Wait for the owner’s computer — Parks a generation that runs on a person’s own plan until their computer is back. Machine-only.
Needs creative:handle · changes data
403 machine_only).https://app.getera.io/api/v1/creative_generations/{id}/actions/park_generation- Canonical address:
POST /api/v1/actions/creative.park_generation
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Generation · id of creative_generation |
waiting | object / array | yes | Runner job |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_running | Only a running generation can wait. |
bad_waiting | The runner job it waits for must be named. |
What it writes: creative_generation (update) — running → waiting
Errors: 401 no or expired credential · 403 the credential does not hold creative:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_running, bad_waiting · 429 slow down
creative.record_use
Record where a result was used — Remembers that a result went onto a product, a post, an email or the website.
Needs creative:write · changes data
https://app.getera.io/api/v1/creative_generations/{id}/actions/record_use- Canonical address:
POST /api/v1/actions/creative.record_use
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Generation · id of creative_generation |
target | string | yes | Used on · product, blog_post, campaign, website, files, download, social, export, marketplace · max 20 |
targetId | string | Record · max 64 | |
label | string | Name · max 200 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | Only a finished generation can be used. |
bad_target | The place must be one Creative Studio knows. |
Events it publishes: creative.used
What it writes: creative_generation (update) — Appends the use
curl -X POST https://app.getera.io/api/v1/creative_generations/CREATIVE_GENERATION_ID/actions/record_use \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"target":"Used on"}'{
"ok": true,
"result": "…",
"events": [
"creative.used"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.creativeGenerations.recordUse("CREATIVE_GENERATION_ID", {
target: "Used on"
})era creative_generations record_use CREATIVE_GENERATION_ID \
--target "Used on"{
"name": "era__creative__record_use",
"arguments": {
"id": "CREATIVE_GENERATION_ID",
"target": "Used on"
}
}Errors: 401 no or expired credential · 403 the credential does not hold creative:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, bad_target · 429 slow down
creative.remove
Remove from history — Takes a finished generation off the history. Its files stay in the Files library.
Needs creative:delete · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/creative_generations/{id}/actions/remove- Canonical address:
POST /api/v1/actions/creative.remove
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Generation · id of creative_generation |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The generation must exist and be finished. |
What it writes: creative_generation (delete) — Deletes the history row
curl -X POST https://app.getera.io/api/v1/creative_generations/CREATIVE_GENERATION_ID/actions/remove \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": []
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.creativeGenerations.remove("CREATIVE_GENERATION_ID")era creative_generations remove CREATIVE_GENERATION_ID{
"name": "era__creative__remove",
"arguments": {
"id": "CREATIVE_GENERATION_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold creative:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found · 429 slow down
creative.remove_ad
Remove from the ad library — Takes an ad out of the library.
Needs creative:delete · removes or cannot be undone
https://app.getera.io/api/v1/creative_ads/{id}/actions/remove_ad- Canonical address:
POST /api/v1/actions/creative.remove_ad
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Ad · id of creative_ad |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The ad must be in this workspace’s library. |
What it writes: creative_ad (delete) — Deletes the ad
curl -X POST https://app.getera.io/api/v1/creative_ads/CREATIVE_AD_ID/actions/remove_ad \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": []
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.creativeAds.removeAd("CREATIVE_AD_ID")era creative_ads remove_ad CREATIVE_AD_ID{
"name": "era__creative__remove_ad",
"arguments": {
"id": "CREATIVE_AD_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold creative:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found · 429 slow down
creative.remove_voice
Remove a voice — Deletes a cloned voice (also at the provider).
Needs creative:delete · removes or cannot be undone · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/creative_voices/{id}/actions/remove_voice- Canonical address:
POST /api/v1/actions/creative.remove_voice
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Voice · id of creative_voice |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The voice must exist in this workspace. |
What it writes: creative_voice (delete) — Deletes the voice
curl -X POST https://app.getera.io/api/v1/creative_voices/CREATIVE_VOICE_ID/actions/remove_voice \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": []
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.creativeVoices.removeVoice("CREATIVE_VOICE_ID")era creative_voices remove_voice CREATIVE_VOICE_ID{
"name": "era__creative__remove_voice",
"arguments": {
"id": "CREATIVE_VOICE_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold creative:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found · 429 slow down
creative.resume_provider
Check the provider result again — Polls the saved MuAPI job again, without purchasing another generation.
Needs creative:write · changes data
https://app.getera.io/api/v1/creative_generations/{id}/actions/resume_provider- Canonical address:
POST /api/v1/actions/creative.resume_provider
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Generation · id of creative_generation |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | A failed studio tool must have a saved provider request to resume. |
Events it publishes: creative.requested
What it writes: creative_generation (update) — Requeues polling of the saved provider job
curl -X POST https://app.getera.io/api/v1/creative_generations/CREATIVE_GENERATION_ID/actions/resume_provider \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"creative.requested"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.creativeGenerations.resumeProvider("CREATIVE_GENERATION_ID")era creative_generations resume_provider CREATIVE_GENERATION_ID{
"name": "era__creative__resume_provider",
"arguments": {
"id": "CREATIVE_GENERATION_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold creative:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found · 429 slow down
creative.save_ad
Save an ad to the library — Keeps an ad you like (from the Meta Ad Library, a link or a screenshot) so Creative Studio can make one like it for your product.
Needs creative:write · changes data
https://app.getera.io/api/v1/creative_ads/actions/save_ad- Canonical address:
POST /api/v1/actions/creative.save_ad
Input
| Field | Type | Required | Notes |
|---|---|---|---|
source | string | yes | From · meta_ad_library, url, upload or own · max 20 |
title | string | yes | Name · max 200 |
externalId | string | Library id · max 120 | |
advertiser | string | Advertiser · max 200 | |
body | string | Ad text · max 4000 | |
linkUrl | string | Link · max 2000 | |
snapshotUrl | string | Ad preview link · max 2000 | |
mediaFileIds | object / array | Screenshots | |
mediaUrls | object / array | Media links | |
platforms | object / array | Platforms | |
startedOn | string | Running since · max 10 | |
tags | object / array | Tags | |
notes | string | Why we like it · max 2000 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
bad_source | The source is one the library knows. |
empty_title | An ad needs a name. |
What it writes: creative_ad (upsert) — Adds (or refreshes) the ad in the library
curl -X POST https://app.getera.io/api/v1/creative_ads/actions/save_ad \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"source":"From","title":"Name"}'{
"ok": true,
"result": "…",
"events": []
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.creativeAds.saveAd({
source: "From",
title: "Name"
})era creative_ads save_ad \
--source "From" \
--title "Name"{
"name": "era__creative__save_ad",
"arguments": {
"source": "From",
"title": "Name"
}
}Errors: 401 no or expired credential · 403 the credential does not hold creative:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 bad_source, empty_title · 429 slow down
creative.save_brand_kit
Save the brand kit — Name, colours, fonts, logo and tone of voice — Creative Studio adds them to every generation unless you switch it off for one.
Needs creative:write · changes data
https://app.getera.io/api/v1/creative_brand_kits/actions/save_brand_kit- Canonical address:
POST /api/v1/actions/creative.save_brand_kit
Input
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | yes | Brand name · max 120 |
tagline | string | Tagline · max 200 | |
voice | string | Tone of voice · max 600 | |
audience | string | Who it is for · max 300 | |
colours | object / array | Colours · Up to 8 hex colours, e.g. ["#1F2937", "#7C83FD"]. | |
fonts | object / array | Fonts · Up to 4 font names. | |
logoFileId | uuid | Logo · id of file | |
referenceFileIds | object / array | Reference images · File ids of photos that show the look. | |
wordsUse | string | Words we use · max 600 | |
wordsAvoid | string | Words and looks to avoid · max 600 | |
applyByDefault | boolean | Use in every generation | |
sector | string | Sector · food, cafe, bakery, beauty, barber, fashion, jewellery, cosmetics, home, health, realestate, ecommerce. · max 40 | |
syncedFrom | object / array | Taken from the workspace |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
empty_name | The brand kit needs a name. |
bad_colour | Colours are hex values like #1A2B3C. |
unknown_sector | The sector must be one Creative Studio knows. |
What it writes: creative_brand_kit (upsert) — Writes the one brand kit of the workspace
curl -X POST https://app.getera.io/api/v1/creative_brand_kits/actions/save_brand_kit \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"name":"Brand name"}'{
"ok": true,
"result": "…",
"events": []
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.creativeBrandKits.saveBrandKit({
name: "Brand name"
})era creative_brand_kits save_brand_kit \
--name "Brand name"{
"name": "era__creative__save_brand_kit",
"arguments": {
"name": "Brand name"
}
}Errors: 401 no or expired credential · 403 the credential does not hold creative:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 empty_name, bad_colour, unknown_sector · 429 slow down
creative.save_caption
Save the post text — Keeps the caption and hashtags that go with a result, for posting and sharing.
Needs creative:write · changes data
https://app.getera.io/api/v1/creative_generations/{id}/actions/save_caption- Canonical address:
POST /api/v1/actions/creative.save_caption
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Result · id of creative_generation |
text | string | yes | Text · max 2200 |
hashtags | object / array | Hashtags · Up to 30, with or without #. | |
channel | string | Channel · max 40 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
empty_caption | A caption has words. |
not_found | The result must exist in this workspace. |
What it writes: creative_generation (update) — Sets the caption
curl -X POST https://app.getera.io/api/v1/creative_generations/CREATIVE_GENERATION_ID/actions/save_caption \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"text":"Text"}'{
"ok": true,
"result": "…",
"events": []
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.creativeGenerations.saveCaption("CREATIVE_GENERATION_ID", {
text: "Text"
})era creative_generations save_caption CREATIVE_GENERATION_ID \
--text "Text"{
"name": "era__creative__save_caption",
"arguments": {
"id": "CREATIVE_GENERATION_ID",
"text": "Text"
}
}Errors: 401 no or expired credential · 403 the credential does not hold creative:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 empty_caption, not_found · 429 slow down
creative.schedule_post
Post to social media — Posts a Creative Studio result (or files) to an Instagram, Facebook, TikTok or YouTube account now or at a time. Always asks before posting.
Needs creative:write · reaches outside (sends, publishes or charges) · asks a person to confirm when Era runs it
https://app.getera.io/api/v1/creative_posts/actions/schedule_post- Canonical address:
POST /api/v1/actions/creative.schedule_post
Input
| Field | Type | Required | Notes |
|---|---|---|---|
generationId | uuid | Result · id of creative_generation | |
fileIds | object / array | Files · File ids to post, when not a whole result. | |
channel | string | yes | Where · instagram, facebook, tiktok or youtube · max 20 |
connectionId | uuid | yes | Account · The connected account (Settings → Apps). |
caption | string | Caption · max 2200 | |
title | string | Title (YouTube) · max 100 | |
scheduledAt | string | When · ISO date-time; empty = now. · max 40 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
bad_channel | The channel must be Instagram, Facebook, TikTok or YouTube. |
not_ready | Only a finished result can be posted. |
nothing_to_post | There must be a picture or video to post. |
wrong_media | TikTok and YouTube take a video; YouTube needs a title. |
no_connection | The account must be connected and usable by the person posting. |
bad_time | The time must be a real date at most six months ahead. |
Events it publishes: creative.post_scheduled
What it writes: creative_post (insert) — A scheduled post; the studio posts it when it is due
curl -X POST https://app.getera.io/api/v1/creative_posts/actions/schedule_post \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"channel":"Where","connectionId":"CONNECTION_ID"}'{
"ok": true,
"result": "…",
"events": [
"creative.post_scheduled"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.creativePosts.schedulePost({
channel: "Where",
connectionId: "CONNECTION_ID"
})era creative_posts schedule_post \
--channel "Where" \
--connection-id "CONNECTION_ID"{
"name": "era__creative__schedule_post",
"arguments": {
"channel": "Where",
"connectionId": "CONNECTION_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold creative:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 bad_channel, not_ready, nothing_to_post, wrong_media, no_connection, bad_time · 429 slow down
creative.set_favourite
Favourite — Stars a generation so it is easy to find again.
Needs creative:write · changes data
https://app.getera.io/api/v1/creative_generations/{id}/actions/set_favourite- Canonical address:
POST /api/v1/actions/creative.set_favourite
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Generation · id of creative_generation |
favourite | boolean | yes | Favourite |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The generation must exist in this workspace. |
What it writes: creative_generation (update) — Sets the star
curl -X POST https://app.getera.io/api/v1/creative_generations/CREATIVE_GENERATION_ID/actions/set_favourite \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"favourite":true}'{
"ok": true,
"result": "…",
"events": []
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.creativeGenerations.setFavourite("CREATIVE_GENERATION_ID", {
favourite: true
})era creative_generations set_favourite CREATIVE_GENERATION_ID \
--favourite true{
"name": "era__creative__set_favourite",
"arguments": {
"id": "CREATIVE_GENERATION_ID",
"favourite": true
}
}Errors: 401 no or expired credential · 403 the credential does not hold creative:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found · 429 slow down
creative.start_generation
Start a generation — Claims a queued generation for the worker that will make it. Machine-only.
Needs creative:handle · changes data
403 machine_only).https://app.getera.io/api/v1/creative_generations/{id}/actions/start_generation- Canonical address:
POST /api/v1/actions/creative.start_generation
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Generation · id of creative_generation |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_queued | Only a queued generation can start, and only once. |
What it writes: creative_generation (update) — queued → running
Errors: 401 no or expired credential · 403 the credential does not hold creative:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_queued · 429 slow down
creative.vary
More like this — New takes on a finished picture: the same subject and product, a fresh composition, angle and light. 1–4 at a time.
Needs creative:write · changes data
https://app.getera.io/api/v1/creative_generations/{id}/actions/vary- Canonical address:
POST /api/v1/actions/creative.vary
Input
| Field | Type | Required | Notes |
|---|---|---|---|
id | uuid | yes | Filled from the path {id} · Picture · id of creative_generation |
count | number | How many · min 1 · max 4 | |
note | string | What to change · max 600 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | Only a finished picture can be varied. |
no_source | Your own plan or key must be able to make it. |
budget_exceeded | The workspace AI budget must have room. |
Events it publishes: creative.requested
What it writes: creative_generation (insert) — Queues the variations
curl -X POST https://app.getera.io/api/v1/creative_generations/CREATIVE_GENERATION_ID/actions/vary \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"count":1}'{
"ok": true,
"result": "…",
"events": [
"creative.requested"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.creativeGenerations.vary("CREATIVE_GENERATION_ID", {
count: 1
})era creative_generations vary CREATIVE_GENERATION_ID \
--count 1{
"name": "era__creative__vary",
"arguments": {
"id": "CREATIVE_GENERATION_ID",
"count": 1
}
}Errors: 401 no or expired credential · 403 the credential does not hold creative:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, no_source, budget_exceeded · 429 slow down
Data models
CreativeAd
Table creative_ad · Resource creative_ads
GET /api/v1/creative_adsGET /api/v1/creative_ads/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
created_by | string (uuid) · uuid | yes | app_user | |
source | string · text | meta_ad_library, url, upload, own | ||
external_id | string · text | yes | ||
title | string · text | |||
advertiser | string · text | yes | ||
body | string · text | yes | ||
link_url | string · text | yes | ||
snapshot_url | string · text | yes | ||
media_file_ids | string[] · uuid[] | |||
media_urls | string[] · text[] | |||
platforms | string[] · text[] | |||
started_on | string (date) · date | yes | ||
tags | string[] · text[] | |||
notes | string · text | yes | ||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
CreativeBrandKit
Table creative_brand_kit · Resource creative_brand_kits
GET /api/v1/creative_brand_kitsGET /api/v1/creative_brand_kits/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
name | string · text | |||
tagline | string · text | yes | ||
voice | string · text | yes | ||
audience | string · text | yes | ||
colours | string[] · text[] | |||
fonts | string[] · text[] | |||
logo_file_id | string (uuid) · uuid | yes | ||
reference_file_ids | string[] · uuid[] | |||
words_use | string · text | yes | ||
words_avoid | string · text | yes | ||
apply_by_default | boolean · bool | |||
updated_by | string (uuid) · uuid | yes | app_user | |
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz | |||
sector | string · text | yes | ||
synced_from | object · jsonb | yes | ||
synced_at | string (date-time) · timestamptz | yes |
Primary key: id
Checks:
CHECK (((array_length(colours, 1) IS NULL) OR (array_length(colours, 1) <= 8)))
CreativeGeneration
Table creative_generation · Resource creative_generations
GET /api/v1/creative_generationsGET /api/v1/creative_generations/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
created_by | string (uuid) · uuid | yes | app_user | |
kind | string · text | image, video, tts, audio | ||
prompt | string · text | |||
preset | string · text | yes | ||
model | string · text | |||
used_model | string · text | yes | ||
provider | string · text | yes | ||
source_kind | string · text | yes | ||
aspect | string · text | yes | ||
duration_sec | string (decimal) · numeric | yes | ||
count | integer · int4 | |||
voice | string · text | yes | ||
input_file_ids | string[] · uuid[] | |||
status | string · text | queued, running, waiting, succeeded, failed, cancelled | ||
cost_usd | string (decimal) · numeric | |||
refunded | boolean · bool | |||
outputs | object · jsonb | |||
error | string · text | yes | ||
favourite | boolean · bool | |||
uses | object · jsonb | |||
surface | string · text | |||
started_at | string (date-time) · timestamptz | yes | ||
finished_at | string (date-time) · timestamptz | yes | ||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz | |||
connection | object · jsonb | yes | ||
input_urls | string[] · text[] | |||
waiting | object · jsonb | yes | ||
input_video_ids | string[] · uuid[] | |||
resolution | string · text | yes | ||
tool | string · text | yes | ||
input_audio_ids | string[] · uuid[] | |||
brand | object · jsonb | yes | ||
harness | string · text | yes | ||
upscale | integer · int2 | yes | ||
voice_id | string (uuid) · uuid | yes | ||
parent_id | string (uuid) · uuid | yes | creative_generation | |
root_id | string (uuid) · uuid | yes | creative_generation | |
version | integer · int2 | |||
edit_note | string · text | yes | ||
edit_check | object · jsonb | yes | ||
batch_id | string (uuid) · uuid | yes | ||
batch_kind | string · text | yes | ||
batch_label | string · text | yes | ||
shot | string · text | yes | ||
channel | string · text | yes | ||
product_id | string (uuid) · uuid | yes | ||
source_generation_id | string (uuid) · uuid | yes | creative_generation | |
caption | object · jsonb | yes | ||
layout | object · jsonb | yes | ||
higgsfield | object · jsonb | yes | ||
studio | string · text | yes |
Primary key: id
Checks:
CHECK (((batch_kind IS NULL) OR (batch_kind = ANY (ARRAY['set'::text, 'batch'::text, 'variations'::text]))))CHECK (((batch_label IS NULL) OR (length(batch_label) <= 120)))CHECK (((count >= 1) AND (count <= 4)))CHECK (((edit_note IS NULL) OR (length(edit_note) <= 2000)))CHECK (((resolution IS NULL) OR (resolution = ANY (ARRAY['480p'::text, '720p'::text, '1080p'::text]))))CHECK (((source_kind IS NULL) OR (source_kind = ANY (ARRAY['native'::text, 'byos'::text, 'byok'::text])))) NOT VALIDCHECK (((studio IS NULL) OR (studio = ANY (ARRAY['image'::text, 'video'::text, 'cinema'::text, 'character'::text, 'lipsync'::text, 'motion'::text, 'recast'::text, 'clip'::text, 'layers'::text, 'audio'::text, 'marketing'::text, 'product'::text]))))CHECK (((tool IS NULL) OR (tool = ANY (ARRAY['lip_sync'::text, 'talking_photo'::text, 'face_swap'::text, 'upscale'::text, 'video_edit'::text, 'layout'::text, 'photo_reel'::text, 'higgsfield'::text, 'image_grade'::text]))))CHECK (((upscale IS NULL) OR (upscale = ANY (ARRAY[2, 4]))))CHECK (((version >= 1) AND (version <= 999)))
CreativePost
Table creative_post · Resource creative_posts
GET /api/v1/creative_postsGET /api/v1/creative_posts/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
created_by | string (uuid) · uuid | yes | app_user | |
generation_id | string (uuid) · uuid | yes | creative_generation | |
file_ids | string[] · uuid[] | |||
channel | string · text | instagram, facebook, tiktok, youtube | ||
connection_id | string (uuid) · uuid | |||
connection_label | string · text | yes | ||
caption | string · text | |||
title | string · text | yes | ||
scheduled_at | string (date-time) · timestamptz | |||
status | string · text | scheduled, posting, posted, failed, cancelled | ||
external_id | string · text | yes | ||
external_url | string · text | yes | ||
error | string · text | yes | ||
attempts | integer · int4 | |||
posted_at | string (date-time) · timestamptz | yes | ||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
CreativeVoice
Table creative_voice · Resource creative_voices
GET /api/v1/creative_voicesGET /api/v1/creative_voices/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
created_by | string (uuid) · uuid | yes | app_user | |
name | string · text | |||
provider | string · text | |||
external_voice_id | string · text | yes | ||
sample_file_ids | string[] · uuid[] | |||
consent_by | string (uuid) · uuid | yes | app_user | |
consent_at | string (date-time) · timestamptz | |||
source_kind | string · text | yes | ||
connection_id | string (uuid) · uuid | yes | ||
status | string · text | pending, ready, failed | ||
error | string · text | yes | ||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Checks:
CHECK ((provider = 'elevenlabs'::text))CHECK (((source_kind IS NULL) OR (source_kind = 'byok'::text)))
Events
Workflows and webhooks react to these events.
| Event | What happens | Payload |
|---|---|---|
creative.failed | A generation failed. | id, error, recipientUserId |
creative.generated | A generation finished; its results are in Files. | id, kind, count, recipientUserId |
creative.post_failed | A social post could not go out. | id, channel, error, recipientUserId |
creative.post_scheduled | A result was scheduled to post on social media. | id, channel, generationId, scheduledAt |
creative.posted | A post went out on Instagram, Facebook, TikTok or YouTube. | id, channel, url, recipientUserId |
creative.requested | Somebody (or Era, or a workflow) asked for an image, video, voice-over or music. | id, kind, preset |
creative.used | A result was put on a product, a post, an email or the website. | id, target, targetId |
creative.voice_requested | Somebody asked to clone a voice (with the speaker’s consent). | id, name |