API reference
Build scoped integrations with the SupportWhale REST API.
Public integration API
The versioned integration API uses organization-bound Bearer keys. It does not expose the internal administration API. Every request must match the organization and scopes on the key. Use separate keys for separate integrations and organizations.
Open an endpoint below to inspect its parameters, response schema and required scope. Authorize with your own key to send a real request. The reference does not store your key. Responses may contain your organization’s data: clear the key and responses before sharing your screen.
Create an API key, choose scopes, restrict access, rotate or revoke, first integration, realtime events.
Versions and compatibility
The current contract is v1. Additive fields may appear without a new version; clients should ignore unknown fields. Breaking path, field-type or permission changes require a new API version and a published migration notice in this reference. No deprecation is currently scheduled. Do not treat browser endpoints as a stable integration contract.
Errors and retries
401 means the key is missing, invalid, expired or revoked. 403 means the organization, resource or scope is not allowed. 429 means the rate limit was reached: honor Retry-After before retrying. Use exponential backoff with jitter for transient 5xx failures. Never retry a write blindly; inspect the endpoint’s idempotency contract first. The API has no public outbound webhook subscription contract.
GET /integrations/orgs/{orgId}/conversations
List conversations
Required scope: conversations:read
Contract
{
"path": "/integrations/orgs/{orgId}/conversations",
"method": "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"
}
}
}
}
}
}GET /integrations/orgs/{orgId}/conversations/{id}/messages
List messages
Required scope: conversations:read
Contract
{
"path": "/integrations/orgs/{orgId}/conversations/{id}/messages",
"method": "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 /integrations/orgs/{orgId}/conversations/{id}/messages
Send a reply or internal note
Required scope: conversations:write
Contract
{
"path": "/integrations/orgs/{orgId}/conversations/{id}/messages",
"method": "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
}
}
}
}
}GET /integrations/orgs/{orgId}/contacts
List contacts
Required scope: contacts:read
Contract
{
"path": "/integrations/orgs/{orgId}/contacts",
"method": "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"
}
}
}
}
}
}GET /integrations/orgs/{orgId}/agents
List AI agents
Required scope: agents:read
Contract
{
"path": "/integrations/orgs/{orgId}/agents",
"method": "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"
}
}
}
}
}
}GET /integrations/orgs/{orgId}/team
List team memberships
Required scope: team:read
Contract
{
"path": "/integrations/orgs/{orgId}/team",
"method": "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"
}
}
}
}
}
}GET /integrations/orgs/{orgId}/widget
Read the organization widget
Required scope: widget:read
Contract
{
"path": "/integrations/orgs/{orgId}/widget",
"method": "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"
}
}
}
}
}
}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
}