{"openapi":"3.1.0","jsonSchemaDialect":"https://json-schema.org/draft/2020-12/schema","info":{"title":"SupportWhale public integration API","version":"1.0.0","description":"Organization-scoped server integrations. Bearer keys cannot access browser/session, platform-admin, billing or Socket.IO endpoints. 120 requests per key per minute and 600 per organization per minute. Each key requires its issuer to remain an active owner/admin. Public v1: additive response fields are compatible; breaking changes require a new version and migration notice. No deprecation scheduled. No public webhook subscription endpoints."},"servers":[{"url":"https://api.supportwhale.com/v1","description":"Production. For local development use your configured API base, normally http://localhost:3000/v1."}],"tags":[{"name":"conversations"},{"name":"contacts"},{"name":"agents"},{"name":"team"},{"name":"widget"}],"paths":{"/integrations/orgs/{orgId}/conversations":{"get":{"operationId":"listConversations","summary":"List conversations","description":"Required scope: conversations:read. ","tags":["conversations"],"security":[{"bearerApiKey":[]}],"x-required-scopes":["conversations:read"],"parameters":[{"name":"orgId","in":"path","required":true,"description":"The one organization bound to the key.","schema":{"type":"string","pattern":"^[a-f0-9]{24}$","example":"507f1f77bcf86cd799439011"}},{"name":"after","in":"query","required":false,"description":"Exclusive cursor: id of the final item from the previous page. Results are ordered by id ascending.","schema":{"type":"string","pattern":"^[a-f0-9]{24}$","example":"507f1f77bcf86cd799439011"}},{"name":"limit","in":"query","required":false,"description":"Page size. Continue with after until a page has fewer than limit results.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Conversation"},"maxItems":100}}}},"400":{"description":"Invalid parameters or message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, expired or revoked key; issuer no longer authorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Organization or scope is not allowed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource does not exist in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1,"example":60}}},"description":"Per-key or organization rate limit exceeded. Wait retryAfter seconds from the JSON response before retrying.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected server failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporary service failure; retry reads with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/integrations/orgs/{orgId}/conversations/{id}/messages":{"get":{"operationId":"listMessages","summary":"List conversation messages","description":"Required scope: conversations:read. Includes private team notes. Never expose this response to visitors.","tags":["conversations"],"security":[{"bearerApiKey":[]}],"x-required-scopes":["conversations:read"],"parameters":[{"name":"orgId","in":"path","required":true,"description":"The one organization bound to the key.","schema":{"type":"string","pattern":"^[a-f0-9]{24}$","example":"507f1f77bcf86cd799439011"}},{"name":"id","in":"path","required":true,"description":"Conversation identifier within this organization.","schema":{"type":"string","pattern":"^[a-f0-9]{24}$","example":"507f1f77bcf86cd799439011"}},{"name":"after","in":"query","required":false,"description":"Exclusive cursor: id of the final item from the previous page. Results are ordered by id ascending.","schema":{"type":"string","pattern":"^[a-f0-9]{24}$","example":"507f1f77bcf86cd799439011"}},{"name":"limit","in":"query","required":false,"description":"Page size. Continue with after until a page has fewer than limit results.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Message"},"maxItems":100}}}},"400":{"description":"Invalid parameters or message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, expired or revoked key; issuer no longer authorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Organization or scope is not allowed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource does not exist in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1,"example":60}}},"description":"Per-key or organization rate limit exceeded. Wait retryAfter seconds from the JSON response before retrying.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected server failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporary service failure; retry reads with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"sendMessage","summary":"Send a reply or internal note","description":"Required scope: conversations:write. Uses normal message delivery and audit behavior. Repeated clientMessageId with the same key and conversation returns the original message; do not reuse an ID for different content. Rotation retains the key identifier; a replacement key has a separate replay namespace.","tags":["conversations"],"security":[{"bearerApiKey":[]}],"x-required-scopes":["conversations:write"],"parameters":[{"name":"orgId","in":"path","required":true,"description":"The one organization bound to the key.","schema":{"type":"string","pattern":"^[a-f0-9]{24}$","example":"507f1f77bcf86cd799439011"}},{"name":"id","in":"path","required":true,"description":"Conversation identifier within this organization.","schema":{"type":"string","pattern":"^[a-f0-9]{24}$","example":"507f1f77bcf86cd799439011"}}],"responses":{"201":{"description":"Message created, or existing message returned for the same replay ID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SentMessage"}}}},"400":{"description":"Invalid parameters or message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, expired or revoked key; issuer no longer authorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Organization or scope is not allowed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource does not exist in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1,"example":60}}},"description":"Per-key or organization rate limit exceeded. Wait retryAfter seconds from the JSON response before retrying.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected server failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporary service failure; retry reads with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessage"},"example":{"body":"Thanks for your message.","clientMessageId":"reply-order-42","internal":false}}}}}},"/integrations/orgs/{orgId}/contacts":{"get":{"operationId":"listContacts","summary":"List contacts","description":"Required scope: contacts:read. ","tags":["contacts"],"security":[{"bearerApiKey":[]}],"x-required-scopes":["contacts:read"],"parameters":[{"name":"orgId","in":"path","required":true,"description":"The one organization bound to the key.","schema":{"type":"string","pattern":"^[a-f0-9]{24}$","example":"507f1f77bcf86cd799439011"}},{"name":"after","in":"query","required":false,"description":"Exclusive cursor: id of the final item from the previous page. Results are ordered by id ascending.","schema":{"type":"string","pattern":"^[a-f0-9]{24}$","example":"507f1f77bcf86cd799439011"}},{"name":"limit","in":"query","required":false,"description":"Page size. Continue with after until a page has fewer than limit results.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Contact"},"maxItems":100}}}},"400":{"description":"Invalid parameters or message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, expired or revoked key; issuer no longer authorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Organization or scope is not allowed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource does not exist in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1,"example":60}}},"description":"Per-key or organization rate limit exceeded. Wait retryAfter seconds from the JSON response before retrying.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected server failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporary service failure; retry reads with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/integrations/orgs/{orgId}/agents":{"get":{"operationId":"listAgents","summary":"List AI agents","description":"Required scope: agents:read. Returns up to 100 agent summaries. Provider credentials and inference costs are never included.","tags":["agents"],"security":[{"bearerApiKey":[]}],"x-required-scopes":["agents:read"],"parameters":[{"name":"orgId","in":"path","required":true,"description":"The one organization bound to the key.","schema":{"type":"string","pattern":"^[a-f0-9]{24}$","example":"507f1f77bcf86cd799439011"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Agent"},"maxItems":100}}}},"400":{"description":"Invalid parameters or message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, expired or revoked key; issuer no longer authorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Organization or scope is not allowed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource does not exist in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1,"example":60}}},"description":"Per-key or organization rate limit exceeded. Wait retryAfter seconds from the JSON response before retrying.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected server failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporary service failure; retry reads with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/integrations/orgs/{orgId}/team":{"get":{"operationId":"listTeam","summary":"List team memberships","description":"Required scope: team:read. Returns up to 100 memberships, without session or credential information.","tags":["team"],"security":[{"bearerApiKey":[]}],"x-required-scopes":["team:read"],"parameters":[{"name":"orgId","in":"path","required":true,"description":"The one organization bound to the key.","schema":{"type":"string","pattern":"^[a-f0-9]{24}$","example":"507f1f77bcf86cd799439011"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TeamMember"},"maxItems":100}}}},"400":{"description":"Invalid parameters or message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, expired or revoked key; issuer no longer authorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Organization or scope is not allowed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource does not exist in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1,"example":60}}},"description":"Per-key or organization rate limit exceeded. Wait retryAfter seconds from the JSON response before retrying.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected server failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporary service failure; retry reads with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/integrations/orgs/{orgId}/widget":{"get":{"operationId":"getWidget","summary":"Read the organization widget","description":"Required scope: widget:read. One logical widget per organization. Domain allowlists affect widget embedding, not the key’s tenant boundary.","tags":["widget"],"security":[{"bearerApiKey":[]}],"x-required-scopes":["widget:read"],"parameters":[{"name":"orgId","in":"path","required":true,"description":"The one organization bound to the key.","schema":{"type":"string","pattern":"^[a-f0-9]{24}$","example":"507f1f77bcf86cd799439011"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Widget"}}}},"400":{"description":"Invalid parameters or message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, expired or revoked key; issuer no longer authorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Organization or scope is not allowed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource does not exist in this organization.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1,"example":60}}},"description":"Per-key or organization rate limit exceeded. Wait retryAfter seconds from the JSON response before retrying.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"500":{"description":"Unexpected server failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Temporary service failure; retry reads with backoff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearerApiKey":{"type":"http","scheme":"bearer","bearerFormat":"SupportWhale API key","description":"Create in Settings → API keys as owner/admin. Secret shown once. Optional expiry up to 366 days. Rotation invalidates the old secret immediately; revocation is irreversible. Send only in Authorization, never in a URL. Available scopes: conversations:read, conversations:write, contacts:read, agents:read, team:read, widget:read"}},"schemas":{"Conversation":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-f0-9]{24}$","example":"507f1f77bcf86cd799439011"},"status":{"type":"string","enum":["open","closed"]},"tags":{"type":"array","items":{"type":"string"}},"contactId":{"type":"string","pattern":"^[a-f0-9]{24}$","example":"507f1f77bcf86cd799439011"},"sourceDomain":{"type":["string","null"]},"lastMessageAt":{"type":["string","null"],"format":"date-time"}},"required":["id","status","tags","contactId","sourceDomain","lastMessageAt"],"additionalProperties":false},"Message":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-f0-9]{24}$","example":"507f1f77bcf86cd799439011"},"body":{"type":"string"},"internal":{"type":"boolean"},"senderType":{"type":"string","enum":["agent","visitor","ai","system"]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","body","internal","senderType","createdAt"],"additionalProperties":false},"SentMessage":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-f0-9]{24}$","example":"507f1f77bcf86cd799439011"},"body":{"type":"string"},"internal":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","body","internal","createdAt"],"additionalProperties":false},"SendMessage":{"type":"object","properties":{"body":{"type":"string","minLength":1,"maxLength":5000,"pattern":"\\S","description":"Markdown text. Attachments are not supported by this integration endpoint."},"clientMessageId":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,64}$","description":"Stable application-generated replay ID. Reuse it only for retries of this same message with the same key and conversation."},"internal":{"type":"boolean","default":false,"description":"True creates a private team note; false sends a customer-visible reply."}},"required":["body","clientMessageId"],"additionalProperties":false},"Contact":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-f0-9]{24}$","example":"507f1f77bcf86cd799439011"},"name":{"type":"string"},"email":{"type":"string"},"language":{"type":["string","null"]}},"required":["id","name","email","language"],"additionalProperties":false},"Agent":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-f0-9]{24}$","example":"507f1f77bcf86cd799439011"},"name":{"type":"string"},"status":{"type":"string","enum":["draft","ready","paused"]}},"required":["id","name","status"],"additionalProperties":false},"TeamMember":{"type":"object","properties":{"id":{"type":"string","pattern":"^[a-f0-9]{24}$","example":"507f1f77bcf86cd799439011"},"userId":{"type":"string","pattern":"^[a-f0-9]{24}$","example":"507f1f77bcf86cd799439011"},"role":{"type":"string","enum":["owner","admin","agent"]}},"required":["id","userId","role"],"additionalProperties":false},"Widget":{"type":"object","properties":{"name":{"type":"string"},"embedMode":{"type":"string","enum":["allowlist","all"]},"allowedDomains":{"type":"array","items":{"type":"string"}}},"required":["name","embedMode","allowedDomains"],"additionalProperties":false},"Error":{"type":"object","properties":{"statusCode":{"type":"integer"},"message":{"type":["string","array"],"items":{"type":"string"}},"error":{"type":"string"},"code":{"type":"string"},"retryAfter":{"type":"integer","minimum":1}},"required":["message"],"additionalProperties":true}}}}