{"openapi":"3.1.0","info":{"title":"SocialFaktory API","version":"1"},"servers":[{"url":"https://www.socialfaktory.com/v1"}],"security":[{"bearer":[]}],"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer"}},"schemas":{"Error":{"type":"object","required":["kind"],"properties":{"kind":{"type":"string","enum":["not_found","validation_failed","scope_required","brand_not_allowed","subscription_required","insufficient_tokens","credit_cap_exceeded","idempotency_in_progress","idempotency_key_reuse","reconnect_required","busy","platform_unavailable","service_unavailable","reference_unavailable","unsupported","tool_failed","payload_too_large","rate_limited","unauthorized"]},"code":{"type":"string"},"message":{"type":"string"}}}}},"paths":{"/brands":{"get":{"operationId":"list_brands","summary":"List brands","description":"List the brands this token may act for. A pinned token sees only the brand it is pinned to.","tags":["brands"],"parameters":[],"responses":{"200":{"description":"List brands","content":{"application/json":{"schema":{"properties":{"brands":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"status":{"type":"string"},"language":{"type":"string"}},"required":["id","name","status","language"]}}},"required":["brands"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/channels":{"get":{"operationId":"list_channels","summary":"List channels","description":"List a brand connected social channels with the caption and media limits each one enforces.","tags":["channels"],"parameters":[{"name":"brand_id","in":"query","required":false,"schema":{"type":"string","description":"The brand this call belongs to. Required unless the token is pinned to a brand."}}],"responses":{"200":{"description":"List channels","content":{"application/json":{"schema":{"properties":{"channels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string"},"handle":{"type":["string","null"]},"status":{"type":"string"},"caption_limit":{"type":["integer","null"]},"media_required":{"type":"boolean"},"max_media":{"type":["integer","null"]}},"required":["id","provider","status"]}}},"required":["channels"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/channels/{channel_id}":{"get":{"operationId":"get_channel_requirements","summary":"Get channel requirements","description":"Ask the connected social account what it accepts right now: its limits, the TikTok privacy levels the creator allows, and the Pinterest boards a pin can go to.","tags":["channels"],"parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"brand_id","in":"query","required":false,"schema":{"type":"string","description":"The brand this call belongs to. Required unless the token is pinned to a brand."}}],"responses":{"200":{"description":"Get channel requirements","content":{"application/json":{"schema":{"properties":{"channel_id":{"type":"string"},"provider":{"type":"string"},"caption_limit":{"type":["integer","null"]},"media_required":{"type":"boolean"},"max_media":{"type":["integer","null"]},"creator_info":{"type":["object","null"]},"boards":{"type":["array","object","null"]}},"required":["channel_id","provider","media_required"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/formats":{"get":{"operationId":"list_formats","summary":"List formats","description":"List the video formats a generation can be built from, and the render styles they can be drawn in.","tags":["formats"],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"List formats","content":{"application/json":{"schema":{"properties":{"formats":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"key":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"category":{"type":["string","null"]},"render_style_key":{"type":["string","null"]},"credit_multiplier":{"type":"number"},"actor_required":{"type":"boolean"},"product_required":{"type":"boolean"},"product_forbidden":{"type":"boolean"}},"required":["id","key","name","credit_multiplier"]}},"render_styles":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"key":{"type":"string"},"name":{"type":"string"},"category":{"type":["string","null"]},"default":{"type":"boolean"}},"required":["id","key","name"]}},"page":{"type":"integer"},"pages":{"type":"integer"}},"required":["formats","render_styles","page","pages"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/actors":{"get":{"operationId":"list_actors","summary":"List actors","description":"List the actors this account can cast: its own and the ready-made cast, by name, 25 a page. Pass an id as actor_id or actor_ids to quote_generation and create_generation.","tags":["actors"],"parameters":[{"name":"query","in":"query","required":false,"schema":{"type":"string"}},{"name":"kind","in":"query","required":false,"schema":{"type":"string","enum":["person","mascot"]}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"List actors","content":{"application/json":{"schema":{"properties":{"actors":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string","enum":["person","mascot"]},"own":{"type":"boolean"},"portrait_url":{"type":["string","null"]}},"required":["id","name","kind","own"]}},"page":{"type":"integer"},"pages":{"type":"integer"}},"required":["actors","page","pages"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/media":{"get":{"operationId":"list_media","summary":"List media","description":"Browse the finished exports in a brand media library, by folder or by name.","tags":["media"],"parameters":[{"name":"brand_id","in":"query","required":false,"schema":{"type":"string","description":"The brand this call belongs to. Required unless the token is pinned to a brand."}},{"name":"folder_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"query","in":"query","required":false,"schema":{"type":"string"}},{"name":"kind","in":"query","required":false,"schema":{"type":"string"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"List media","content":{"application/json":{"schema":{"properties":{"media":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"kind":{"type":["string","null"]},"aspect_ratio":{"type":["string","null"]},"file_url":{"type":["string","null"]},"pages":{"type":"array"},"created_at":{"type":"string"}},"required":["id","created_at"]}},"folders":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}},"page":{"type":"integer"},"pages":{"type":"integer"}},"required":["media","folders","page","pages"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/wallet":{"get":{"operationId":"get_wallet","summary":"Get wallet","description":"Read the credit balance of the account the token belongs to, and its auto top-up settings (read-only: they change only in the Billing page).","tags":["wallet"],"parameters":[],"responses":{"200":{"description":"Get wallet","content":{"application/json":{"schema":{"properties":{"available_balance":{"type":"integer"},"reserved_balance":{"type":"integer"},"total_balance":{"type":"integer"},"auto_top_up":{"type":["object","null"],"properties":{"enabled":{"type":"boolean"},"threshold":{"type":"integer"},"pack_credits":{"type":"integer"}},"required":["enabled","threshold","pack_credits"]}},"required":["available_balance","reserved_balance","total_balance","auto_top_up"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/reference_checks":{"post":{"operationId":"check_reference","summary":"Check reference","description":"Ask whether a video link can be used as the source of a swap, and if so record it on the account and start fetching that third-party video from its host. Safe to call again while the fetch is running: it reports progress rather than fetching twice.","tags":["reference_checks"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"properties":{"url":{"type":"string"}},"required":["url"],"additionalProperties":false,"type":"object"}}}},"responses":{"200":{"description":"Check reference","content":{"application/json":{"schema":{"properties":{"supported":{"type":"boolean"},"reference_id":{"type":"string"},"status":{"type":"string"},"source_url":{"type":"string"},"video_url":{"type":["string","null"]},"has_audio":{"type":["boolean","null"]}},"required":["supported"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/generations/quote":{"post":{"operationId":"quote_generation","summary":"Quote generation","description":"Show how many credits a generation would use before it starts, so the user can agree to it. Inputs it would refuse to run come back as a refusal instead.","tags":["generations"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"properties":{"workflow":{"type":"object","properties":{"workflow_type":{"type":"string"},"brand_id":{"type":"string"},"video_type_id":{"type":"string"},"render_style":{"type":"string"},"render_style_id":{"type":"string"},"hook_id":{"type":"string"},"voice_id":{"type":"string"},"script":{"type":"string"},"spoken_script":{"type":"string"},"scene_brief":{"type":"string"},"type_description":{"type":"string"},"instruction":{"type":"string"},"negative_prompt":{"type":"string"},"caption_preset":{"type":"string"},"aspect_ratio":{"type":"string"},"reframe_aspect_ratio":{"type":"string"},"target_duration":{"type":"integer"},"target_language":{"type":"string"},"edit_kind":{"type":"string"},"product_kind":{"type":"string"},"tone":{"type":"string"},"actor_id":{"type":"string"},"actor_ids":{"type":"array","items":{"type":"string"},"description":"The cast, lead first. A remake (video_swap or trend_remake) takes exactly one actor, who replaces the main person in the clip."},"user_product_id":{"type":"string"},"source_workflow_id":{"type":"string"},"source_url":{"type":"string","description":"The link the generation starts from: the web page a launch animation reads, or the video a trend remake, composed video or video swap copies. Either way, one the user made or has the rights to use."},"mode":{"type":"string","enum":["template","custom"]},"brief":{"type":"string"},"asset_ids":{"type":"array","maxItems":20,"items":{"type":"string"},"description":"Animated videos only: the library files the composition shows. Every other lane refuses them; a prompt_video takes files as references instead."},"prompt":{"type":"string","maxLength":2000},"resolution":{"type":"string","enum":["480p","720p"]},"sound":{"type":"boolean"},"references":{"type":"array","maxItems":50,"items":{"type":"object","properties":{"kind":{"type":"string","enum":["image","video","audio"]},"source":{"type":"string","enum":["upload","media","link","actor","product","result"]},"id":{"type":"string"},"name":{"type":"string"},"trim":{"type":"array","minItems":2,"maxItems":2,"items":{"type":"number","minimum":0}}},"required":["kind","source","id","name"],"additionalProperties":false},"description":"The files a prompt_video works from, each called in the prompt as @ and its name: images, video clips (30 seconds in all, cut with trim [start, end] in seconds) and audio. Source upload takes the editor_asset id get_upload returns once the upload is ready, actor an actor id, product a product id, link the source_url of a video_reference upload, result a finished prompt_video generation id."},"reference_url":{"type":"string"},"music":{"type":"object","properties":{"kind":{"type":"string","enum":["library","upload","none"]},"id":{"type":"string"},"asset_id":{"type":"string"}},"required":["kind"],"additionalProperties":false},"start":{"type":"number","minimum":0},"seconds":{"type":"number","minimum":4,"maximum":90},"keep_source_audio":{"type":"boolean"}},"additionalProperties":false}},"required":["workflow"],"additionalProperties":false,"type":"object"}}}},"responses":{"200":{"description":"Quote generation","content":{"application/json":{"schema":{"properties":{"quote":{"type":"object","properties":{"total":{"type":"integer"},"lines":{"type":"array","items":{"type":"object"}},"duration_seconds":{"type":["number","null"]},"approximate":{"type":"boolean"}},"required":["total","lines"]},"credit_cap_remaining":{"type":["integer","null"]}},"required":["quote"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/generations":{"post":{"operationId":"create_generation","summary":"Create generation","description":"Run a video generation, spending credits already in the account. Quote it first with quote_generation and run only once the user agrees to the quote: the token monthly credit cap is the only other guard on the spend.","tags":["generations"],"parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"properties":{"workflow":{"type":"object","properties":{"workflow_type":{"type":"string"},"brand_id":{"type":"string"},"video_type_id":{"type":"string"},"render_style":{"type":"string"},"render_style_id":{"type":"string"},"hook_id":{"type":"string"},"voice_id":{"type":"string"},"script":{"type":"string"},"spoken_script":{"type":"string"},"scene_brief":{"type":"string"},"type_description":{"type":"string"},"instruction":{"type":"string"},"negative_prompt":{"type":"string"},"caption_preset":{"type":"string"},"aspect_ratio":{"type":"string"},"reframe_aspect_ratio":{"type":"string"},"target_duration":{"type":"integer"},"target_language":{"type":"string"},"edit_kind":{"type":"string"},"product_kind":{"type":"string"},"tone":{"type":"string"},"actor_id":{"type":"string"},"actor_ids":{"type":"array","items":{"type":"string"},"description":"The cast, lead first. A remake (video_swap or trend_remake) takes exactly one actor, who replaces the main person in the clip."},"user_product_id":{"type":"string"},"source_workflow_id":{"type":"string"},"source_url":{"type":"string","description":"The link the generation starts from: the web page a launch animation reads, or the video a trend remake, composed video or video swap copies. Either way, one the user made or has the rights to use."},"mode":{"type":"string","enum":["template","custom"]},"brief":{"type":"string"},"asset_ids":{"type":"array","maxItems":20,"items":{"type":"string"},"description":"Animated videos only: the library files the composition shows. Every other lane refuses them; a prompt_video takes files as references instead."},"prompt":{"type":"string","maxLength":2000},"resolution":{"type":"string","enum":["480p","720p"]},"sound":{"type":"boolean"},"references":{"type":"array","maxItems":50,"items":{"type":"object","properties":{"kind":{"type":"string","enum":["image","video","audio"]},"source":{"type":"string","enum":["upload","media","link","actor","product","result"]},"id":{"type":"string"},"name":{"type":"string"},"trim":{"type":"array","minItems":2,"maxItems":2,"items":{"type":"number","minimum":0}}},"required":["kind","source","id","name"],"additionalProperties":false},"description":"The files a prompt_video works from, each called in the prompt as @ and its name: images, video clips (30 seconds in all, cut with trim [start, end] in seconds) and audio. Source upload takes the editor_asset id get_upload returns once the upload is ready, actor an actor id, product a product id, link the source_url of a video_reference upload, result a finished prompt_video generation id."},"reference_url":{"type":"string"},"music":{"type":"object","properties":{"kind":{"type":"string","enum":["library","upload","none"]},"id":{"type":"string"},"asset_id":{"type":"string"}},"required":["kind"],"additionalProperties":false},"start":{"type":"number","minimum":0},"seconds":{"type":"number","minimum":4,"maximum":90},"keep_source_audio":{"type":"boolean"}},"additionalProperties":false}},"required":["workflow"],"additionalProperties":false,"type":"object"}}}},"responses":{"201":{"description":"Create generation","content":{"application/json":{"schema":{"properties":{"generation_id":{"type":"string"},"status":{"type":"string"},"credit_cost":{"type":"integer"}},"required":["generation_id","status","credit_cost"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"list_generations","summary":"List generations","description":"List the generations this token may see, newest first.","tags":["generations"],"parameters":[{"name":"brand_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"List generations","content":{"application/json":{"schema":{"properties":{"generations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"workflow_type":{"type":"string"},"status":{"type":"string"},"lifecycle":{"type":"string"},"video_url":{"type":["string","null"]},"created_at":{"type":"string"}},"required":["id","workflow_type","status","lifecycle","created_at"]}},"page":{"type":"integer"},"pages":{"type":"integer"}},"required":["generations","page","pages"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/generations/{generation_id}":{"get":{"operationId":"get_generation","summary":"Get generation","description":"Read one generation: where it is, the finished video, and the editor asset it produced. While it runs, stage is its current step, waiting_on names what holds it up and deadline_at is when it would be failed for that. A run waiting on the user will not move until the person acts, so ask them rather than poll.","tags":["generations"],"parameters":[{"name":"generation_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Get generation","content":{"application/json":{"schema":{"properties":{"id":{"type":"string"},"workflow_type":{"type":"string"},"status":{"type":"string"},"lifecycle":{"type":"string"},"stage":{"type":["string","null"]},"waiting_on":{"type":["object","null"],"properties":{"reason":{"type":"string","enum":["provider","media","user","blocked","unknown"]},"key":{"type":["string","null"]}},"required":["reason","key"]},"deadline_at":{"type":["string","null"]},"script":{"type":["string","null"]},"video_url":{"type":["string","null"]},"cover_image_url":{"type":["string","null"]},"error_code":{"type":["string","null"]},"error_message":{"type":["string","null"]},"credit_cost":{"type":"integer"},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"editor_asset":{"type":["object","null"]},"created_at":{"type":"string"}},"required":["id","workflow_type","status","lifecycle","created_at"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/text_generations":{"post":{"operationId":"generate_text","summary":"Generate text","description":"Write post variants for a brand from a brief, a link, a finished generation or a media item. Spends the user credits: 3 are reserved up front against the token monthly credit cap, and the write settles at what it used, which can land a little over the cap.","tags":["text_generations"],"parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"properties":{"brand_id":{"type":"string"},"brief":{"type":"string"},"source_url":{"type":"string"},"generation_id":{"type":"string"},"media_id":{"type":"string"},"platform":{"type":"string","enum":["x","linkedin"]}},"required":["brand_id"],"additionalProperties":false,"type":"object"}}}},"responses":{"201":{"description":"Generate text","content":{"application/json":{"schema":{"properties":{"text_generation_id":{"type":"string"},"status":{"type":"string"}},"required":["text_generation_id","status"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/text_generations/{text_generation_id}":{"get":{"operationId":"get_text_generation","summary":"Get text generation","description":"Read one text generation and the variants it wrote.","tags":["text_generations"],"parameters":[{"name":"text_generation_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"brand_id","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Get text generation","content":{"application/json":{"schema":{"properties":{"id":{"type":"string"},"status":{"type":"string"},"platform":{"type":"string"},"mode":{"type":"string"},"variants":{"type":"array"}},"required":["id","status","platform","mode","variants"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/uploads":{"post":{"operationId":"create_upload","summary":"Create upload","description":"Open a ticket for a file from the user machine, content the service has not seen. Send the bytes in one raw HTTPS PUT to the returned url with exactly the returned headers, then call complete_upload. Limits: 2 GB, 30 minutes, 4096 px a side, 50 megapixels. An editor asset, the default purpose, needs its kind. Purpose video_reference stores a trend remake's source video: pass the ready ticket's video_reference.source_url to create_generation. Purpose actor turns a portrait photo of the user, or of someone who agreed to be cast, into an actor: it needs a name, takes jpg, png, webp or gif up to 10 MB and 4096 px a side, and the ready ticket carries actor.id for create_generation.","tags":["uploads"],"parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"properties":{"filename":{"type":"string"},"byte_size":{"type":"integer","minimum":1},"content_type":{"type":"string"},"purpose":{"type":"string","enum":["editor_asset","video_reference","actor"]},"kind":{"type":"string","enum":["video","image","audio","font"]},"name":{"type":"string","maxLength":80}},"required":["filename","byte_size","content_type"],"additionalProperties":false,"type":"object"}}}},"responses":{"201":{"description":"Create upload","content":{"application/json":{"schema":{"properties":{"upload_id":{"type":"string"},"status":{"type":"string"},"url":{"type":"string"},"headers":{"type":"object"},"expires_in":{"type":"integer"}},"required":["upload_id","status","url","headers","expires_in"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/uploads/{upload_id}/complete":{"post":{"operationId":"complete_upload","summary":"Complete upload","description":"Tell the service the bytes are in place and start the ingest. Safe to call again: the ticket is claimed once, so a repeat reports progress rather than ingesting twice.","tags":["uploads"],"parameters":[{"name":"upload_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Complete upload","content":{"application/json":{"schema":{"type":"object","properties":{"upload_id":{"type":"string"},"status":{"type":"string"},"error_code":{"type":["string","null"]},"error_message":{"type":["string","null"]},"editor_asset":{"type":["object","null"],"properties":{"id":{"type":"string"},"kind":{"type":"string"},"duration_ms":{"type":["integer","null"]},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"mime_type":{"type":["string","null"]},"byte_size":{"type":["integer","null"]},"thumbnail_url":{"type":["string","null"]}}},"video_reference":{"type":["object","null"],"properties":{"id":{"type":"string"},"source_url":{"type":"string"}}},"actor":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"portrait_url":{"type":["string","null"]}}}},"required":["upload_id","status"]}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/uploads/{upload_id}":{"get":{"operationId":"get_upload","summary":"Get upload","description":"Read an upload ticket. Poll it every 2 seconds, backing off to 5, for up to 16 minutes: the ingest gives up at 15, so the ticket always reaches ready or failed inside that window. A ready video ticket carries the editor asset id and its thumbnail_url, which a Pinterest pin needs. A ready actor ticket carries the actor id and its portrait_url.","tags":["uploads"],"parameters":[{"name":"upload_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Get upload","content":{"application/json":{"schema":{"type":"object","properties":{"upload_id":{"type":"string"},"status":{"type":"string"},"error_code":{"type":["string","null"]},"error_message":{"type":["string","null"]},"editor_asset":{"type":["object","null"],"properties":{"id":{"type":"string"},"kind":{"type":"string"},"duration_ms":{"type":["integer","null"]},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"mime_type":{"type":["string","null"]},"byte_size":{"type":["integer","null"]},"thumbnail_url":{"type":["string","null"]}}},"video_reference":{"type":["object","null"],"properties":{"id":{"type":"string"},"source_url":{"type":"string"}}},"actor":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"portrait_url":{"type":["string","null"]}}}},"required":["upload_id","status"]}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/products":{"post":{"operationId":"create_product","summary":"Create product","description":"Turn a product photo into a product an actor can present in a generation. Pass exactly one of editor_asset_id, an image uploaded with create_upload, or source_url, the https link of a product page, whose preview image the service reads. Pass the product id as user_product_id to quote_generation and create_generation.","tags":["products"],"parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"properties":{"name":{"type":"string","minLength":1,"maxLength":80},"editor_asset_id":{"type":"string"},"source_url":{"type":"string","pattern":"^https://"}},"required":["name"],"additionalProperties":false,"type":"object"}}}},"responses":{"201":{"description":"Create product","content":{"application/json":{"schema":{"properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"kind":{"type":"string","enum":["product","application_screenshot","website_screenshot","tablet_screenshot"]},"image_url":{"type":"string"}},"required":["id","name","kind","image_url"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"list_products","summary":"List products","description":"List the products of this account, newest first, 25 a page. Pass an id as user_product_id to quote_generation and create_generation.","tags":["products"],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"List products","content":{"application/json":{"schema":{"properties":{"products":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"kind":{"type":"string","enum":["product","application_screenshot","website_screenshot","tablet_screenshot"]},"image_url":{"type":"string"}},"required":["id","name","kind","image_url"]}},"page":{"type":"integer"},"pages":{"type":"integer"}},"required":["products","page","pages"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/images/quote":{"post":{"operationId":"quote_image","summary":"Quote image","description":"Show how many credits one image costs before it is made: credits from a prompt alone, reference_credits when a picture from the library guides it.","tags":["images"],"parameters":[],"responses":{"200":{"description":"Quote image","content":{"application/json":{"schema":{"properties":{"credits":{"type":"integer"},"reference_credits":{"type":"integer"}},"required":["credits","reference_credits"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/images":{"post":{"operationId":"generate_image","summary":"Generate image","description":"Make one image from a prompt, spending credits already in the account at the price quote_image names. Each request makes one image with its own image_generation_id. A reference_asset_id guides it with a picture from the library. Poll get_image every 2 to 5 seconds until it succeeds or fails; a failed image gives its credits back.","tags":["images"],"parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"properties":{"brand_id":{"type":"string","description":"The brand this call belongs to. Required unless the token is pinned to a brand."},"prompt":{"type":"string","pattern":"\\S"},"aspect":{"type":"string","enum":["9:16","3:4","1:1","16:9"]},"reference_asset_id":{"type":"string"}},"required":["prompt"],"additionalProperties":false,"type":"object"}}}},"responses":{"201":{"description":"Generate image","content":{"application/json":{"schema":{"properties":{"image_generation_id":{"type":"string"},"status":{"type":"string"},"credit_cost":{"type":"integer"}},"required":["image_generation_id","status","credit_cost"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/images/{image_generation_id}":{"get":{"operationId":"get_image","summary":"Get image","description":"Read one image: its status, pending, succeeded or failed. Once it succeeded it carries the editor asset that create_posts publishes and a file_url to download it, valid for an hour.","tags":["images"],"parameters":[{"name":"image_generation_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"brand_id","in":"query","required":false,"schema":{"type":"string","description":"The brand this call belongs to. Required unless the token is pinned to a brand."}}],"responses":{"200":{"description":"Get image","content":{"application/json":{"schema":{"properties":{"id":{"type":"string"},"status":{"type":"string","enum":["pending","succeeded","failed"]},"editor_asset_id":{"type":["string","null"]},"file_url":{"type":["string","null"]}},"required":["id","status","editor_asset_id","file_url"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/posts":{"post":{"operationId":"create_posts","summary":"Create posts","description":"Compose one post per channel from the same media. Drafts by default, which stay inside the service and run no checks; send them one at a time with send_post so a refusal names the channel it belongs to. status \"scheduled\" instead checks every target first, creates nothing if one fails, then hands the posts to the publisher in the background for the social accounts the user connected: they come back queued, and list_posts shows each one turn scheduled, then published.","tags":["posts"],"parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"properties":{"brand_id":{"type":"string","description":"The brand this call belongs to. Required unless the token is pinned to a brand."},"generation_id":{"type":"string"},"media_item_id":{"type":"string"},"editor_asset_id":{"type":"string"},"scheduled_at":{"type":"string"},"status":{"type":"string","enum":["draft","scheduled"]},"targets":{"type":"array","minItems":1,"items":{"type":"object","properties":{"channel_id":{"type":"string"},"caption":{"type":"string"},"parts":{"type":"array","items":{"type":"string"}},"tiktok_privacy_level":{"type":"string","enum":["public_to_everyone","mutual_follow_friends","follower_of_creator","self_only"]},"tiktok_allow_comment":{"type":"boolean"},"tiktok_allow_duet":{"type":"boolean"},"tiktok_allow_stitch":{"type":"boolean"},"tiktok_your_brand":{"type":"boolean"},"tiktok_branded_content":{"type":"boolean"},"pinterest_board_id":{"type":"string"},"youtube_title":{"type":"string","maxLength":100},"youtube_description":{"type":"string"},"youtube_privacy":{"type":"string","enum":["public","unlisted","private"]},"youtube_short":{"type":"boolean"}},"required":["channel_id"],"additionalProperties":false}}},"required":["targets"],"additionalProperties":false,"type":"object"}}}},"responses":{"201":{"description":"Create posts","content":{"application/json":{"schema":{"properties":{"posts":{"type":"array","items":{"type":"object"}}},"required":["posts"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"list_posts","summary":"List posts","description":"List posts with their scheduled time, the link they were released at, and their metrics. A post just sent stays queued until the publisher takes it, then turns scheduled. A release link and metrics appear once the channel reports back, which takes a few minutes.","tags":["posts"],"parameters":[{"name":"brand_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["draft","scheduled","published","failed","queued"]}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"List posts","content":{"application/json":{"schema":{"properties":{"posts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"caption":{"type":["string","null"]},"scheduled_at":{"type":["string","null"]},"failure_code":{"type":["string","null"]},"channel_id":{"type":"string"},"provider":{"type":"string"}},"required":["id","status","channel_id","provider"]}},"page":{"type":"integer"},"pages":{"type":"integer"}},"required":["posts","page","pages"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/posts/{post_id}":{"get":{"operationId":"get_post","summary":"Get post","description":"Read one post: its status, scheduled time, the link it was released at, and its metrics. A post just sent stays queued until the publisher takes it, then turns scheduled, then published or failed. The release link and metrics fill in after it goes live, once the channel reports back.","tags":["posts"],"parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Get post","content":{"application/json":{"schema":{"properties":{"id":{"type":"string"},"status":{"type":"string"},"caption":{"type":["string","null"]},"scheduled_at":{"type":["string","null"]},"failure_code":{"type":["string","null"]},"channel_id":{"type":"string"},"provider":{"type":"string"}},"required":["id","status","channel_id","provider"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"delete_post","summary":"Delete post","description":"Delete a post from the account. A scheduled one is withdrawn from the live social channel first, inside this call. A post already published stays online on its channel: only the record here goes. Rescheduling is a delete followed by create_posts.","tags":["posts"],"parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"brand_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","maxLength":128}}],"responses":{"200":{"description":"Delete post","content":{"application/json":{"schema":{"properties":{"post_id":{"type":"string"},"deleted":{"type":"boolean"}},"required":["post_id","deleted"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/posts/{post_id}/send":{"post":{"operationId":"send_post","summary":"Send post","description":"Send one draft to the social account the user connected for its channel, at its scheduled time or straight away. The channel checks run inside this call, so a refusal comes back here with its code. The post then returns queued and the publisher takes it from there: poll list_posts to see it turn scheduled and then published.","tags":["posts"],"parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"properties":{"brand_id":{"type":"string"},"mode":{"type":"string","enum":["schedule","now"]}},"required":["mode"],"additionalProperties":false,"type":"object"}}}},"responses":{"202":{"description":"Send post","content":{"application/json":{"schema":{"properties":{"id":{"type":"string"},"status":{"type":"string"},"caption":{"type":["string","null"]},"scheduled_at":{"type":["string","null"]},"failure_code":{"type":["string","null"]},"channel_id":{"type":"string"},"provider":{"type":"string"}},"required":["id","status","channel_id","provider"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhook_endpoints":{"post":{"operationId":"create_webhook_endpoint","summary":"Create webhook endpoint","description":"Register an https URL that hears about the work this connection can read: post.scheduled, post.published, post.failed, generation.finished and text_generation.finished. Each event is a POST of JSON with its id, type, created_at and data (object, id, brand_id, status); read the object through its GET. The SocialFaktory-Signature header carries t=\u003cunix seconds\u003e,v1=\u003chex HMAC-SHA256 of \"\u003ct\u003e.\u003craw body\u003e\" under the secret\u003e. Reject a t older than 5 minutes, and dedupe on the event id, since a retry resends the same event. The secret is shown in this answer only, and again to a retry of this call under the same idempotency key. A connection holds at most 5 endpoints, and one whose deliveries fail 10 events in a row turns disabled.","tags":["webhook_endpoints"],"parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","maxLength":128}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"properties":{"url":{"type":"string"}},"required":["url"],"additionalProperties":false,"type":"object"}}}},"responses":{"201":{"description":"Create webhook endpoint","content":{"application/json":{"schema":{"properties":{"id":{"type":"string"},"url":{"type":"string"},"status":{"type":"string"},"secret":{"type":"string"}},"required":["id","url","status","secret"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"get":{"operationId":"list_webhook_endpoints","summary":"List webhook endpoints","description":"List the webhook endpoints this connection registered, without their secrets. A disabled endpoint receives nothing; delete it and register its URL again to resume.","tags":["webhook_endpoints"],"parameters":[],"responses":{"200":{"description":"List webhook endpoints","content":{"application/json":{"schema":{"properties":{"webhook_endpoints":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"status":{"type":"string","enum":["active","disabled"]},"last_delivered_at":{"type":["string","null"]}},"required":["id","url","status","last_delivered_at"]}}},"required":["webhook_endpoints"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/webhook_endpoints/{webhook_endpoint_id}":{"delete":{"operationId":"delete_webhook_endpoint","summary":"Delete webhook endpoint","description":"Delete one webhook endpoint this connection registered. Its URL receives nothing more, including the retries of events already sent.","tags":["webhook_endpoints"],"parameters":[{"name":"webhook_endpoint_id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","maxLength":128}}],"responses":{"200":{"description":"Delete webhook endpoint","content":{"application/json":{"schema":{"properties":{"webhook_endpoint_id":{"type":"string"},"deleted":{"type":"boolean"}},"required":["webhook_endpoint_id","deleted"],"type":"object"}}}},"default":{"description":"The error envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}}}