Authentication
Tenant administrators create keys under Dashboard → Developers. The plaintext key is displayed once. Send it using either header below; never place it in frontend code.
Authorization: Bearer umk_live_<prefix>_<secret>
# or
X-API-Key: umk_live_<prefix>_<secret>Read endpoints require chat:read; create/send endpoints require chat:write; webhook management requires webhooks:manage. Revoked or expired keys return HTTP 401.
Errors and rate limits
Reads allow 300 requests/minute per client IP, conversation creation 60/minute, and message writes 120/minute. HTTP 429 includes Retry-After.
{
"statusCode": 403,
"error": "Forbidden",
"message": "API key lacks required scope: chat:write"
}Conversations
GET /v1/chat/conversations
Cursor pagination. Optional query parameters: limit (1–100, default 50), cursor (ISO timestamp of last lastMessageAt), status (open, pending, resolved, closed), and source.
curl "https://umneyconnect.com/api/v1/chat/conversations?limit=50&status=open" \
-H "Authorization: Bearer $UMNEY_API_KEY"{
"data": [{
"id": "8bb7f3b8-7e0f-4a86-bd84-b39d669f06c1",
"name": "Website visitor",
"source": "widget",
"status": "open",
"priority": "normal",
"assignedUserId": null,
"visitorId": "74a57547-03e2-48f2-a037-23886b14949c",
"unreadCount": 1,
"lastMessageAt": "2026-07-15T12:00:00.000Z",
"createdAt": "2026-07-15T11:58:00.000Z",
"updatedAt": "2026-07-15T12:00:00.000Z"
}],
"meta": { "limit": 50, "nextCursor": null }
}POST /v1/chat/conversations
Creates a new conversation. Requires chat:write. Optional visitor object upserts a visitor by externalId. Priority defaults to "normal".
curl -X POST "https://umneyconnect.com/api/v1/chat/conversations" \
-H "Authorization: Bearer $UMNEY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Order assistance",
"priority": "high",
"visitor": {
"externalId": "customer_2481",
"displayName": "Amina",
"email": "amina@example.com",
"attributes": { "plan": "business" }
}
}'Messages
GET /v1/chat/conversations/:id/messages
Returns messages oldest-first. Use after with an ISO timestamp for incremental polling and limit (1–100, default 50).
curl "https://umneyconnect.com/api/v1/chat/conversations/$CONVERSATION_ID/messages?limit=100" \
-H "X-API-Key: $UMNEY_API_KEY"POST /v1/chat/conversations/:id/messages
Creates a system-origin integration message (max 16 384 characters). The conversation must be in open or pending status; closed/resolved conversations return HTTP 404.
curl -X POST "https://umneyconnect.com/api/v1/chat/conversations/$CONVERSATION_ID/messages" \
-H "Authorization: Bearer $UMNEY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"content":"Your order has shipped."}'{
"data": {
"id": "f34db22c-2e8b-4fa1-99b5-aa4aed198aae",
"channelId": "8bb7f3b8-7e0f-4a86-bd84-b39d669f06c1",
"tenantId": "5fa6309f-1b28-4edc-b521-b63da4522658",
"senderId": null,
"visitorId": null,
"senderType": "system",
"content": "Your order has shipped.",
"metadata": { "origin": "api", "apiKeyId": "…" },
"createdAt": "2026-07-15T12:02:00.000Z"
}
}Website widget
Create a widget and add every production HTTPS origin under Dashboard → Developers, then paste the generated script before your closing body tag.
<script
src="https://umneyconnect.com/widget.js"
data-site-key="umw_<your-site-key>"
data-locale="en"
async>
</script>Localization
Set data-locale on the script tag to a BCP-47 code (e.g. "fr", "pt-BR"). The widget resolves the exact locale code first, then falls back to the base language, then to built-in English defaults. Configure locale strings in the dashboard under each widget's settings, or programmatically:
PUT /tenants/:tenantId/chat/widgets/:widgetId/locales/:locale
Authorization: Bearer <jwt>
Content-Type: application/json
{
"strings": {
"prechat.heading": "Comment pouvons-nous aider?",
"prechat.name": "Votre nom",
"prechat.email": "Adresse e-mail",
"prechat.start": "Démarrer le chat",
"chat.send": "Envoyer",
"chat.typing": "L'agent est en train d'écrire…",
"rating.heading": "Évaluez votre expérience",
"rating.success": "Merci pour votre avis!"
}
}Supported string keys: launcher.open, launcher.close, prechat.heading, prechat.name, prechat.email, prechat.start, prechat.offline, chat.send, chat.placeholder, chat.offline, chat.typing, chat.attachment, rating.heading, rating.1–rating.5, rating.comment, rating.submit, rating.success, and error.* keys.
Multiple widgets / brands
Each widget has its own site key, branding, working hours, and locales. Include multiple <script> tags with different data-site-key values to run independent chat widgets on the same page (e.g. sales vs. support brands).
The loader opens an Umney-hosted iframe. Visitor JWTs are conversation-scoped and are never interchangeable with staff access tokens.
Outbound webhooks
Subscribe to real-time HTTPS POST callbacks for conversation lifecycle events. Configure endpoints under Dashboard → Developers → Outbound webhooks, or manage them programmatically with a webhooks:manage API key.
Events
| Event | Description |
|---|---|
conversation.created | A new conversation channel is opened. |
message.created | A message (customer, agent, AI, or system) is sent. |
conversation.assigned | An agent is assigned or reassigned. |
conversation.closed | The conversation is closed or resolved. |
csat.submitted | The visitor submits a satisfaction rating. |
Payload format
POST https://your-server.com/webhooks/umney
Content-Type: application/json
X-Umney-Signature: sha256=<hex-hmac>
X-Umney-Event: message.created
{
"event": "message.created",
"timestamp": "2026-07-15T12:02:00.000Z",
"data": {
"id": "f34db22c-2e8b-4fa1-99b5-aa4aed198aae",
"channelId": "8bb7f3b8-...",
"senderType": "customer",
"content": "Hello, I need help with my order."
}
}Signature verification
Every delivery includes an X-Umney-Signature header containing sha256=<hex>, an HMAC-SHA256 of the raw request body using your webhook signing secret. Always verify the signature before processing the payload. The signing secret is shown once when you create the endpoint.
Node.js (crypto module):
const crypto = require('node:crypto');
function verifyWebhookSignature(rawBody, signatureHeader, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('hex');
try {
return crypto.timingSafeEqual(
Buffer.from(signatureHeader, 'utf8'),
Buffer.from(expected, 'utf8'),
);
} catch {
return false; // length mismatch throws
}
}
// Usage in an Express handler:
app.post('/webhooks/umney', express.raw({ type: '*/*' }), (req, res) => {
const sig = req.headers['x-umney-signature'];
if (!sig || !verifyWebhookSignature(req.body, sig, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
console.log(event.event, event.data);
res.sendStatus(200);
});Web Crypto (Cloudflare Workers / Edge):
async function verifyWebhookSignature(rawBody, signatureHeader, secret) {
const enc = new TextEncoder();
const key = await crypto.subtle.importKey(
'raw', enc.encode(secret),
{ name: 'HMAC', hash: 'SHA-256' },
false, ['sign'],
);
const sig = await crypto.subtle.sign('HMAC', key, enc.encode(rawBody));
const expected = 'sha256=' + [...new Uint8Array(sig)]
.map(b => b.toString(16).padStart(2, '0'))
.join('');
// Constant-time comparison
if (signatureHeader.length !== expected.length) return false;
const a = enc.encode(signatureHeader);
const b = enc.encode(expected);
let mismatch = 0;
for (let i = 0; i < a.length; i++) mismatch |= a[i] ^ b[i];
return mismatch === 0;
}Retries and auto-disable
Failed deliveries (non-2xx response or network error) are retried with exponential backoff up to 5 attempts. After sustained failures the endpoint is automatically paused and can be re-enabled from the dashboard. Respond with HTTP 200–299 within 10 seconds to acknowledge receipt.
Dashboard test and delivery logs
Use the Test button on any webhook endpoint in Dashboard → Developers to send a synthetic event and verify connectivity. The expandable delivery log shows the last 50 deliveries with event type, HTTP status, attempts, errors, and timestamps.
API management
Create, update, test, and delete webhook subscriptions programmatically using API keys with webhooks:manage scope:
# List subscriptions
GET /tenants/:tenantId/webhooks
Authorization: Bearer <api-key>
# Create subscription
POST /tenants/:tenantId/webhooks
{ "name": "CRM sync", "url": "https://…", "events": ["message.created"] }
# Update (toggle active, change events)
PATCH /tenants/:tenantId/webhooks/:id
{ "active": false }
# Send test event
POST /tenants/:tenantId/webhooks/:id/test
# List recent deliveries
GET /tenants/:tenantId/webhooks/:id/deliveries
# Delete
DELETE /tenants/:tenantId/webhooks/:idWhatsApp service
Umney owns the Meta Cloud API integration and assigns a WhatsApp phone-number ID to each tenant. Contact Umney support to request a number assignment. Inbound messages appear in the same agent inbox and tenant AI agent.
Meta permits free-form replies only within 24 hours of the customer's latest inbound message. Outside that window, an approved template is required and the agent send endpoint returns HTTP 409 instead of falsely marking a reply as sent.