{"openapi":"3.1.0","info":{"title":"Vitrinify API","version":"1.0.0","description":"Generate, schedule and publish short-form content for one workspace. Authenticate with a workspace API key (Settings → API keys): `Authorization: Bearer vfy_…`. Scopes: read, generate, publish. Rate limit: 60 requests/min per key (generate: 5/min). Errors: `{ \"error\": { \"code\", \"message\", \"messages\": { \"tr\", \"en\" } } }`."},"servers":[{"url":"https://vitrinify.app/v1"}],"security":[{"apiKey":[]}],"tags":[{"name":"content","description":"Content candidates and the calendar"},{"name":"posts","description":"Publishing results"}],"paths":{"/content/generate":{"post":{"operationId":"generateContent","summary":"Generate content candidates","description":"Generates candidates for the workspace's approved brand profile through the same pipeline as the app: sector rules, compliance engine (blocked candidates are dropped, flagged ones carry notes), daily generation limit of the plan. Slides are rendered in the background (`renderStatus`).\n\nRequired scope: `generate`.","tags":["content"],"security":[{"apiKey":[]}],"x-required-scope":"generate","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"n":{"default":5,"description":"Number of candidates to generate (default 5).","type":"integer","minimum":1,"maximum":20},"format":{"default":"slideshow","description":"Content format; the workspace's sector must offer it.","type":"string","enum":["slideshow","hook-demo","voiceover","meme"]}},"additionalProperties":false}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"requested":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"created":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"blocked":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Candidates dropped by the compliance engine."},"duplicates":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"string","description":"ISO 8601"},"format":{"type":"string"},"status":{"type":"string","enum":["queued","saved","skipped"]},"hook":{"type":"string"},"slides":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"body":{"type":"string"}},"required":["title"],"additionalProperties":false}},"caption":{"type":"string"},"hashtags":{"type":"array","items":{"type":"string"}},"aiGenerated":{"type":"boolean"},"compliance":{"type":"object","properties":{"status":{"type":"string","enum":["ok","flagged"],"description":"Blocked content is never stored."},"notes":{"type":"array","items":{"type":"string"}}},"required":["status","notes"],"additionalProperties":false},"renderStatus":{"type":"string","enum":["pending","rendering","done","failed"]},"media":{"type":"array","items":{"type":"object","properties":{"aspect":{"type":"string","enum":["9:16","4:5"]},"mime":{"type":"string"},"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"url":{"type":["string","null"]}},"required":["aspect","mime","index","url"],"additionalProperties":false}},"video":{"anyOf":[{"type":"object","properties":{"url":{"type":["string","null"]},"durationSec":{"type":"number"}},"required":["url","durationSec"],"additionalProperties":false},{"type":"null"}]},"scheduledAt":{"type":["string","null"]},"scheduledPlatforms":{"type":"array","items":{"type":"string","enum":["instagram","tiktok"]}},"approval":{"anyOf":[{"type":"string","enum":["approved","pending","stale"]},{"type":"null"}],"description":"Approval state in approval mode; null when the mode is off."}},"required":["id","createdAt","format","status","hook","slides","caption","hashtags","aiGenerated","compliance","renderStatus","media","video","scheduledAt","scheduledPlatforms","approval"],"additionalProperties":false}}},"required":["requested","created","blocked","duplicates","items"],"additionalProperties":false}}}},"400":{"description":"Invalid request — e.g. INVALID_REQUEST, INVALID_SCHEDULE","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or revoked API key — e.g. API_KEY_INVALID, UNAUTHENTICATED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Plan limit reached — e.g. QUOTA_EXCEEDED, PLAN_LIMIT_SAVED_ITEMS, PLAN_LIMIT_ACCOUNTS, PLAN_LIMIT_WORKSPACES, INSUFFICIENT_CREDITS","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Scope, role, approval or TikTok consent refusal — e.g. API_SCOPE_FORBIDDEN, FORBIDDEN, ROLE_FORBIDDEN, APPROVAL_REQUIRED, APPROVAL_STALE, TIKTOK_UI_CONSENT_REQUIRED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Refused by a business rule (compliance, account, format…)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After) — e.g. RATE_LIMITED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds"}}},"500":{"description":"Unexpected error — e.g. INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"A service is temporarily unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/content":{"get":{"operationId":"listContent","summary":"List content","description":"Content of the workspace, newest first, cursor-paginated.\n\nRequired scope: `read`.","tags":["content"],"security":[{"apiKey":[]}],"x-required-scope":"read","parameters":[{"name":"status","in":"query","required":false,"schema":{"description":"queued = new candidate, saved = kept, skipped = dropped.","type":"string","enum":["queued","saved","skipped"]}},{"name":"limit","in":"query","required":false,"schema":{"default":20,"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"description":"`nextCursor` of the previous page.","type":"string","minLength":1,"maxLength":200}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"string","description":"ISO 8601"},"format":{"type":"string"},"status":{"type":"string","enum":["queued","saved","skipped"]},"hook":{"type":"string"},"slides":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"body":{"type":"string"}},"required":["title"],"additionalProperties":false}},"caption":{"type":"string"},"hashtags":{"type":"array","items":{"type":"string"}},"aiGenerated":{"type":"boolean"},"compliance":{"type":"object","properties":{"status":{"type":"string","enum":["ok","flagged"],"description":"Blocked content is never stored."},"notes":{"type":"array","items":{"type":"string"}}},"required":["status","notes"],"additionalProperties":false},"renderStatus":{"type":"string","enum":["pending","rendering","done","failed"]},"media":{"type":"array","items":{"type":"object","properties":{"aspect":{"type":"string","enum":["9:16","4:5"]},"mime":{"type":"string"},"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"url":{"type":["string","null"]}},"required":["aspect","mime","index","url"],"additionalProperties":false}},"video":{"anyOf":[{"type":"object","properties":{"url":{"type":["string","null"]},"durationSec":{"type":"number"}},"required":["url","durationSec"],"additionalProperties":false},{"type":"null"}]},"scheduledAt":{"type":["string","null"]},"scheduledPlatforms":{"type":"array","items":{"type":"string","enum":["instagram","tiktok"]}},"approval":{"anyOf":[{"type":"string","enum":["approved","pending","stale"]},{"type":"null"}],"description":"Approval state in approval mode; null when the mode is off."}},"required":["id","createdAt","format","status","hook","slides","caption","hashtags","aiGenerated","compliance","renderStatus","media","video","scheduledAt","scheduledPlatforms","approval"],"additionalProperties":false}},"nextCursor":{"type":["string","null"]}},"required":["items","nextCursor"],"additionalProperties":false}}}},"400":{"description":"Invalid request — e.g. INVALID_REQUEST, INVALID_SCHEDULE","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or revoked API key — e.g. API_KEY_INVALID, UNAUTHENTICATED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Scope, role, approval or TikTok consent refusal — e.g. API_SCOPE_FORBIDDEN, FORBIDDEN, ROLE_FORBIDDEN, APPROVAL_REQUIRED, APPROVAL_STALE, TIKTOK_UI_CONSENT_REQUIRED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After) — e.g. RATE_LIMITED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds"}}},"500":{"description":"Unexpected error — e.g. INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/content/{id}/schedule":{"post":{"operationId":"scheduleContent","summary":"Schedule content","description":"Saves the item (counts against the plan's monthly saved items) and places it on the calendar; it is published automatically at that time through the same queue as the app (compliance re-checked right before posting). In approval mode the slot only posts after an approver approves it in the app. TikTok: only when TikTok settings and explicit consent were given on the TikTok post screen in the app for this content; otherwise `TIKTOK_UI_CONSENT_REQUIRED`.\n\nRequired scope: `publish`.","tags":["content"],"security":[{"apiKey":[]}],"x-required-scope":"publish","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"at":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?(Z|[+-]\\d{2}:\\d{2})$","description":"ISO 8601 date-time with an offset or Z, in the future."},"platforms":{"minItems":1,"maxItems":2,"type":"array","items":{"type":"string","enum":["instagram","tiktok"]},"description":"TikTok only works when TikTok settings + consent were given in the app (see docs)."}},"required":["at","platforms"],"additionalProperties":false}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"scheduledAt":{"type":"string"},"platforms":{"type":"array","items":{"type":"string","enum":["instagram","tiktok"]}},"approval":{"anyOf":[{"type":"string","enum":["approved","pending","stale"]},{"type":"null"}]}},"required":["id","scheduledAt","platforms","approval"],"additionalProperties":false}}}},"400":{"description":"Invalid request — e.g. INVALID_REQUEST, INVALID_SCHEDULE","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or revoked API key — e.g. API_KEY_INVALID, UNAUTHENTICATED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Plan limit reached — e.g. QUOTA_EXCEEDED, PLAN_LIMIT_SAVED_ITEMS, PLAN_LIMIT_ACCOUNTS, PLAN_LIMIT_WORKSPACES, INSUFFICIENT_CREDITS","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Scope, role, approval or TikTok consent refusal — e.g. API_SCOPE_FORBIDDEN, FORBIDDEN, ROLE_FORBIDDEN, APPROVAL_REQUIRED, APPROVAL_STALE, TIKTOK_UI_CONSENT_REQUIRED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in the key's workspace) — e.g. NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the item's current state — e.g. PUBLISH_DUPLICATE, INSTAGRAM_REPOST_BLOCKED, TIKTOK_REPOST_BLOCKED, SCHEDULE_IN_PROGRESS, PUBLISH_NOT_READY, VIDEO_NOT_READY","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Refused by a business rule (compliance, account, format…)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After) — e.g. RATE_LIMITED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds"}}},"500":{"description":"Unexpected error — e.g. INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/content/{id}/publish":{"post":{"operationId":"publishContent","summary":"Publish content now","description":"Queues a post through the app's publishing queue (account checks, compliance, render readiness, duplicate and outcome-unknown guards). Poll `GET /posts/{id}` for the result. In approval mode only approved content is posted (`APPROVAL_REQUIRED` / `APPROVAL_STALE`). TikTok uses the settings + consent given in the app (`TIKTOK_UI_CONSENT_REQUIRED` otherwise).\n\nRequired scope: `publish`.","tags":["content"],"security":[{"apiKey":[]}],"x-required-scope":"publish","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string","enum":["instagram","tiktok"]},"accountId":{"description":"Connected Instagram account id; required only when several are connected.","type":"string","minLength":1,"maxLength":64}},"required":["platform"],"additionalProperties":false}}}},"responses":{"202":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"contentId":{"type":"string"},"platform":{"type":"string","enum":["instagram","tiktok"]},"account":{"type":"string"},"status":{"type":"string","enum":["queued","publishing","published","failed"]},"outcomeUnknown":{"type":"boolean","description":"The post may be live although the platform did not confirm it; never re-posted."},"url":{"type":["string","null"]},"errorCode":{"type":["string","null"]},"createdAt":{"type":"string"},"publishedAt":{"type":["string","null"]}},"required":["id","contentId","platform","account","status","outcomeUnknown","url","errorCode","createdAt","publishedAt"],"additionalProperties":false}}}},"400":{"description":"Invalid request — e.g. INVALID_REQUEST, INVALID_SCHEDULE","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or revoked API key — e.g. API_KEY_INVALID, UNAUTHENTICATED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Plan limit reached — e.g. QUOTA_EXCEEDED, PLAN_LIMIT_SAVED_ITEMS, PLAN_LIMIT_ACCOUNTS, PLAN_LIMIT_WORKSPACES, INSUFFICIENT_CREDITS","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Scope, role, approval or TikTok consent refusal — e.g. API_SCOPE_FORBIDDEN, FORBIDDEN, ROLE_FORBIDDEN, APPROVAL_REQUIRED, APPROVAL_STALE, TIKTOK_UI_CONSENT_REQUIRED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in the key's workspace) — e.g. NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the item's current state — e.g. PUBLISH_DUPLICATE, INSTAGRAM_REPOST_BLOCKED, TIKTOK_REPOST_BLOCKED, SCHEDULE_IN_PROGRESS, PUBLISH_NOT_READY, VIDEO_NOT_READY","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Refused by a business rule (compliance, account, format…)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After) — e.g. RATE_LIMITED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds"}}},"500":{"description":"Unexpected error — e.g. INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/posts/{id}":{"get":{"operationId":"getPost","summary":"Get a post","description":"Publishing state of a post. `outcomeUnknown: true` means the platform accepted the post but its final state could not be confirmed: check the profile; it is never re-posted automatically.\n\nRequired scope: `read`.","tags":["posts"],"security":[{"apiKey":[]}],"x-required-scope":"read","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"contentId":{"type":"string"},"platform":{"type":"string","enum":["instagram","tiktok"]},"account":{"type":"string"},"status":{"type":"string","enum":["queued","publishing","published","failed"]},"outcomeUnknown":{"type":"boolean","description":"The post may be live although the platform did not confirm it; never re-posted."},"url":{"type":["string","null"]},"errorCode":{"type":["string","null"]},"createdAt":{"type":"string"},"publishedAt":{"type":["string","null"]}},"required":["id","contentId","platform","account","status","outcomeUnknown","url","errorCode","createdAt","publishedAt"],"additionalProperties":false}}}},"400":{"description":"Invalid request — e.g. INVALID_REQUEST, INVALID_SCHEDULE","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, invalid or revoked API key — e.g. API_KEY_INVALID, UNAUTHENTICATED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Scope, role, approval or TikTok consent refusal — e.g. API_SCOPE_FORBIDDEN, FORBIDDEN, ROLE_FORBIDDEN, APPROVAL_REQUIRED, APPROVAL_STALE, TIKTOK_UI_CONSENT_REQUIRED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found (or not in the key's workspace) — e.g. NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited (see Retry-After) — e.g. RATE_LIMITED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds"}}},"500":{"description":"Unexpected error — e.g. INTERNAL_ERROR","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Workspace API key (`vfy_…`), created by the workspace owner."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Stable machine-readable code."},"message":{"type":"string","description":"Message in the language of Accept-Language (tr default)."},"messages":{"type":"object","properties":{"tr":{"type":"string"},"en":{"type":"string"}},"required":["tr","en"],"additionalProperties":false},"reasons":{"type":"array","items":{"type":"string"}}},"required":["code","message","messages"],"additionalProperties":false}},"required":["error"],"additionalProperties":false},"ContentItem":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"string","description":"ISO 8601"},"format":{"type":"string"},"status":{"type":"string","enum":["queued","saved","skipped"]},"hook":{"type":"string"},"slides":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"body":{"type":"string"}},"required":["title"],"additionalProperties":false}},"caption":{"type":"string"},"hashtags":{"type":"array","items":{"type":"string"}},"aiGenerated":{"type":"boolean"},"compliance":{"type":"object","properties":{"status":{"type":"string","enum":["ok","flagged"],"description":"Blocked content is never stored."},"notes":{"type":"array","items":{"type":"string"}}},"required":["status","notes"],"additionalProperties":false},"renderStatus":{"type":"string","enum":["pending","rendering","done","failed"]},"media":{"type":"array","items":{"type":"object","properties":{"aspect":{"type":"string","enum":["9:16","4:5"]},"mime":{"type":"string"},"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"url":{"type":["string","null"]}},"required":["aspect","mime","index","url"],"additionalProperties":false}},"video":{"anyOf":[{"type":"object","properties":{"url":{"type":["string","null"]},"durationSec":{"type":"number"}},"required":["url","durationSec"],"additionalProperties":false},{"type":"null"}]},"scheduledAt":{"type":["string","null"]},"scheduledPlatforms":{"type":"array","items":{"type":"string","enum":["instagram","tiktok"]}},"approval":{"anyOf":[{"type":"string","enum":["approved","pending","stale"]},{"type":"null"}],"description":"Approval state in approval mode; null when the mode is off."}},"required":["id","createdAt","format","status","hook","slides","caption","hashtags","aiGenerated","compliance","renderStatus","media","video","scheduledAt","scheduledPlatforms","approval"],"additionalProperties":false}}}}