Widget
Operations tagged Widget in the public API.
About Widget
The widget is the chat bubble a website embeds for a chatbot. Its look and behaviour live in the chatbot's settings; the sites it may load on are the chatbot's allowlist of hostnames (allowed_domains). An empty allowlist lets it load on any site; otherwise the page's host must match an entry exactly, or as a subdomain of a *.example.com entry. The widget serves only an active chatbot.
Installations. A website plugin (WordPress) registers its site with POST /plugins/install: the site's host joins the allowlist and stays there while the installation is active — a host an active installation owns cannot be removed, and a whole-list replace keeps it. The plugin reports its version with POST /plugins/heartbeat; DELETE /plugins/{id} disconnects it (the row stays, removed).
IDs you will meet.
chatbotId— the chatbot id, in every/chatbots/{chatbotId}/widget…path.- Installation
id— an integer, fromPOST /plugins/installorGET /chatbots/{chatbotId}/widget/installations; the plugin sends it back asinstallation_id.
IDs on this page
- Chatbot id
<CHATBOT_ID> - Integer.
Get it from
- GET /v1/chatbots returns it as
data[].id - POST /v1/chatbots returns it as
id - GET /v1/chatbots/by_handle/{handle} returns it as
id - Webhook chatbot.created carries it as
data.object.id - In the dashboard, it is the number in a chatbot's address:
/dashboard/chatbots/{id}.
- GET /v1/chatbots returns it as
- Allowlisted host
<HOST> - A hostname exactly as the allowlist stores it, without scheme or path (e.g.
shop.example.comor*.example.com).Get it from
- GET /v1/chatbots/{chatbotId}/widget/domains returns it as
allowed_domains[] - GET /v1/chatbots/{chatbotId}/widget returns it as
allowed_domains[]
- GET /v1/chatbots/{chatbotId}/widget/domains returns it as
- Plugin installation id
<INSTALLATION_ID> - Integer.
Get it from
- POST /v1/plugins/install returns it as
id - GET /v1/chatbots/{chatbotId}/widget/installations returns it as
data[].id - Webhook plugin.installed carries it as
data.object.id
- POST /v1/plugins/install returns it as
Retrieve widget settings
status (the widget serves only active chatbots), its allowlist, welcome message, language and the widget settings bag (position, colours, launcher and the other look-and-behaviour keys). The same settings is on the chatbot object.- Required scope
- widget:read
- Idempotency
- Read-only
- Rate limits
Integer.chatbotId(path)Chatbot idPlaceholder in the samples:
<CHATBOT_ID>Get it from
- GET /v1/chatbots returns it as
data[].id - POST /v1/chatbots returns it as
id - GET /v1/chatbots/by_handle/{handle} returns it as
id - Webhook chatbot.created carries it as
data.object.id - In the dashboard, it is the number in a chatbot's address:
/dashboard/chatbots/{id}.
- GET /v1/chatbots returns it as
Path parameters
chatbotIdintegerRequiredThe chatbot id.
Responses
- 200Success.
objectstringRequiredAlways
widget_settingsidintegerRequiredThe chatbot id.statusstringRequiredThe chatbot's lifecycle status; the widget serves only anactivechatbot.allowed_domainsarray of stringRequiredHostnames allowed to embed the widget, without scheme.welcome_messagestringnullableRequiredThe first message the widget shows;nullfor the default greeting.primary_languagestringRequiredThe chatbot's content language, e.g.English.settingsobjectRequiredThe widget look-and-behaviour keys set on the chatbot. A key never set is absent.
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither.Error codesACCOUNT_SELECTOR_NOT_SUPPORTED
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 404The resource does not exist on this account.
Error codesNOT_FOUND
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X GET 'https://api.agentency.com/v1/chatbots/<CHATBOT_ID>/widget' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"Response
Example 200 response
{
"object": "widget_settings",
"id": 1,
"status": "active",
"allowed_domains": [
"shop.example.com"
],
"welcome_message": null,
"primary_language": "English",
"settings": []
}Update widget settings
settings and returns the widget settings. Keys you omit keep their value; to reset a key, send it with null where the key allows it. Only the documented widget keys are accepted — any other key is 422 VALIDATION_FAILED. Embedded widgets pick the change up on their next load.
Emits chatbot.updated, then widget.updated.- Required scope
- widget:write
- Idempotency
- Accepts Idempotency-Key
- Webhook events
- chatbot.updatedwidget.updated
- Rate limits
Integer.chatbotId(path)Chatbot idPlaceholder in the samples:
<CHATBOT_ID>Get it from
- GET /v1/chatbots returns it as
data[].id - POST /v1/chatbots returns it as
id - GET /v1/chatbots/by_handle/{handle} returns it as
id - Webhook chatbot.created carries it as
data.object.id - In the dashboard, it is the number in a chatbot's address:
/dashboard/chatbots/{id}.
- GET /v1/chatbots returns it as
Path parameters
chatbotIdintegerRequiredThe chatbot id.
Header parameters
Idempotency-KeystringOptionalMakes a retry safe. The first 2xx answer is stored for 24 h and replayed for the same key withIdempotency-Replayed: true; a retry while the first request runs is 409IDEMPOTENCY_IN_FLIGHT; the same key with a different payload is 422IDEMPOTENCY_KEY_REUSED; a malformed key is 400INVALID_IDEMPOTENCY_KEY.Min length:
16Max length:128Pattern:^[A-Za-z0-9_-]+$
Request body
settingsobjectRequiredThe widget keys to change, merged into the storedsettings: keys you omit keep their value. Only the documented keys are accepted; any other key is 422VALIDATION_FAILED.
Responses
- 200Success.
objectstringRequiredAlways
widget_settingsidintegerRequiredThe chatbot id.statusstringRequiredThe chatbot's lifecycle status; the widget serves only anactivechatbot.allowed_domainsarray of stringRequiredHostnames allowed to embed the widget, without scheme.welcome_messagestringnullableRequiredThe first message the widget shows;nullfor the default greeting.primary_languagestringRequiredThe chatbot's content language, e.g.English.settingsobjectRequiredThe widget look-and-behaviour keys set on the chatbot. A key never set is absent.
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither. A mutation that honoursIdempotency-Keyalso returnsINVALID_IDEMPOTENCY_KEYwhen that header is not 16–128[A-Za-z0-9_-]characters.Error codesACCOUNT_SELECTOR_NOT_SUPPORTEDINVALID_IDEMPOTENCY_KEY
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 404The resource does not exist on this account.
Error codesNOT_FOUND
- 409The request conflicts with the current resource state.
Error codesIDEMPOTENCY_IN_FLIGHT
- 422Validation failed. See
error.details.Error codesIDEMPOTENCY_KEY_REUSEDVALIDATION_FAILED
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X PUT 'https://api.agentency.com/v1/chatbots/<CHATBOT_ID>/widget' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"
-d '{
"settings": "settings"
}'Response
Example 200 response
{
"object": "widget_settings",
"id": 1,
"status": "inactive",
"allowed_domains": [],
"welcome_message": "Hi! Ask me about orders, shipping or refunds.",
"primary_language": "English",
"settings": {
"widget_position": "bottom-right"
}
}List allowed widget domains
*. entry.- Required scope
- widget:read
- Idempotency
- Read-only
- Rate limits
Integer.chatbotId(path)Chatbot idPlaceholder in the samples:
<CHATBOT_ID>Get it from
- GET /v1/chatbots returns it as
data[].id - POST /v1/chatbots returns it as
id - GET /v1/chatbots/by_handle/{handle} returns it as
id - Webhook chatbot.created carries it as
data.object.id - In the dashboard, it is the number in a chatbot's address:
/dashboard/chatbots/{id}.
- GET /v1/chatbots returns it as
Path parameters
chatbotIdintegerRequiredThe chatbot id.
Responses
- 200Success.
objectstringRequiredAlways
widget_domainsidintegerRequiredThe chatbot id.allowed_domainsarray of stringRequiredHostnames allowed to embed the widget, without scheme.
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither.Error codesACCOUNT_SELECTOR_NOT_SUPPORTED
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 404The resource does not exist on this account.
Error codesNOT_FOUND
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X GET 'https://api.agentency.com/v1/chatbots/<CHATBOT_ID>/widget/domains' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"Response
Example 200 response
{
"object": "widget_domains",
"id": 1,
"allowed_domains": [
"shop.example.com"
]
}Replace the widget allowlist
*. wildcard; a host an active plugin installation owns is kept even when you leave it out. More hosts than the cap is 422 WIDGET_DOMAIN_LIMIT_REACHED. It emits no webhook event.
To change one host without rewriting the list, use POST or DELETE on the domains instead.- Required scope
- widget:write
- Idempotency
- Accepts Idempotency-Key
- Webhook events
- None
- Rate limits
Integer.chatbotId(path)Chatbot idPlaceholder in the samples:
<CHATBOT_ID>Get it from
- GET /v1/chatbots returns it as
data[].id - POST /v1/chatbots returns it as
id - GET /v1/chatbots/by_handle/{handle} returns it as
id - Webhook chatbot.created carries it as
data.object.id - In the dashboard, it is the number in a chatbot's address:
/dashboard/chatbots/{id}.
- GET /v1/chatbots returns it as
Path parameters
chatbotIdintegerRequiredThe chatbot id.
Header parameters
Idempotency-KeystringOptionalMakes a retry safe. The first 2xx answer is stored for 24 h and replayed for the same key withIdempotency-Replayed: true; a retry while the first request runs is 409IDEMPOTENCY_IN_FLIGHT; the same key with a different payload is 422IDEMPOTENCY_KEY_REUSED; a malformed key is 400INVALID_IDEMPOTENCY_KEY.Min length:
16Max length:128Pattern:^[A-Za-z0-9_-]+$
Request body
allowed_domainsarray of stringRequiredThe complete new allowlist: hostnames without scheme, or*.example.comwildcards, at most 50 by default. Each entry is normalised (scheme, port, path and case removed).[]lets the widget load on any site. A host an active plugin installation owns is kept even when left out.Max items:
50
Responses
- 200Success.
objectstringRequiredAlways
widget_domainsidintegerRequiredThe chatbot id.allowed_domainsarray of stringRequiredHostnames allowed to embed the widget, without scheme.
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither. A mutation that honoursIdempotency-Keyalso returnsINVALID_IDEMPOTENCY_KEYwhen that header is not 16–128[A-Za-z0-9_-]characters.Error codesACCOUNT_SELECTOR_NOT_SUPPORTEDINVALID_IDEMPOTENCY_KEY
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 404The resource does not exist on this account.
Error codesNOT_FOUND
- 409The request conflicts with the current resource state.
Error codesIDEMPOTENCY_IN_FLIGHT
- 422Validation failed. See
error.details.Error codesIDEMPOTENCY_KEY_REUSEDVALIDATION_FAILED
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X PUT 'https://api.agentency.com/v1/chatbots/<CHATBOT_ID>/widget/domains' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"
-d '{
"allowed_domains": []
}'Response
Example 200 response
{
"object": "widget_domains",
"id": 1,
"allowed_domains": [
"example.com",
"*.example.com"
]
}Allow a widget domain
WIDGET_DOMAIN_LIMIT_REACHED; an invalid host is 422. It emits no webhook event.
Adding the first host switches the widget from loading anywhere to loading only on the listed hosts.- Required scope
- widget:write
- Idempotency
- Accepts Idempotency-Key
- Webhook events
- None
- Rate limits
Integer.chatbotId(path)Chatbot idPlaceholder in the samples:
<CHATBOT_ID>Get it from
- GET /v1/chatbots returns it as
data[].id - POST /v1/chatbots returns it as
id - GET /v1/chatbots/by_handle/{handle} returns it as
id - Webhook chatbot.created carries it as
data.object.id - In the dashboard, it is the number in a chatbot's address:
/dashboard/chatbots/{id}.
- GET /v1/chatbots returns it as
Path parameters
chatbotIdintegerRequiredThe chatbot id.
Header parameters
Idempotency-KeystringOptionalMakes a retry safe. The first 2xx answer is stored for 24 h and replayed for the same key withIdempotency-Replayed: true; a retry while the first request runs is 409IDEMPOTENCY_IN_FLIGHT; the same key with a different payload is 422IDEMPOTENCY_KEY_REUSED; a malformed key is 400INVALID_IDEMPOTENCY_KEY.Min length:
16Max length:128Pattern:^[A-Za-z0-9_-]+$
Request body
POST /v1/chatbots/{id}/widget/domains.
This used to be validated inline, which put the field error at
error.details.domain while every other 422 on the surface puts it at
errors + error.details.fields. A FormRequest routes it through the one
renderer, so VALIDATION_FAILED means the same shape everywhere.domainstringRequiredThe hostname to allow, e.g.shop.example.com, or*.example.comto allow every subdomain ofexample.com(a wildcard over a public suffix such as*.co.ukis refused). A scheme, port or path is stripped.Max length:
255
Responses
- 200Success.
objectstringRequiredAlways
widget_domainsidintegerRequiredThe chatbot id.allowed_domainsarray of stringRequiredHostnames allowed to embed the widget, without scheme.
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither. A mutation that honoursIdempotency-Keyalso returnsINVALID_IDEMPOTENCY_KEYwhen that header is not 16–128[A-Za-z0-9_-]characters.Error codesACCOUNT_SELECTOR_NOT_SUPPORTEDINVALID_IDEMPOTENCY_KEY
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 404The resource does not exist on this account.
Error codesNOT_FOUND
- 409The request conflicts with the current resource state.
Error codesIDEMPOTENCY_IN_FLIGHT
- 422Validation failed. See
error.details.Error codesIDEMPOTENCY_KEY_REUSEDVALIDATION_FAILED
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X POST 'https://api.agentency.com/v1/chatbots/<CHATBOT_ID>/widget/domains' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"
-d '{
"domain": "domain"
}'Response
Example 200 response
{
"object": "widget_domains",
"id": 1,
"allowed_domains": [
"example.com",
"*.example.com",
"shop.example.org"
]
}List plugin installations
active or removed.- Required scope
- widget:read
- Idempotency
- Read-only
- Rate limits
Integer.chatbotId(path)Chatbot idPlaceholder in the samples:
<CHATBOT_ID>Get it from
- GET /v1/chatbots returns it as
data[].id - POST /v1/chatbots returns it as
id - GET /v1/chatbots/by_handle/{handle} returns it as
id - Webhook chatbot.created carries it as
data.object.id - In the dashboard, it is the number in a chatbot's address:
/dashboard/chatbots/{id}.
- GET /v1/chatbots returns it as
Path parameters
chatbotIdintegerRequiredThe chatbot id.
Query parameters
limitintegerOptionalPage size, clamped to 1–100.Default:
20Minimum:1Maximum:100starting_afterstringOptionalCursor from a previous page'snext_cursor: returns the page after it. A cursor minted for another list,sortororderis 422INVALID_CURSOR.ending_beforestringOptionalCursor from a previous page'sprevious_cursor: returns the page before it. Wins when both cursors are sent.created[gte]stringOptionalOnly rows created on or after this instant:YYYY-MM-DD(whole UTC day) or an RFC 3339 timestamp with a zone. Anything else is 422INVALID_PARAMETER.created[lte]stringOptionalOnly rows created on or before this instant:YYYY-MM-DD(whole UTC day) or an RFC 3339 timestamp with a zone. Anything else is 422INVALID_PARAMETER.
Responses
- 200Success.
objectstringRequiredAlways
listdataarray of WidgetInstallationResourceRequiredhas_morebooleanRequirednext_cursorstringnullableRequiredprevious_cursorstringnullableRequiredurlstringRequired
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither.Error codesACCOUNT_SELECTOR_NOT_SUPPORTED
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 404The resource does not exist on this account.
Error codesNOT_FOUND
- 422Validation failed. See
error.details.Error codesINVALID_CURSORINVALID_PARAMETER
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X GET 'https://api.agentency.com/v1/chatbots/<CHATBOT_ID>/widget/installations' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"Response
Example 200 response
{
"object": "list",
"data": [
{
"object": "widget_installation",
"id": 1,
"status": "active",
"domain": "shop.example.com",
"site_url": "https://shop.example.com",
"platform": "wordpress",
"plugin_version": "1.0.0",
"last_seen_at": "2026-01-01T00:00:00+00:00"
}
],
"has_more": false,
"next_cursor": null,
"previous_cursor": null,
"url": "/v1/chatbots/1/widget/installations"
}Retrieve widget usage
requests_today still shows the last active day's count.- Required scope
- widget:read
- Idempotency
- Read-only
- Rate limits
Integer.chatbotId(path)Chatbot idPlaceholder in the samples:
<CHATBOT_ID>Get it from
- GET /v1/chatbots returns it as
data[].id - POST /v1/chatbots returns it as
id - GET /v1/chatbots/by_handle/{handle} returns it as
id - Webhook chatbot.created carries it as
data.object.id - In the dashboard, it is the number in a chatbot's address:
/dashboard/chatbots/{id}.
- GET /v1/chatbots returns it as
Path parameters
chatbotIdintegerRequiredThe chatbot id.
Responses
- 200Success.
objectstringRequiredAlways
widget_usageidintegerRequiredThe chatbot id.requests_todayintegerRequiredWidget requests counted on the last day the widget served one, refreshed at most once a minute.queries_countintegerRequiredChat turns the chatbot has answered, on every surface (a stored counter).
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither.Error codesACCOUNT_SELECTOR_NOT_SUPPORTED
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 404The resource does not exist on this account.
Error codesNOT_FOUND
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X GET 'https://api.agentency.com/v1/chatbots/<CHATBOT_ID>/widget/usage' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"Response
Example 200 response
{
"object": "widget_usage",
"id": 1,
"requests_today": 0,
"queries_count": 0
}Remove a widget domain
WIDGET_DOMAIN_PLUGIN_MANAGED (error.details.installation_id names it): disconnect the installation with DELETE /plugins/{id} instead. It emits no webhook event.
Removing the last host lets the widget load on any site again.- Required scope
- widget:write
- Idempotency
- Accepts Idempotency-Key
- Webhook events
- None
- Rate limits
Integer.chatbotId(path)Chatbot idPlaceholder in the samples:
<CHATBOT_ID>Get it from
- GET /v1/chatbots returns it as
data[].id - POST /v1/chatbots returns it as
id - GET /v1/chatbots/by_handle/{handle} returns it as
id - Webhook chatbot.created carries it as
data.object.id - In the dashboard, it is the number in a chatbot's address:
/dashboard/chatbots/{id}.
- GET /v1/chatbots returns it as
A hostname exactly as the allowlist stores it, without scheme or path (e.g.host(path)Allowlisted hostshop.example.comor*.example.com).Placeholder in the samples:
<HOST>Get it from
- GET /v1/chatbots/{chatbotId}/widget/domains returns it as
allowed_domains[] - GET /v1/chatbots/{chatbotId}/widget returns it as
allowed_domains[]
- GET /v1/chatbots/{chatbotId}/widget/domains returns it as
Path parameters
chatbotIdintegerRequiredThe chatbot id.hoststringRequiredThe host to remove, as the allowlist lists it (e.g.shop.example.com).
Header parameters
Idempotency-KeystringOptionalMakes a retry safe. The first 2xx answer is stored for 24 h and replayed for the same key withIdempotency-Replayed: true; a retry while the first request runs is 409IDEMPOTENCY_IN_FLIGHT; the same key with a different payload is 422IDEMPOTENCY_KEY_REUSED; a malformed key is 400INVALID_IDEMPOTENCY_KEY.Min length:
16Max length:128Pattern:^[A-Za-z0-9_-]+$
Responses
- 200Success.
objectstringRequiredAlways
widget_domainsidintegerRequiredThe chatbot id.allowed_domainsarray of stringRequiredHostnames allowed to embed the widget, without scheme.
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither. A mutation that honoursIdempotency-Keyalso returnsINVALID_IDEMPOTENCY_KEYwhen that header is not 16–128[A-Za-z0-9_-]characters.Error codesACCOUNT_SELECTOR_NOT_SUPPORTEDINVALID_IDEMPOTENCY_KEY
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 404The resource does not exist on this account.
Error codesNOT_FOUND
- 409The request conflicts with the current resource state.
Error codesIDEMPOTENCY_IN_FLIGHT
- 422Validation failed. See
error.details.Error codesIDEMPOTENCY_KEY_REUSED
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X DELETE 'https://api.agentency.com/v1/chatbots/<CHATBOT_ID>/widget/domains/<HOST>' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"Response
Example 200 response
{
"object": "widget_domains",
"id": 1,
"allowed_domains": [
"example.com",
"*.example.com"
]
}Register a website plugin installation
201. The site must be HTTPS. A site already registered for the chatbot is the same installation — a removed one is revived — and only an installation that becomes active emits plugin.installed and mails the account owner, so a retry is safe.
402 when the plan has no website embed; 422 when the site is not HTTPS, the chatbot's installation registry or allowlist is full, or the host cannot be stored.- Required scope
- widget:write
- Idempotency
- Accepts Idempotency-Key
- Webhook events
- plugin.installed
- Rate limits
Integer.chatbot_id(body)Chatbot idPlaceholder in the samples:
<CHATBOT_ID>Get it from
- GET /v1/chatbots returns it as
data[].id - POST /v1/chatbots returns it as
id - GET /v1/chatbots/by_handle/{handle} returns it as
id - Webhook chatbot.created carries it as
data.object.id - In the dashboard, it is the number in a chatbot's address:
/dashboard/chatbots/{id}.
- GET /v1/chatbots returns it as
Header parameters
Idempotency-KeystringOptionalMakes a retry safe. The first 2xx answer is stored for 24 h and replayed for the same key withIdempotency-Replayed: true; a retry while the first request runs is 409IDEMPOTENCY_IN_FLIGHT; the same key with a different payload is 422IDEMPOTENCY_KEY_REUSED; a malformed key is 400INVALID_IDEMPOTENCY_KEY.Min length:
16Max length:128Pattern:^[A-Za-z0-9_-]+$
Request body
chatbot_idintegerRequiredThe chatbot whose widget the site embeds. A chatbot of another account is 422VALIDATION_FAILED.platformstringRequiredThe plugin's platform.Possible values
wordpressMax length:
64site_urlstringRequiredThe site URL (HTTPS, at most 255 characters). Its normalized host is added to the widget allowlist.Format:
uriMax length:255plugin_versionstringnullableOptionalThe plugin's version, e.g.1.4.0: letters, digits,.,_,-and+, at most 32 characters.Max length:
32Pattern:^[A-Za-z0-9._\-+]+$
Responses
- 201
WidgetInstallationResourceobjectstringRequiredAlways
widget_installationidintegerRequiredThe installation id. The plugin sends it asinstallation_idon each heartbeat;DELETE /plugins/{id}disconnects it.statusstringRequiredactive(the site owns its allowlist entry) orremoved(disconnected;POST /plugins/installrevives it).Possible values
activeremoveddomainstringRequiredThe site's normalised host, the entry the installation keeps on the widget allowlist.site_urlstringRequiredThe site URL the plugin registered.platformstringRequiredThe plugin platform, e.g.wordpress.plugin_versionstringnullableRequiredThe plugin version the site last reported;nullwhen it never sent one.last_seen_atstringnullableRequiredWhen the site last registered or sent a heartbeat (ISO 8601, UTC);nullbefore the first.
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither. A mutation that honoursIdempotency-Keyalso returnsINVALID_IDEMPOTENCY_KEYwhen that header is not 16–128[A-Za-z0-9_-]characters.Error codesACCOUNT_SELECTOR_NOT_SUPPORTEDINVALID_IDEMPOTENCY_KEY
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 404The resource does not exist on this account.
Error codesNOT_FOUND
- 409The request conflicts with the current resource state.
Error codesIDEMPOTENCY_IN_FLIGHT
- 422Validation failed. See
error.details.Error codesIDEMPOTENCY_KEY_REUSEDVALIDATION_FAILED
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X POST 'https://api.agentency.com/v1/plugins/install' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"
-d '{
"chatbot_id": 1,
"platform": "platform",
"site_url": "site_url",
"plugin_version": "plugin_version"
}'Response
Example 201 response
{
"object": "widget_installation",
"id": 1,
"status": "active",
"domain": "blog.example.com",
"site_url": "https://blog.example.com",
"platform": "wordpress",
"plugin_version": "1.4.0",
"last_seen_at": "2026-01-01T00:00:00+00:00"
}Report a plugin heartbeat
last_seen_at) and, when sent, its plugin_version, and returns the installation. A heartbeat never revives a removed installation or re-adds its host to the allowlist — the response shows status: removed, and only POST /plugins/install reconnects the site. It emits no webhook event.- Required scope
- widget:write
- Idempotency
- Accepts Idempotency-Key
- Webhook events
- None
- Rate limits
Integer.installation_id(body)Plugin installation idPlaceholder in the samples:
<INSTALLATION_ID>Get it from
- POST /v1/plugins/install returns it as
id - GET /v1/chatbots/{chatbotId}/widget/installations returns it as
data[].id - Webhook plugin.installed carries it as
data.object.id
- POST /v1/plugins/install returns it as
Header parameters
Idempotency-KeystringOptionalMakes a retry safe. The first 2xx answer is stored for 24 h and replayed for the same key withIdempotency-Replayed: true; a retry while the first request runs is 409IDEMPOTENCY_IN_FLIGHT; the same key with a different payload is 422IDEMPOTENCY_KEY_REUSED; a malformed key is 400INVALID_IDEMPOTENCY_KEY.Min length:
16Max length:128Pattern:^[A-Za-z0-9_-]+$
Request body
installation_idintegerRequiredThe installation idPOST /plugins/installreturned. An installation of another account is 404NOT_FOUND.Minimum:
1plugin_versionstringnullableOptionalThe plugin version now running, at most 32 characters. Omit it to keep the stored version.Max length:
32
Responses
- 200
WidgetInstallationResourceobjectstringRequiredAlways
widget_installationidintegerRequiredThe installation id. The plugin sends it asinstallation_idon each heartbeat;DELETE /plugins/{id}disconnects it.statusstringRequiredactive(the site owns its allowlist entry) orremoved(disconnected;POST /plugins/installrevives it).Possible values
activeremoveddomainstringRequiredThe site's normalised host, the entry the installation keeps on the widget allowlist.site_urlstringRequiredThe site URL the plugin registered.platformstringRequiredThe plugin platform, e.g.wordpress.plugin_versionstringnullableRequiredThe plugin version the site last reported;nullwhen it never sent one.last_seen_atstringnullableRequiredWhen the site last registered or sent a heartbeat (ISO 8601, UTC);nullbefore the first.
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither. A mutation that honoursIdempotency-Keyalso returnsINVALID_IDEMPOTENCY_KEYwhen that header is not 16–128[A-Za-z0-9_-]characters.Error codesACCOUNT_SELECTOR_NOT_SUPPORTEDINVALID_IDEMPOTENCY_KEY
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 404The resource does not exist on this account.
Error codesNOT_FOUND
- 409The request conflicts with the current resource state.
Error codesIDEMPOTENCY_IN_FLIGHT
- 422Validation failed. See
error.details.Error codesIDEMPOTENCY_KEY_REUSEDVALIDATION_FAILED
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X POST 'https://api.agentency.com/v1/plugins/heartbeat' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"
-d '{
"installation_id": 1,
"plugin_version": "plugin_version"
}'Response
Example 200 response
{
"object": "widget_installation",
"id": 1,
"status": "active",
"domain": "blog.example.com",
"site_url": "https://blog.example.com",
"platform": "wordpress",
"plugin_version": "1.4.1",
"last_seen_at": "2026-01-01T00:00:00+00:00"
}Disconnect a plugin installation
removed and drops its host from the allowlist, unless another active installation of the chatbot still claims that host. The installation stays listed (status: removed) so a later POST /plugins/install revives it. Only the call that actually moves it from active to removed emits plugin.uninstalled.- Required scope
- widget:write
- Idempotency
- Accepts Idempotency-Key
- Webhook events
- plugin.uninstalled
- Rate limits
Integer.id(path)Plugin installation idPlaceholder in the samples:
<INSTALLATION_ID>Get it from
- POST /v1/plugins/install returns it as
id - GET /v1/chatbots/{chatbotId}/widget/installations returns it as
data[].id - Webhook plugin.installed carries it as
data.object.id
- POST /v1/plugins/install returns it as
Path parameters
idintegerRequiredThe plugin installation id.
Header parameters
Idempotency-KeystringOptionalMakes a retry safe. The first 2xx answer is stored for 24 h and replayed for the same key withIdempotency-Replayed: true; a retry while the first request runs is 409IDEMPOTENCY_IN_FLIGHT; the same key with a different payload is 422IDEMPOTENCY_KEY_REUSED; a malformed key is 400INVALID_IDEMPOTENCY_KEY.Min length:
16Max length:128Pattern:^[A-Za-z0-9_-]+$
Responses
- 200Success.
objectstringRequiredAlways
widget_installationidintegerRequireddeletedbooleanRequired
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither. A mutation that honoursIdempotency-Keyalso returnsINVALID_IDEMPOTENCY_KEYwhen that header is not 16–128[A-Za-z0-9_-]characters.Error codesACCOUNT_SELECTOR_NOT_SUPPORTEDINVALID_IDEMPOTENCY_KEY
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 404The resource does not exist on this account.
Error codesNOT_FOUND
- 409The request conflicts with the current resource state.
Error codesIDEMPOTENCY_IN_FLIGHT
- 422Validation failed. See
error.details.Error codesIDEMPOTENCY_KEY_REUSED
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X DELETE 'https://api.agentency.com/v1/plugins/<INSTALLATION_ID>' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"Response
Example 200 response
{
"object": "widget_installation",
"id": 1,
"deleted": true
}