← Umney Connect

Umney Live Chat API

Build tenant-owned conversation workflows with scoped API keys, or install the hosted website widget without exposing dashboard credentials.

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.1rating.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

EventDescription
conversation.createdA new conversation channel is opened.
message.createdA message (customer, agent, AI, or system) is sent.
conversation.assignedAn agent is assigned or reassigned.
conversation.closedThe conversation is closed or resolved.
csat.submittedThe 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/:id

WhatsApp 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.