{"openapi":"3.0.3","info":{"title":"Capci API","version":"1.2.0","description":"Programmatic access to Capci's branded-content generation engine — full parity with the Studio web app. Posts are a single POST returning a PNG; diagrams are a conversational resource (create → refine → export against one opaque id); chart/brand-kit are deterministic (no LLM); carousel produces a multi-slide PDF; animate/batch offload to an async job you poll via /jobs. Requires an active Developer plan.","contact":{"name":"Capci","url":"https://capci.app"}},"servers":[{"url":"https://api.capci.app/api/v1"}],"security":[{"ApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Generate a key from your Capci account (Settings → Developer API). Shown once at creation."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string","description":"Machine-readable error code."},"message":{"type":"string"},"issues":{"type":"array","description":"Present on validation_error — one entry per invalid field.","items":{"type":"object","properties":{"field":{"type":"string"},"message":{"type":"string"}}}}}},"Brand":{"type":"object","properties":{"name":{"type":"string"},"handle":{"type":"string"},"logo":{"type":"string","description":"Data URI or URL."},"colors":{"type":"object","properties":{"primary":{"type":"string"},"secondary":{"type":"string"}}}}},"DiagramType":{"type":"string","enum":["architecture","flowchart","sequence","class","er","mindmap","gantt"],"description":"What kind of diagram to draw. `architecture` = cloud/system topology with real provider icons; `er` = entity-relationship."},"DiagramColors":{"type":"object","description":"Optional palette override (hex). Omitted fields fall back to your brand colors / theme defaults.","properties":{"primary":{"type":"string"},"secondary":{"type":"string"},"background":{"type":"string"}}},"Diagram":{"type":"object","description":"A diagram resource. `image` is the current render; keep `id` to refine or re-export it — diagrams persist permanently, no expiry.","properties":{"id":{"type":"string","description":"Opaque diagram id — pass to /diagrams/{id}/refine and GET /diagrams/{id}."},"type":{"$ref":"#/components/schemas/DiagramType"},"title":{"type":"string"},"size":{"type":"string","enum":["landscape","portrait","square","story"]},"theme":{"type":"string","enum":["light","dark"]},"image":{"type":"string","description":"data:image/png;base64,..."},"note":{"type":"string","description":"Optional short note about what was drawn/changed."}}},"JobStatus":{"type":"object","description":"Async job status. Poll until `status` is `done` or `failed`.","properties":{"status":{"type":"string","enum":["queued","rendering","done","failed"]},"image":{"type":"string","description":"Present only when status=done — data:image/png or image/gif base64."},"error":{"type":"string","description":"Present only when status=failed."}}}},"responses":{"Unauthorized":{"description":"Missing or invalid x-api-key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"PlanRequired":{"description":"API access requires an active Developer plan.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ValidationError":{"description":"Request body failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests — back off and retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InsufficientCredits":{"description":"Account does not have enough credits for this call.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ServiceDown":{"description":"The generation provider is temporarily unavailable — retry shortly.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"paths":{"/posts":{"post":{"summary":"Generate a branded post image","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string","maxLength":4000},"format":{"type":"string","enum":["cover","roadmap","listicle","checklist","resources","comparison","quote","stat","grid","code","poster","announcement","offer","testimonial","event","ad","hiring","showcase","campaign"],"description":"Pin a specific format instead of letting the engine auto-select one from the prompt."},"size":{"type":"string","enum":["portrait","story","landscape"],"default":"portrait"},"theme":{"type":"string","default":"light"},"style":{"type":"string","default":"glass"},"layout":{"type":"string"},"brand":{"$ref":"#/components/schemas/Brand"},"unbranded":{"type":"boolean","description":"Strip the brand mark (name/logo) for this render only — colors are kept. Does not touch your saved brand profile."}}}}}},"responses":{"200":{"description":"Generated image + planned content.","content":{"application/json":{"schema":{"type":"object","properties":{"pattern":{"type":"string"},"content":{"type":"object"},"image":{"type":"string","description":"data:image/png;base64,..."},"usage":{"type":"object"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/PlanRequired"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/ServiceDown"}}}},"/diagrams":{"post":{"summary":"Create a diagram from a plain-English prompt","description":"Generates a branded diagram and returns it with an opaque `id`. Keep the id to iterate: POST /diagrams/{id}/refine applies follow-up instructions, GET /diagrams/{id} re-exports the current state in any theme/size for free. Diagrams persist permanently, no expiry.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string","maxLength":4000,"description":"Describe the system, flow, schema, or plan in plain English."},"type":{"$ref":"#/components/schemas/DiagramType"},"size":{"type":"string","enum":["landscape","portrait","square","story"],"description":"Omit to use your saved Developer default (Account → Developer), else landscape."},"theme":{"type":"string","enum":["light","dark"],"description":"Omit to use your saved Developer default, else light."},"style":{"type":"string","enum":["glass","flat","blueprint","editorial"],"default":"glass","description":"Visual skin — architecture diagrams only."},"nodeStyle":{"type":"string","enum":["structured","freeform"],"description":"Architecture only: structured = boxed pills, freeform = icon-only with no boxes. Omit to use your saved Developer default, else structured."},"title":{"type":"string","maxLength":80,"description":"Optional fixed title; omit to let the engine write one."},"transparent":{"type":"boolean","description":"Render with a transparent background. Omit to use your saved Developer default, else false."},"colors":{"$ref":"#/components/schemas/DiagramColors"},"brand":{"$ref":"#/components/schemas/Brand","description":"Per-request brand override — render this diagram for a different brand without changing your saved profile. Persists on this diagram's refines too."},"unbranded":{"type":"boolean","description":"Strip the brand mark (name/logo) for this render only — colors are kept. Does not touch your saved brand profile."}}}}}},"responses":{"200":{"description":"The new diagram.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Diagram"}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/PlanRequired"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/ServiceDown"}}}},"/diagrams/{id}":{"get":{"summary":"Re-render / export the current diagram state (free)","description":"No generation charge — use it to export the same diagram in a different theme, size, or as a raw PNG.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"theme","in":"query","schema":{"type":"string","enum":["light","dark"]}},{"name":"size","in":"query","schema":{"type":"string","enum":["landscape","portrait","square","story"]}},{"name":"transparent","in":"query","schema":{"type":"string","enum":["1","true","0","false"]}},{"name":"format","in":"query","schema":{"type":"string","enum":["png"]},"description":"Set to `png` to receive the raw image bytes instead of JSON."}],"responses":{"200":{"description":"Current render (JSON by default, image/png when format=png).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Diagram"}},"image/png":{"schema":{"type":"string","format":"binary"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Diagram not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"summary":"Rename a diagram (no LLM, free)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["title"],"properties":{"title":{"type":"string","maxLength":80,"description":"Empty string clears the title."}}}}}},"responses":{"200":{"description":"The renamed diagram.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Diagram"}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"description":"Diagram not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/diagrams/{id}/refine":{"post":{"summary":"Refine a diagram — content edit (with prompt) or free restyle (without)","description":"`prompt` is OPTIONAL here, not required. WITH a prompt: a conversational content edit — e.g. \"add a cache between the API and the database\", \"group the data services\". WITHOUT one: a free, instant restyle applying only the display fields below (theme/size/transparent/colors/nodeStyle) — no generation call spent, no wait. The diagram keeps its full edit history server-side either way.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"prompt":{"type":"string","maxLength":4000,"description":"What to change, in plain English. Omit for a free restyle of the display fields below only."},"size":{"type":"string","enum":["landscape","portrait","square","story"]},"theme":{"type":"string","enum":["light","dark"]},"nodeStyle":{"type":"string","enum":["structured","freeform"],"description":"Architecture only: boxed pills vs icon-only."},"title":{"type":"string","maxLength":80},"transparent":{"type":"boolean"},"colors":{"$ref":"#/components/schemas/DiagramColors"},"brand":{"$ref":"#/components/schemas/Brand"},"unbranded":{"type":"boolean","description":"Strip the brand mark for this render only."}}}}}},"responses":{"200":{"description":"The updated diagram.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Diagram"}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/PlanRequired"},"404":{"description":"Diagram not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/ServiceDown"}}}},"/brand":{"post":{"summary":"Set the default brand profile applied to every generation","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Brand"}}}},"responses":{"200":{"description":"Updated brand profile.","content":{"application/json":{"schema":{"type":"object","properties":{"brand":{"$ref":"#/components/schemas/Brand"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/chart":{"post":{"summary":"Render a data-driven chart/KPI image (deterministic, no LLM)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["chartType"],"properties":{"chartType":{"type":"string","enum":["bar","line","donut","kpi"]},"title":{"type":"string"},"subtitle":{"type":"string"},"unit":{"type":"string"},"series":{"type":"array","description":"bar/line: [{label, value}, ...]"},"slices":{"type":"array","description":"donut: [{label, value}, ...]"},"tiles":{"type":"array","description":"kpi: [{value, label}, ...]"},"theme":{"type":"string","enum":["light","dark"],"default":"light"},"style":{"type":"string","default":"glass"},"size":{"type":"string","enum":["portrait","story","landscape","square"],"default":"portrait"},"palette":{"type":"object","properties":{"primary":{"type":"string"},"secondary":{"type":"string"},"bg":{"type":"string"}}},"brand":{"$ref":"#/components/schemas/Brand"}}}}}},"responses":{"200":{"description":"Rendered chart.","content":{"application/json":{"schema":{"type":"object","properties":{"pattern":{"type":"string"},"content":{"type":"object"},"image":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/PlanRequired"}}}},"/render":{"post":{"summary":"Re-render content you already hold — reskin/resize, no LLM, free","description":"Take the `content` returned by a prior /posts or /chart call, tweak it or change size/theme/brand, and get a fresh render without spending another plan call.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["content"],"properties":{"format":{"type":"string","enum":["cover","roadmap","listicle","checklist","resources","comparison","quote","stat","grid","code","poster","announcement","offer","testimonial","event","ad","hiring","showcase","campaign","chart"],"description":"Which pattern this content belongs to (also accepts `pattern` for round-tripping a /posts response as-is)."},"content":{"type":"object","description":"A previously-returned `content` object, optionally edited."},"size":{"type":"string","enum":["portrait","story","landscape","square"]},"theme":{"type":"string","enum":["light","dark"]},"brand":{"$ref":"#/components/schemas/Brand"}}}}}},"responses":{"200":{"description":"Re-rendered image.","content":{"application/json":{"schema":{"type":"object","properties":{"pattern":{"type":"string"},"content":{"type":"object"},"image":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/carousel":{"post":{"summary":"Generate a multi-slide swipeable carousel + PDF","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string","maxLength":4000},"theme":{"type":"string","enum":["light","dark"],"default":"light"},"style":{"type":"string","default":"glass"},"size":{"type":"string","enum":["portrait","square"],"default":"portrait","description":"Carousels only support portrait or square."},"brand":{"$ref":"#/components/schemas/Brand"}}}}}},"responses":{"200":{"description":"Slides + assembled PDF.","content":{"application/json":{"schema":{"type":"object","properties":{"pattern":{"type":"string"},"slides":{"type":"array","items":{"type":"string"}},"pdf":{"type":"string"},"content":{"type":"object"},"usage":{"type":"object"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/PlanRequired"},"503":{"$ref":"#/components/responses/ServiceDown"}}}},"/carousel/render":{"post":{"summary":"Re-render edited carousel slides — no LLM, free","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["slides"],"properties":{"slides":{"type":"array","items":{"type":"object"},"description":"The (possibly edited) slide array from a prior /carousel call."},"theme":{"type":"string","enum":["light","dark"],"default":"light"},"style":{"type":"string","default":"glass"},"size":{"type":"string","enum":["portrait","square"],"default":"portrait"},"brand":{"$ref":"#/components/schemas/Brand"}}}}}},"responses":{"200":{"description":"Re-rendered slides + PDF.","content":{"application/json":{"schema":{"type":"object","properties":{"pattern":{"type":"string"},"slides":{"type":"array","items":{"type":"string"}},"pdf":{"type":"string"},"content":{"type":"object"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/animate":{"post":{"summary":"Turn content you already hold — or an existing diagram — into a reveal GIF","description":"Offloads to an async job (returns a jobId to poll via /jobs/{id}) when the render worker is configured, otherwise renders inline and returns the GIF directly. Two mutually exclusive input modes: (1) `format` + `content` for a prior /posts or /chart response, or (2) `diagramId` alone to animate an existing diagram session from /diagrams — same black-box contract, the diagram's authored source is never echoed back.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"format":{"type":"string","enum":["cover","roadmap","listicle","checklist","resources","comparison","quote","stat","grid","code","poster","announcement","offer","testimonial","event","ad","hiring","showcase","campaign","chart"],"description":"Required with `content`; omit when using `diagramId`."},"content":{"type":"object","description":"Required with `format`; omit when using `diagramId`."},"diagramId":{"type":"string","description":"Animate an existing diagram from /diagrams instead of raw content. Cannot be combined with `format`/`content`."},"anim":{"type":"string","enum":["flow","build"],"default":"flow"},"speed":{"type":"string","enum":["slow","normal","fast"],"default":"normal"},"size":{"type":"string","enum":["portrait","story","landscape","square"]},"brand":{"$ref":"#/components/schemas/Brand"}}}}}},"responses":{"200":{"description":"Inline GIF (worker not configured).","content":{"application/json":{"schema":{"type":"object","properties":{"gif":{"type":"string"}}}}}},"202":{"description":"Offloaded — poll GET /jobs/{jobId}.","content":{"application/json":{"schema":{"type":"object","properties":{"async":{"type":"boolean"},"jobId":{"type":"string"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/PlanRequired"},"404":{"description":"diagramId not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/batch":{"post":{"summary":"Bulk / data-merge — one pattern + N items → N async renders","description":"Requires the render worker to be configured (503 batch_unavailable otherwise, no inline fallback). Billed once for the whole valid batch.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["items"],"properties":{"format":{"type":"string","enum":["cover","roadmap","listicle","checklist","resources","comparison","quote","stat","grid","code","poster","announcement","offer","testimonial","event","ad","hiring","showcase","campaign","chart"]},"base":{"type":"object","description":"Fields shared by every item."},"items":{"type":"array","maxItems":200,"items":{"type":"object"},"description":"Per-item overrides merged onto `base`."},"theme":{"type":"string","enum":["light","dark"],"default":"light"},"style":{"type":"string","default":"glass"},"size":{"type":"string","enum":["portrait","story","landscape","square"],"default":"portrait"},"brand":{"$ref":"#/components/schemas/Brand"}}}}}},"responses":{"202":{"description":"Batch accepted — poll GET /jobs/batch/{batchId}.","content":{"application/json":{"schema":{"type":"object","properties":{"batchId":{"type":"string"},"total":{"type":"integer"},"skipped":{"type":"array"},"jobIds":{"type":"array","items":{"type":"string"}}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/PlanRequired"},"503":{"description":"Render worker not configured.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/jobs/{id}":{"get":{"summary":"Poll a single async render job (from /animate)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Job status.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobStatus"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Not your job.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Job not found or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/jobs/batch/{batchId}":{"get":{"summary":"Poll a batch of async render jobs (from /batch)","parameters":[{"name":"batchId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Aggregate batch status — `images` (in original order) once every item has settled.","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["pending","done"]},"done":{"type":"integer"},"failed":{"type":"integer"},"total":{"type":"integer"},"images":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer"},"image":{"type":"string"}}}},"failures":{"type":"array"},"skipped":{"type":"array"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Not your batch.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Batch not found or expired.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/brandkit":{"get":{"summary":"List available brand-kit assets (banners, covers, cards)","responses":{"200":{"description":"Asset catalog.","content":{"application/json":{"schema":{"type":"object","properties":{"assets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"group":{"type":"string"},"title":{"type":"string"},"w":{"type":"integer"},"h":{"type":"integer"}}}}}}}}}}}},"/brandkit/render":{"post":{"summary":"Render a brand-kit asset from your saved brand (deterministic, no LLM, free)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["id"],"properties":{"id":{"type":"string","description":"Asset id from GET /brandkit."},"theme":{"type":"string"},"tagline":{"type":"string","description":"Overrides your saved brand tagline for this render."},"pills":{"type":"array","items":{"type":"string"},"maxItems":10}}}}}},"responses":{"200":{"description":"Rendered asset.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"width":{"type":"integer"},"height":{"type":"integer"},"image":{"type":"string"}}}}}},"400":{"description":"Unknown asset id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/logo":{"post":{"summary":"Generate a logo mark from your saved brand (pay-per-use)","description":"The one AI-image action left in Capci proper. First logo is free; every one after costs credits (25/logo), independent of plan tier.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string","maxLength":2000,"description":"What the mark should depict, e.g. \"a minimalist mountain peak\"."}}}}}},"responses":{"200":{"description":"Generated logo (transparent PNG).","content":{"application/json":{"schema":{"type":"object","properties":{"image":{"type":"string"},"freeLeft":{"type":"integer"},"credits":{"type":"integer"}}}}}},"400":{"$ref":"#/components/responses/ValidationError"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/InsufficientCredits"},"503":{"$ref":"#/components/responses/ServiceDown"}}}},"/schemas":{"get":{"summary":"List every render pattern and its content schema","description":"Use this to discover which `pattern` values exist and what shape of `content` each expects before calling /posts with a specific layout.","responses":{"200":{"description":"Pattern catalog.","content":{"application/json":{"schema":{"type":"object","properties":{"patterns":{"type":"array","items":{"type":"object"}}}}}}}}}}}}