Sonela Integrations guide · for your developers

Your assistant, in the apps your people already use

Your people already ask each other questions in Slack, Microsoft Teams and the other apps they work in all day. With Sonela's API, a small service of yours can carry those questions to your assistant and post the answers back: from the same manuals and records, and read-only, as everywhere else. The API is how the platform carries the assistant into other apps, and this page is the shape of it; the API guide is the full reference.

How an integration is shaped

Three parts, and only the middle one is yours to build.

  1. The app your people use. A chat platform such as Slack, Microsoft Teams or Google Chat, a helpdesk, an intranet, or a mobile app of your own. Most chat platforms let you register a bot that receives the messages people send it.
  2. A small service of yours. A function on your server or in your cloud that receives each message from that app, asks Sonela, and posts the answer back. It is the one place the API key lives.
  3. Sonela's API. It answers from your workspace's manuals and, where it has them, its records and lookups, under the same guards as the assistant on your pages.

Sonela does not ship a ready-made app for any chat platform: that platform's developer documentation covers registering the bot and the platform's own rules for it. What follows holds whichever platform you choose.

Before you start

  • A workspace on the Growth or Scale plan; the API is part of those two.
  • A bot or app registered with your chat platform, and an address where your service can receive its messages.
  • Somewhere to keep a secret: your server's secret store or an environment variable.

A key for the integration

In the dashboard, open Integration › API keys › New key. Give it the chat scope alone: an integration asks questions and needs nothing else. Choose the data source it asks through, the customer whose records it asks about, the language it answers in, and limits near what your people really ask.

  • One key per integration, so ending one never ends another.
  • The key begins sk_live_ and is shown once. Keep it in your service's secret store — never in the bot's settings on the chat platform, in a page or in source control.
  • What a key reaches is fixed when you make it. To change it, make a new key and revoke the old one. Sonela emails everyone in your workspace when a key is made or rotated.

Decide who may ask

The key, not the person typing, decides whose records an answer reads. Sonela does not learn who in the chat asked, so once the bot is in a channel, everyone who can write to it reads what the key can read.

  • Add the bot only where everyone may see those answers: a team's own channel, not one shared with guests or people from another company.
  • If groups may see different data, give each its own key, and pick the key in your service from where the message came from — the team or the channel, as the chat platform reports it — never from anything typed in the message.
  • Check that each message really comes from the platform, using the signature or token it sends with every request, before your service does anything else with it.
  • The bot answers a private chat under the same key: the answer is private, but what it can read is no narrower.

Pass the question on

When a message arrives, your service sends its text to the API with the key as a bearer token. A message is at most 2,000 characters; tell the person when theirs is longer, rather than cutting it short.

Node, nothing to install

// Your API key (sk_live_...). It stays in your service.
const apiKey = process.env.SONELA_API_KEY;

// The newest conversation state for each thread, kept where you keep the thread.
const conversations = new Map();

async function askSonela(threadId, message) {
  const body = { message, conversation: conversations.get(threadId) };
  let response = await post(body);
  let answer = await readJson(response);

  if (answer.code === 'conversation_invalid') {
    conversations.delete(threadId); // expired or no longer valid: start afresh
    response = await post({ message });
    answer = await readJson(response);
  }

  if (!response.ok) {
    return { refused: answer.code ?? response.status, text: answer.title };
  }

  if (answer.conversation) {
    conversations.set(threadId, answer.conversation.state);
  }

  return { text: answer.reply, tables: answer.tables };
}

function post(body) {
  return fetch('https://api.sonela.ai/api/v1/chat', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${apiKey}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(body),
  });
}

const readJson = (response) => response.json().catch(() => ({}));

Any language that can send an HTTPS request will do. The API answers servers only, so this call belongs in your service and never in a page.

One thread, one conversation

Every answer carries a sealed conversation state. Keep the newest one for each thread or chat, under the id the platform gives it, and send it with that thread's next message, so a follow-up such as “and last month?” is understood.

  • The state holds the conversation's recent words, sealed so that only Sonela can open it, and only under this key. Store it with the same care as the thread itself, and delete it when the thread is done.
  • It changes with every answer, and expires 24 hours after the answer that carried it.
  • A state that no longer opens is answered 400 conversation_invalid: send the message again without it, and the conversation starts afresh.
  • Sonela keeps no conversation of its own for the API: the state you hold is the conversation.

Post the answer

  • reply is text and may carry Markdown. Most chat platforms read a formatting of their own, so convert lists, emphasis and links to theirs.
  • tables holds the rows an answer rests on when it read your records. Post a short one as text in the message, and a long one as a file or a link into your own app, inside the platform's message size limit.
  • When answered is false, the question was turned away before the assistant saw it. Post reply as it is — it says so in a sentence — and no question credit is spent.
  • Most chat platforms expect your service to acknowledge a message within seconds, sooner than an answer that reads your records may be ready. Acknowledge at once, then post the answer when it arrives.
  • To show the answer as it is written, ask for a stream with Accept: text/event-stream: post a short placeholder, edit it as text arrives — a few times a second at most, since platforms limit how often a message may be edited — and replace it with the final event, which is the answer.

Limits and refusals

Each key has its own limits, a number of requests a minute and of questions a day, and every question counts against your plan like one asked anywhere else. A refusal is JSON with a status and a code: turn it into a plain sentence in the chat rather than letting the bot fall silent.

  • 429 with api_key_day_spent: the key's questions for the day are spent, until midnight UTC.
  • 429 with quota_exceeded: the plan's question credits are spent until they reset.
  • 503 with assistant_not_ready or data_source_unavailable, or 502 with provider_failed: say so, and let the person ask again later. Retry once at most, never in a loop.

The API guide lists every refusal and what to do about each.

What stays true

  • Read-only: no message in any chat can change your data, as with every other way into Sonela.
  • Sonela checks the key on every request, so revoking it in the dashboard stops the bot at its next question.
  • The API takes text and answers in text and tables: no pictures, voice or documents travel this way.
  • What the chat platform keeps of a conversation is between you and that platform, under your agreement with it.

The security brief sets out what Sonela holds and where it stops.

Building an integration?

Tell us which app your people work in and what they need to ask, and we will help you shape the first one.

Write to us sonela.ai