Operators
Operators are the human team that answers a tenant’s conversations. The
backend surface (/api/v1/relay/*, signed with HMAC#1) lets your
backend provision operators and mint tokens for them, without your users
ever holding a relay password.
This is the “merchant chat” model: a merchant from your marketplace becomes an operator scoped to their own queues, and authenticates through your identity system — the relay trusts your HMAC#1 signature.
Human lane vs bot lane
Section titled “Human lane vs bot lane”A reminder of the model (see Overview): when a visitor
session is created, the mode field picks the queue.
bot(default) — messages go to the RAG assistant until an escalation, which hands them off to the human queue.human— the first message directly creates a pending assignment and the bot never steps in.
Operators serve the human lane. The routing_keys list restricts them
to certain queues (for example a merchant’s store ids); null or an empty
list means “tenant-wide operator”, who sees every conversation.
Provision an operator
Section titled “Provision an operator”POST /relay/provision/operator creates-or-reuses an operator for one of
your own users. The operation is idempotent on (email, calling tenant): re-provisioning refreshes the membership’s routing_keys (a
merchant who gains a second store) instead of failing. The password is
random and never returned — the token exchange below is the only
authentication path.
- Your backend authenticates the merchant with your system (session, application JWT, etc.).
- Your backend calls
/relay/provision/operatorsigned with HMAC#1, with the merchant’s email, display name androuting_keys. - The relay creates the operator (or refreshes its membership) and returns
its
operator_id.
from bloonio_chat_relay_client import get_chat_client
chat = get_chat_client() # signs HMAC#1 automatically# The exact SDK method wraps POST /relay/provision/operator.curl $BASE_URL/api/v1/relay/provision/operator \ -X POST \ -H "X-Bloonio-Tenant-Id: 019e4ae7-1a2b-7c3d-8e4f-5a6b7c8d9e0f" \ -H "X-Bloonio-Timestamp: 1718960000000" \ -H "X-Bloonio-Signature: <signature>" \ -H "Content-Type: application/json" \ -d '{ "email": "merchant@acme.com", "display_name": "Acme Boutique", "routing_keys": ["store_42", "store_77"] }'Response — 201 on creation, 200 when the operator already existed
(created: false):
{ "status_code": 201, "data": { "operator_id": "019e4af0-...", "email": "merchant@acme.com", "display_name": "Acme Boutique", "tenant_id": "019e4ae7-1a2b-7c3d-8e4f-5a6b7c8d9e0f", "routing_keys": ["store_42", "store_77"], "created": true }, "message": "Operator provisioned"}Body fields: email (stable tenant-side key, required), display_name
(required), avatar_url (optional), routing_keys (optional, up to 50
entries; null = tenant-wide).
Mint an operator token
Section titled “Mint an operator token”POST /relay/fetch/operator-token exchanges the tenant’s HMAC#1 trust for
an operator JWT. Keep this call behind your own user authentication: the
identity on the relay side then rests on your system.
curl $BASE_URL/api/v1/relay/fetch/operator-token \ -X POST \ -H "X-Bloonio-Tenant-Id: 019e4ae7-1a2b-7c3d-8e4f-5a6b7c8d9e0f" \ -H "X-Bloonio-Timestamp: 1718960000000" \ -H "X-Bloonio-Signature: <signature>" \ -H "Content-Type: application/json" \ -d '{"email": "merchant@acme.com"}'{ "status_code": 200, "data": { "operator_id": "019e4af0-...", "display_name": "Acme Boutique", "operator_token": "<jwt>", "expires_at": 1719564800, "tenant_id": "019e4ae7-1a2b-7c3d-8e4f-5a6b7c8d9e0f", "routing_keys": ["store_42", "store_77"] }, "message": "Operator token minted"}The token is valid for ~7 days (expires_at in unix seconds). The operator
uses it to open their WebSocket and serve their queue.
The scoping invariant: tids
Section titled “The scoping invariant: tids”The minted JWT carries only the calling tenant’s membership in its
tids claim ({tenant_id: role}). An operator can belong to several
tenants (the “Slack workspace” model), but:
Issuance conditions: the operator must exist and be active (otherwise
404), and have an active membership in the calling tenant (otherwise
403).
Next steps
Section titled “Next steps”- Wire up the visitor-side widget. See Widget & SDK.
- React to agent-assignment events. See Webhooks.
- Manage operators by hand. See Console.
- The HMAC#1 signing recipe. See Authentication.