{"openapi":"3.1.0","info":{"title":"SAGARIS Public API","version":"1.0.0","description":"Versioned public REST API (/api/v1). Authenticate with a workspace-scoped API key as a Bearer token (Authorization: Bearer ros_live_...). The workspace is resolved from the key; no client-supplied workspace id is ever trusted. Writes are rate-limited per key, and the writes that declare an Idempotency-Key header honour it. This document is served unauthenticated as developer documentation.","termsOfService":"https://www.sagaris.ai/terms","contact":{"name":"SAGARIS Support","url":"https://www.sagaris.ai/contact","email":"support@sagaris.ai"}},"servers":[{"url":"https://www.sagaris.ai/api/v1","description":"Versioned public API surface."}],"security":[{"ApiKeyAuth":[]}],"tags":[{"name":"Contacts","description":"Create, update, tag, and list contacts."},{"name":"Activities","description":"Log manual activities on a contact."},{"name":"Deals","description":"Create opportunities for workspace contacts."},{"name":"Sequences","description":"List sequences and enroll contacts."},{"name":"Suppression","description":"Add emails to the workspace suppression list."},{"name":"Social checklist","description":"Read the Social checklist and what is due, and hand an item's output to a person for approval. Nothing here publishes or sends."}],"paths":{"/linkedin/actions":{"get":{"operationId":"getLinkedinActions","summary":"Read assigned LinkedIn actions and their exact campaign/sequence context","security":[{"ApiKeyAuth":["linkedin.read"]}],"parameters":[{"name":"status","in":"query","schema":{"type":"string"}},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"Workspace-scoped actions; never a send authorization"},"401":{"description":"Missing, malformed, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key does not hold the required scope for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database / API-keys table is not provisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"manageLinkedinExecution","summary":"Read the brief, record sender health, heartbeat, schedules, runs and observed replies, claim, start or report a LinkedIn action","description":"Health observations record genuine sender measurements, never contact touches or send permission. Use null for unknown metrics; never invent healthy values. Counts must describe the same completed invitation cohort. Evidence expires after 24 hours; replaying an observation ID does not refresh it. Execute only after a successful start response, only once, using the exact copy it returns. Stop for login or CAPTCHA. Report outcome_unknown after an ambiguous browser result; never blindly retry. The server owns sequence advancement. Engine actions (source engine, planned by the parallel LinkedIn engine for the account named by setup) are claimable only when that account's automation mode and windows allow; a 409 names the reason and the next window. A persistent agent (a ChatGPT Dot or any always-on client) starts every run with command brief: it returns the account, its automation windows, today's remaining limits, how many actions are claimable now, whether a person asked for a reconnect, the event names it can subscribe to, and the current instructions with a version. It reports its recurring schedules with schedule_report (RFC 5545 RRULE lines with a time zone, plus how often it checks in), each run with run_report (idempotent on runId), and replies, received messages and accepted invitations it observed with sync (idempotent on externalKey; a reply is logged like a rep-logged reply and stops the engine's open work for that contact; an unmatched profile is recorded without its excerpt). When a person asks for a reconnect, acknowledge with reconnect_ack, then ask the owner to sign in through your private sign-in form or by taking over your browser, never by typing or receiving their password, and send a ready heartbeat; that heartbeat resolves the request. None of brief, schedule_report, run_report, sync or reconnect_ack sends, approves, creates, edits or completes an action; a result with its lease token is the only way an action completes.","security":[{"ApiKeyAuth":["linkedin.execute"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["command"],"properties":{"command":{"type":"string","enum":["setup","health_observation","connection_accepted","heartbeat","brief","schedule_report","run_report","sync","reconnect_ack","claim","start","result"]},"observation":{"type":"object","additionalProperties":false,"required":["observationId","profileUrl","observedAt","windowStartedAt","windowEndedAt","invitationsSent","invitationsAccepted","invitationsWithdrawn","warnings","accountAgeDays","evidence"],"properties":{"observationId":{"type":"string","format":"uuid"},"profileUrl":{"type":"string","maxLength":500},"observedAt":{"type":"string","format":"date-time"},"windowStartedAt":{"type":"string","format":"date-time"},"windowEndedAt":{"type":"string","format":"date-time"},"invitationsSent":{"type":["integer","null"],"minimum":0,"maximum":1000000},"invitationsAccepted":{"type":["integer","null"],"minimum":0,"maximum":1000000},"invitationsWithdrawn":{"type":["integer","null"],"minimum":0,"maximum":1000000},"warnings":{"type":["integer","null"],"minimum":0,"maximum":1000},"accountAgeDays":{"type":["integer","null"],"minimum":0,"maximum":36525},"evidence":{"type":"string","minLength":1,"maxLength":2000}}},"session":{"type":"string","enum":["ready","login_required","captcha","restricted"]},"profileUrl":{"type":"string","description":"Observed signed-in sender profile URL, required for heartbeat"},"actionId":{"type":"string","format":"uuid"},"leaseToken":{"type":"string","format":"uuid"},"outcome":{"type":"string","enum":["connection_sent","message_sent","already_connected","profile_unavailable","login_required","captcha","rate_limited","not_sent","outcome_unknown"]},"evidence":{"type":"string","minLength":1,"maxLength":2000},"schedules":{"type":"array","maxItems":12,"description":"schedule_report: the recurring schedules the agent configured for itself.","items":{"type":"object","additionalProperties":false,"required":["label","rrule","timezone"],"properties":{"label":{"type":"string","minLength":1,"maxLength":80},"rrule":{"type":"string","minLength":6,"maxLength":300,"description":"RFC 5545 recurrence rule, with or without the RRULE: prefix"},"timezone":{"type":"string","minLength":1,"maxLength":64,"description":"IANA time zone the rule is read in"},"nextRunAt":{"type":"string","format":"date-time"}}}},"checkInMinutes":{"type":"integer","minimum":5,"maximum":1440,"description":"schedule_report: how often the agent checks in inside a window"},"runId":{"type":"string","format":"uuid","description":"run_report: the agent's id for this run; a repeat returns the stored report"},"startedAt":{"type":"string","format":"date-time"},"finishedAt":{"type":"string","format":"date-time"},"status":{"type":"string","enum":["ok","partial","failed"],"description":"run_report: how the run ended"},"claimed":{"type":"integer","minimum":0,"maximum":10000},"completed":{"type":"integer","minimum":0,"maximum":10000},"failed":{"type":"integer","minimum":0,"maximum":10000},"synced":{"type":"integer","minimum":0,"maximum":10000},"notes":{"type":"string","maxLength":500,"description":"run_report: a short note; redacted before it is stored"},"observations":{"type":"array","minItems":1,"maxItems":50,"description":"sync: replies, received messages and accepted invitations observed in the LinkedIn session.","items":{"type":"object","additionalProperties":false,"required":["kind","profileUrl","observedAt","externalKey"],"properties":{"kind":{"type":"string","enum":["reply","connection_accepted","message_received"]},"profileUrl":{"type":"string","minLength":1,"maxLength":500,"description":"The other person's LinkedIn profile URL"},"observedAt":{"type":"string","format":"date-time"},"externalKey":{"type":"string","minLength":1,"maxLength":200,"description":"The agent's stable key for this observation; a repeat is answered already_recorded"},"excerpt":{"type":"string","maxLength":500,"description":"A short excerpt of the reply; redacted before it is stored, and never stored for an unmatched profile"}}}}}}}}},"responses":{"200":{"description":"Recorded transition, brief, report or per-observation sync result"},"401":{"description":"Missing, malformed, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key does not hold the required scope for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Held, stale claim, quota, unbound agent, or conflicting result"},"503":{"description":"The database / API-keys table is not provisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contacts":{"get":{"tags":["Contacts"],"summary":"List contacts","description":"List contacts in the key's workspace, newest first. Requires the contacts.read scope.","operationId":"listContacts","security":[{"ApiKeyAuth":["contacts.read"]}],"parameters":[{"name":"limit","in":"query","required":false,"description":"Page size. Clamped to 1..100; defaults to 25.","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"offset","in":"query","required":false,"description":"Zero-based row offset. Defaults to 0.","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"A paginated list of contacts.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Contact"}},"meta":{"$ref":"#/components/schemas/ListMeta"},"error":{"type":"null"}},"required":["data","meta","error"]}}}},"401":{"description":"Missing, malformed, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key does not hold the required scope for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Query failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database / API-keys table is not provisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Contacts"],"summary":"Create a contact","description":"Create a contact in the key's workspace. At least one of email, first_name, or last_name is required. The row is stamped source=public_api. Requires the contacts.write scope.","operationId":"createContact","security":[{"ApiKeyAuth":["contacts.write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque client-supplied key. A retry with the same key replays the original response instead of writing again.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"At least one of email, first_name, or last_name is required.","properties":{"email":{"type":"string","nullable":true},"first_name":{"type":"string","nullable":true},"last_name":{"type":"string","nullable":true},"company":{"type":"string","nullable":true},"job_title":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"linkedin_url":{"type":"string","nullable":true},"website":{"type":"string","nullable":true}}}}}},"responses":{"201":{"description":"The created contact.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactEnvelope"}}}},"400":{"description":"Invalid JSON body or field validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key does not hold the required scope for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-key write rate limit exceeded (60 requests / 60s).","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Insert failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database / API-keys table is not provisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contacts/assert":{"put":{"tags":["Contacts"],"summary":"Assert a contact by email","operationId":"assertContact","description":"Create or update a live contact by case-insensitive email in the key workspace. Returns 201 on create or 200 on update. Omitted fields are preserved; empty strings clear optional profile values. Reuses canonical create/update billing and rate gates. Idempotency-Key is ignored: retries match the same live email. No lifecycle, consent, workspace or provenance overrides. Concurrent assertions converge through the live-email unique index; a race retry can consume a second write-rate token.","security":[{"ApiKeyAuth":["contacts.write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["email"],"properties":{"email":{"type":"string","format":"email","maxLength":320},"first_name":{"type":"string","maxLength":500},"last_name":{"type":"string","maxLength":500},"company":{"type":"string","maxLength":500},"job_title":{"type":"string","maxLength":500},"phone":{"type":"string","maxLength":500},"website":{"type":"string","maxLength":2000,"description":"Absolute URL or empty string."},"linkedin_url":{"type":"string","maxLength":2000,"description":"Absolute URL or empty string."}}}}}},"responses":{"200":{"description":"Updated contact.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactEnvelope"}}}},"201":{"description":"Created contact.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactEnvelope"}}}},"400":{"description":"Invalid JSON body or field validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Workspace billing gate blocked this write."},"403":{"description":"The key does not hold the required scope for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Concurrent email conflict; retry the assertion."},"429":{"description":"Per-key write rate limit exceeded (60 requests / 60s).","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Database or audit failure; retry the assertion."},"503":{"description":"The database / API-keys table is not provisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contacts/query":{"post":{"tags":["Contacts"],"summary":"Query contacts","operationId":"queryContacts","description":"Filter live contacts in the key workspace. Filters are ANDed; email is case-insensitive literal equality, company/source exact equality and updated_since inclusive. Equal timestamps are ordered by id. Shares the 60 requests/minute key budget with writes. Offset pages are not a snapshot: use overlapping changed-since windows and deduplicate ids while records change.","security":[{"ApiKeyAuth":["contacts.read"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"filter":{"type":"object","additionalProperties":false,"properties":{"email":{"type":"string","format":"email","maxLength":320},"company":{"type":"string","minLength":1,"maxLength":500},"source":{"type":"string","minLength":1,"maxLength":100},"updated_since":{"type":"string","format":"date-time"}}},"sort":{"type":"string","enum":["created_at","updated_at"],"default":"created_at"},"direction":{"type":"string","enum":["asc","desc"],"default":"desc"},"limit":{"type":"integer","minimum":1,"maximum":100,"default":25},"offset":{"type":"integer","minimum":0,"maximum":100000,"default":0}}}}}},"responses":{"200":{"description":"Matching contacts and pagination.","content":{"application/json":{"schema":{"type":"object","required":["data","meta","error"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Contact"}},"meta":{"$ref":"#/components/schemas/ListMeta"},"error":{"type":"null"}}}}}},"400":{"description":"Invalid JSON body or field validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key does not hold the required scope for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-key write rate limit exceeded (60 requests / 60s).","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Query failure."},"503":{"description":"The database / API-keys table is not provisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contacts/{id}":{"get":{"tags":["Contacts"],"summary":"Get a contact","operationId":"getContact","description":"Retrieve one live contact by UUID in the authenticated key workspace. Unknown, archived and foreign contacts return 404.","security":[{"ApiKeyAuth":["contacts.read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The contact id, which must belong to the key's workspace.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The contact.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactEnvelope"}}}},"400":{"description":"Invalid JSON body or field validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key does not hold the required scope for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The referenced resource was not found in the key's workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Query failure."},"503":{"description":"The database / API-keys table is not provisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Contacts"],"summary":"Update contact fields","description":"Update whitelisted fields of a workspace-owned contact. Uses the same field whitelist and normalizers as the internal edit path and writes contact_field_corrections provenance. Requires the contacts.write scope. `lifecycle` is a GATED CRM stage change, not a plain field: it must respect the stage adjacency rule, advancing to `lead` requires a confirmed meeting on the contact's lead, and advancing to `opportunity` requires a rep-confirmed BANT assessment. An unearned move is refused (422 `meeting_required` / 400 `bant_required`), and an API key can never force past a gate (403 `force_not_permitted`). The data-quality columns (`data_confidence`, `provenance`, `confidence_score`) are SERVER-OWNED: they are derived from the edit itself and stamped at the `public_api` confidence band, so supplying them is ignored rather than persisted.","operationId":"updateContact","security":[{"ApiKeyAuth":["contacts.write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The contact id, which must belong to the key's workspace.","schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque client-supplied key. A retry with the same key replays the original response instead of writing again.","schema":{"type":"string"}}],"requestBody":{"required":true,"description":"A partial set of the contact fields accepted by sanitizeContactUpdate (the internal PATCH whitelist), optionally plus the gated `lifecycle` stage. At least one supported field must be supplied. Unsupported and server-owned keys (`data_confidence`, `provenance`, `confidence_score`) are ignored.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"responses":{"200":{"description":"The updated contact (public field projection).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactEnvelope"}}}},"400":{"description":"Invalid JSON body or field validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key does not hold the required scope for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The referenced resource was not found in the key's workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-key write rate limit exceeded (60 requests / 60s).","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Lookup, update, or provenance-write failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database / API-keys table is not provisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contacts/{id}/activities":{"post":{"tags":["Activities"],"summary":"Log an activity on a contact","description":"Log a manual activity or note on a workspace-owned contact. Only hand-loggable activity types are accepted; the row is stamped metadata.source=public_api. Outbound types advance the contact's last_contacted_at. Requires the activities.write scope.","operationId":"logContactActivity","security":[{"ApiKeyAuth":["activities.write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The contact id, which must belong to the key's workspace.","schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque client-supplied key. A retry with the same key replays the original response instead of writing again.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["type"],"properties":{"type":{"type":"string","description":"A hand-loggable activity type.","enum":["note_added","call_made","call_received","voicemail_left","meeting_booked","meeting_completed","sms_sent","linkedin_message"]},"subject":{"type":"string","description":"Required for every type except note_added (where it defaults to \"Note\")."},"body":{"type":"string","description":"Required when type is note_added; optional otherwise."},"metadata":{"type":"object","additionalProperties":true,"description":"Optional caller metadata; source and api_key_id are stamped by the server."}}}}}},"responses":{"201":{"description":"The created activity.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Activity"},"error":{"type":"null"}},"required":["data","error"]}}}},"400":{"description":"Invalid JSON body or field validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key does not hold the required scope for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The referenced resource was not found in the key's workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-key write rate limit exceeded (60 requests / 60s).","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Lookup, insert, or last-contacted update failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database / API-keys table is not provisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/contacts/{id}/tags":{"post":{"tags":["Contacts"],"summary":"Add tags to a contact","description":"Add one or more tags to a workspace-owned contact. Tags are lower-cased, de-duplicated, sorted, additive, and capped at 50. Requires the contacts.write scope.","operationId":"addContactTags","security":[{"ApiKeyAuth":["contacts.write"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The contact id, which must belong to the key's workspace.","schema":{"type":"string","format":"uuid"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque client-supplied key. A retry with the same key replays the original response instead of writing again.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Provide tags[] or a single tag. At least one non-empty tag is required.","properties":{"tags":{"type":"array","items":{"type":"string"}},"tag":{"type":"string"}}}}}},"responses":{"200":{"description":"The contact id and its merged tag list.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}}},"required":["id","tags"]},"error":{"type":"null"}},"required":["data","error"]}}}},"400":{"description":"Invalid JSON body or field validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key does not hold the required scope for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The referenced resource was not found in the key's workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-key write rate limit exceeded (60 requests / 60s).","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Lookup or update failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database / API-keys table is not provisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/deals":{"post":{"tags":["Deals"],"summary":"Create a deal / opportunity","description":"Create an opportunity for a workspace-owned contact. When the contact's lifecycle requires it, a valid bant_assessment is mandatory (same BANT gate as the internal create path). Requires the deals.write scope.","operationId":"createDeal","security":[{"ApiKeyAuth":["deals.write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque client-supplied key. A retry with the same key replays the original response instead of writing again.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["contact_id","title"],"properties":{"contact_id":{"type":"string","description":"A contact in the key's workspace."},"title":{"type":"string"},"value":{"type":"number","description":"Deal value; defaults to 0.","default":0},"currency":{"type":"string","description":"ISO currency code; defaults to USD.","default":"USD"},"stage":{"type":"string","default":"prospecting","enum":["prospecting","qualification","proposal","negotiation","closed_won","closed_lost"]},"bant_assessment":{"type":"object","additionalProperties":true,"description":"Required only when the contact's lifecycle gate demands BANT confirmation."}}}}}},"responses":{"201":{"description":"The created opportunity.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Deal"},"error":{"type":"null"}},"required":["data","error"]}}}},"400":{"description":"Validation failure or BANT confirmation required (code bant_required).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key does not hold the required scope for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The referenced resource was not found in the key's workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-key write rate limit exceeded (60 requests / 60s).","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Lookup or insert failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database / API-keys table is not provisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sequences":{"get":{"tags":["Sequences"],"summary":"List sequences","description":"List email sequences in the key's workspace, newest first. Requires the sequences.read scope.","operationId":"listSequences","security":[{"ApiKeyAuth":["sequences.read"]}],"parameters":[{"name":"limit","in":"query","required":false,"description":"Page size. Clamped to 1..100; defaults to 25.","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"offset","in":"query","required":false,"description":"Zero-based row offset. Defaults to 0.","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"A paginated list of sequences.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Sequence"}},"meta":{"$ref":"#/components/schemas/ListMeta"},"error":{"type":"null"}},"required":["data","meta","error"]}}}},"401":{"description":"Missing, malformed, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key does not hold the required scope for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Query failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database / API-keys table is not provisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sequences/enroll":{"post":{"tags":["Sequences"],"summary":"Enroll a contact in a sequence","description":"Enroll a workspace-owned contact into a workspace-owned email sequence. The sequence must carry an applied approval whose authorized audience covers the contact (PRD §29: approval cannot be bypassed via the API); otherwise the request is rejected with 409. Requires the sequences.write scope.","operationId":"enrollInSequence","security":[{"ApiKeyAuth":["sequences.write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque client-supplied key. A retry with the same key replays the original response instead of writing again.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["sequence_id","contact_id"],"properties":{"sequence_id":{"type":"string","description":"A sequence in the key's workspace."},"contact_id":{"type":"string","description":"A contact in the key's workspace."}}}}}},"responses":{"201":{"description":"The created enrollment.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SequenceEnrollment"},"error":{"type":"null"}},"required":["data","error"]}}}},"400":{"description":"Invalid JSON body or field validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key does not hold the required scope for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The sequence or contact was not found in the key's workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The sequence has no applied approval (`sequence_not_authorized`), or the contact is outside the applied approval's authorized audience (`contact_not_authorized`). Enrollment through the API cannot bypass approval (PRD §29).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-key write rate limit exceeded (60 requests / 60s).","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Lookup or insert failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database / API-keys table is not provisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/suppression":{"post":{"tags":["Suppression"],"summary":"Add to suppression list","description":"Add an email to the workspace suppression list. Provide an email directly, or a contact_id that resolves to its email within the key's workspace. Upserts on (workspace_id, email). Requires the suppression.write scope.","operationId":"addSuppression","security":[{"ApiKeyAuth":["suppression.write"]}],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque client-supplied key. A retry with the same key replays the original response instead of writing again.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"Provide either email or contact_id.","properties":{"email":{"type":"string","format":"email"},"contact_id":{"type":"string","description":"A contact in the key's workspace; resolves to its email."},"reason":{"type":"string","default":"manual","description":"Suppression reason; defaults to manual."}}}}}},"responses":{"201":{"description":"The suppression row.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Suppression"},"error":{"type":"null"}},"required":["data","error"]}}}},"400":{"description":"Invalid JSON body or field validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key does not hold the required scope for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"The referenced resource was not found in the key's workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-key write rate limit exceeded (60 requests / 60s).","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Lookup or upsert failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database / API-keys table is not provisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/social/checklist/items":{"get":{"tags":["Social checklist"],"summary":"List the Social checklist with this workspace's status","description":"Every Social checklist catalog item with the status recorded for the key's workspace, its evidence, its frequency and next due date, its assessed state (assessment: done, missing, unknown or not_applicable, with the evidence or the reason and its sources; a person's recorded status always wins), its executor record (evidence verdict, the person's action, exceptions, priority, dependencies, runner), any approval card waiting on it or a change a person asked for, and what an assistant may do with it (agent.can_record_evidence, agent.accepts_output_kinds). Read only. Requires the social_checklist.read scope.","operationId":"listSocialChecklistItems","security":[{"ApiKeyAuth":["social_checklist.read"]}],"parameters":[{"name":"kind","in":"query","required":false,"description":"Only one-time or only ongoing items.","schema":{"type":"string","enum":["one_time","ongoing"]}},{"name":"frequency","in":"query","required":false,"description":"Only items of this frequency.","schema":{"type":"string","enum":["once","daily","with_every_post","weekly","monthly","every_3_months","every_6_months","yearly","as_needed"]}},{"name":"status","in":"query","required":false,"description":"Only items with this status in the workspace (not_assessed means nothing recorded yet).","schema":{"type":"string","enum":["not_assessed","not_started","in_progress","done","blocked","not_applicable"]}},{"name":"limit","in":"query","required":false,"description":"Page size. Clamped to 1..100; defaults to 25.","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"offset","in":"query","required":false,"description":"Zero-based row offset. Defaults to 0.","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"A page of checklist items.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SocialChecklistItem"}},"meta":{"$ref":"#/components/schemas/SocialChecklistListMeta"},"error":{"type":"null"}},"required":["data","meta","error"]}}}},"400":{"description":"Invalid JSON body or field validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key does not hold the required scope for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-key read rate limit exceeded (120 requests / 60s).","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Query failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database / API-keys table is not provisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/social/checklist/due":{"get":{"tags":["Social checklist"],"summary":"List the Social checklist items due now","description":"The checklist items due now in the key's workspace, by the runner's own rules and order: a change a person asked for on a card first (with their note), then the default checklist by priority, one-time work before ongoing. Not listed: test-first and replaced items, done, not applicable, blocked, an item with a card waiting for a person, a one-time item closed by a denied card or waiting on its dependencies, and event-triggered items (with every post, as needed). Each entry carries due_reason. Read only. Requires the social_checklist.read scope.","operationId":"listDueSocialChecklistItems","security":[{"ApiKeyAuth":["social_checklist.read"]}],"parameters":[{"name":"limit","in":"query","required":false,"description":"Page size. Clamped to 1..100; defaults to 25.","schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"offset","in":"query","required":false,"description":"Zero-based row offset. Defaults to 0.","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"A page of due checklist items.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SocialChecklistDueItem"}},"meta":{"$ref":"#/components/schemas/SocialChecklistListMeta"},"error":{"type":"null"}},"required":["data","meta","error"]}}}},"401":{"description":"Missing, malformed, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key does not hold the required scope for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-key read rate limit exceeded (120 requests / 60s).","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Query failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database / API-keys table is not provisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/social/checklist/items/{id}/context":{"get":{"tags":["Social checklist"],"summary":"Get one Social checklist item with the context to do it","description":"One checklist item in full (task, done-when, why, sources) with its status in the key's workspace, and the workspace facts the work is done from: the saved brand voice, the profile's facts (the only claims a draft may make), each social connection's state (never a token), and the ten most recent posts. A part that cannot be read says unavailable. Read only. Requires the social_checklist.read scope.","operationId":"getSocialChecklistItemContext","security":[{"ApiKeyAuth":["social_checklist.read"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A Social checklist item id from the catalog, such as OT-02 (one-time) or ON-46 (ongoing).","schema":{"type":"string","pattern":"^(OT|ON)-[0-9]{2}$"}}],"responses":{"200":{"description":"The item and its context.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SocialChecklistItemContext"},"error":{"type":"null"}},"required":["data","error"]}}}},"400":{"description":"Invalid JSON body or field validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key does not hold the required scope for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No catalog item has this id (code item_not_found).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-key read rate limit exceeded (120 requests / 60s).","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Query failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database / API-keys table is not provisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/social/checklist/items/{id}/submissions":{"post":{"tags":["Social checklist"],"summary":"Submit a Social checklist item's output for approval","description":"Hand one item's finished output to a person. A social_post becomes one post at needs_review in the Social review queue. A document, for an item whose person action is Approve, Request change or Deny, becomes one pending approval card; when a person asked for a change on the item's last card, it is the redo. 409 card_waiting while a card waits for a person, 409 output_kind_not_accepted when the item does not take that output. Nothing is published, sent, approved or scheduled by this call: a person decides. Requires the social_checklist.submit scope.","operationId":"submitSocialChecklistItemOutput","security":[{"ApiKeyAuth":["social_checklist.submit"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A Social checklist item id from the catalog, such as OT-02 (one-time) or ON-46 (ongoing).","schema":{"type":"string","pattern":"^(OT|ON)-[0-9]{2}$"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque client-supplied key. A retry with the same key replays the original response instead of writing again.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"output_kind social_post takes platform, title, body, scheduled_for. output_kind document takes title, body, summary. A field the kind does not take is refused.","properties":{"output_kind":{"type":"string","enum":["social_post","document"],"description":"social_post goes to the Social review queue; document becomes an approval card."},"platform":{"type":"string","enum":["linkedin","x","instagram","facebook","tiktok","blog","youtube","youtube_shorts","pinterest","threads"],"description":"social_post only: the channel the post is written for."},"title":{"type":"string","maxLength":300,"description":"Required for a document (at most 200 characters, the card's title); optional for a social_post."},"body":{"type":"string","maxLength":20000,"description":"The finished work a person reviews. A social_post body is at most 20000 characters; a document's summary and body together fit an approval card, at most 4000."},"scheduled_for":{"type":"string","format":"date-time","description":"social_post only: a suggested publish time in the future. The reviewer sets the real one."},"summary":{"type":"string","maxLength":500,"description":"document only: one or two lines shown above the body on the approval card."}},"required":["output_kind","body"],"additionalProperties":false}}}},"responses":{"201":{"description":"The draft post or approval card, waiting for a person.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SocialChecklistSubmission"},"error":{"type":"null"}},"required":["data","error"]}}}},"400":{"description":"Invalid JSON body or field validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"The workspace is read-only for billing reasons and cannot write through a key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key does not hold the required scope for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No catalog item has this id (code item_not_found).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-key write rate limit exceeded (60 requests / 60s).","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Insert failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database / API-keys table is not provisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/social/checklist/items/{id}/evidence":{"post":{"tags":["Social checklist"],"summary":"Record evidence on a Social checklist observation item","description":"Record what an assistant found for one of the observation items whose done-when is a recorded finding (agent.can_record_evidence is true). The mark is stored as an assistant's (status_source agent, naming this key and no person) and is never written over a person's mark or one SAGARIS detected itself (409 person_mark, detected_mark). Any other item answers 409 approval_required: submit its output instead. Nothing live changes. Requires the social_checklist.record scope.","operationId":"recordSocialChecklistItemEvidence","security":[{"ApiKeyAuth":["social_checklist.record"]}],"parameters":[{"name":"id","in":"path","required":true,"description":"A Social checklist item id from the catalog, such as OT-02 (one-time) or ON-46 (ongoing).","schema":{"type":"string","pattern":"^(OT|ON)-[0-9]{2}$"}},{"name":"Idempotency-Key","in":"header","required":false,"description":"Opaque client-supplied key. A retry with the same key replays the original response instead of writing again.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["done","in_progress"],"description":"done when the finding is complete; in_progress when part of it is."},"evidence":{"type":"string","maxLength":2000,"description":"What was found, with a link where there is one."},"done_on":{"type":"string","format":"date","description":"status done only: the calendar day it was done (YYYY-MM-DD). Defaults to today."}},"required":["status","evidence"],"additionalProperties":false}}}},"responses":{"200":{"description":"An existing assistant's mark was updated.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SocialChecklistEvidence"},"error":{"type":"null"}},"required":["data","error"]}}}},"201":{"description":"A new assistant's mark was recorded.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SocialChecklistEvidence"},"error":{"type":"null"}},"required":["data","error"]}}}},"400":{"description":"Invalid JSON body or field validation failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"The workspace is read-only for billing reasons and cannot write through a key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key does not hold the required scope for this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No catalog item has this id (code item_not_found).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"The item needs a person (approval_required), SAGARIS marks it itself (detected_by_sagaris, detected_mark), or a person has recorded it (person_mark). Nothing was written.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Per-key write rate limit exceeded (60 requests / 60s).","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Write failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"The database / API-keys table is not provisioned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"ApiKeyAuth":{"type":"http","scheme":"bearer","description":"Workspace-scoped API key presented as a Bearer token (Authorization: Bearer ros_live_...). The workspace is resolved from the key."}},"schemas":{"Error":{"type":"object","description":"The standard v1 error envelope.","properties":{"data":{"type":"null"},"error":{"type":"string","description":"Human-readable error message."},"code":{"type":"string","description":"Machine-readable error code (e.g. validation_failed, rate_limited)."},"correlationId":{"type":"string","description":"Present on unexpected 500s for support correlation."}},"required":["data","error"]},"ListMeta":{"type":"object","properties":{"total":{"type":"integer","nullable":true},"limit":{"type":"integer"},"offset":{"type":"integer"},"hasMore":{"type":"boolean"}},"required":["total","limit","offset","hasMore"]},"Contact":{"type":"object","description":"The public contact projection returned by list and create.","properties":{"id":{"type":"string"},"email":{"type":"string","nullable":true},"first_name":{"type":"string","nullable":true},"last_name":{"type":"string","nullable":true},"company":{"type":"string","nullable":true},"job_title":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"linkedin_url":{"type":"string","nullable":true},"website":{"type":"string","nullable":true},"lifecycle":{"type":"string","nullable":true},"source":{"type":"string","nullable":true},"tags":{"type":"array","items":{"type":"string"},"nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"ContactEnvelope":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Contact"},"error":{"type":"null"}},"required":["data","error"]},"Activity":{"type":"object","properties":{"id":{"type":"string"},"contact_id":{"type":"string"},"workspace_id":{"type":"string"},"type":{"type":"string"},"subject":{"type":"string","nullable":true},"body":{"type":"string","nullable":true},"metadata":{"type":"object","additionalProperties":true},"created_at":{"type":"string","format":"date-time"}}},"Deal":{"type":"object","properties":{"id":{"type":"string"},"contact_id":{"type":"string"},"workspace_id":{"type":"string"},"title":{"type":"string"},"value":{"type":"number"},"currency":{"type":"string"},"stage":{"type":"string"},"probability":{"type":"number","nullable":true},"expected_close_date":{"type":"string","format":"date","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"Sequence":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"status":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"SequenceEnrollment":{"type":"object","properties":{"id":{"type":"string"},"sequence_id":{"type":"string"},"contact_id":{"type":"string"},"workspace_id":{"type":"string"},"status":{"type":"string"},"current_step":{"type":"integer"},"enrolled_at":{"type":"string","format":"date-time","nullable":true}}},"Suppression":{"type":"object","properties":{"id":{"type":"string"},"workspace_id":{"type":"string"},"email":{"type":"string"},"reason":{"type":"string","nullable":true},"source":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"SocialChecklistListMeta":{"type":"object","properties":{"total":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"},"hasMore":{"type":"boolean"},"catalog_version":{"type":"string"},"today":{"type":"string","format":"date","description":"The server's calendar day (UTC) due dates are computed from."},"approval_cards":{"type":"string","enum":["read","unavailable"],"description":"Whether the approval cards could be read; unavailable where the card table is not migrated."},"agent_rule":{"type":"string","description":"The approval rule an assistant works under."}},"required":["total","limit","offset","hasMore","catalog_version","today","approval_cards","agent_rule"]},"SocialChecklistItem":{"type":"object","description":"One catalog item with its status in the key's workspace.","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["one_time","ongoing"]},"frequency":{"type":"string"},"frequency_label":{"type":"string"},"area":{"type":"string"},"task":{"type":"string"},"done_when":{"type":"string","nullable":true},"automation":{"type":"string","enum":["auto","ai_draft_human_approves","notify_human","human_only"]},"automation_label":{"type":"string"},"applies_to":{"type":"string"},"platforms":{"type":"string"},"status":{"type":"string","enum":["not_assessed","not_started","in_progress","done","blocked","not_applicable"]},"status_label":{"type":"string"},"evidence":{"type":"string","nullable":true},"last_done_on":{"type":"string","format":"date","nullable":true},"status_source":{"type":"string","enum":["member","system","agent"],"nullable":true},"status_updated_at":{"type":"string","format":"date-time","nullable":true},"due":{"type":"object","properties":{"kind":{"type":"string"},"label":{"type":"string"},"due_on":{"type":"string","format":"date","nullable":true},"state":{"type":"string","enum":["overdue","due_today","upcoming"],"nullable":true}}},"assessment":{"type":"object","nullable":true,"description":"The item's assessed state (spec F-05): a person's recorded status where there is one, else SAGARIS's weekly baseline assessment from evidence, else unknown with the reason it is not yet assessed here.","properties":{"state":{"type":"string","enum":["done","missing","unknown","not_applicable"]},"state_label":{"type":"string"},"reason":{"type":"string","description":"The evidence (done, missing) or the reason (unknown, not applicable)."},"sources":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string"},"label":{"type":"string"},"url":{"type":"string","nullable":true},"on":{"type":"string","format":"date","nullable":true}}}},"by":{"type":"string","enum":["person","sagaris","assistant","catalog"]},"assessed_on":{"type":"string","format":"date","nullable":true}}},"executor":{"type":"object","description":"The catalog's executor record for the item.","properties":{"verdict":{"type":"string","enum":["proven","agreed","required","supported","test_first","replaced"]},"verdict_label":{"type":"string"},"verdict_review":{"type":"string","nullable":true,"enum":["pending_owner_review"],"description":"pending_owner_review: the verdict (test_first or replaced) is the spec's proposal and the owner has not reviewed it yet. Null for a proven, agreed, required or supported item."},"is_default":{"type":"boolean","description":"In the default checklist every workspace runs: a proven, agreed, required or supported verdict, or an item kept in the run while its proposed verdict waits for the owner's review."},"person_action":{"type":"string","enum":["none","approve_change_deny"]},"person_action_label":{"type":"string"},"exceptions":{"type":"array","items":{"type":"string"}},"priority":{"type":"integer","minimum":1,"maximum":4},"priority_label":{"type":"string"},"depends_on":{"type":"array","items":{"type":"string"}},"runner":{"type":"string","nullable":true,"description":"The runner that executes the item on SAGARIS's schedule; null while none is built. The item is still assessed (assessment)."}}},"card":{"type":"object","nullable":true,"description":"An approval card waiting for a person, or a change a person asked for that no redo has answered.","properties":{"state":{"type":"string","enum":["pending","change_requested"]},"card_id":{"type":"string"},"revision":{"type":"integer"},"change_note":{"type":"string","nullable":true}}},"agent":{"type":"object","properties":{"can_record_evidence":{"type":"boolean"},"accepts_output_kinds":{"type":"array","items":{"type":"string","enum":["social_post","document"]}}}}}},"SocialChecklistDueItem":{"allOf":[{"$ref":"#/components/schemas/SocialChecklistItem"},{"type":"object","properties":{"due_reason":{"type":"string","enum":["change_requested","not_assessed","not_started","in_progress","never_done","overdue","due_today"]}}}]},"SocialChecklistItemContext":{"type":"object","properties":{"item":{"allOf":[{"$ref":"#/components/schemas/SocialChecklistItem"},{"type":"object","properties":{"verdict_basis":{"type":"string"},"ai_does":{"type":"string"},"phase":{"type":"object","nullable":true},"official_check":{"type":"string","nullable":true},"frequency_detail":{"type":"string","nullable":true},"why":{"type":"string"},"notes":{"type":"string","nullable":true},"sources":{"type":"array","items":{"type":"string"}},"value":{"type":"array","items":{"type":"string"}}}}]},"workspace":{"type":"object","properties":{"brand_voice":{"type":"object","description":"status set, not_set or unavailable, and the saved voice fields."},"profile":{"type":"object","description":"What the workspace profile states; empty fields mean nothing is on file."},"connections":{"type":"object","description":"status read or unavailable, and each provider's state (never a token)."},"recent_posts":{"type":"object","description":"status read or unavailable, and up to ten recent posts."}}},"catalog_version":{"type":"string"},"today":{"type":"string","format":"date"},"agent_rule":{"type":"string"}}},"SocialChecklistSubmission":{"type":"object","properties":{"item_id":{"type":"string"},"output_kind":{"type":"string","enum":["social_post","document"]},"destination":{"type":"string","enum":["social_review_queue","approval_inbox"]},"post":{"type":"object","description":"social_post: the created post, status needs_review."},"card":{"type":"object","description":"document: the approval card receipt, status pending, with the run that recorded it and the change request it answers, if any."},"audit_recorded":{"type":"boolean"},"next":{"type":"string"}}},"SocialChecklistEvidence":{"type":"object","properties":{"item_id":{"type":"string"},"status":{"type":"string","enum":["done","in_progress"]},"evidence":{"type":"string"},"last_done_on":{"type":"string","format":"date","nullable":true},"status_source":{"type":"string","enum":["agent"]},"updated_at":{"type":"string","format":"date-time"},"audit_recorded":{"type":"boolean"}}}}}}