{"openapi":"3.1.0","info":{"title":"Sendinel API","version":"1.0","description":"Sendinel public REST API.\nAuthenticate with an API key. Approved first-party integrations may use a\nshort-lived scoped JWT:\nAuthorization: Bearer <token>\n"},"servers":[{"url":"https://sendinel.ai/api/v1","description":"Production"},{"url":"http://localhost:3000/api/v1","description":"Local development"}],"security":[{"ApiKeyAuth":[]},{"OAuth2":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API Key or scoped JWT"},"OAuth2":{"type":"oauth2","description":"OAuth 2.0 authorization code flow with PKCE (RFC 7636).\nDiscovery: https://sendinel.ai/.well-known/oauth-authorization-server\nResource metadata: https://sendinel.ai/.well-known/oauth-protected-resource\n","flows":{"authorizationCode":{"authorizationUrl":"https://sendinel.ai/oauth/authorize","tokenUrl":"https://sendinel.ai/oauth/token","refreshUrl":"https://sendinel.ai/oauth/token","scopes":{"contacts":"Contacts, lists, and email logs","campaigns":"Campaign management and sending","analytics":"Stats, deliverability health, and reporting","segments":"Audience segments","drafts":"AI drafts","sites":"Sending sites","read":"Read-only access","write":"Read and write access","admin":"Full administrative access"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{}}}}},"AiContextEvent":{"type":"object","required":["source","workspace_id","project_id","event_type","title","detail","idempotency_key"],"properties":{"source":{"type":"string","enum":["synchronex"]},"workspace_id":{"type":"string"},"project_id":{"type":"string","format":"uuid"},"event_type":{"type":"string","enum":["strategic_segment_intent","business_priority","customer_journey_signal","campaign_opportunity","risk_signal"]},"title":{"type":"string"},"detail":{"type":"string"},"evidence":{"type":"object","additionalProperties":true},"suggested_actions":{"type":"array","items":{"type":"object","additionalProperties":true}},"idempotency_key":{"type":"string"}}},"Site":{"type":"object","required":["id","name","slug","from_email"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":"string"},"from_name":{"type":"string"},"from_email":{"type":"string","format":"email"},"sending_domain":{"type":"string"},"website_url":{"type":"string","format":"uri","nullable":true},"active":{"type":"boolean"}}},"Contact":{"type":"object","required":["id","email"],"properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string","format":"email"},"first_name":{"type":"string","nullable":true},"last_name":{"type":"string","nullable":true},"company_id":{"type":"string","format":"uuid","nullable":true},"company":{"type":"object","nullable":true,"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"domain":{"type":"string","nullable":true}}},"tags":{"type":"array","items":{"type":"string"}},"lifecycle_stage":{"type":"string","nullable":true},"global_unsubscribed":{"type":"boolean"}}},"SegmentRule":{"type":"object","additionalProperties":true},"Segment":{"type":"object","required":["id","name","rules"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"rules":{"$ref":"#/components/schemas/SegmentRule"}}},"Template":{"type":"object","required":["id","name","category","brief_template"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"category":{"type":"string"},"description":{"type":"string"},"subject_hint":{"type":"string"},"brief_template":{"type":"string"},"body_html":{"type":"string","nullable":true},"tags":{"type":"array","items":{"type":"string"}}}},"CampaignStep":{"type":"object","required":["id","campaign_id","step_order","subject"],"properties":{"id":{"type":"string","format":"uuid"},"campaign_id":{"type":"string","format":"uuid"},"step_order":{"type":"integer"},"subject":{"type":"string"},"body_html":{"type":"string","nullable":true},"body_text":{"type":"string","nullable":true},"delay_days":{"type":"integer"},"delay_hours":{"type":"integer"}}},"Campaign":{"type":"object","required":["id","site_id","name","type","status"],"properties":{"id":{"type":"string","format":"uuid"},"site_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"type":{"type":"string","enum":["sequence","broadcast","triggered"]},"status":{"type":"string","enum":["draft","active","paused","archived"]},"description":{"type":"string","nullable":true}}},"EventRequest":{"type":"object","required":["event"],"properties":{"event":{"type":"string"},"email":{"type":"string","format":"email"},"user_id":{"type":"string"},"properties":{"type":"object","additionalProperties":true}}},"TriggerRequest":{"type":"object","required":["event"],"properties":{"event":{"type":"string"},"email":{"type":"string","format":"email"},"properties":{"type":"object","additionalProperties":true}}},"SendRequest":{"type":"object","required":["site_id","to","subject"],"properties":{"site_id":{"type":"string","format":"uuid"},"to":{"type":"string","format":"email"},"subject":{"type":"string"},"html":{"type":"string"},"text":{"type":"string"}}},"ConversionRequest":{"type":"object","required":["event"],"properties":{"event":{"type":"string"},"email":{"type":"string","format":"email"},"value":{"type":"number"},"properties":{"type":"object","additionalProperties":true}}},"SynchronexProvisionRequest":{"type":"object","required":["owner_email","bundle","synchronex_user_id"],"properties":{"owner_email":{"type":"string","format":"email"},"bundle":{"type":"string","enum":["synchronex_pro"]},"synchronex_user_id":{"type":"string"},"synchronex_team_id":{"type":"string","description":"Synchronex team to link to the provisioned Sendinel organization."},"synchronex_account_id":{"type":"string"},"synchronex_account_no":{"type":"string"},"synchronex_workspace_id":{"type":"string"},"synchronex_workspace_name":{"type":"string"},"synchronex_brand_id":{"type":"string"},"synchronex_brand_name":{"type":"string"},"period_start":{"type":"string","format":"date-time"},"period_end":{"type":"string","format":"date-time"},"approval_mode":{"type":"string","enum":["standard","autonomous"],"description":"Optional. Written to email.projects.approval_mode on create. Omitted means standard, matching the column default. On an idempotent re-provision the stored value changes only when this field is sent."}}},"SynchronexProvisionResponse":{"type":"object","required":["project_id","org_id","project_api_key","send_cap","cap_context"],"properties":{"project_id":{"type":"string","format":"uuid"},"org_id":{"type":"string","format":"uuid","description":"Persist this as the linked Sendinel organization for the Synchronex team."},"project_api_key":{"type":"string","nullable":true,"description":"Returned only when a new project key is created."},"send_cap":{"type":"integer","example":10000},"cap_context":{"type":"string","enum":["synchronex_bundle"]},"approval_mode":{"type":"string","enum":["standard","autonomous"],"description":"Present when this call wrote approval_mode. Omitted when an idempotent call left the stored value unchanged."}}},"ServiceProjectLookupResponse":{"type":"object","required":["project_id","org_id","project_name","plan","sites"],"properties":{"project_id":{"type":"string","format":"uuid"},"org_id":{"type":"string","format":"uuid"},"project_name":{"type":"string"},"plan":{"type":"string"},"sites":{"type":"array","items":{"type":"object","required":["site_id","site_name"],"properties":{"site_id":{"type":"string","format":"uuid"},"site_name":{"type":"string"},"sending_domain":{"type":"string","nullable":true},"website_url":{"type":"string","format":"uri","nullable":true}}}},"synchronex_team_id":{"type":"string","nullable":true},"synchronex_workspace_id":{"type":"string","nullable":true},"synchronex_workspace_name":{"type":"string","nullable":true},"synchronex_brand_id":{"type":"string","nullable":true},"synchronex_brand_name":{"type":"string","nullable":true}}}}},"paths":{"/approvals":{"get":{"summary":"List approval requests","parameters":[{"name":"status","in":"query","schema":{"type":"string","default":"pending"}},{"name":"limit","in":"query","schema":{"type":"integer","maximum":100}}],"responses":{"200":{"description":"Approval requests"}}}},"/approvals/{id}":{"get":{"summary":"Get approval status without consuming it","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Approval status"}}}},"/platform-connections/{id}/events":{"get":{"summary":"List events for a platform connection","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","schema":{"type":"integer","maximum":100}}],"responses":{"200":{"description":"Platform events"}}}},"/platform-connections":{"get":{"summary":"List platform connections","responses":{"200":{"description":"Platform connections"}}},"post":{"summary":"Create or update a platform connection","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["platform","credentials"],"properties":{"platform":{"type":"string"},"display_name":{"type":"string"},"credentials":{"type":"object","additionalProperties":true},"config":{"type":"object","additionalProperties":true}}}}}},"responses":{"201":{"description":"Platform connection created or updated"}}}},"/platform-connections/{id}":{"delete":{"summary":"Disconnect a platform connection","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Platform connection removed"}}}},"/platform-connections/{id}/import":{"post":{"summary":"Trigger a contact/data import for a platform connection","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"artifact_types":{"type":"array","items":{"type":"string","enum":["contacts","templates","campaigns","suppressions","segments","enrollments","newsletters","broadcasts","transactional","snippets","collections","messages"]}},"options":{"type":"object","additionalProperties":true},"field_mapping":{"type":"object","additionalProperties":{"type":"string"}}}}}}},"responses":{"200":{"description":"Import job created or updated"},"404":{"description":"Platform connection not found"},"409":{"description":"Platform connection is inactive"},"422":{"description":"Platform does not support imports, or stored credentials are invalid"}}}},"/platform-connections/{id}/triggers":{"post":{"summary":"Configure platform trigger mappings","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["triggers"],"properties":{"triggers":{"type":"array","items":{"type":"object","additionalProperties":true}}}}}}},"responses":{"200":{"description":"Trigger mappings updated"}}}},"/dmarc/status":{"get":{"summary":"Get DMARC inbound status, Cloudflare setup, and monitor state","parameters":[{"name":"site_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"DMARC status"}}}},"/dmarc/publish-cloudflare":{"post":{"summary":"Publish or preview the merged DMARC record in the organization's connected Cloudflare zone","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["domain"],"properties":{"domain":{"type":"string"},"dry_run":{"type":"boolean"}}}}}},"responses":{"200":{"description":"DMARC record published or previewed"}}}},"/dmarc/monitors":{"get":{"summary":"List DMARC monitors","responses":{"200":{"description":"DMARC monitors"}}},"post":{"summary":"Enable DMARC monitoring for a domain","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["domain"],"properties":{"domain":{"type":"string"}}}}}},"responses":{"201":{"description":"DMARC monitor enabled"}}}},"/dmarc/monitors/{domain}":{"get":{"summary":"Get DMARC monitor detail","parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"DMARC monitor detail"}}},"delete":{"summary":"Disable a DMARC monitor","parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"DMARC monitor disabled"}}}},"/migrations/campaigns":{"post":{"summary":"Start a campaign migration import job","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["provider"],"properties":{"provider":{"type":"string"},"api_key":{"type":"string"},"oauth_token":{"type":"string"},"platform_connection_id":{"type":"string","format":"uuid"}}}}}},"responses":{"201":{"description":"Migration job created"}}}},"/migrations":{"get":{"summary":"List migration jobs","responses":{"200":{"description":"Migration jobs"}}}},"/migrations/{id}":{"get":{"summary":"Get a migration job and log history","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Migration job status"}}}},"/org/usage":{"get":{"summary":"Get organization usage","responses":{"200":{"description":"Organization usage"}}}},"/org/members":{"get":{"summary":"List organization members","responses":{"200":{"description":"Organization members"}}},"post":{"summary":"Add, update, or invite an organization member","description":"The requested organization must own the authenticated project.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["org_id","email","role"],"properties":{"org_id":{"type":"string","format":"uuid"},"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["admin","member","viewer"]}}}}}},"responses":{"200":{"description":"Existing member or invitation returned"},"201":{"description":"Member or invitation created"},"403":{"description":"Project/organization mismatch or plan member limit reached"}}}},"/org/members/{userId}":{"delete":{"summary":"Remove an organization member","description":"Organization owners cannot be removed through this endpoint.","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["org_id"],"properties":{"org_id":{"type":"string","format":"uuid"}}}}}},"responses":{"200":{"description":"Member removed or already absent"},"403":{"description":"Project/organization mismatch or owner removal attempted"}}}},"/org/invitations":{"post":{"summary":"Create or dedupe an organization invitation","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["admin","member"]}}}}}},"responses":{"201":{"description":"Invitation created"}}}},"/org/plans":{"get":{"summary":"List available plan limits","responses":{"200":{"description":"Plan limits"}}}},"/automations":{"get":{"summary":"List automation sources","parameters":[{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":200}},{"in":"query","name":"status","schema":{"type":"string","enum":["active","paused"]}},{"in":"query","name":"site_id","schema":{"type":"string","format":"uuid"}},{"in":"query","name":"source_type","schema":{"type":"string","enum":["rss","webhook","schedule","github"]}},{"in":"query","name":"cursor","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Automation list with cursor pagination"}}},"post":{"summary":"Create an automation source","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","source_type","site_id"],"properties":{"name":{"type":"string"},"source_type":{"type":"string","enum":["rss","webhook","schedule","github"]},"site_id":{"type":"string","format":"uuid"},"format":{"type":"string"},"feed_url":{"type":"string"},"schedule_cron":{"type":"string"},"brief_template":{"type":"string"},"max_words":{"type":"integer"},"tone_override":{"type":"string"},"cta_url":{"type":"string"},"cta_text":{"type":"string"},"github_repo":{"type":"string"},"github_label_filter":{"type":"string"}}}}}},"responses":{"201":{"description":"Automation created"}}}},"/automations/{id}":{"get":{"summary":"Get an automation source","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Automation source detail"}}},"patch":{"summary":"Update an automation source","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"200":{"description":"Automation source updated"}}},"delete":{"summary":"Delete an automation source","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Automation deleted"}}}},"/automations/{id}/history":{"get":{"summary":"List automation trigger history","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":200}},{"in":"query","name":"status","schema":{"type":"string","enum":["pending","drafted","approved","sent","skipped"]}},{"in":"query","name":"start_date","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"end_date","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"cursor","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Automation history with cursor pagination"}}}},"/automations/{id}/preview":{"get":{"summary":"Preview recent automation source items","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Automation preview items"}}}},"/automations/enroll":{"post":{"summary":"Enroll a subscriber into a triggered campaign","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["subscriber_id"],"properties":{"subscriber_id":{"type":"string","format":"uuid"},"campaign_id":{"type":"string","format":"uuid"},"site_id":{"type":"string","format":"uuid"},"trigger_event_name":{"type":"string"},"trigger_tag":{"type":"string"},"next_send_at":{"type":"string","format":"date-time"},"context":{"type":"object","additionalProperties":true}}}}}},"responses":{"200":{"description":"Enrollment result"}}}},"/automations/{id}/pause":{"post":{"summary":"Pause an automation source","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Automation paused"}}}},"/automations/{id}/resume":{"post":{"summary":"Resume an automation source","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Automation resumed"}}}},"/automations/{id}/trigger":{"post":{"summary":"Trigger automation draft generation","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Trigger result"}}}},"/social-posts/adapt":{"post":{"summary":"Adapt content into social posts","responses":{"200":{"description":"Adapted social copy"}}}},"/advisor/subject-lines":{"post":{"summary":"Generate or create subject line variants","responses":{"200":{"description":"Subject line recommendations"}}}},"/advisor/content-topics":{"get":{"summary":"Recommend content topics","responses":{"200":{"description":"Content topic recommendations"}}}},"/advisor/campaign/{id}":{"get":{"summary":"Get campaign advisor recommendations","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Campaign advisor result"}}}},"/advisor/send-time":{"get":{"summary":"Recommend send times","responses":{"200":{"description":"Send time recommendations"}}}},"/advisor/recovery-plan":{"get":{"summary":"Recommend deliverability recovery actions","responses":{"200":{"description":"Recovery plan"}}}},"/advisor/portfolio-analysis":{"get":{"summary":"Analyze portfolio email performance","responses":{"200":{"description":"Portfolio analysis"}}}},"/sites/{id}/warmup":{"patch":{"summary":"Update, pause, or resume site warmup schedule","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Warmup schedule"}}}},"/sites/{id}/dnsbl":{"get":{"summary":"Get latest DNSBL snapshots for a site","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"DNSBL snapshots"}}}},"/sites/{id}/send-queue/dlq":{"get":{"summary":"List dead-letter queue entries for a site project","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"unresolved_only","in":"query","schema":{"type":"boolean","default":true}}],"responses":{"200":{"description":"Dead-letter queue entries"}}}},"/sites/{id}/enrollment-dlq":{"get":{"summary":"List dead-lettered campaign enrollments for a site","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"unresolved_only","in":"query","schema":{"type":"boolean","default":true}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":50,"default":20}}],"responses":{"200":{"description":"Enrollment dead-letter queue entries"}}}},"/sites/{id}/enrollment-dlq/replay":{"post":{"summary":"Replay dead-lettered campaign enrollments for a site","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"max_count":{"type":"integer","minimum":1,"maximum":1000,"default":100},"failed_after":{"type":"string","format":"date-time"}}}}}},"responses":{"200":{"description":"Replayed and skipped enrollment counts"}}}},"/sites/{id}/send-queue":{"get":{"summary":"Get queued and scheduled send status for a site","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Queue summary by channel, audience size, and next send time"}}}},"/sites/{id}/send-queue/cancel":{"post":{"summary":"Cancel unclaimed queued sends for a site","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"campaign_id":{"type":"string","format":"uuid"},"created_before":{"type":"string","format":"date-time"}}}}}},"responses":{"200":{"description":"Queue rows cancelled"}}}},"/sites/{id}/send-queue/retry":{"post":{"summary":"Make unclaimed retry rows immediately eligible for a site","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"campaign_id":{"type":"string","format":"uuid"},"queue_ids":{"type":"array","minItems":1,"maxItems":200,"items":{"type":"string","format":"uuid"}}}}}}},"responses":{"200":{"description":"Queue rows made immediately eligible"}}}},"/sites/{id}/send-queue/rows":{"get":{"summary":"List individual queued sends with retry details for a site","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"campaign_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","description":"Comma-separated queue statuses","schema":{"type":"string","example":"pending,retry"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"Individual queued sends with errors and next retry times"}}}},"/sites/{id}/send-queue/resend":{"post":{"summary":"Requeue DLQ sends for a site","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"max_count":{"type":"integer","minimum":1,"maximum":1000},"failed_after":{"type":"string","format":"date-time"}}}}}},"responses":{"200":{"description":"DLQ sends requeued"}}}},"/sites/{id}/send-queue/throttle":{"post":{"summary":"Set or clear a per-site send throttle","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["send_rate_limit"],"properties":{"send_rate_limit":{"nullable":true,"oneOf":[{"type":"integer","minimum":1,"maximum":100000},{"type":"null"}]}}}}}},"responses":{"200":{"description":"Throttle updated"}}}},"/sites/{id}/smart-schedule":{"get":{"summary":"Recommend best send windows for a site","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"campaign_type","in":"query","schema":{"type":"string","enum":["email","sms","social"]}}],"responses":{"200":{"description":"Recommended send windows from historical audience activity"}}}},"/gdpr":{"post":{"summary":"Delete a contact and related data under GDPR workflow","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["contact_id"],"properties":{"contact_id":{"type":"string","format":"uuid"},"reason":{"type":"string"}}}}}},"responses":{"200":{"description":"GDPR delete result"}}}},"/identify":{"post":{"summary":"Identify or upsert a contact profile","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}}},"responses":{"200":{"description":"Contact identified"},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/events":{"get":{"summary":"List recorded contact events","parameters":[{"in":"query","name":"contact_email","schema":{"type":"string","format":"email"}},{"in":"query","name":"event_type","schema":{"type":"string"}},{"in":"query","name":"limit","schema":{"type":"integer","maximum":500}}],"responses":{"200":{"description":"Events"}}},"post":{"summary":"Record a custom event","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventRequest"}}}},"responses":{"202":{"description":"Event accepted"}}}},"/trigger":{"post":{"summary":"Trigger automation evaluation for a contact event","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TriggerRequest"}}}},"responses":{"202":{"description":"Trigger accepted"}}}},"/send":{"post":{"summary":"Send a transactional email","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendRequest"}}}},"responses":{"202":{"description":"Send accepted"}}}},"/sms/send":{"post":{"summary":"Send an SMS message","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["to_number","body"],"properties":{"to_number":{"type":"string","example":"+14155551234"},"body":{"type":"string"},"site_id":{"type":"string","format":"uuid"},"contact_id":{"type":"string","format":"uuid"},"campaign_id":{"type":"string","format":"uuid"},"campaign_step_id":{"type":"string","format":"uuid"},"classification":{"type":"string","enum":["marketing","transactional","conversational"]}}}}}},"responses":{"200":{"description":"SMS sent"}}}},"/sms/consent":{"post":{"summary":"Set contact SMS consent (opt-in/opt-out)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["contact_id"],"properties":{"contact_id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["opt_in","opt_out"],"default":"opt_in"},"source":{"type":"string","default":"api"},"consent_text":{"type":"string","nullable":true}}}}}},"responses":{"200":{"description":"Consent saved"}}}},"/sms":{"post":{"summary":"Send an SMS message (alias of /sms/send)","responses":{"200":{"description":"SMS sent"}}}},"/sms/campaigns":{"post":{"summary":"Create an SMS campaign","responses":{"201":{"description":"SMS campaign created"}}}},"/sms/log":{"get":{"summary":"List SMS delivery log rows","responses":{"200":{"description":"SMS log"}}}},"/sms/inbound":{"get":{"summary":"List inbound SMS replies","responses":{"200":{"description":"Inbound SMS replies"}}}},"/sms/provider":{"get":{"summary":"Get default SMS provider status","responses":{"200":{"description":"SMS provider status"}}}},"/sms/cost-preview":{"get":{"summary":"Estimate SMS send cost","parameters":[{"in":"query","name":"body","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"SMS cost estimate"}}}},"/provision":{"post":{"summary":"Provision an account-scoped messaging channel","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"channel":{"type":"string","enum":["sms","whatsapp","telegram"],"default":"sms"},"provider":{"type":"string","enum":["telnyx","twilio","meta","telegram"]},"friendly_name":{"type":"string"},"area_code":{"type":"string","description":"3-digit US area code for managed SMS provisioning"},"phone_number":{"type":"string","description":"Required for Twilio BYO when channel=sms"},"credentials":{"type":"object","additionalProperties":true,"description":"Provider-specific BYO credentials:\n- Twilio SMS: account_sid, auth_token\n- WhatsApp Meta: access_token, phone_number_id, business_account_id?, verify_token?, app_secret?\n- Telegram: bot_token, bot_username?\n"}}}}}},"responses":{"200":{"description":"Existing active provision returned idempotently"},"201":{"description":"Channel provisioned"}}}},"/provision/{channel_handle}":{"delete":{"summary":"Deprovision an account-scoped messaging channel","parameters":[{"in":"path","name":"channel_handle","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Channel deprovisioned"}}}},"/channels/send":{"post":{"summary":"Send an outbound channel message through the account-scoped transport","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["accountId","channel","to","body","clientMessageId"],"properties":{"accountId":{"type":"string","format":"uuid","channel":{"type":"string","enum":["sms","whatsapp","telegram"]}},"to":{"type":"object","description":"Exactly one of contactId, handle, or broadcastListId","properties":{"contactId":{"type":"string"},"handle":{"type":"string"},"broadcastListId":{"type":"string"}}},"body":{"type":"string"},"clientMessageId":{"type":"string"},"attachments":{"type":"array","description":"Optional for SMS. WhatsApp and Telegram attachment delivery is not implemented yet.","items":{"type":"object","additionalProperties":true}}}}}}},"responses":{"200":{"description":"Message accepted or idempotently returned"}}}},"/conversion":{"post":{"summary":"Record a conversion event","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversionRequest"}}}},"responses":{"202":{"description":"Conversion accepted"}}}},"/domains":{"get":{"summary":"List sending domains","responses":{"200":{"description":"Sending domains with linked senders and DNS status"}}},"post":{"summary":"Register a sending domain","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"region":{"type":"string"}}}}}},"responses":{"201":{"description":"Domain registered"}}}},"/domains/{id}":{"get":{"summary":"Get sending domain detail","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Domain detail including expected DNS records"}}},"delete":{"summary":"Delete a sending domain","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Domain deleted"}}}},"/domains/{id}/health":{"get":{"summary":"Get sending domain health history","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"query","name":"days","schema":{"type":"integer","minimum":1,"maximum":90}}],"responses":{"200":{"description":"Domain health and warmup history across linked sites"}}}},"/domains/{id}/verify":{"post":{"summary":"Trigger sending domain verification","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Verification result"}}}},"/service/projects/provision":{"post":{"summary":"Provision a Synchronex-bundled Sendinel project","description":"Customer bundle provisioning only. Requires a service API key with the service:provision scope. The internal Synchronex platform project is intentionally excluded from this customer crosswalk.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SynchronexProvisionRequest"}}}},"responses":{"200":{"description":"Existing project returned idempotently","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SynchronexProvisionResponse"}}}},"201":{"description":"Project provisioned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SynchronexProvisionResponse"}}}}}},"delete":{"summary":"Deprovision a Synchronex-bundled Sendinel project","description":"Customer bundle provisioning only. Requires a service API key with the service:provision scope. Suspends the project, revokes its project API key, zeroes the send limit, and resets approval_mode to standard. The internal Synchronex platform project is intentionally excluded from this customer crosswalk.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["synchronex_user_id"],"properties":{"owner_email":{"type":"string","format":"email"},"synchronex_user_id":{"type":"string"},"synchronex_account_id":{"type":"string"},"synchronex_account_no":{"type":"string"},"synchronex_team_id":{"type":"string"},"synchronex_workspace_id":{"type":"string"},"synchronex_brand_id":{"type":"string"},"synchronex_brand_name":{"type":"string"}}}}}},"responses":{"200":{"description":"Project deprovisioned or already absent"}}}},"/observer/connections":{"post":{"summary":"Create or re-affirm a read-only Observer Mode connection","description":"Requires a service API key with the service:provision scope. Registers a read-only ESP/CRM connection (connection_mode=observe) for a Synchronex account already linked to an active Sendinel project — distinct from the migrate-mode platform connections used for one-time data import.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["synchronex_account_id","provider","auth"],"properties":{"synchronex_account_id":{"type":"string"},"provider":{"type":"string"},"auth":{"type":"object"},"label":{"type":"string"},"workspace_hint":{"type":"string"}}}}}},"responses":{"201":{"description":"Connection created or re-affirmed"},"404":{"description":"Synchronex account is not linked to an active Sendinel project"},"422":{"description":"Missing or unsupported provider/auth fields"}}},"get":{"summary":"List Observer Mode connections for a Synchronex account","parameters":[{"name":"synchronex_account_id","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Observer connections for the account"},"422":{"description":"synchronex_account_id is required"}}}},"/observer/oauth/start":{"post":{"summary":"Start an OAuth Observer Mode connection","description":"Requires a service API key with the service:provision scope. Returns a provider authorization URL for Synchronex to redirect a user through an OAuth consent flow. The callback writes a connection_mode=observe platform connection and returns the browser to Synchronex.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["synchronex_account_id","provider","return_to","state"],"properties":{"synchronex_account_id":{"type":"string"},"provider":{"type":"string","enum":["hubspot","shopify","stripe","calendly"]},"return_to":{"type":"string","format":"uri"},"state":{"type":"string"},"label":{"type":"string"},"workspace_hint":{"type":"string"}}}}}},"responses":{"200":{"description":"Provider authorization URL","content":{"application/json":{"schema":{"type":"object","required":["authorization_url"],"properties":{"authorization_url":{"type":"string","format":"uri"}}}}}},"403":{"description":"Signed project does not own this Synchronex account"},"404":{"description":"Synchronex account is not linked to an active Sendinel project"},"422":{"description":"Missing or invalid observer OAuth request fields"}}}},"/observer/connections/{connectionId}":{"get":{"summary":"Get an Observer Mode connection's status","parameters":[{"name":"connectionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Connection status"},"404":{"description":"Observer connection not found"}}},"delete":{"summary":"Disconnect an Observer Mode connection","parameters":[{"name":"connectionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Connection disconnected"},"404":{"description":"Observer connection not found"}}}},"/service/projects/{projectId}":{"get":{"summary":"Resolve a Sendinel project for Synchronex reconciliation","description":"Read-only first-party lookup for existing Sendinel projects. Requires the Synchronex shared service token, or a first-party service key / scoped JWT carrying `service:provision`. Returns project, org, site, and Synchronex crosswalk fields only. Excludes credentials, encrypted keys, and billing data.","parameters":[{"name":"projectId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Project reconciliation payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceProjectLookupResponse"}}}},"403":{"description":"Missing first-party service access"},"404":{"description":"Project not found"}}}},"/synchronex/content-opportunity/signals":{"get":{"summary":"Export content-opportunity signals for Synchronex","description":"Read-only pull surface for Synchronex workers to consume Sendinel's content-opportunity signal classes and proposal-backed recommendations. The catalog includes all planned classes; current live producer output is lifecycle plus cadence-gap from create_content_recommendation proposals.","parameters":[{"in":"query","name":"since","schema":{"type":"string","format":"date-time"},"description":"Return proposal-backed signals generated after this ISO timestamp."},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":500}},{"in":"query","name":"status","schema":{"type":"string"},"description":"Comma-separated proposal statuses, or `all`. Defaults to pending, stale, applied, and auto_applied."},{"in":"query","name":"signal_class","schema":{"type":"string","enum":["engagement","lifecycle","revenue","cadence_gap","deliverability","segment_shift","content_inventory","cross_channel","portfolio_gap"]}}],"responses":{"200":{"description":"Content-opportunity signal export page","content":{"application/json":{"schema":{"type":"object","required":["synchronex_account_id","signal_classes","signals","next_cursor"],"properties":{"synchronex_account_id":{"type":"string","nullable":true},"signal_classes":{"type":"array","items":{"type":"object","required":["id","label","description","source_tables","status","producer"],"properties":{"id":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"},"source_tables":{"type":"array","items":{"type":"string"}},"status":{"type":"string","enum":["live","planned"]},"producer":{"type":"string","nullable":true}}}},"signals":{"type":"array","items":{"type":"object","required":["id","event","proposal_id","project_id","org_id","signal_classes","target","recommendation","metrics","status","generated_at","updated_at","source"],"properties":{"id":{"type":"string"},"event":{"type":"string","enum":["sendinel.content.recommendation"]},"proposal_id":{"type":"string","format":"uuid"},"project_id":{"type":"string","format":"uuid"},"org_id":{"type":"string","format":"uuid"},"signal_classes":{"type":"array","items":{"type":"string"}},"target":{"type":"object","additionalProperties":true},"recommendation":{"type":"object","additionalProperties":true},"metrics":{"type":"object","additionalProperties":true},"confidence":{"type":"integer","nullable":true},"status":{"type":"string"},"generated_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"source":{"type":"object","additionalProperties":true}}}},"next_cursor":{"type":"string","nullable":true}}}}}},"500":{"description":"Could not load content-opportunity signals"}}}},"/synchronex/recommendations/approve":{"post":{"summary":"Apply a Synchronex CEO-approved campaign recommendation","description":"Return leg for Synchronex CEO approvals of a sendinel.campaign.recommendation signal. Requires a service API key with the service:provision scope; the request's synchronex_account_id must match the project's linked account. Applies the underlying start_campaign or end_campaign agent_proposal.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["proposal_id","synchronex_account_id"],"properties":{"proposal_id":{"type":"string","format":"uuid"},"synchronex_account_id":{"type":"string"},"event_id":{"type":"string"}}}}}},"responses":{"200":{"description":"Proposal applied"},"403":{"description":"Missing service:provision scope, or synchronex_account_id does not match the project's linked account"},"422":{"description":"Proposal type is not start_campaign or end_campaign"}}}},"/synchronex/replies/post":{"post":{"summary":"Post an approved LinkedIn comment reply","description":"Return leg for Synchronex to post an approved reply to a LinkedIn comment (#3875). Requires exact match of sha256(text) against stored reply_draft.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["engagement_id","text"],"properties":{"engagement_id":{"type":"string"},"text":{"type":"string"},"body_sha256":{"type":"string"}}}}}},"responses":{"200":{"description":"Reply posted"},"404":{"description":"Engagement not found"},"422":{"description":"Text sha256 does not match stored reply draft"}}}},"/synchronex/signals":{"get":{"summary":"Replay Synchronex signal webhooks","parameters":[{"in":"query","name":"since","schema":{"type":"string"},"description":"ISO timestamp or prior event_id cursor."},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":500}}],"responses":{"200":{"description":"Signal replay page"}}}},"/synchronex/signals/summary":{"get":{"summary":"Summarize recent Synchronex signal activity","parameters":[{"in":"query","name":"window_days","schema":{"type":"integer","minimum":1,"maximum":90}}],"responses":{"200":{"description":"Signal activity summary"}}}},"/sites":{"get":{"summary":"List sites","responses":{"200":{"description":"Sites"}}},"post":{"summary":"Create a site","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Site"}}}},"responses":{"201":{"description":"Site created"}}}},"/sites/{id}":{"get":{"summary":"Get a site","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Site"}}},"patch":{"summary":"Update a site","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Site"}}}},"responses":{"200":{"description":"Site updated"}}}},"/contacts":{"get":{"summary":"List contacts","parameters":[{"in":"query","name":"search","schema":{"type":"string"}},{"in":"query","name":"site_id","schema":{"type":"string","format":"uuid"}},{"in":"query","name":"company_id","schema":{"type":"string","format":"uuid"}},{"in":"query","name":"company_domain","schema":{"type":"string"}}],"responses":{"200":{"description":"Contacts"}}},"post":{"summary":"Create a contact","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}}},"responses":{"201":{"description":"Contact created"}}}},"/drafts":{"get":{"summary":"List AI draft artifacts","parameters":[{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":200}},{"in":"query","name":"status","schema":{"type":"string","enum":["all","pending","approved","rejected","expired"]}},{"in":"query","name":"site_id","schema":{"type":"string","format":"uuid"}},{"in":"query","name":"campaign_id","schema":{"type":"string","format":"uuid"}},{"in":"query","name":"cursor","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Draft list"}}},"post":{"summary":"Create an AI draft for review","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["site_id","subject","body_html"],"properties":{"site_id":{"type":"string","format":"uuid"},"subject":{"type":"string"},"body_html":{"type":"string"},"body_text":{"type":"string"},"campaign_id":{"type":"string","format":"uuid"},"prompt":{"type":"string"},"model":{"type":"string"},"preview_text":{"type":"string"},"brand_asset_ids":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Stable brand_media_assets ids explicitly referenced by the draft."}}}}}},"responses":{"201":{"description":"Draft created"}}}},"/drafts/{id}":{"get":{"summary":"Get an AI draft and its latest review","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Draft detail with latest non-stale review"}}}},"/drafts/{id}/approve":{"post":{"summary":"Approve an AI draft","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Draft approved"}}}},"/drafts/{id}/regenerate":{"post":{"summary":"Regenerate an AI draft review","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"202":{"description":"Draft review regeneration accepted"}}}},"/drafts/{id}/reject":{"post":{"summary":"Reject an AI draft","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Draft rejected"}}}},"/forms":{"get":{"summary":"List signup forms","parameters":[{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":200}},{"in":"query","name":"site_id","schema":{"type":"string","format":"uuid"}},{"in":"query","name":"status","schema":{"type":"string"}},{"in":"query","name":"search","schema":{"type":"string"}},{"in":"query","name":"cursor","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Signup forms"}}},"post":{"summary":"Create a signup form","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","slug"],"properties":{"name":{"type":"string"},"slug":{"type":"string"},"site_id":{"type":"string","format":"uuid"},"fields":{"type":"array","items":{"type":"object","additionalProperties":true}},"settings":{"type":"object","additionalProperties":true}}}}}},"responses":{"201":{"description":"Signup form created"}}}},"/forms/{id}":{"get":{"summary":"Get signup form detail","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Signup form detail with embed code"}}},"patch":{"summary":"Update a signup form","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"200":{"description":"Signup form updated"}}},"delete":{"summary":"Delete a signup form","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Signup form deleted"}}}},"/forms/{id}/submissions":{"get":{"summary":"List signup form submissions","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":200}},{"in":"query","name":"cursor","schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"Signup form submission history"}}}},"/email-log":{"get":{"summary":"List delivery events","parameters":[{"in":"query","name":"campaign_id","schema":{"type":"string","format":"uuid"}},{"in":"query","name":"contact_id","schema":{"type":"string","format":"uuid"}},{"in":"query","name":"site_id","schema":{"type":"string","format":"uuid"}},{"in":"query","name":"status","schema":{"type":"string","description":"Comma-separated delivery statuses"}},{"in":"query","name":"start_date","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"end_date","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"cursor","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":500}}],"responses":{"200":{"description":"Email delivery events with cursor pagination"}}}},"/email-log/export":{"get":{"summary":"Export delivery events","parameters":[{"in":"query","name":"campaign_id","schema":{"type":"string","format":"uuid"}},{"in":"query","name":"contact_id","schema":{"type":"string","format":"uuid"}},{"in":"query","name":"site_id","schema":{"type":"string","format":"uuid"}},{"in":"query","name":"status","schema":{"type":"string","description":"Comma-separated delivery statuses"}},{"in":"query","name":"start_date","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"end_date","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"format","schema":{"type":"string","enum":["csv","jsonl"]}}],"responses":{"200":{"description":"Email delivery events export"}}}},"/content-blocks":{"get":{"summary":"List reusable content blocks","parameters":[{"in":"query","name":"category","schema":{"type":"string"}}],"responses":{"200":{"description":"Reusable content blocks"}}},"post":{"summary":"Create a reusable content block","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","html"],"properties":{"name":{"type":"string"},"html":{"type":"string"},"description":{"type":"string"},"category":{"type":"string"},"thumbnail":{"type":"string"}}}}}},"responses":{"201":{"description":"Content block created"}}}},"/content-blocks/{id}":{"get":{"summary":"Get a reusable content block","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Content block detail"}}},"patch":{"summary":"Update a reusable content block","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"200":{"description":"Content block updated"}}},"delete":{"summary":"Delete a reusable content block","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Content block deleted"}}}},"/pages":{"get":{"summary":"List landing pages","parameters":[{"in":"query","name":"status","schema":{"type":"string","enum":["draft","published","archived"]}},{"in":"query","name":"site_id","schema":{"type":"string","format":"uuid"}},{"in":"query","name":"search","schema":{"type":"string"}},{"in":"query","name":"cursor","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":200}}],"responses":{"200":{"description":"Landing pages with cursor pagination"}}},"post":{"summary":"Create a landing page","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["title","slug"],"properties":{"title":{"type":"string"},"slug":{"type":"string"},"site_id":{"type":"string","format":"uuid"},"blocks":{"type":"array","items":{"type":"object","additionalProperties":true}},"form_id":{"type":"string","format":"uuid"},"meta_title":{"type":"string"},"meta_description":{"type":"string"}}}}}},"responses":{"201":{"description":"Landing page created"}}}},"/pages/{id}":{"get":{"summary":"Get a landing page","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Landing page detail"}}},"patch":{"summary":"Update a landing page","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"200":{"description":"Landing page updated"}}},"delete":{"summary":"Delete a landing page","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Landing page deleted"}}}},"/asset-links":{"get":{"summary":"List gated asset links","parameters":[{"in":"query","name":"status","schema":{"type":"string","enum":["draft","active","archived"]}},{"in":"query","name":"site_id","schema":{"type":"string","format":"uuid"}},{"in":"query","name":"cursor","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":200}}],"responses":{"200":{"description":"Gated asset links with cursor pagination"}}},"post":{"summary":"Create a gated asset link","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["title","slug","asset_type","asset_url"],"properties":{"title":{"type":"string"},"slug":{"type":"string"},"asset_type":{"type":"string","enum":["pdf","page","deck","video","other"]},"asset_url":{"type":"string","format":"uri"},"site_id":{"type":"string","format":"uuid"},"source_attribution":{"type":"string"},"gate_headline":{"type":"string"},"gate_body":{"type":"string"}}}}}},"responses":{"201":{"description":"Gated asset link created"}}}},"/asset-links/{id}":{"get":{"summary":"Get a gated asset link","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Gated asset link detail"}}},"patch":{"summary":"Update a gated asset link","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"200":{"description":"Gated asset link updated"}}},"delete":{"summary":"Archive a gated asset link","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Gated asset link archived"}}}},"/asset-links/{id}/views":{"get":{"summary":"List gated asset link views","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"query","name":"cursor","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":200}}],"responses":{"200":{"description":"Per-view gated asset events"}}}},"/signatures":{"get":{"summary":"List signature blocks","parameters":[{"in":"query","name":"site_id","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Signature blocks"}}},"post":{"summary":"Create a signature block","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["site_id","name","html"],"properties":{"site_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"html":{"type":"string"},"is_default":{"type":"boolean"}}}}}},"responses":{"201":{"description":"Signature block created"}}}},"/signatures/{id}":{"get":{"summary":"Get a signature block","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Signature block detail"}}},"patch":{"summary":"Update a signature block","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"200":{"description":"Signature block updated"}}},"delete":{"summary":"Delete a signature block","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Signature block deleted"}}}},"/sending-domains":{"get":{"summary":"List managed sending domains","responses":{"200":{"description":"Managed sending domains"}}},"post":{"summary":"Provision a managed sending domain","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["parent"],"properties":{"parent":{"type":"string","enum":["sendinel.ai","synchronex.ai"]},"label":{"type":"string"}}}}}},"responses":{"201":{"description":"Managed sending domain provisioned"}}}},"/sending-domains/{id}":{"get":{"summary":"Get a managed sending domain","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Managed sending domain detail"}}},"delete":{"summary":"Delete a managed sending domain","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Managed sending domain deleted"}}}},"/social-posts":{"get":{"summary":"List social posts","parameters":[{"in":"query","name":"status","schema":{"type":"string"}},{"in":"query","name":"platform","schema":{"type":"string"}},{"in":"query","name":"site_id","schema":{"type":"string","format":"uuid"}},{"in":"query","name":"scheduled_after","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"scheduled_before","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"cursor","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":200}}],"responses":{"200":{"description":"Social posts with cursor pagination"}}},"post":{"summary":"Create a social post","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["title","content","platforms"],"properties":{"title":{"type":"string"},"content":{"type":"string"},"platforms":{"type":"array","items":{"type":"string"}},"scheduled_at":{"type":"string","format":"date-time"},"queue_requested":{"type":"boolean"},"platform_captions":{"type":"object","additionalProperties":true},"linkedin_company_crosspost":{"type":"boolean","description":"When true, also publish the LinkedIn post to the provided company page target."},"linkedin_company_page_id":{"type":"string","description":"UploadPost target_linkedin_page_id for the LinkedIn company page."},"media_urls":{"type":"array","items":{"type":"string"}},"destination_url":{"type":"string"},"site_id":{"type":"string","format":"uuid"},"timezone":{"type":"string"}}}}}},"responses":{"201":{"description":"Social post created"}}}},"/social-posts/{id}":{"get":{"summary":"Get a social post","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Social post detail"}}},"patch":{"summary":"Update a social post","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"200":{"description":"Social post updated"}}},"delete":{"summary":"Archive a social post","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Social post archived"}}}},"/templates/{id}/preview":{"get":{"summary":"Preview a template with seeded sample content","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"query","name":"refresh","schema":{"type":"integer","enum":[1]}}],"responses":{"200":{"description":"Template preview render"}}}},"/scoring-rules":{"get":{"summary":"Get project scoring rules","responses":{"200":{"description":"Project scoring rules"}}},"put":{"summary":"Update project scoring rules","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"number"}}}}},"responses":{"200":{"description":"Project scoring rules updated"}}}},"/channels/roster":{"post":{"summary":"Sync a project channel roster","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["entries"],"properties":{"entries":{"type":"array","minItems":1,"maxItems":1000,"items":{"type":"object","required":["channel","handle"],"properties":{"channel":{"type":"string","enum":["sms","whatsapp","telegram","email"]},"handle":{"type":"string"},"contact_id":{"type":"string","format":"uuid","nullable":true}}}}}}}}},"responses":{"200":{"description":"Channel roster synced"}}}},"/social-connections":{"get":{"summary":"List connected social accounts and connection health","responses":{"200":{"description":"Org-scoped social connections with account identity, status, and expiry","content":{"application/json":{"schema":{"type":"object","required":["connections"],"properties":{"connections":{"type":"array","items":{"type":"object","properties":{"platform":{"type":"string"},"account_name":{"type":"string","nullable":true},"account_id":{"type":"string","nullable":true},"account_status":{"type":"string"},"expires_at":{"type":"string","format":"date-time","nullable":true},"follower_count":{"type":"integer","nullable":true},"follower_count_synced_at":{"type":"string","format":"date-time","nullable":true},"updated_at":{"type":"string","format":"date-time"},"created_at":{"type":"string","format":"date-time"}}}},"message":{"type":"string","description":"Present when no social accounts are connected"}}}}}}}}},"/social-inbox":{"get":{"summary":"List inbound social engagements","parameters":[{"in":"query","name":"status","schema":{"type":"string","enum":["all","open","responded","dismissed"]}},{"in":"query","name":"platform","schema":{"type":"string"}},{"in":"query","name":"social_post_id","schema":{"type":"string","format":"uuid"}},{"in":"query","name":"cursor","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":200}}],"responses":{"200":{"description":"Inbound social engagements with cursor pagination"}}}},"/social-analytics/campaigns":{"get":{"summary":"Get social campaign analytics","parameters":[{"in":"query","name":"site_id","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"query","name":"campaign_id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Social campaign analytics"}}}},"/social-analytics/portfolio":{"get":{"summary":"Get aggregate social portfolio analytics","parameters":[{"in":"query","name":"days","schema":{"type":"integer","minimum":1,"maximum":365}},{"in":"query","name":"group_by","schema":{"type":"string","enum":["day","week","month"]}}],"responses":{"200":{"description":"Aggregate social impressions, engagement, posts, clicks, conversions, followers, and period/platform breakdowns"}}}},"/social-analytics/engagements":{"get":{"summary":"List inbound social engagements for worker context","parameters":[{"in":"query","name":"status","schema":{"type":"string"},"description":"Engagement status to filter by; use all to disable filtering."}],"responses":{"200":{"description":"Social engagement inbox rows joined to post title/platform context"}}}},"/embed/token":{"post":{"summary":"Issue an embed token","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["scopes"],"properties":{"scopes":{"type":"array","minItems":1,"items":{"type":"string"}},"ttl_seconds":{"type":"integer","minimum":60,"maximum":900}}}}}},"responses":{"200":{"description":"Embed token issued"}}}},"/dmarc-reports":{"get":{"summary":"List DMARC report summaries","parameters":[{"in":"query","name":"site_id","schema":{"type":"string","format":"uuid"}},{"in":"query","name":"domain","schema":{"type":"string"}},{"in":"query","name":"start_date","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"end_date","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"cursor","schema":{"type":"string","format":"date-time"}},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":200}}],"responses":{"200":{"description":"DMARC report summaries with cursor pagination"}}}},"/recipients/{id}/export":{"get":{"summary":"Export recipient data","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Recipient data export"}}}},"/service/embed-session":{"post":{"summary":"Mint a service embed session token","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["project_id","owner_email","synchronex_user_id"],"properties":{"project_id":{"type":"string","format":"uuid"},"owner_email":{"type":"string","format":"email"},"synchronex_user_id":{"type":"string"},"synchronex_tenant_claim":{"type":"string"}}}}}},"responses":{"200":{"description":"Embed session token minted"}}}},"/export":{"get":{"summary":"Export tenant data","parameters":[{"in":"query","name":"site_id","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"query","name":"format","schema":{"type":"string","enum":["json","csv"]}}],"responses":{"200":{"description":"Tenant export bundle or uploaded export reference"}}}},"/brand-brain":{"get":{"summary":"Get project brand brain settings","responses":{"200":{"description":"Project brand brain settings"}}},"patch":{"summary":"Update project brand brain settings","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"200":{"description":"Project brand brain settings updated"}}}},"/brand-kit":{"get":{"summary":"Get a project's assembled brand kit (assets, templates, campaigns, brand settings)","parameters":[{"in":"query","name":"kit_id","required":false,"schema":{"type":"string","format":"uuid"},"description":"Assemble a specific kit. Omit to get the most recently created kit for the project."},{"in":"query","name":"kind","required":false,"schema":{"type":"string"},"description":"Filter assets to one BrandAssetKind (logo, image, document, font, avatar, video)."},{"in":"query","name":"collection","required":false,"schema":{"type":"string"}},{"in":"query","name":"tag","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Assembled brand kit"},"404":{"description":"No brand kit exists for this project, or the specified kit_id was not found"}}}},"/brand-kit/assets":{"get":{"summary":"List a project's confirmed brand assets across ALL brand kits (not scoped to a single kit)","description":"The project-wide sibling of GET /brand-kit, which assembles a single kit (defaulting to the most recently created one). Use this to see everything in the project's media library without assuming there's only one kit.","parameters":[{"in":"query","name":"kind","required":false,"schema":{"type":"string"},"description":"Filter assets to one BrandAssetKind (logo, image, document, font, avatar, video)."},{"in":"query","name":"collection","required":false,"schema":{"type":"string"}},{"in":"query","name":"tag","required":false,"schema":{"type":"string"}},{"in":"query","name":"reference_purpose","required":false,"schema":{"type":"string","enum":["product","style","scene","brand-mark"]},"description":"Filter assets whose reference_purposes array includes this value."},{"in":"query","name":"limit","required":false,"schema":{"type":"integer","minimum":1,"maximum":100},"description":"Max assets to return (default 50)."},{"in":"query","name":"offset","required":false,"schema":{"type":"integer","minimum":0},"description":"Number of matching assets to skip (default 0)."}],"responses":{"200":{"description":"Confirmed assets with signed download URLs, counts by kind, and pagination metadata"},"422":{"description":"Invalid kind filter"}}}},"/brand-kit/assets/{id}/rehost":{"post":{"summary":"Rehost a brand asset for email or social use","description":"Creates a real hosted copy of an existing brand asset using the dashboard media rehost pipeline. Use this when a brand asset read returns `url: null`, such as a Drive-linked asset, before referencing it in generated content.\n","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"},"description":"Brand media asset id."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["purpose"],"properties":{"purpose":{"type":"string","enum":["email","social"]},"platforms":{"type":"array","items":{"type":"string"},"description":"Optional target platform context for social validation."}}}}}},"responses":{"200":{"description":"Hosted asset URL and asset metadata"},"404":{"description":"Asset not found"},"415":{"description":"Asset cannot be rehosted for the requested purpose"},"422":{"description":"Invalid purpose"}}}},"/brand-kit/import-text":{"post":{"summary":"Import raw Brand Core / Brand Brain / Content Library / Brand Kit companion markdown text","description":"Parses a companion's raw text reply and applies whatever it finds (voice fields into brand_settings, colors, templates, social posts) to the current project. Same deterministic parsers as the dashboard's paste-back flow.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["text"],"properties":{"text":{"type":"string","description":"Raw markdown text from the Brand Core / Brand Kit companion output."}}}}}},"responses":{"200":{"description":"Import summary (fields/colors/templates/social posts saved)"},"400":{"description":"Text is missing, or no recognized Brand Core/Brand Brain/Content Library/Brand Kit section was found"},"404":{"description":"Project not found"}}}},"/brand-kit/ingest-assets":{"post":{"summary":"Ingest brand assets from public HTTPS URLs into the project media library","description":"Downloads each asset URL server-side (private/internal addresses and redirects are rejected) and persists it the same way a dashboard upload would. Raw file bytes are not accepted in the request body — provide URLs only.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["assets"],"properties":{"assets":{"type":"array","minItems":1,"maxItems":25,"items":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","description":"Public HTTPS asset URL."},"kind":{"type":"string","enum":["logo","image","document","font","avatar","video"]},"filename":{"type":"string"}}}},"collection":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}}}}}}},"responses":{"200":{"description":"Per-asset ingest results plus imported/failed counts"},"400":{"description":"assets is missing, empty, or exceeds the 25-item limit"},"404":{"description":"Project not found"}}}},"/brand-kit/github-connections":{"get":{"summary":"List approved GitHub Brand Kit sync connections","description":"Returns repo + path-prefix bindings registered for the authenticated project.","responses":{"200":{"description":"Registered GitHub connections"},"404":{"description":"Project not found"}}},"post":{"summary":"Register an approved GitHub repo path for Brand Kit ingest","description":"Creates or updates a project-scoped repo/path_prefix binding that the GitHub ingest endpoint uses to validate every incoming file path.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["repo","path_prefix"],"properties":{"repo":{"type":"string","example":"owner/repo"},"path_prefix":{"type":"string","example":"marketing"},"brand_kit_id":{"type":"string","format":"uuid","nullable":true}}}}}},"responses":{"201":{"description":"Registered GitHub connection"},"400":{"description":"Invalid repo, path_prefix, or brand_kit_id"},"404":{"description":"Project not found"}}}},"/brand-kit/github-connections/{id}":{"delete":{"summary":"Revoke an approved GitHub Brand Kit sync connection","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deleted GitHub connection"},"404":{"description":"Project or connection not found"}}}},"/brand-kit/github-ingest":{"post":{"summary":"Push GitHub-sourced files into the Brand Kit media library","description":"Accepts a Synchronex-pushed batch of repo-relative files, rejects the whole request when no repo binding exists, and rejects individual files outside the registered path prefix while importing in-scope siblings. Imported assets use source imported with metadata.cloud_provider github.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["repo","files"],"properties":{"repo":{"type":"string","example":"owner/repo"},"ref":{"type":"string"},"brand_kit_id":{"type":"string","format":"uuid","nullable":true},"collection":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"files":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"object","required":["path"],"properties":{"path":{"type":"string","example":"marketing/logos/wordmark.svg"},"content_base64":{"type":"string","description":"Base64-encoded file bytes. Required unless content_url is provided."},"content_url":{"type":"string","format":"uri","description":"Public HTTPS URL fetched with the same SSRF protections as ingest-assets."},"mime_type":{"type":"string"},"sha":{"type":"string"}}}}}}}}},"responses":{"200":{"description":"Per-file GitHub ingest results plus imported, failed, and rejected_out_of_scope counts"},"400":{"description":"Invalid request body, repo, files, or brand_kit_id"},"403":{"description":"No approved GitHub connection exists for this repo"},"404":{"description":"Project not found"}}}},"/blog":{"get":{"summary":"List blog posts","parameters":[{"in":"query","name":"status","schema":{"type":"string","enum":["draft","scheduled","published","archived"]},"description":"Optional status filter."},{"in":"query","name":"limit","schema":{"type":"integer"},"description":"Max rows (1-200, default 50)."}],"responses":{"200":{"description":"Blog posts for the project."}}},"post":{"summary":"Ingest a blog post (staged as draft for review)","description":"Sender-owns-schema ingest — only `title` is required; every other field is mapped from whatever arrives and the full raw payload is preserved. The post lands as `draft` for review; publishing to the user's own site is a separate step. Requires the `blog:write` scope.\n","responses":{"201":{"description":"Created blog post (draft)."},"422":{"description":"Missing or invalid field."}}}},"/newsletters":{"get":{"summary":"List newsletters","description":"Requires the `newsletters:read` scope.","parameters":[{"in":"query","name":"status","schema":{"type":"string","enum":["active","paused","archived"]}},{"in":"query","name":"limit","schema":{"type":"integer"},"description":"Max rows (1-200, default 50)."}],"responses":{"200":{"description":"Newsletters for the project."}}},"post":{"summary":"Create a newsletter publication","description":"Requires the `newsletters:write` scope.","responses":{"201":{"description":"Created newsletter."},"409":{"description":"Newsletter slug already exists."},"422":{"description":"Missing or invalid field."}}}},"/newsletters/{id}":{"get":{"summary":"Get a newsletter","description":"Requires the `newsletters:read` scope.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Newsletter row."},"404":{"description":"Newsletter not found."}}},"patch":{"summary":"Update a newsletter","description":"Requires the `newsletters:write` scope.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Updated newsletter row."},"404":{"description":"Newsletter not found."},"409":{"description":"Newsletter slug already exists."}}}},"/newsletters/{id}/issues":{"get":{"summary":"List newsletter issues","description":"Requires the `newsletters:read` scope.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"query","name":"status","schema":{"type":"string","enum":["draft","scheduled","sent","archived"]}}],"responses":{"200":{"description":"Newsletter issue rows."}}},"post":{"summary":"Create a newsletter issue draft","description":"Requires the `newsletters:write` scope.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Created newsletter issue draft."},"404":{"description":"Newsletter not found."}}}},"/newsletters/{id}/issues/{issueId}/schedule":{"post":{"summary":"Schedule a newsletter issue","description":"Requires the `newsletters:write` scope. Creates or reuses the linked campaign broadcast, enrolls active newsletter subscribers, and sets campaign send_at.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"path","name":"issueId","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["scheduled_at"],"properties":{"scheduled_at":{"type":"string","format":"date-time"}}}}}},"responses":{"200":{"description":"Scheduled newsletter issue, linked campaign id, and enrollment count."},"409":{"description":"Issue cannot be scheduled from its current state."},"422":{"description":"Missing site_id, content, or valid scheduled_at."}}}},"/newsletters/{id}/issues/{issueId}/send":{"post":{"summary":"Send a newsletter issue now","description":"Requires the `newsletters:write` scope. Creates or reuses the linked campaign broadcast, enrolls active newsletter subscribers, and creates a Blog archive row.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"path","name":"issueId","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Sent newsletter issue, linked campaign id, enrollment count, and archive metadata."},"409":{"description":"Issue has already been sent."},"422":{"description":"Missing site_id or issue content."}}}},"/newsletters/{id}/subscribe":{"post":{"summary":"Add or reactivate a newsletter subscription","description":"Requires the `newsletters:write` scope.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Newsletter subscription row."}}},"delete":{"summary":"Unsubscribe from one newsletter","description":"Requires the `newsletters:write` scope. This does not create a global suppression.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Newsletter-scoped unsubscribe result."}}}},"/newsletters/{id}/subscribers":{"get":{"summary":"List newsletter subscribers","description":"Requires the `newsletters:read` scope.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"query","name":"status","schema":{"type":"string","enum":["pending","active","unsubscribed"]}},{"in":"query","name":"limit","schema":{"type":"integer","default":50,"minimum":1,"maximum":200}}],"responses":{"200":{"description":"Newsletter subscriptions for this publication."}}}},"/newsletters/{id}/stats":{"get":{"summary":"Get newsletter stats","description":"Requires the `newsletters:read` scope. Issue-level analytics intentionally return `pending_phase_4` in this core slice.","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Newsletter subscriber count and explicit Phase 4 analytics placeholder."}}}},"/content-sources":{"get":{"summary":"List content sources available to worker context","parameters":[{"in":"query","name":"site","schema":{"type":"string"},"description":"Optional site filter, accepting either site UUID or display id."}],"responses":{"200":{"description":"Content source rows without credential/token fields"}}}},"/surveys":{"get":{"summary":"List surveys available to worker context","responses":{"200":{"description":"Survey list scoped to the authenticated API key's project"}}}},"/blackout-dates":{"get":{"summary":"List campaign scheduling blackout dates","responses":{"200":{"description":"Blackout date list ordered by date ascending"}}}},"/revenue":{"get":{"summary":"List conversion revenue records and summary","parameters":[{"in":"query","name":"site","schema":{"type":"string"},"description":"Optional site filter, accepting either site UUID or display id."},{"in":"query","name":"search","schema":{"type":"string"},"description":"Search order id or conversion event name."},{"in":"query","name":"page","schema":{"type":"integer"}},{"in":"query","name":"pageSize","schema":{"type":"integer"}}],"responses":{"200":{"description":"Conversion rows plus total/count/average revenue summary"}}}},"/campaigns/{id}/steps/{stepId}":{"get":{"summary":"Get a campaign step","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"path","name":"stepId","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Campaign step detail"}}},"patch":{"summary":"Update a campaign step","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"path","name":"stepId","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"200":{"description":"Campaign step updated"}}},"delete":{"summary":"Delete a campaign step","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"path","name":"stepId","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Campaign step deleted"}}}},"/openapi.json":{"get":{"summary":"Get the OpenAPI specification as JSON","responses":{"200":{"description":"OpenAPI specification document"}}}},"/ai/context-events":{"post":{"summary":"Submit Synchronex strategic context into the AI work queue","description":"Stores a project-scoped Synchronex context event and may translate it into reviewable AI work items and approval-gated proposals.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AiContextEvent"}}}},"responses":{"200":{"description":"Duplicate idempotency key; existing context event returned"},"201":{"description":"Context event accepted and translated into AI work"},"401":{"description":"Missing or invalid API key"},"403":{"description":"Admin scope required"},"422":{"description":"Invalid context event payload"}}}},"/webhooks/cal-com/{connectionId}":{"post":{"summary":"Receive a Cal.com webhook for a platform connection","parameters":[{"in":"path","name":"connectionId","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Cal.com webhook processed"}}}},"/webhooks/calendly/{connectionId}":{"post":{"summary":"Receive a Calendly webhook for a platform connection","parameters":[{"in":"path","name":"connectionId","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Calendly webhook processed"}}}},"/webhooks/hubspot/{connectionId}":{"post":{"summary":"Receive a HubSpot webhook for a platform connection","parameters":[{"in":"path","name":"connectionId","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"HubSpot webhook processed"}}}},"/webhooks/shopify/{connectionId}":{"post":{"summary":"Receive a Shopify webhook for a platform connection","parameters":[{"in":"path","name":"connectionId","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Shopify webhook processed"}}}},"/webhooks/stripe/{connectionId}":{"post":{"summary":"Receive a Stripe webhook for a platform connection","parameters":[{"in":"path","name":"connectionId","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Stripe webhook processed"}}}},"/webhooks/stripe-connect":{"post":{"summary":"Receive a platform-level Stripe Connect webhook","security":[],"responses":{"200":{"description":"Stripe Connect webhook processed"},"400":{"description":"Invalid or missing Stripe signature"},"500":{"description":"Stripe Connect webhook signing secret is not configured"}}}},"/webhooks/typeform/{connectionId}":{"post":{"summary":"Receive a Typeform webhook for a platform connection","parameters":[{"in":"path","name":"connectionId","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Typeform webhook processed"}}}},"/webhooks/custom/{connectionId}":{"post":{"summary":"Receive a signed custom-app webhook event (single event or batch)","parameters":[{"in":"path","name":"connectionId","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"header","name":"X-Sendinel-Signature","required":true,"schema":{"type":"string"},"description":"HMAC-SHA256 signature over the raw request body, format sha256=<hex>"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"type":"object","required":["email","event"],"properties":{"email":{"type":"string"},"event":{"type":"string"},"data":{"type":"object"},"event_id":{"type":"string"}}},{"type":"object","required":["events"],"properties":{"events":{"type":"array","maxItems":100,"items":{"type":"object","required":["email","event"],"properties":{"email":{"type":"string"},"event":{"type":"string"},"data":{"type":"object"},"event_id":{"type":"string"}}}}}}]}}}},"responses":{"200":{"description":"Custom webhook event(s) processed"},"400":{"description":"Missing or invalid signature"},"404":{"description":"Connection not found"}}}},"/contacts/import":{"post":{"summary":"Bulk import contacts","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["contacts"],"properties":{"contacts":{"type":"array","items":{"$ref":"#/components/schemas/Contact"}}}}}}},"responses":{"200":{"description":"Import result"}}}},"/contacts/batch":{"post":{"summary":"Batch upsert contacts by email","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["contacts"],"properties":{"contacts":{"type":"array","maxItems":500,"items":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"},"first_name":{"type":"string"},"last_name":{"type":"string"},"custom_properties":{"type":"object","additionalProperties":true},"tags":{"type":"array","maxItems":50,"items":{"type":"string"}}}}}}}}}},"responses":{"202":{"description":"Batch result"}}}},"/contacts/tags":{"post":{"summary":"Bulk add or remove contact tags","responses":{"200":{"description":"Tags updated"}}}},"/contacts/estimate":{"post":{"summary":"Estimate audience size from contact filters","responses":{"200":{"description":"Audience estimate"}}}},"/contacts/delivery-state":{"get":{"summary":"Read delivery verdicts for subscribers","description":"Read-only. Sendinel is the authority for suppression and bounce state.\n`status` is one of `no_record`, `deliverable`, `hard_bounced`, or\n`suppressed_other`. `no_record` means Sendinel has no deliverability\nverdict (never sent, or only a non-permanent bounce carried on\n`bounce_type` and `reason`). `matched: false` means no subscriber row.\nNeither value is deliverable. `bounce_type: permanent` is set only for\n`hard_bounced`. Reason is a category code, not a complaint body.\nPass `source_id` (the contact view's source id, including `sendinel:<id>`)\nfor one lookup. Omit it to page every subscriber in the project.\n","parameters":[{"in":"query","name":"source_id","schema":{"type":"string","maxLength":200}},{"in":"query","name":"cursor","schema":{"type":"string","format":"uuid"}},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"Delivery verdicts. An empty project returns an empty list. An unknown source_id returns one matched=false no_record row."},"422":{"description":"Invalid cursor or source_id"}}}},"/contacts/{id}":{"get":{"summary":"Get a contact","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Contact"}}},"patch":{"summary":"Update a contact","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}}},"responses":{"200":{"description":"Contact updated"}}},"delete":{"summary":"Delete a contact","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Contact deleted"}}}},"/contacts/{id}/delivery-state":{"get":{"summary":"Read one subscriber's delivery verdict","description":"Same verdict contract as /contacts/delivery-state. A missing subscriber\nis HTTP 200 with matched=false and status=no_record, not a deliverable\naddress and not an empty body.\n","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Delivery verdict for the subscriber, or matched=false when the id is unknown"}}}},"/contacts/{id}/unsubscribe":{"post":{"summary":"Globally unsubscribe a contact","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Contact unsubscribed"}}}},"/contacts/{id}/timeline":{"get":{"summary":"Get a contact activity timeline","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Contact timeline"}}}},"/contacts/{id}/enrich":{"post":{"summary":"Enrich a contact from connected providers","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Contact enrichment result"}}}},"/segments":{"get":{"summary":"List segments","responses":{"200":{"description":"Segments"}}},"post":{"summary":"Create a segment","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Segment"}}}},"responses":{"201":{"description":"Segment created"}}}},"/segments/{id}":{"get":{"summary":"Get a segment","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Segment"}}},"patch":{"summary":"Update a segment","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Segment"}}}},"responses":{"200":{"description":"Segment updated"}}},"delete":{"summary":"Delete a segment","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Segment deleted"}}}},"/segments/{id}/preview":{"get":{"summary":"Preview segment membership","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Preview result"}}}},"/templates":{"get":{"summary":"List templates","responses":{"200":{"description":"Templates"}}},"post":{"summary":"Create a template","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Template"}}}},"responses":{"201":{"description":"Template created"}}}},"/templates/{id}":{"get":{"summary":"Get a template","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Template"}}},"patch":{"summary":"Update a template","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Template"}}}},"responses":{"200":{"description":"Template updated"}}},"delete":{"summary":"Archive a template","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Template archived"}}}},"/campaigns":{"get":{"summary":"List campaigns","responses":{"200":{"description":"Campaigns"}}},"post":{"summary":"Create a campaign","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Campaign"}}}},"responses":{"201":{"description":"Campaign created"}}}},"/campaigns/{id}":{"get":{"summary":"Get a campaign","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Campaign"}}},"patch":{"summary":"Update a campaign","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"status":{"type":"string"},"send_at":{"type":"string","format":"date-time","nullable":true},"segment_id":{"type":"string","format":"uuid","nullable":true},"site_id":{"type":"string","format":"uuid"},"topic":{"type":"string","nullable":true},"trigger_event_name":{"type":"string","nullable":true},"trigger_tag":{"type":"string","nullable":true},"send_in_contact_timezone":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Campaign updated"}}},"delete":{"summary":"Delete a campaign (requires campaign not active)","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Campaign deleted"}}}},"/campaigns/{id}/status":{"get":{"summary":"Campaign delivery status with per-channel breakdown","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Delivery status — { campaign_id, status, delivery_status, channel_statuses[], updated_at }"}}}},"/campaigns/{id}/clone":{"post":{"summary":"Clone a campaign","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"201":{"description":"Campaign cloned"}}}},"/campaigns/review":{"post":{"summary":"Run the campaign portfolio review","description":"Evaluates every campaign in the project for completeness, dormancy, redundancy, and effectiveness, optionally raising agent_proposals (start_campaign / end_campaign / modify_campaign) and emitting sendinel.campaign.recommendation signals to Synchronex.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"dry_run":{"type":"boolean","description":"Compute findings only, without raising proposals."},"dormant_draft_days":{"type":"integer"},"abandoned_days":{"type":"integer"}}}}}},"responses":{"200":{"description":"Review results and any proposals raised"}}}},"/campaigns/{id}/steps":{"get":{"summary":"List campaign steps","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Campaign steps"}}},"post":{"summary":"Add a campaign step","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignStep"}}}},"responses":{"201":{"description":"Step created"}}}},"/campaigns/{id}/enrollments":{"post":{"summary":"Enroll one contact or an entire segment into a campaign","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"contact_id":{"type":"string","format":"uuid"},"segment_id":{"type":"string","format":"uuid"},"site_id":{"type":"string","format":"uuid"}}}}}},"responses":{"200":{"description":"Enrollments created"}}}},"/campaigns/{id}/enrollments/{contactId}":{"get":{"summary":"Get one contact's campaign enrollment status","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"path","name":"contactId","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Enrollment status"}}},"delete":{"summary":"Remove one contact from a campaign","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"path","name":"contactId","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Enrollment removed"}}}},"/campaigns/{id}/steps/{stepId}/test":{"post":{"summary":"Send a campaign step test email","responses":{"200":{"description":"Test email sent"}}}},"/campaigns/ab-tests":{"post":{"summary":"Create an A/B test from a campaign step","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["step_id","name"],"properties":{"step_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"metric":{"type":"string","enum":["open_rate","click_rate","conversion"],"default":"open_rate"},"split_ratio":{"type":"integer","minimum":1,"maximum":99,"default":50},"test_type":{"type":"string","enum":["full","subject_only"],"default":"full"}}}}}},"responses":{"200":{"description":"A/B test created"}}},"patch":{"summary":"Update A/B test metadata or select winner","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["test_id"],"properties":{"test_id":{"type":"string","format":"uuid"},"variant_label":{"type":"string"},"subject":{"type":"string"},"body_html":{"type":"string"},"action":{"type":"string"},"winner_variant_label":{"type":"string"},"send_to_remainder":{"type":"boolean"}}}}}},"responses":{"200":{"description":"A/B test updated"}}},"get":{"summary":"Check A/B test significance","parameters":[{"in":"query","name":"testId","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"A/B significance result"}}}},"/campaigns/{id}/stats":{"get":{"summary":"Per-campaign delivery metrics","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"query","name":"site_id","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Stats — { sent, opened, clicked, bounced, unsubscribed, open_rate, click_rate }"}}}},"/analytics/portfolio":{"get":{"summary":"Cross-site portfolio email analytics rollup","responses":{"200":{"description":"Portfolio analytics"}}}},"/analytics/product-usage":{"get":{"summary":"Product usage analytics from PostHog for worker context","responses":{"200":{"description":"PostHog identify/event/DAU rollup or configured false when PostHog is unavailable"},"502":{"description":"PostHog analytics query failed"}}}},"/analytics/overview":{"get":{"summary":"Site-level email overview metrics for a lookback window","parameters":[{"in":"query","name":"site_id","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"query","name":"days","schema":{"type":"integer","minimum":1,"maximum":365}}],"responses":{"200":{"description":"Overview metrics including sender health, list growth, and tracking-aware engagement rates"}}}},"/analytics/campaigns":{"get":{"summary":"Per-campaign analytics for a site over the recent lookback window","parameters":[{"in":"query","name":"site_id","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":50}}],"responses":{"200":{"description":"Campaign analytics sorted by sent volume"}}}},"/analytics/revenue":{"get":{"summary":"Stripe connector revenue, churn, MRR, and subscription lifecycle analytics","parameters":[{"in":"query","name":"days","schema":{"type":"integer","minimum":1,"maximum":365}},{"in":"query","name":"connection_id","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Revenue analytics including MRR, logo churn rate, lifecycle counts, collected revenue, and failed payments"}}}},"/analytics/engagement":{"get":{"summary":"Contact engagement lifecycle analytics","responses":{"200":{"description":"Engagement analytics"}}}},"/analytics/conversion":{"post":{"summary":"Record a conversion event (analytics alias)","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversionRequest"}}}},"responses":{"200":{"description":"Conversion recorded"}}}},"/analytics/performance":{"get":{"summary":"Email performance trends and best send times","responses":{"200":{"description":"Performance analytics"}}}},"/sites/{id}/deliverability":{"get":{"summary":"Site deliverability status with DMARC monitor summary","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deliverability status"}}}},"/sites/{id}/domain-health":{"get":{"summary":"Site domain health history","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Domain health snapshots"}}}},"/sites/{id}/insights":{"get":{"summary":"Site analytics insights","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Insights"}}}},"/sites/{id}/abuse-monitor":{"get":{"summary":"Site complaint and abuse monitor","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Abuse monitor"}}}},"/lists":{"get":{"summary":"List contact lists with subscriber counts","parameters":[{"in":"query","name":"site_id","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Lists — { lists[{ id, name, subscriber_count }] }"}}}},"/lists/{id}/subscribers":{"get":{"summary":"Page through a contact list's subscribers","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}},{"in":"query","name":"page","schema":{"type":"integer","minimum":1}}],"responses":{"200":{"description":"Subscribers — { subscribers[{ id, email, status, subscribed_at }], total, page }"}}}},"/social/request-reply":{"post":{"summary":"Request a LinkedIn personal reply proposal card","description":"Parses a LinkedIn comment or post URL, drafts a suggested reply using transient in-memory context (never stored or logged), and creates a pending proposal card for the target member. Never posts directly; a human Accept is required.","operationId":"requestSocialReply","tags":["Social"],"security":[{"apiKeyAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["comment_or_post_url"],"properties":{"comment_or_post_url":{"type":"string","description":"The LinkedIn comment or post URL/URN to reply to."},"comment_text_for_drafting":{"type":"string","description":"The comment (or post) text, needed to draft the reply. Used once in memory, never stored or logged. Without it the request returns 422 and no card is created."},"target_member_id":{"type":"string","format":"uuid","description":"Target member UUID. Required for API keys lacking member identity."}}}}}},"responses":{"200":{"description":"Existing pending proposal card returned (deduplication)"},"201":{"description":"Proposal card created","content":{"application/json":{"schema":{"type":"object","properties":{"card_id":{"type":"string","format":"uuid"},"status":{"type":"string"},"duplicate":{"type":"boolean"},"approve_link":{"type":"string"},"message":{"type":"string"},"post_urn":{"type":"string"},"comment_urn":{"type":"string","nullable":true}}}}}},"400":{"description":"Bad request"},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"429":{"description":"Rate limit exceeded"}}}},"/social/campaigns":{"get":{"summary":"List social campaigns","responses":{"200":{"description":"Social campaigns"}}},"post":{"summary":"Create a social campaign with posts (honors Idempotency-Key)","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"site_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"posts":{"type":"array","items":{"type":"object","properties":{"platform":{"type":"string"},"scheduled_at":{"type":"string","format":"date-time"},"linkedin_company_crosspost":{"type":"boolean"},"linkedin_company_page_id":{"type":"string"},"content":{"type":"object"}}}}}}}}},"responses":{"201":{"description":"Created — { campaign_id, status, posts[] }"}}}},"/social/campaigns/{id}":{"get":{"summary":"Get a social campaign with its posts","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Social campaign"}}},"patch":{"summary":"Update social campaign status/name — status 'active' dispatches held posts","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["draft","active","paused","completed"]},"name":{"type":"string"}}}}}},"responses":{"200":{"description":"Updated"}}}},"/social/campaigns/{id}/posts":{"post":{"summary":"Add a post to a social campaign","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["platform","content"],"properties":{"platform":{"type":"string"},"linkedin_company_crosspost":{"type":"boolean"},"linkedin_company_page_id":{"type":"string"},"scheduled_at":{"type":"string","format":"date-time"},"content":{"type":"object"}}}}}},"responses":{"201":{"description":"Post created"}}}},"/suppressions":{"get":{"summary":"List suppressions","responses":{"200":{"description":"Suppressions — cursor pagination on created_at"}}},"post":{"summary":"Suppress an email address","responses":{"201":{"description":"Suppression created"}}},"delete":{"summary":"Remove a suppression by email","parameters":[{"in":"query","name":"email","required":true,"schema":{"type":"string","format":"email"}}],"responses":{"200":{"description":"Suppression removed"}}}},"/suppressions/{id}":{"delete":{"summary":"Remove a suppression by id","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Suppression removed"}}}},"/support/tickets":{"get":{"summary":"List support tickets for the caller's organization","description":"Returns customer-safe status for tickets belonging to the caller's organization. Scoped to the caller's org_id. Supports optional ticket reference and status filters.","parameters":[{"name":"ticket","in":"query","description":"Filter to a single ticket by SND number or integer ID (e.g. SND-1015 or 1015)","schema":{"type":"string"}},{"name":"status","in":"query","description":"Filter tickets by status (e.g. open, in_progress, closed)","schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","maximum":100}},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"List of support tickets"}}},"post":{"summary":"File a support ticket on the tenant's behalf (bug report, feature request or feedback)","description":"Creates a ticket in the same pipeline as the dashboard support form, including the GitHub issue and Slack alert. Attributed to the acting user (user-bound key or OAuth caller), falling back to the project owner. Supports Idempotency-Key.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["title","description"],"properties":{"title":{"type":"string"},"description":{"type":"string"},"type":{"type":"string","enum":["bug","feature","feedback","other"],"default":"bug"},"priority":{"type":"string","enum":["low","medium","high","critical"],"default":"medium"},"page_context":{"type":"object","additionalProperties":true}}}}}},"responses":{"201":{"description":"Ticket created"},"422":{"description":"Missing or invalid field, or the acting user could not be resolved"}}}},"/support/tickets/{ref}/reply":{"post":{"summary":"Post an exact Kevin-approved support ticket reply","description":"Platform-admin principal and admin scope only. The SHA-256 hash must match the exact body. An audit row is written before the comment; a failed insert gets a failure audit. No email or automatic status change. Resolved or closed tickets require explicit reopen.","parameters":[{"name":"ref","in":"path","required":true,"schema":{"type":"string","pattern":"^SND-[0-9]+$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["body","approval_ref","body_sha256"],"properties":{"body":{"type":"string"},"approval_ref":{"type":"string"},"body_sha256":{"type":"string","pattern":"^[0-9a-f]{64}$"},"reopen":{"type":"boolean"}}}}}},"responses":{"201":{"description":"Comment posted"},"403":{"description":"Not available to tenant principals"},"409":{"description":"Resolved or closed ticket requires reopen"},"422":{"description":"Missing input or exact-text hash mismatch"},"429":{"description":"Organization hourly reply limit exceeded"},"503":{"description":"Audit write failed; no comment was posted"}}}},"/webhook-subscriptions":{"get":{"summary":"List registered outbound webhooks (secrets never returned)","responses":{"200":{"description":"Subscriptions"}}},"post":{"summary":"Register an outbound webhook — signing secret returned once","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url","events"],"properties":{"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string"}},"site_id":{"type":"string","format":"uuid"},"description":{"type":"string"},"secret":{"type":"string","description":"Optional caller-supplied signing secret (16-256 chars)"}}}}}},"responses":{"201":{"description":"Created — includes the signing secret (only time it is returned)"}}}},"/webhook-subscriptions/{id}":{"patch":{"summary":"Update an outbound webhook (url, events, active, description)","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Updated"}}},"delete":{"summary":"Remove an outbound webhook","parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Removed"}}}},"/embed/token/validate":{"get":{"summary":"Verify a Sendinel-issued embed token (server-to-server, for embedding platforms)","description":"Non-consuming (peek) validation — the same token may be checked repeatedly across a full embed session, not just once. Auth via Authorization Bearer header or embed_token query param.","parameters":[{"in":"query","name":"embed_token","required":false,"description":"Embed token, if not supplied via the Authorization Bearer header","schema":{"type":"string"}}],"responses":{"200":{"description":"Token is valid — returns the project and scopes it grants","content":{"application/json":{"schema":{"type":"object","properties":{"project_id":{"type":"string","format":"uuid"},"scopes":{"type":"array","items":{"type":"string"}}}}}}},"401":{"description":"Embed token missing, invalid, or expired"},"403":{"description":"Embed token does not grant the required scope"}}}}}}