Send a widget message

View as Markdown
Sends one visitor message in a session from `POST /widget/init` and returns the agent's reply synchronously. Operator replies typed later in the dashboard do not come back here; pick them up with `GET /widget/poll`. Send `application/json` for text. To attach one image (PNG, JPG, WEBP, GIF, HEIC, HEIF, AVIF, TIFF) or PDF of up to 10 MB, send `multipart/form-data` with the file in `file` and the other fields as form fields; `message` is still required. HEIC/AVIF/TIFF are converted and photos rotated upright before the agent reads them. A file over 10 MB is cut off by the upload parser and currently fails with 500 rather than the 413 body. Credentials, origin check and per-minute limits are the same as `POST /widget/init`; see there. **Side effects.** On the first message creates the `webchat` conversation (bound to the widget's agent) and links it to the session; stores the visitor message and the reply; calls the language model; draws one AI message from the workspace's monthly allowance when a model wrote the reply; if the session has `metadata.email`, finds or creates the contact. The reply is also pushed to other open tabs of the session over the widget WebSocket. When the workspace is out of AI messages the visitor gets a neutral 503 and the workspace owner receives an in-app notification (once per day). No webhook event is emitted for the message itself. **Idempotency.** Not idempotent. A retry after a timeout stores the visitor message again, generates a second reply and spends another AI message and another unit of the daily cap. De-duplicate replies by `message_id`. **Access** - **Required scope:** None; `jl_` API keys are not used. The widget credential plus a matching `x-widget-parent-origin` authorise the call. - **Rate limit:** Widget public — 60 requests/min per IP, the widget's `rate_limit_rpm` per credential, 300 requests/min per workspace, plus a daily cap on widget messages per workspace (5,000 by default, resets at 00:00 UTC); the general limiter also counts 120 requests/min per IP. See [Rate limits](/rate-limits). - **Plan:** Available on every plan. Replies draw from the plan's AI-message allowance.

Headers

x-widget-api-keystringOptionalformat: "^[0-9a-f]{64}$"

Secret widget API key (plaintext_api_key from POST /api/widgets). Takes precedence over x-widget-id and wid. Send one of the three. Never expose it in a browser.

x-widget-idstringOptionalformat: "uuid"

Public widget id, the credential the embed script uses. Used when x-widget-api-key is absent.

x-widget-parent-originstringRequiredformat: "uri"

Origin of the page embedding the widget. Must match one of the widget’s allowed_origins; see POST /widget/init.

Query parameters

widstringOptionalformat: "uuid"
Public widget id as a query parameter. Used when neither header is present.

Request

This endpoint expects an object.
session_idstringRequiredformat: "uuid"

Session id returned by POST /widget/init.

messagestringRequired1-4000 characters
The visitor's text, 1 to 4000 characters. Required even when a file is attached.
agent_namestringOptional1-40 characters

Display name the UI shows for the agent (from branding.agent_names); the reply is signed with it and it is saved on the session.

langstringOptional2-10 characters

Primary language subtag of the visitor’s UI (es, en); used as the reply language and saved on the session.

identity_tokenstringOptional<=512 characters

Signed identity used only by Jelliu’s own in-app support widget. Integrations do not need it; an invalid token, or one for another workspace, is ignored.

filestringOptionalformat: "binary"

multipart/form-data only. One image (PNG, JPG, WEBP, GIF, HEIC, HEIF, AVIF, TIFF) or PDF, up to 10 MB.

Response

The agent's reply.
dataobject

Errors

400
Bad Request Error
401
Unauthorized Error
403
Forbidden Error
404
Not Found Error
413
Content Too Large Error
415
Unsupported Media Type Error
429
Too Many Requests Error
503
Service Unavailable Error