{"openapi":"3.1.0","info":{"title":"Brandsoara Integration API","version":"1.0.0","description":"Server-to-server marketing API. One key accesses one company. Review and publish through the Brandsoara web app. All resource responses are uncached. Currency values are company-defined; use a consistent currency."},"servers":[{"url":"/api/v1"}],"components":{"securitySchemes":{"ApiKey":{"type":"http","scheme":"bearer","description":"Server-side API key created in Integrations. Never use in browser/mobile code."}},"schemas":{"Brand":{"type":"object","additionalProperties":false,"required":["business_name","website","description","audience","offer","approved_claims","tone","goal"],"properties":{"business_name":{"type":"string","minLength":1,"maxLength":100},"website":{"type":"string","format":"uri","maxLength":2000},"description":{"type":"string","minLength":20,"maxLength":4000},"audience":{"type":"string","minLength":10,"maxLength":2000},"offer":{"type":"string","minLength":5,"maxLength":2000},"approved_claims":{"type":"string","minLength":5,"maxLength":3000},"tone":{"type":"string","minLength":3,"maxLength":500},"goal":{"type":"string","enum":["leads","bookings","sales","signups","demos"]},"logo_url":{"type":"string","description":"Public HTTPS image URL or empty string."},"primary_color":{"type":"string","pattern":"^(#[0-9a-fA-F]{6})?$"}}},"CampaignInput":{"type":"object","additionalProperties":false,"required":["name","goal","audience","offer","landing_url","platforms","post_count"],"properties":{"name":{"type":"string","minLength":3,"maxLength":120},"goal":{"type":"string","enum":["leads","bookings","sales","signups","demos"]},"audience":{"type":"string","minLength":10,"maxLength":2000},"offer":{"type":"string","minLength":5,"maxLength":2000},"landing_url":{"type":"string","format":"uri","maxLength":2000},"platforms":{"type":"array","minItems":1,"maxItems":4,"items":{"type":"string","enum":["instagram","facebook","linkedin","x"]}},"post_count":{"type":"integer","minimum":1,"maximum":12},"model":{"type":"string","enum":["gpt-4o","gpt-6.1-sol"]}}},"Outcome":{"type":"object","additionalProperties":false,"required":["kind","event_key"],"properties":{"kind":{"type":"string","enum":["lead","booking","sale","signup","demo"]},"event_key":{"type":"string","minLength":1,"maxLength":120,"description":"Stable identifier for this actual outcome; do not reuse for different outcomes."},"post_id":{"type":"string","format":"uuid"},"value":{"type":"number","minimum":0,"maximum":100000000,"default":0,"description":"Sales only. Use a consistent currency for this company."}}},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"request_id":{"type":"string","format":"uuid"}}}}},"Post":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"platform":{"type":"string"},"caption":{"type":"string"},"image_url":{"type":["string","null"]},"status":{"type":"string"},"scheduled_at":{"type":["string","null"]},"published_at":{"type":["string","null"]},"external_id":{"type":["string","null"]}}},"Campaign":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"status":{"type":"string"},"review_path":{"type":"string"},"posts":{"type":"array","items":{"$ref":"#/components/schemas/Post"}}}}}},"paths":{"/health":{"get":{"summary":"Service liveness (not database readiness)","responses":{"200":{"description":"Service process is responding."}}}},"/brand":{"get":{"summary":"Read company brand","description":"Requires brand:read.","security":[{"ApiKey":[]}],"responses":{"200":{"description":"Company brand under data."},"401":{"description":"Invalid, expired, or revoked API key."},"403":{"description":"Insufficient permission."},"422":{"description":"Validation failed."},"429":{"description":"Rate or daily generation limit exceeded. See Retry-After."},"503":{"description":"Service or generation unavailable; use the same idempotency key when retrying."}}},"put":{"summary":"Replace company brand settings","description":"Requires brand:write.","security":[{"ApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Brand"}}}},"responses":{"200":{"description":"Saved brand under data."},"401":{"description":"Invalid, expired, or revoked API key."},"403":{"description":"Insufficient permission."},"422":{"description":"Validation failed."},"429":{"description":"Rate or daily generation limit exceeded. See Retry-After."},"503":{"description":"Service or generation unavailable; use the same idempotency key when retrying."}}}},"/campaigns":{"get":{"summary":"List campaigns","description":"Requires campaigns:read.","security":[{"ApiKey":[]}],"responses":{"200":{"description":"Campaigns under data and a nullable next_cursor."},"401":{"description":"Invalid, expired, or revoked API key."},"403":{"description":"Insufficient permission."},"422":{"description":"Validation failed."},"429":{"description":"Rate or daily generation limit exceeded. See Retry-After."},"503":{"description":"Service or generation unavailable; use the same idempotency key when retrying."}},"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"after","in":"query","schema":{"type":"string","format":"uuid"}}]},"post":{"summary":"Request a campaign and queue draft generation","description":"Requires campaigns:generate.","security":[{"ApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignInput"}}}},"responses":{"200":{"description":"Identical request replayed; same campaign ID."},"202":{"description":"Accepted and durably queued. data contains id, model, review_path, status_url, replayed."},"401":{"description":"Invalid, expired, or revoked API key."},"403":{"description":"Insufficient permission."},"409":{"description":"Idempotency key conflict, company brand not configured, or insufficient company AI credits (credits_unavailable). Identical accepted retries remain available at zero credits."},"422":{"description":"Validation failed."},"429":{"description":"Rate or daily generation limit exceeded. See Retry-After."},"503":{"description":"Service or generation unavailable; use the same idempotency key when retrying."}},"parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,120}$"},"description":"Unique per logical request, shared across a company’s keys; retain for retries."}]}},"/campaigns/{id}":{"get":{"summary":"Read campaign status and posts","description":"Requires campaigns:read.","security":[{"ApiKey":[]}],"responses":{"200":{"description":"Campaign under data."},"401":{"description":"Invalid, expired, or revoked API key."},"403":{"description":"Insufficient permission."},"404":{"description":"Campaign not found in this company."},"422":{"description":"Validation failed."},"429":{"description":"Rate or daily generation limit exceeded. See Retry-After."},"503":{"description":"Service or generation unavailable; use the same idempotency key when retrying."}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}]}},"/campaigns/{id}/results":{"get":{"summary":"Read campaign outcome metrics","description":"Requires campaigns:read.","security":[{"ApiKey":[]}],"responses":{"200":{"description":"data contains clicks, conversions, revenue, and per-channel results."},"401":{"description":"Invalid, expired, or revoked API key."},"403":{"description":"Insufficient permission."},"422":{"description":"Validation failed."},"429":{"description":"Rate or daily generation limit exceeded. See Retry-After."},"503":{"description":"Service or generation unavailable; use the same idempotency key when retrying."}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}]}},"/campaigns/{id}/outcomes":{"post":{"summary":"Record a business outcome","description":"Requires outcomes:write.","security":[{"ApiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Outcome"}}}},"responses":{"200":{"description":"Identical event_key replay accepted; duplicate true."},"201":{"description":"Outcome accepted."},"401":{"description":"Invalid, expired, or revoked API key."},"403":{"description":"Insufficient permission."},"409":{"description":"event_key reused with different outcome data."},"422":{"description":"Validation failed."},"429":{"description":"Rate or daily generation limit exceeded. See Retry-After."},"503":{"description":"Service or generation unavailable; use the same idempotency key when retrying."}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}]}}}}