Reference
Engage API
Public endpoints under /v1/engage used by AI Chat, Managed Forms and Support Desk — requests, responses and errors — plus the console management endpoints.
Public endpoints
Base URL https://api.buckhy.com. Every public endpoint except /sdk.js requires X-Api-Key: bk_live_… and a request Origin that is allow-listed for the site (requests without an Origin header — servers and mobile apps — are accepted).
| Method | Path | App | Purpose |
|---|---|---|---|
GET |
/v1/engage/sdk.js |
all | The embeddable loader. No key. |
GET |
/v1/engage/config |
all | Health, module flags and branding. Drives show/hide. |
POST |
/v1/engage/chat |
AI Chat | One visitor turn. |
GET |
/v1/engage/conversations/:id?visitorToken=… |
AI Chat | A visitor reads their own thread. |
POST |
/v1/engage/forms/:form |
Managed Forms | Store a managed form submission. |
POST |
/v1/engage/support |
Support Desk | Create a support ticket. |
Common errors from key and origin checks:
| Status | Message |
|---|---|
401 |
missing api key · invalid or revoked engage key |
403 |
engage site not found · engage site is disabled · origin not allowed for this site |
403 |
MODULE_DISABLED — the module for that endpoint is off |
429 |
Rate limit: 120 requests / 15 minutes per key |
503 |
key verification unavailable — retry with backoff |
GET /config
{
"ok": true,
"name": "Northbeam Outfitters",
"assistantName": "Trail Guide",
"modules": { "chat": true, "forms": true, "support": true },
"identityMode": "anonymous",
"branding": { "launcherLabel": "Ask the trail crew", "accent": "#1457d9", "greeting": "Planning a trek?" }
}
Cached by the SDK and CDNs for 5 minutes (Cache-Control: public, max-age=300).
POST /chat
Rate limit: 12 messages / minute per key and visitor IP.
Request
| Property | Type | Required | Notes |
|---|---|---|---|
message |
string | yes | ≤ 2,000 characters |
visitorToken |
string | no | From a previous response; resumes the thread |
visitor |
{ id?, name?, email? } |
no | email is required when the site's identity mode is email |
page, pageTitle |
string | no | Where the visitor is; shown in the console |
Response
{
"success": true,
"visitorToken": "evt_…",
"conversationId": "6a9…",
"answer": "You can return used boots within 30 days…",
"citations": [{ "slug": "returns", "title": "30-day trail returns" }],
"escalate": false,
"status": "ai"
}
While a person holds the thread the reply is { "success": true, "queued": true, "status": "agent", … } and the assistant stays silent. When escalate is true the answer points to the support form and a chat.escalated webhook is delivered.
| Status | Code | Meaning |
|---|---|---|
400 |
— | message is required |
400 |
IDENTITY_REQUIRED |
Site requires an email before chatting |
403 |
MODULE_DISABLED |
Chat is off |
503 |
ASSISTANT_UNAVAILABLE |
The model could not answer; the visitor's message is kept |
GET /conversations/:id?visitorToken=… returns { status, messages[] { role, content, citations, sentAt } } for the visitor's own thread; 404 when the token does not match.
POST /forms/:form
:form is the form label (≤ 40 characters, default contact).
| Property | Type | Required | Notes |
|---|---|---|---|
fields |
object | yes | Up to 24 string fields, 2,000 characters each. website, url and homepage are treated as honeypots and dropped. |
page |
string | no | ≤ 300 |
The ticket's contact is read from fields.name, fields.email and fields.phone; its subject from fields.subject; its message from fields.message or fields.notes.
201 { "success": true, "id": "ENG-F-5C213AA2", "successMessage": "Thank you." }, or 200 with "duplicate": true on replay. successMessage is present when a configured form policy applies. Missing configured required fields or an unconfigured form ID in strict mode returns 400. Disabled integrations return 403. 400 fields are required is returned when nothing survives sanitisation.
POST /support
| Property | Type | Required | Notes |
|---|---|---|---|
name |
string | yes | ≤ 120 |
email |
string | yes | Valid address, ≤ 160 |
message |
string | yes | ≤ 5,000 |
phone |
string | no | ≤ 30 |
category |
string | no | ≤ 40; default general |
subject |
string | no | ≤ 200; defaults to the first 80 characters of the message |
page |
string | no | ≤ 300 |
conversationId |
string | no | Links the ticket to a chat thread |
201 { "success": true, "ticketId": "ENG-T-7F3A9C21" }, or 200 with "duplicate": true on an idempotent replay. 400 with name and a valid email are required or message is required.
Idempotency
/chat is naturally idempotent per message. /support and /forms/:form accept an Idempotency-Key header (8–100 characters, A–Z a–z 0–9 _ -):
- Same key, same content →
200with the original id andduplicate: true. - Same key, different content →
409. - Malformed keys are ignored, not rejected.
The SDK generates a new key per submission attempt and reuses it on retry; do the same in your own clients.
Console endpoints
The developer console manages sites through /api/engage/sites, authenticated with the portal's service credentials (X-Portal-Key and X-Identity-Sub). Site owners only ever see their own sites. Listed for completeness; use the console UI.
| Method | Path | Purpose |
|---|---|---|
GET / POST |
/ |
List / create sites |
PATCH |
/:id |
Name, origins, modules, identity mode, answer mode, branding, webhook, status |
GET / PUT |
/:id/knowledge |
Read / replace the context and articles |
POST |
/:id/keys |
Issue a key, revoking the previous one |
GET |
/:id/stats |
Ticket and conversation counts, knowledge size |
GET |
/:id/tickets?status&source&page |
Ticket inbox |
POST |
/:id/tickets/:ticketId/reply |
Agent reply { message, resolve? } → status in_progress or resolved |
GET |
/:id/conversations?status&page |
Chat transcripts |
POST |
/:id/conversations/:cid/reply |
Take over { message } |
POST |
/:id/conversations/:cid/release |
Hand back to the assistant |
POST |
/:id/conversations/:cid/close |
Close the thread |
POST |
/:id/webhook/test |
Send one signed webhook.test event |
