Skip to main content
Developers

Osonflow API

Everything your team does in Osonflow, from your own code. Start chats and get AI replies inside your app, sync conversations and contacts to your CRM, keep the knowledge base current, and react to events the moment they happen.

Base URL
https://nautical-gazelle-675.eu-west-1.convex.site/v1
Version
v1 · 60 endpoints
Format
JSON over HTTPS, keys sent as Bearer tokens
On this page

Guide

Introduction

The Osonflow API is a REST API. You send JSON over HTTPS to https://nautical-gazelle-675.eu-west-1.convex.site/v1, authenticate with an API key from your dashboard, and get JSON back — a data field when a call succeeds, an error field when it does not.

Anything you can do in the dashboard has an endpoint: contacts, conversations and their messages, the knowledge base, assistants, tools, saved replies, workflows, event webhooks, analytics and voice calls. Customer messages sent through the API are answered by the same assistant, knowledge base and tools as your website widget, and every conversation appears in your team's inbox.

Every key is tied to one organization and can only see that organization's data. What each key may do, and how much, is set on the Developer API page of your dashboard.

Guide

Quickstart: a chat in three calls

  1. In your dashboard, open Developer API and create a key with the Chat only access preset. Copy it — it is shown once.
  2. Store it on your server as OSONFLOW_API_KEY. Never put it in a mobile app or a web page: anyone could copy it from there.
  3. Create a contact, then start a conversation with their first message. The response already holds the assistant's reply.
# 1. Who is writing
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/contacts" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Dilnoza Karimova", "email": "dilnoza@example.uz" }'

# 2. Their first message starts the conversation and returns the reply
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/conversations" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "contactId": "CONTACT_ID", "message": "Buyurtmam qachon yetib keladi?" }'

# 3. Every next message goes to the same conversation
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/conversations/CONVERSATION_ID/messages" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "text": "Buyurtma raqamim 1042" }'

The reply is written in the customer's language, from your knowledge base and with your tools, exactly as in the widget. When the assistant hands the conversation to your team, it stops answering; your team replies from the inbox, or you reply through Reply as your team.

Guide

Authentication

Send your key in the Authorization header of every call:

Header
Authorization: Bearer osf_live_4Hq2…

Keys start with osf_live_. Osonflow stores only a fingerprint of each key, so a lost key cannot be shown again — roll it instead, which replaces it with a new one that has the same permissions and limits. Revoking a key stops it immediately, and so does switching API access off for the whole organization. Keys can also be set to stop working after 30, 90 or 365 days.

Keys work from servers only, unless you say otherwise. A browser always tells the server which website a call came from, and calls from websites a key does not list are refused with origin_not_allowed. If you do need to call from a browser, list the website on the key; Osonflow then answers the browser's preflight and the call succeeds from that site only. You can also limit a key to IP addresses or ranges such as 198.51.100.0/24.

Guide

Permissions

Each endpoint needs one permission, shown next to it in the reference. A key only gets the permissions you tick when creating it, and you can change them later without replacing the key. Three presets cover the common cases:

PresetWhat it allows
Full accessEverything the API can do.
Read onlyCan look at everything, can change nothing.
Chat onlyFor your own chat window or app: create contacts, send messages, read replies.
PermissionAllows
chatStart conversations and send customer messages that the assistant answers.
conversations:readList conversations and read their messages.
conversations:writeReply as your team, change status, priority and assignee, and delete conversations.
contacts:readList contacts and read what the assistant remembers about them.
contacts:writeCreate contacts and update their name and email.
knowledge:readList and search the documents the assistant answers from.
knowledge:writeAdd documents, websites and files, and remove them.
assistants:readRead each assistant's greeting, instructions and look.
assistants:writeCreate assistants, change their settings and publish them.
tools:readList the actions the assistant can take.
tools:writeCreate, change and remove the assistant's actions.
saved_replies:readList your team's saved replies.
saved_replies:writeCreate, change and remove saved replies.
workflows:readList workflows, read their steps and see how they perform.
workflows:writeCreate, change, publish, switch off and delete workflows.
webhooks:readList event webhooks and their delivery history.
webhooks:writeCreate, change and remove event webhooks.
analytics:readRead resolution rates, topics, sentiment and lead numbers.
voice:readList AI voice conversations and read their transcripts.

GET /v1/me and GET /v1/usage work with any key.

Guide

Limits

Limits keep a runaway script from flooding your inbox or spending your AI budget. Every one of them is adjustable, at three levels:

  • Your organization sets its limits on the Developer API page. They are shared by all of its keys.
  • A key can be given tighter limits of its own, counted separately — so one busy integration cannot use up what the others need. A key's limits can never be looser than its organization's.
  • Osonflow sets the most any organization can choose, shown as the maximum below.

Each call counts toward requests. Calls that change something also count toward changes, calls that run an AI model toward AI calls, and knowledge uploads toward knowledge imports — the reference shows which, for every endpoint. Per-minute and per-hour limits refill continuously; per-day limits reset at midnight UTC.

LimitCountsDefaultMaximum
Requests per minuteEvery call, whatever it does.1206,000
Requests per dayEvery call, counted per UTC day.20,0005,000,000
Changes per minuteCalls that create, change or delete something.603,000
AI calls per minuteCalls that run an AI model — chat messages and knowledge search. These spend your AI budget.20600
AI calls per dayThe same AI calls, counted per UTC day.1,000200,000
Knowledge imports per hourDocuments, websites and files added to the knowledge base.301,000

Size limits cap a single call. Going over them returns 413 payload_too_large.

LimitCapsDefaultMaximum
Largest pageThe most items one list call returns.100 items500 items
Longest messageThe longest message text a call can send.4,000 characters32,000 characters
Largest requestThe biggest request body, including uploaded files.1,024 KB10,240 KB

Successful responses carry X-RateLimit-Limit and X-RateLimit-Remaining for requests per minute. When a limit is reached the call is refused with 429 rate_limited, a Retry-After header in seconds, and a message naming the limit. Nothing is counted against your other limits when a call is refused. Call GET /v1/usage to see what is left for today. Every call is logged on the Developer API page for 14 days by default (adjustable from 1 to 30).

Guide

Errors

A failed call returns an HTTP status and a JSON body with a stable code you can branch on, a message written for people, and the requestId — the same value as the X-Request-Id header. Quote it if you contact support.

Error
{
  "error": {
    "code": "rate_limited",
    "message": "You reached your organization's limit of 20 AI calls per minute.",
    "requestId": "req_05c3808474464c72956d0b44",
    "retryAfterSeconds": 16
  }
}
StatusCodeMeaning
400invalid_requestA field is missing, has the wrong type, or is out of range.
401missing_api_keyNo Authorization: Bearer header was sent.
401invalid_api_keyThe key does not exist, was revoked, or has expired.
403api_disabledAPI access is switched off for this organization.
403insufficient_scopeThe key is not allowed to call this endpoint.
403ip_not_allowedThe key only accepts calls from certain IP addresses.
403origin_not_allowedThe call came from a browser and the key does not allow that website.
403forbiddenThe action is switched off for this organization.
403plan_limit_reachedYour plan does not include this, or you reached its maximum.
404not_foundThe endpoint, or the thing you asked for, does not exist in this organization.
405method_not_allowedThe path exists but not with this HTTP method.
409conflictThe change clashes with the current state, such as deleting a live workflow.
413payload_too_largeThe request body or message is bigger than your limit.
429rate_limitedA limit was reached. Wait for Retry-After seconds before trying again.
500internal_errorSomething went wrong on our side. Retry with backoff.

Unknown fields in a request body are rejected rather than ignored, so a typo shows up as an error instead of a change that silently did nothing. Retry 429 and 5xx responses with backoff; do not retry other 4xx responses unchanged.

Guide

Pagination

List endpoints return a page at a time. Ask for up to your largest page with limit (20 by default). When hasMore is true, pass the returned nextCursor as cursor to get the next page. Cursors are opaque; do not build or change them.

curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/conversations?limit=100" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"

# Then pass the nextCursor from that response
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/conversations?limit=100&cursor=NEXT_CURSOR" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"

Guide

Webhooks

Instead of asking for changes, let Osonflow tell you. Create a webhook in the dashboard or with POST /v1/webhooks, choose events, and Osonflow will POST each one to your URL as it happens.

EventSent when
contact_session.createdA new contact left their details, or an anonymous visitor identified themselves.
conversation.createdA conversation started.
conversation.status_changedA conversation was handed over, resolved or reopened — by the AI, your team or the API.
message.receivedA customer sent a message.
message.sentSomeone on your team replied.
Delivery body
{
  "id": "evt_3b1c9a52-6f0e-4d8b-9a7c-2e4f6b8d0a1c",
  "type": "message.received",
  "attempt": 1,
  "occurredAt": "2026-09-18T09:41:07.000Z",
  "organizationId": "org_2ZkQ8pX1vB4nM7cR",
  "payload": {
    "conversationId": "jx9a4k2m7p1q8r3s6t0v5w2y4z7b1c9d",
    "contactSessionId": "kd7f2m9qv1c8d4e6t3w5y0b2n7h1j9s4",
    "prompt": "Buyurtmam qachon yetib keladi?",
    "attachmentCount": 0
  }
}

Each delivery carries x-osonflow-event-id, x-osonflow-event-type, x-osonflow-attempt and x-osonflow-signature. The signature is an HMAC-SHA256 of the raw body, keyed with the webhook's signing secret. Check it before trusting a delivery:

import crypto from "node:crypto"

// Verify against the raw body exactly as it arrived, before parsing it.
export function isFromOsonflow(rawBody, signatureHeader, secret) {
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", secret).update(rawBody).digest("hex")
  const a = Buffer.from(expected)
  const b = Buffer.from(signatureHeader ?? "")

  return a.length === b.length && crypto.timingSafeEqual(a, b)
}

Answer with any 2xx status. If your server is unreachable, times out, or answers 408, 429 or 5xx, Osonflow tries again after 15 seconds, 1 minute and 5 minutes — four attempts in all. Other 4xx answers are treated as final. Because a delivery can arrive more than once, use the event id to ignore repeats. Recent deliveries and your server's responses are listed by List deliveries.

Guide

Conventions

  • Ids are strings. Treat them as opaque and store them as they are.
  • Times are ISO 8601 strings in UTC, such as 2026-09-18T09:41:07.000Z.
  • Every record says what it is in an object field — conversation, message, contact and so on.
  • Missing values are null, not left out, so every field of a record is always present.
  • Updates are partial: send only the fields you want to change. Where a field can be cleared, send null.
  • Versioning: within v1, new endpoints and new fields may appear, but nothing is renamed or removed. Write your code to ignore fields it does not know.

API reference

Account

Check which key you are using, what it may do and how much of your limits is left.

Get the current key

GET/v1/me

Returns the organization and key behind the request, its scopes, and the limits that apply to it.

Permission any keyCounts toward requests
Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/me" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "object": "api_key",
    "organizationId": "org_2ZkQ8pX1vB4nM7cR",
    "key": {
      "id": "k2x6a0d4g8j2m6p0s4v8y2b6e0h4k8n2",
      "name": "CRM sync",
      "prefix": "osf_live_4Hq2",
      "scopes": [
        "conversations:read",
        "contacts:read"
      ],
      "expiresAt": null,
      "createdAt": "2026-09-12T08:15:02.000Z"
    },
    "limits": {
      "requestsPerMinute": 120,
      "requestsPerDay": 20000,
      "writesPerMinute": 60,
      "aiRequestsPerMinute": 20,
      "aiRequestsPerDay": 1000,
      "knowledgeImportsPerHour": 30,
      "maxPageSize": 100,
      "maxMessageChars": 4000,
      "maxBodyKb": 1024
    },
    "apiVersion": "v1"
  }
}

Get today's usage

GET/v1/usage

How many calls this key and your whole organization made today (UTC), and how many are left.

Permission any keyCounts toward requests
Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/usage" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "object": "usage",
    "day": "2026-09-18",
    "organization": {
      "requests": 1840,
      "aiRequests": 212,
      "errors": 9
    },
    "key": {
      "requests": 610,
      "aiRequests": 0,
      "errors": 2
    },
    "remaining": {
      "requestsToday": 18160,
      "aiRequestsToday": 788
    },
    "limits": {
      "requestsPerMinute": 120,
      "requestsPerDay": 20000,
      "writesPerMinute": 60,
      "aiRequestsPerMinute": 20,
      "aiRequestsPerDay": 1000,
      "knowledgeImportsPerHour": 30,
      "maxPageSize": 100,
      "maxMessageChars": 4000,
      "maxBodyKb": 1024
    }
  }
}

API reference

Contacts

The people who talk to your assistant. A contact is needed before a conversation can start.

List contacts

GET/v1/contacts

Contacts who left their details, newest first. Anonymous visitors are not included.

Permission contacts:readCounts toward requests

Query parameters

searchstring

Matches name, email, phone, handle, page or language.

segmentstring

waiting — handed to your team and the customer spoke last. newcomers — first seen in the last 7 days. no_chats — never started a chat.

allwaitingnewcomersno_chats
limitinteger

How many items to return, from 1 up to your largest page. Defaults to 20.

cursorstring

The nextCursor from the previous page.

Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/contacts?limit=20" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": [
    {
      "id": "kd7f2m9qv1c8d4e6t3w5y0b2n7h1j9s4",
      "object": "contact",
      "name": "Dilnoza Karimova",
      "email": "dilnoza@example.uz",
      "isAnonymous": false,
      "channel": "Web",
      "externalId": "user_1842",
      "phone": null,
      "socialHandle": null,
      "language": "uz",
      "timezone": "Asia/Tashkent",
      "pageUrl": "https://shop.example.uz/checkout",
      "referrer": null,
      "conversationCount": 1,
      "latestConversationId": "jx9a4k2m7p1q8r3s6t0v5w2y4z7b1c9d",
      "latestConversationStatus": "unresolved",
      "awaitingReply": false,
      "firstSeenAt": "2026-09-12T08:15:02.000Z",
      "lastActiveAt": "2026-09-18T09:41:07.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Create a contact

POST/v1/contacts

Adds a person your assistant can talk to. Use the returned id to start a conversation.

Permission contacts:writeCounts toward requests, changes

Body (JSON)

namestringRequired

Up to 40 characters.

emailstringRequired

Up to 50 characters.

externalIdstring

Your own id for this person, stored with the contact.

languagestring

Language tag, such as uz or ru.

timezonestring

IANA time zone, such as Asia/Tashkent.

pageUrlstring

The page they were on.

referrerstring

Where they came from.

Request
curl -X POST "https://nautical-gazelle-675.eu-west-1.convex.site/v1/contacts" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Dilnoza Karimova",
  "email": "dilnoza@example.uz",
  "externalId": "user_1842",
  "language": "uz",
  "timezone": "Asia/Tashkent"
}'
Response
{
  "data": {
    "id": "kd7f2m9qv1c8d4e6t3w5y0b2n7h1j9s4",
    "object": "contact",
    "name": "Dilnoza Karimova",
    "email": "dilnoza@example.uz",
    "isAnonymous": false,
    "channel": "Web",
    "externalId": "user_1842",
    "phone": null,
    "socialHandle": null,
    "language": "uz",
    "timezone": "Asia/Tashkent",
    "pageUrl": "https://shop.example.uz/checkout",
    "referrer": null,
    "conversationCount": 0,
    "latestConversationId": null,
    "latestConversationStatus": null,
    "awaitingReply": false,
    "firstSeenAt": "2026-09-12T08:15:02.000Z",
    "lastActiveAt": "2026-09-18T09:41:07.000Z"
  }
}

Get a contact

GET/v1/contacts/:contactId

One contact, with how many conversations they had and whether they are waiting on you.

Permission contacts:readCounts toward requests

Path parameters

contactIdstringRequired

The contact's id.

Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/contacts/kd7f2m9qv1c8d4e6t3w5y0b2n7h1j9s4" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "id": "kd7f2m9qv1c8d4e6t3w5y0b2n7h1j9s4",
    "object": "contact",
    "name": "Dilnoza Karimova",
    "email": "dilnoza@example.uz",
    "isAnonymous": false,
    "channel": "Web",
    "externalId": "user_1842",
    "phone": null,
    "socialHandle": null,
    "language": "uz",
    "timezone": "Asia/Tashkent",
    "pageUrl": "https://shop.example.uz/checkout",
    "referrer": null,
    "conversationCount": 1,
    "latestConversationId": "jx9a4k2m7p1q8r3s6t0v5w2y4z7b1c9d",
    "latestConversationStatus": "unresolved",
    "awaitingReply": false,
    "firstSeenAt": "2026-09-12T08:15:02.000Z",
    "lastActiveAt": "2026-09-18T09:41:07.000Z"
  }
}

Update a contact

PATCH/v1/contacts/:contactId

Change a contact's name or email. Send only the fields you want to change.

Permission contacts:writeCounts toward requests, changes

Path parameters

contactIdstringRequired

The contact's id.

Body (JSON)

namestring

Up to 40 characters.

emailstring

Up to 50 characters.

Request
curl -X PATCH "https://nautical-gazelle-675.eu-west-1.convex.site/v1/contacts/kd7f2m9qv1c8d4e6t3w5y0b2n7h1j9s4" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "dilnoza.karimova@example.uz"
}'
Response
{
  "data": {
    "id": "kd7f2m9qv1c8d4e6t3w5y0b2n7h1j9s4",
    "object": "contact",
    "name": "Dilnoza Karimova",
    "email": "dilnoza.karimova@example.uz",
    "isAnonymous": false,
    "channel": "Web",
    "externalId": "user_1842",
    "phone": null,
    "socialHandle": null,
    "language": "uz",
    "timezone": "Asia/Tashkent",
    "pageUrl": "https://shop.example.uz/checkout",
    "referrer": null,
    "conversationCount": 1,
    "latestConversationId": "jx9a4k2m7p1q8r3s6t0v5w2y4z7b1c9d",
    "latestConversationStatus": "unresolved",
    "awaitingReply": false,
    "firstSeenAt": "2026-09-12T08:15:02.000Z",
    "lastActiveAt": "2026-09-18T09:41:07.000Z"
  }
}

Get what the assistant remembers

GET/v1/contacts/:contactId/memory

The assistant's running summary of this contact across conversations. data is null until there is something to remember.

Permission contacts:readCounts toward requests

Path parameters

contactIdstringRequired

The contact's id.

Customer memory is part of paid plans; on other plans data is always null.

Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/contacts/kd7f2m9qv1c8d4e6t3w5y0b2n7h1j9s4/memory" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "object": "customer_memory",
    "email": "dilnoza@example.uz",
    "name": "Dilnoza Karimova",
    "summary": "Returning customer in Tashkent who usually asks about delivery.",
    "preferredLanguage": "uz",
    "recentIntents": [
      "delivery_time"
    ],
    "notableFacts": [
      "Prefers evening delivery"
    ],
    "history": [
      {
        "channel": "chat",
        "intent": "delivery_time",
        "status": "resolved",
        "summary": "Asked about delivery time for order 1042.",
        "at": "2026-09-18T09:41:07.000Z"
      }
    ],
    "totals": {
      "conversations": 4,
      "escalations": 1,
      "resolved": 3
    },
    "lastSeenAt": "2026-09-18T09:41:07.000Z",
    "updatedAt": "2026-09-18T09:41:12.000Z"
  }
}

List customer memories

GET/v1/customer-memories

Every customer the assistant remembers, most recently seen first.

Permission contacts:readCounts toward requests

Query parameters

limitinteger

How many to return, up to your largest page. Defaults to 20.

Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/customer-memories?limit=20" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": [
    {
      "object": "customer_memory",
      "email": "dilnoza@example.uz",
      "name": "Dilnoza Karimova",
      "summary": "Returning customer in Tashkent who usually asks about delivery.",
      "preferredLanguage": "uz",
      "recentIntents": [
        "delivery_time"
      ],
      "notableFacts": [
        "Prefers evening delivery"
      ],
      "history": [
        {
          "channel": "chat",
          "intent": "delivery_time",
          "status": "resolved",
          "summary": "Asked about delivery time for order 1042.",
          "at": "2026-09-18T09:41:07.000Z"
        }
      ],
      "totals": {
        "conversations": 4,
        "escalations": 1,
        "resolved": 3
      },
      "lastSeenAt": "2026-09-18T09:41:07.000Z",
      "updatedAt": "2026-09-18T09:41:12.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

API reference

Conversations

Chats between a contact and your assistant or team. Send customer messages to get AI replies, or reply as your team.

List conversations

GET/v1/conversations

Newest first. Filters are applied to each page, so a page can hold fewer items than limit.

Permission conversations:readCounts toward requests

Query parameters

statusstring

unresolved — the assistant is handling it. escalated — handed to your team.

unresolvedescalatedresolved
sourcestring

Whether the assistant or a published workflow ran the conversation.

widgetworkflow
contactIdstring

Only this contact's conversations.

assignedstring

Whether someone on your team owns it.

assignedunassigned
includeLastMessageboolean

Adds lastMessage to each conversation.

limitinteger

How many items to return, from 1 up to your largest page. Defaults to 20.

cursorstring

The nextCursor from the previous page.

Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/conversations?limit=20" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": [
    {
      "id": "jx9a4k2m7p1q8r3s6t0v5w2y4z7b1c9d",
      "object": "conversation",
      "status": "unresolved",
      "source": "widget",
      "assistantId": "default",
      "priority": null,
      "assignee": null,
      "contact": {
        "id": "kd7f2m9qv1c8d4e6t3w5y0b2n7h1j9s4",
        "name": "Dilnoza Karimova",
        "email": "dilnoza@example.uz",
        "isAnonymous": false
      },
      "unreadForTeam": 1,
      "lastCustomerMessageAt": "2026-09-18T09:41:07.000Z",
      "lastReplyAt": null,
      "firstTeamResponseAt": null,
      "escalatedAt": null,
      "resolvedAt": null,
      "resolvedBy": null,
      "workflow": null,
      "createdAt": "2026-09-18T09:41:07.000Z"
    }
  ],
  "hasMore": true,
  "nextCursor": "20"
}

Start a conversation

POST/v1/conversations

Starts a conversation with the contact's first message and returns the assistant's reply. A conversation always begins with a real message, so message is required.

Permission chatCounts toward requests, changes, AI calls

Body (JSON)

contactIdstringRequired

Who is writing.

messagestringRequired

Their first message.

assistantIdstring

Which assistant answers. Defaults to your default assistant.

greetingboolean

Put the assistant's greeting at the start of the transcript, as the widget does. Defaults to true.

AI replies need an active paid plan. Without one, the customer receives a note that a person will reply.

When a published workflow is live, it runs the conversation instead of the assistant. Workflow steps that call AI or your API can finish after the response is sent — read the messages again to pick them up.

Request
curl -X POST "https://nautical-gazelle-675.eu-west-1.convex.site/v1/conversations" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "contactId": "kd7f2m9qv1c8d4e6t3w5y0b2n7h1j9s4",
  "message": "Buyurtmam qachon yetib keladi?"
}'
Response
{
  "data": {
    "conversation": {
      "id": "jx9a4k2m7p1q8r3s6t0v5w2y4z7b1c9d",
      "object": "conversation",
      "status": "unresolved",
      "source": "widget",
      "assistantId": "default",
      "priority": null,
      "assignee": null,
      "contact": {
        "id": "kd7f2m9qv1c8d4e6t3w5y0b2n7h1j9s4",
        "name": "Dilnoza Karimova",
        "email": "dilnoza@example.uz",
        "isAnonymous": false
      },
      "unreadForTeam": 1,
      "lastCustomerMessageAt": "2026-09-18T09:41:07.000Z",
      "lastReplyAt": null,
      "firstTeamResponseAt": null,
      "escalatedAt": null,
      "resolvedAt": null,
      "resolvedBy": null,
      "workflow": null,
      "createdAt": "2026-09-18T09:41:07.000Z"
    },
    "messages": [
      {
        "id": "m17q8e2r5t9y3u6i0o4p7a1s8d2f5g3h",
        "object": "message",
        "role": "contact",
        "authorName": null,
        "text": "Buyurtmam qachon yetib keladi?",
        "attachments": [],
        "createdAt": "2026-09-18T09:41:07.000Z"
      },
      {
        "id": "m2k7p1v5z9d3h7l1q5u9y3c7g1k5o9s3",
        "object": "message",
        "role": "assistant",
        "authorName": null,
        "text": "Buyurtmangiz 2–3 ish kunida yetkaziladi. Buyurtma raqamingizni yuborsangiz, holatini tekshirib beraman.",
        "attachments": [],
        "createdAt": "2026-09-18T09:41:12.000Z"
      }
    ]
  }
}

Get a conversation

GET/v1/conversations/:conversationId

One conversation. When a workflow is waiting for the customer, workflow shows what it is waiting for, including any buttons.

Permission conversations:readCounts toward requests

Path parameters

conversationIdstringRequired

The conversation's id.

Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/conversations/jx9a4k2m7p1q8r3s6t0v5w2y4z7b1c9d" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "id": "jx9a4k2m7p1q8r3s6t0v5w2y4z7b1c9d",
    "object": "conversation",
    "status": "unresolved",
    "source": "workflow",
    "assistantId": "default",
    "priority": null,
    "assignee": null,
    "contact": {
      "id": "kd7f2m9qv1c8d4e6t3w5y0b2n7h1j9s4",
      "name": "Dilnoza Karimova",
      "email": "dilnoza@example.uz",
      "isAnonymous": false
    },
    "unreadForTeam": 1,
    "lastCustomerMessageAt": "2026-09-18T09:41:07.000Z",
    "lastReplyAt": null,
    "firstTeamResponseAt": null,
    "escalatedAt": null,
    "resolvedAt": null,
    "resolvedBy": null,
    "workflow": {
      "id": "jd2f6h0k4m8p2s6v0y4b8e2h6k0n4q8t",
      "status": "waiting",
      "waitingFor": "buttons",
      "prompt": "Qaysi mavzu bo'yicha?",
      "buttons": [
        {
          "id": "btn_delivery",
          "label": "Yetkazib berish"
        },
        {
          "id": "btn_returns",
          "label": "Qaytarish"
        }
      ]
    },
    "createdAt": "2026-09-18T09:41:07.000Z"
  }
}

Update a conversation

PATCH/v1/conversations/:conversationId

Change status, priority or assignee. Changing the status fires the conversation.status_changed webhook.

Permission conversations:writeCounts toward requests, changes

Path parameters

conversationIdstringRequired

The conversation's id.

Body (JSON)

statusstring

escalated hands it to your team; resolved closes it.

unresolvedescalatedresolved
prioritystring

Send null to clear it.

urgenthighmediumlow
assigneeobject

{ "id": "…", "name": "…" } of the person who owns it, or null to unassign.

Request
curl -X PATCH "https://nautical-gazelle-675.eu-west-1.convex.site/v1/conversations/jx9a4k2m7p1q8r3s6t0v5w2y4z7b1c9d" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "status": "escalated",
  "priority": "high",
  "assignee": {
    "id": "user_2Nf8",
    "name": "Aziz"
  }
}'
Response
{
  "data": {
    "id": "jx9a4k2m7p1q8r3s6t0v5w2y4z7b1c9d",
    "object": "conversation",
    "status": "escalated",
    "source": "widget",
    "assistantId": "default",
    "priority": "high",
    "assignee": {
      "id": "user_2Nf8",
      "name": "Aziz"
    },
    "contact": {
      "id": "kd7f2m9qv1c8d4e6t3w5y0b2n7h1j9s4",
      "name": "Dilnoza Karimova",
      "email": "dilnoza@example.uz",
      "isAnonymous": false
    },
    "unreadForTeam": 1,
    "lastCustomerMessageAt": "2026-09-18T09:41:07.000Z",
    "lastReplyAt": null,
    "firstTeamResponseAt": null,
    "escalatedAt": "2026-09-18T09:41:12.000Z",
    "resolvedAt": null,
    "resolvedBy": null,
    "workflow": null,
    "createdAt": "2026-09-18T09:41:07.000Z"
  }
}

Delete a conversation

DELETE/v1/conversations/:conversationId

Deletes the conversation, its messages and its analytics. This cannot be undone.

Permission conversations:writeCounts toward requests, changes

Path parameters

conversationIdstringRequired

The conversation's id.

Request
curl -X DELETE "https://nautical-gazelle-675.eu-west-1.convex.site/v1/conversations/jx9a4k2m7p1q8r3s6t0v5w2y4z7b1c9d" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "id": "jx9a4k2m7p1q8r3s6t0v5w2y4z7b1c9d",
    "object": "conversation",
    "deleted": true
  }
}

List messages

GET/v1/conversations/:conversationId/messages

The transcript, newest message first. Internal tool calls are left out. Reverse the page to show it top to bottom.

Permission conversations:readCounts toward requests

Path parameters

conversationIdstringRequired

The conversation's id.

Query parameters

limitinteger

How many items to return, from 1 up to your largest page. Defaults to 20.

cursorstring

The nextCursor from the previous page.

role is contact for the customer and assistant for everything sent back, whether by AI or by your team. Team replies carry the person's name in authorName.

Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/conversations/jx9a4k2m7p1q8r3s6t0v5w2y4z7b1c9d/messages?limit=20" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": [
    {
      "id": "m2k7p1v5z9d3h7l1q5u9y3c7g1k5o9s3",
      "object": "message",
      "role": "assistant",
      "authorName": null,
      "text": "Buyurtmangiz 2–3 ish kunida yetkaziladi. Buyurtma raqamingizni yuborsangiz, holatini tekshirib beraman.",
      "attachments": [],
      "createdAt": "2026-09-18T09:41:12.000Z"
    },
    {
      "id": "m17q8e2r5t9y3u6i0o4p7a1s8d2f5g3h",
      "object": "message",
      "role": "contact",
      "authorName": null,
      "text": "Buyurtmam qachon yetib keladi?",
      "attachments": [],
      "createdAt": "2026-09-18T09:41:07.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Send a customer message

POST/v1/conversations/:conversationId/messages

Adds a message from the contact and waits for the assistant's reply. The response holds every new message, starting with the one you sent.

Permission chatCounts toward requests, changes, AI calls

Path parameters

conversationIdstringRequired

The conversation's id.

Body (JSON)

textstringRequired

What the customer wrote.

buttonIdstring

When a workflow is waiting on buttons, the id of the button the customer picked.

A resolved conversation cannot take new messages — start a new one instead.

Once a conversation is handed to your team (escalated), the assistant stops answering and messages holds only the customer's message.

Request
curl -X POST "https://nautical-gazelle-675.eu-west-1.convex.site/v1/conversations/jx9a4k2m7p1q8r3s6t0v5w2y4z7b1c9d/messages" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "Buyurtma raqamim 1042"
}'
Response
{
  "data": {
    "conversation": {
      "id": "jx9a4k2m7p1q8r3s6t0v5w2y4z7b1c9d",
      "object": "conversation",
      "status": "unresolved",
      "source": "widget",
      "assistantId": "default",
      "priority": null,
      "assignee": null,
      "contact": {
        "id": "kd7f2m9qv1c8d4e6t3w5y0b2n7h1j9s4",
        "name": "Dilnoza Karimova",
        "email": "dilnoza@example.uz",
        "isAnonymous": false
      },
      "unreadForTeam": 2,
      "lastCustomerMessageAt": "2026-09-18T09:41:07.000Z",
      "lastReplyAt": null,
      "firstTeamResponseAt": null,
      "escalatedAt": null,
      "resolvedAt": null,
      "resolvedBy": null,
      "workflow": null,
      "createdAt": "2026-09-18T09:41:07.000Z"
    },
    "messages": [
      {
        "id": "m17q8e2r5t9y3u6i0o4p7a1s8d2f5g3h",
        "object": "message",
        "role": "contact",
        "authorName": null,
        "text": "Buyurtma raqamim 1042",
        "attachments": [],
        "createdAt": "2026-09-18T09:41:07.000Z"
      },
      {
        "id": "m2k7p1v5z9d3h7l1q5u9y3c7g1k5o9s3",
        "object": "message",
        "role": "assistant",
        "authorName": null,
        "text": "1042-buyurtmangiz yo'lda, ertaga yetkaziladi.",
        "attachments": [],
        "createdAt": "2026-09-18T09:41:12.000Z"
      }
    ]
  }
}

Reply as your team

POST/v1/conversations/:conversationId/replies

Sends a message from a person on your team. The conversation moves to escalated, so the assistant stops answering, and the reply is delivered on Telegram, WhatsApp or Instagram when that is where the customer wrote from.

Permission conversations:writeCounts toward requests, changes

Path parameters

conversationIdstringRequired

The conversation's id.

Body (JSON)

textstringRequired

The reply.

authorNamestring

Name shown with the reply. Defaults to the API key's name.

authorIdstring

Your id for the person, used to assign the conversation to them.

Request
curl -X POST "https://nautical-gazelle-675.eu-west-1.convex.site/v1/conversations/jx9a4k2m7p1q8r3s6t0v5w2y4z7b1c9d/replies" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "text": "Salom Dilnoza! Kuryer bugun soat 18:00 gacha keladi.",
  "authorName": "Aziz",
  "authorId": "user_2Nf8"
}'
Response
{
  "data": {
    "conversation": {
      "id": "jx9a4k2m7p1q8r3s6t0v5w2y4z7b1c9d",
      "object": "conversation",
      "status": "escalated",
      "source": "widget",
      "assistantId": "default",
      "priority": null,
      "assignee": {
        "id": "user_2Nf8",
        "name": "Aziz"
      },
      "contact": {
        "id": "kd7f2m9qv1c8d4e6t3w5y0b2n7h1j9s4",
        "name": "Dilnoza Karimova",
        "email": "dilnoza@example.uz",
        "isAnonymous": false
      },
      "unreadForTeam": 1,
      "lastCustomerMessageAt": "2026-09-18T09:41:07.000Z",
      "lastReplyAt": "2026-09-18T09:41:12.000Z",
      "firstTeamResponseAt": "2026-09-18T09:41:12.000Z",
      "escalatedAt": null,
      "resolvedAt": null,
      "resolvedBy": null,
      "workflow": null,
      "createdAt": "2026-09-18T09:41:07.000Z"
    },
    "message": {
      "id": "m2k7p1v5z9d3h7l1q5u9y3c7g1k5o9s3",
      "object": "message",
      "role": "assistant",
      "authorName": "Aziz",
      "text": "Salom Dilnoza! Kuryer bugun soat 18:00 gacha keladi.",
      "attachments": [],
      "createdAt": "2026-09-18T09:41:12.000Z"
    }
  }
}

Mark as read

POST/v1/conversations/:conversationId/read

Clears the unread count your team sees for this conversation.

Permission conversations:writeCounts toward requests, changes

Path parameters

conversationIdstringRequired

The conversation's id.

Request
curl -X POST "https://nautical-gazelle-675.eu-west-1.convex.site/v1/conversations/jx9a4k2m7p1q8r3s6t0v5w2y4z7b1c9d/read" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "id": "jx9a4k2m7p1q8r3s6t0v5w2y4z7b1c9d",
    "object": "conversation",
    "status": "unresolved",
    "source": "widget",
    "assistantId": "default",
    "priority": null,
    "assignee": null,
    "contact": {
      "id": "kd7f2m9qv1c8d4e6t3w5y0b2n7h1j9s4",
      "name": "Dilnoza Karimova",
      "email": "dilnoza@example.uz",
      "isAnonymous": false
    },
    "unreadForTeam": 0,
    "lastCustomerMessageAt": "2026-09-18T09:41:07.000Z",
    "lastReplyAt": null,
    "firstTeamResponseAt": null,
    "escalatedAt": null,
    "resolvedAt": null,
    "resolvedBy": null,
    "workflow": null,
    "createdAt": "2026-09-18T09:41:07.000Z"
  }
}

API reference

Knowledge base

The documents, websites and files the assistant answers from.

List knowledge

GET/v1/knowledge

Everything in the knowledge base.

Permission knowledge:readCounts toward requests

Query parameters

categorystring

Only entries in this category.

limitinteger

How many items to return, from 1 up to your largest page. Defaults to 20.

cursorstring

The nextCursor from the previous page.

Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/knowledge?limit=20" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": [
    {
      "id": "e5c8b1n4m7q0w3e6r9t2y5u8i1o4p7a0",
      "object": "knowledge_entry",
      "title": "Delivery and returns",
      "type": "txt",
      "size": "3.2 KB",
      "status": "ready",
      "category": "policies",
      "sourceUrl": null,
      "url": "https://example.convex.cloud/api/storage/…"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Get an entry's content

GET/v1/knowledge/:entryId

The text of an entry. For documents that are not plain text, such as PDFs, content is null and url links to the file.

Permission knowledge:readCounts toward requests

Path parameters

entryIdstringRequired

The entry's id.

Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/knowledge/e5c8b1n4m7q0w3e6r9t2y5u8i1o4p7a0" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "id": "e5c8b1n4m7q0w3e6r9t2y5u8i1o4p7a0",
    "object": "knowledge_content",
    "title": "delivery-and-returns.txt",
    "sourceUrl": null,
    "content": "Orders in Tashkent arrive in 1–2 working days…",
    "url": null
  }
}

Add a text document

POST/v1/knowledge/documents

Adds text the assistant can answer from. Adding the same text again is ignored rather than duplicated.

Permission knowledge:writeCounts toward requests, changes, knowledge imports

Body (JSON)

titlestringRequired

Up to 120 characters.

textstringRequired

The content, up to your largest request.

categorystring

Any label you use to group entries.

Entries are indexed in the background: status is processing until the assistant can use them.

Knowledge imports need an active paid plan.

Request
curl -X POST "https://nautical-gazelle-675.eu-west-1.convex.site/v1/knowledge/documents" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "title": "Delivery and returns",
  "text": "Orders in Tashkent arrive in 1–2 working days. Other regions take 2–4…",
  "category": "policies"
}'
Response
{
  "data": {
    "id": "e5c8b1n4m7q0w3e6r9t2y5u8i1o4p7a0",
    "object": "knowledge_entry",
    "title": "Delivery and returns",
    "type": "txt",
    "size": "3.2 KB",
    "status": "processing",
    "category": "policies",
    "sourceUrl": null,
    "url": "https://example.convex.cloud/api/storage/…",
    "created": true
  }
}

Add a web page

POST/v1/knowledge/websites

Reads a public web page and adds its text to the knowledge base.

Permission knowledge:writeCounts toward requests, changes, knowledge imports

Body (JSON)

urlstringRequired

A public http(s) address.

titlestring

Defaults to the page's own title.

categorystring

Any label you use to group entries.

Request
curl -X POST "https://nautical-gazelle-675.eu-west-1.convex.site/v1/knowledge/websites" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://shop.example.uz/delivery",
  "category": "policies"
}'
Response
{
  "data": {
    "id": "e5c8b1n4m7q0w3e6r9t2y5u8i1o4p7a0",
    "object": "knowledge_entry",
    "title": "Delivery — Example Shop",
    "type": "url",
    "size": "3.2 KB",
    "status": "processing",
    "category": "policies",
    "sourceUrl": "https://shop.example.uz/delivery",
    "url": "https://example.convex.cloud/api/storage/…",
    "created": true
  }
}

Upload a file

POST/v1/knowledge/files

Uploads a PDF, plain-text, CSV, Markdown, HTML or image file. Send it as multipart/form-data.

Permission knowledge:writeCounts toward requests, changes, knowledge imports

Form fields (multipart/form-data)

filefileRequired

The file, within your largest request size.

categorystring

Any label you use to group entries.

Request
curl -X POST "https://nautical-gazelle-675.eu-west-1.convex.site/v1/knowledge/files" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -F "file=@price-list.pdf" \
  -F "category=pricing"
Response
{
  "data": {
    "id": "e5c8b1n4m7q0w3e6r9t2y5u8i1o4p7a0",
    "object": "knowledge_entry",
    "title": "price-list.pdf",
    "type": "pdf",
    "size": "3.2 KB",
    "status": "processing",
    "category": "policies",
    "sourceUrl": null,
    "url": "https://example.convex.cloud/api/storage/…",
    "created": true
  }
}

Delete an entry

DELETE/v1/knowledge/:entryId

Removes an entry. The assistant stops using it straight away.

Permission knowledge:writeCounts toward requests, changes

Path parameters

entryIdstringRequired

The entry's id.

Request
curl -X DELETE "https://nautical-gazelle-675.eu-west-1.convex.site/v1/knowledge/e5c8b1n4m7q0w3e6r9t2y5u8i1o4p7a0" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "id": "e5c8b1n4m7q0w3e6r9t2y5u8i1o4p7a0",
    "object": "knowledge_entry",
    "deleted": true
  }
}

Search knowledge

POST/v1/knowledge/search

Finds the passages that best match a question — the same search the assistant runs before it answers.

Permission knowledge:readCounts toward requests, AI calls

Body (JSON)

querystringRequired

A question or phrase.

limitinteger

How many passages, 1 to 10. Defaults to 5.

Request
curl -X POST "https://nautical-gazelle-675.eu-west-1.convex.site/v1/knowledge/search" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "query": "How long does delivery take?",
  "limit": 3
}'
Response
{
  "data": [
    {
      "object": "knowledge_match",
      "entryId": "e5c8b1n4m7q0w3e6r9t2y5u8i1o4p7a0",
      "title": "Delivery and returns",
      "score": 0.83,
      "text": "Orders in Tashkent arrive in 1–2 working days…"
    }
  ]
}

API reference

Assistants

Each assistant's greeting, instructions, model, tools and look. Changes are saved as a draft until published.

List assistants

GET/v1/assistants

Every assistant in the organization, the default one first.

Permission assistants:readCounts toward requests
Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/assistants" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": [
    {
      "id": "default",
      "object": "assistant",
      "name": "Default agent",
      "isDefault": true,
      "publishedVersion": 7,
      "updatedAt": "2026-09-18T09:41:12.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null,
  "maxAssistants": 5
}

Create an assistant

POST/v1/assistants

Adds a new assistant with default settings. Your plan sets how many you can have.

Permission assistants:writeCounts toward requests, changes

Body (JSON)

namestring

Defaults to “Agent 2”, “Agent 3” and so on.

Request
curl -X POST "https://nautical-gazelle-675.eu-west-1.convex.site/v1/assistants" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Russian storefront"
}'
Response
{
  "data": {
    "id": "agent_m1x9k2_4hq8zt",
    "object": "assistant",
    "name": "Russian storefront",
    "isDefault": false,
    "publishedVersion": 1,
    "updatedAt": "2026-09-18T09:41:12.000Z"
  }
}

Get an assistant

GET/v1/assistants/:assistantId

The live settings (published), the unpublished ones (draft), and the last 20 published versions.

Permission assistants:readCounts toward requests

Path parameters

assistantIdstringRequired

The assistant's id. The default assistant is default.

Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/assistants/default" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "id": "default",
    "object": "assistant",
    "name": "Default agent",
    "isDefault": true,
    "publishedVersion": 7,
    "updatedAt": "2026-09-18T09:41:12.000Z",
    "publishedAt": "2026-09-12T08:15:02.000Z",
    "hasUnpublishedChanges": false,
    "published": {
      "greeting": "Assalomu alaykum! Qanday yordam bera olaman?",
      "instructions": "You are the support assistant for Example Shop…",
      "model": "gpt-4o-mini",
      "suggestions": [
        "Yetkazib berish",
        "Qaytarish",
        "To'lov usullari"
      ],
      "toolIds": [
        "kh3n8b5v2c7x4z1l9k6j3h0g7f4d1s8a"
      ],
      "copy": {
        "homeGreeting": "Salom 👋",
        "homeHeadline": "Qanday yordam kerak?",
        "startChatLabel": "Suhbatni boshlash",
        "inputPlaceholder": "Xabar yozing…",
        "onlineLabel": "Onlayn"
      },
      "theme": {
        "primaryColor": "#4f46e5",
        "assistantName": "Example Shop"
      },
      "appearance": {
        "launcherPosition": "bottom-right",
        "showPoweredBy": true
      },
      "helpTopics": []
    },
    "draft": {
      "greeting": "Assalomu alaykum! Qanday yordam bera olaman?",
      "instructions": "You are the support assistant for Example Shop…",
      "model": "gpt-4o-mini",
      "suggestions": [
        "Yetkazib berish",
        "Qaytarish",
        "To'lov usullari"
      ],
      "toolIds": [
        "kh3n8b5v2c7x4z1l9k6j3h0g7f4d1s8a"
      ],
      "copy": {
        "homeGreeting": "Salom 👋",
        "homeHeadline": "Qanday yordam kerak?",
        "startChatLabel": "Suhbatni boshlash",
        "inputPlaceholder": "Xabar yozing…",
        "onlineLabel": "Onlayn"
      },
      "theme": {
        "primaryColor": "#4f46e5",
        "assistantName": "Example Shop"
      },
      "appearance": {
        "launcherPosition": "bottom-right",
        "showPoweredBy": true
      },
      "helpTopics": []
    },
    "versions": [
      {
        "version": 7,
        "action": "publish",
        "publishedAt": "2026-09-12T08:15:02.000Z",
        "rolledBackFrom": null
      }
    ]
  }
}

Update an assistant

PATCH/v1/assistants/:assistantId

Changes the draft. Send only what you want to change; objects such as theme are merged. Pass "publish": true to put the change live in the same call.

Permission assistants:writeCounts toward requests, changes

Path parameters

assistantIdstringRequired

The assistant's id.

Body (JSON)

namestring

The assistant's name in your dashboard.

greetingstring

The first message a customer sees.

instructionsstring

How the assistant should behave.

modelstring

The chat model, such as gpt-4o-mini.

suggestionsarray

Up to 3 suggested questions.

toolIdsarray

The tools this assistant may use.

copyobject

Widget text: homeGreeting, homeHeadline, startChatLabel, inputPlaceholder, onlineLabel.

themeobject

Colours, logo, font and assistant name.

appearanceobject

Launcher position, size, label and behaviour.

publishboolean

Publish the draft after saving it.

Request
curl -X PATCH "https://nautical-gazelle-675.eu-west-1.convex.site/v1/assistants/default" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "greeting": "Здравствуйте! Чем могу помочь?",
  "suggestions": [
    "Доставка",
    "Возврат",
    "Оплата"
  ],
  "publish": true
}'
Response
{
  "data": {
    "id": "default",
    "object": "assistant",
    "name": "Default agent",
    "isDefault": true,
    "publishedVersion": 8,
    "updatedAt": "2026-09-18T09:41:12.000Z",
    "publishedAt": "2026-09-18T09:41:12.000Z",
    "hasUnpublishedChanges": false,
    "published": {
      "greeting": "Здравствуйте! Чем могу помочь?",
      "instructions": "You are the support assistant for Example Shop…",
      "model": "gpt-4o-mini",
      "suggestions": [
        "Доставка",
        "Возврат",
        "Оплата"
      ],
      "toolIds": [
        "kh3n8b5v2c7x4z1l9k6j3h0g7f4d1s8a"
      ],
      "copy": {
        "homeGreeting": "Salom 👋",
        "homeHeadline": "Qanday yordam kerak?",
        "startChatLabel": "Suhbatni boshlash",
        "inputPlaceholder": "Xabar yozing…",
        "onlineLabel": "Onlayn"
      },
      "theme": {
        "primaryColor": "#4f46e5",
        "assistantName": "Example Shop"
      },
      "appearance": {
        "launcherPosition": "bottom-right",
        "showPoweredBy": true
      },
      "helpTopics": []
    },
    "draft": {
      "greeting": "Здравствуйте! Чем могу помочь?",
      "instructions": "You are the support assistant for Example Shop…",
      "model": "gpt-4o-mini",
      "suggestions": [
        "Доставка",
        "Возврат",
        "Оплата"
      ],
      "toolIds": [
        "kh3n8b5v2c7x4z1l9k6j3h0g7f4d1s8a"
      ],
      "copy": {
        "homeGreeting": "Salom 👋",
        "homeHeadline": "Qanday yordam kerak?",
        "startChatLabel": "Suhbatni boshlash",
        "inputPlaceholder": "Xabar yozing…",
        "onlineLabel": "Onlayn"
      },
      "theme": {
        "primaryColor": "#4f46e5",
        "assistantName": "Example Shop"
      },
      "appearance": {
        "launcherPosition": "bottom-right",
        "showPoweredBy": true
      },
      "helpTopics": []
    },
    "versions": []
  }
}

Publish an assistant

POST/v1/assistants/:assistantId/publish

Puts the draft live on your website and every channel.

Permission assistants:writeCounts toward requests, changes

Path parameters

assistantIdstringRequired

The assistant's id.

Request
curl -X POST "https://nautical-gazelle-675.eu-west-1.convex.site/v1/assistants/default/publish" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "id": "default",
    "object": "assistant",
    "publishedVersion": 8
  }
}

Roll back an assistant

POST/v1/assistants/:assistantId/rollback

Puts an earlier published version live again, as a new version.

Permission assistants:writeCounts toward requests, changes

Path parameters

assistantIdstringRequired

The assistant's id.

Body (JSON)

versionintegerRequired

The version to restore.

Request
curl -X POST "https://nautical-gazelle-675.eu-west-1.convex.site/v1/assistants/default/rollback" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "version": 6
}'
Response
{
  "data": {
    "id": "default",
    "object": "assistant",
    "publishedVersion": 9,
    "rolledBackFrom": 6
  }
}

API reference

Tools

Actions the assistant can take during a chat, such as looking up a spreadsheet or calling your API.

List tools

GET/v1/tools

Built-in tools (knowledge search, hand-off, resolve) and the ones you created.

Permission tools:readCounts toward requests
Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/tools" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": [
    {
      "id": "kh3n8b5v2c7x4z1l9k6j3h0g7f4d1s8a",
      "object": "tool",
      "name": "check_order_status",
      "description": "Look up an order by its number and return its delivery status.",
      "type": "api_request",
      "builtIn": false,
      "enabled": true,
      "channels": {
        "chat": true,
        "voice": false
      },
      "parameters": [
        {
          "name": "order_number",
          "description": "The order number the customer gave.",
          "type": "string",
          "required": true
        }
      ],
      "config": {
        "url": "https://api.example.uz/orders/{{order_number}}",
        "method": "GET"
      },
      "createdAt": "2026-09-12T08:15:02.000Z",
      "updatedAt": "2026-09-18T09:41:12.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Create a tool

POST/v1/tools

Gives the assistant a new action. The assistant decides when to use it from the name, description and parameters.

Permission tools:writeCounts toward requests, changes

Body (JSON)

namestringRequired

Letters, numbers and underscores, starting with a letter.

descriptionstringRequired

When the assistant should use it. Write it for the AI, in English.

typestringRequired

What the tool does when called.

api_requestcustom_webhookgoogle_sheetsgoogle_calendar
parametersarray

What the assistant must collect first: { name, description, type: "string"|"number"|"boolean", required }.

configobject

Settings for the type — for api_request: url, method, headersJson, bodyTemplate; for custom_webhook: webhookUrl, webhookMethod.

enabledboolean

Defaults to true.

channelsobject

{ "chat": true, "voice": false } — where the tool can be used.

Request
curl -X POST "https://nautical-gazelle-675.eu-west-1.convex.site/v1/tools" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "check_order_status",
  "description": "Look up an order by its number and return its delivery status.",
  "type": "api_request",
  "parameters": [
    {
      "name": "order_number",
      "description": "The order number the customer gave.",
      "type": "string",
      "required": true
    }
  ],
  "config": {
    "url": "https://api.example.uz/orders/{{order_number}}",
    "method": "GET"
  },
  "channels": {
    "chat": true,
    "voice": false
  }
}'
Response
{
  "data": {
    "id": "kh3n8b5v2c7x4z1l9k6j3h0g7f4d1s8a",
    "object": "tool",
    "name": "check_order_status",
    "description": "Look up an order by its number and return its delivery status.",
    "type": "api_request",
    "builtIn": false,
    "enabled": true,
    "channels": {
      "chat": true,
      "voice": false
    },
    "parameters": [
      {
        "name": "order_number",
        "description": "The order number the customer gave.",
        "type": "string",
        "required": true
      }
    ],
    "config": {
      "url": "https://api.example.uz/orders/{{order_number}}",
      "method": "GET"
    },
    "createdAt": "2026-09-12T08:15:02.000Z",
    "updatedAt": "2026-09-18T09:41:12.000Z"
  }
}

Get a tool

GET/v1/tools/:toolId

One tool with its full configuration.

Permission tools:readCounts toward requests

Path parameters

toolIdstringRequired

The tool's id.

Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/tools/kh3n8b5v2c7x4z1l9k6j3h0g7f4d1s8a" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "id": "kh3n8b5v2c7x4z1l9k6j3h0g7f4d1s8a",
    "object": "tool",
    "name": "check_order_status",
    "description": "Look up an order by its number and return its delivery status.",
    "type": "api_request",
    "builtIn": false,
    "enabled": true,
    "channels": {
      "chat": true,
      "voice": false
    },
    "parameters": [
      {
        "name": "order_number",
        "description": "The order number the customer gave.",
        "type": "string",
        "required": true
      }
    ],
    "config": {
      "url": "https://api.example.uz/orders/{{order_number}}",
      "method": "GET"
    },
    "createdAt": "2026-09-12T08:15:02.000Z",
    "updatedAt": "2026-09-18T09:41:12.000Z"
  }
}

Update a tool

PATCH/v1/tools/:toolId

Send only what you want to change. Built-in tools can be switched on and off but not renamed.

Permission tools:writeCounts toward requests, changes

Path parameters

toolIdstringRequired

The tool's id.

Body (JSON)

namestring

Letters, numbers and underscores.

descriptionstring

When the assistant should use it.

parametersarray

Replaces the whole list.

configobject

Replaces the whole configuration.

enabledboolean

Switch the tool on or off.

channelsobject

{ "chat": boolean, "voice": boolean }.

Request
curl -X PATCH "https://nautical-gazelle-675.eu-west-1.convex.site/v1/tools/kh3n8b5v2c7x4z1l9k6j3h0g7f4d1s8a" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "enabled": false
}'
Response
{
  "data": {
    "id": "kh3n8b5v2c7x4z1l9k6j3h0g7f4d1s8a",
    "object": "tool",
    "name": "check_order_status",
    "description": "Look up an order by its number and return its delivery status.",
    "type": "api_request",
    "builtIn": false,
    "enabled": false,
    "channels": {
      "chat": true,
      "voice": false
    },
    "parameters": [
      {
        "name": "order_number",
        "description": "The order number the customer gave.",
        "type": "string",
        "required": true
      }
    ],
    "config": {
      "url": "https://api.example.uz/orders/{{order_number}}",
      "method": "GET"
    },
    "createdAt": "2026-09-12T08:15:02.000Z",
    "updatedAt": "2026-09-18T09:41:12.000Z"
  }
}

Delete a tool

DELETE/v1/tools/:toolId

Removes a tool you created. Built-in tools cannot be deleted — switch them off instead.

Permission tools:writeCounts toward requests, changes

Path parameters

toolIdstringRequired

The tool's id.

Request
curl -X DELETE "https://nautical-gazelle-675.eu-west-1.convex.site/v1/tools/kh3n8b5v2c7x4z1l9k6j3h0g7f4d1s8a" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "id": "kh3n8b5v2c7x4z1l9k6j3h0g7f4d1s8a",
    "object": "tool",
    "deleted": true
  }
}

API reference

Saved replies

Answers your team reuses in the inbox.

List saved replies

GET/v1/saved-replies

Most used first.

Permission saved_replies:readCounts toward requests

Query parameters

searchstring

Matches title, text or category.

limitinteger

Up to 200. Defaults to 100.

Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/saved-replies?limit=20" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": [
    {
      "id": "kn4v8c2x6z0l3k7j1h5g9f2d6s0a4q8w",
      "object": "saved_reply",
      "title": "Delivery times",
      "body": "Orders in Tashkent arrive in 1–2 days, other regions in 2–4.",
      "category": "delivery",
      "usageCount": 12,
      "createdAt": "2026-09-12T08:15:02.000Z",
      "updatedAt": "2026-09-18T09:41:12.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Create a saved reply

POST/v1/saved-replies

Adds a reply your team can insert in the inbox.

Permission saved_replies:writeCounts toward requests, changes

Body (JSON)

titlestringRequired

A short name.

bodystringRequired

The reply text.

categorystring

Any label.

Request
curl -X POST "https://nautical-gazelle-675.eu-west-1.convex.site/v1/saved-replies" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "title": "Delivery times",
  "body": "Orders in Tashkent arrive in 1–2 days, other regions in 2–4.",
  "category": "delivery"
}'
Response
{
  "data": {
    "id": "kn4v8c2x6z0l3k7j1h5g9f2d6s0a4q8w",
    "object": "saved_reply",
    "title": "Delivery times",
    "body": "Orders in Tashkent arrive in 1–2 days, other regions in 2–4.",
    "category": "delivery",
    "usageCount": 0,
    "createdAt": "2026-09-12T08:15:02.000Z",
    "updatedAt": "2026-09-18T09:41:12.000Z"
  }
}

Update a saved reply

PATCH/v1/saved-replies/:savedReplyId

Send only what you want to change.

Permission saved_replies:writeCounts toward requests, changes

Path parameters

savedReplyIdstringRequired

The saved reply's id.

Body (JSON)

titlestring

A short name.

bodystring

The reply text.

categorystring

Send null to clear it.

Request
curl -X PATCH "https://nautical-gazelle-675.eu-west-1.convex.site/v1/saved-replies/kn4v8c2x6z0l3k7j1h5g9f2d6s0a4q8w" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "body": "Orders in Tashkent arrive next day, other regions in 2–4."
}'
Response
{
  "data": {
    "id": "kn4v8c2x6z0l3k7j1h5g9f2d6s0a4q8w",
    "object": "saved_reply",
    "title": "Delivery times",
    "body": "Orders in Tashkent arrive next day, other regions in 2–4.",
    "category": "delivery",
    "usageCount": 12,
    "createdAt": "2026-09-12T08:15:02.000Z",
    "updatedAt": "2026-09-18T09:41:12.000Z"
  }
}

Delete a saved reply

DELETE/v1/saved-replies/:savedReplyId

Removes a saved reply.

Permission saved_replies:writeCounts toward requests, changes

Path parameters

savedReplyIdstringRequired

The saved reply's id.

Request
curl -X DELETE "https://nautical-gazelle-675.eu-west-1.convex.site/v1/saved-replies/kn4v8c2x6z0l3k7j1h5g9f2d6s0a4q8w" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "id": "kn4v8c2x6z0l3k7j1h5g9f2d6s0a4q8w",
    "object": "saved_reply",
    "deleted": true
  }
}

API reference

Workflows

Step-by-step flows that run a conversation, and how each one performs.

List workflows

GET/v1/workflows

Most recently changed first. At most one workflow is active at a time.

Permission workflows:readCounts toward requests
Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/workflows" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": [
    {
      "id": "jd2f6h0k4m8p2s6v0y4b8e2h6k0n4q8t",
      "object": "workflow",
      "name": "Order status",
      "description": "Collects an order number and looks it up.",
      "isActive": true,
      "isPublished": true,
      "publishedAt": "2026-09-18T09:41:12.000Z",
      "createdAt": "2026-09-12T08:15:02.000Z",
      "updatedAt": "2026-09-18T09:41:12.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Create a workflow

POST/v1/workflows

Saves a new workflow as a draft. The easiest way to get a valid definition is to read one from an existing workflow and change it.

Permission workflows:writeCounts toward requests, changes

Body (JSON)

namestringRequired

Up to 120 characters.

descriptionstring

What it is for.

definitionobjectRequired

{ schemaVersion, nodes, edges } exactly as the builder saves it.

Request
curl -X POST "https://nautical-gazelle-675.eu-west-1.convex.site/v1/workflows" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Order status",
  "definition": {
    "schemaVersion": 1,
    "nodes": [],
    "edges": []
  }
}'
Response
{
  "data": {
    "id": "jd2f6h0k4m8p2s6v0y4b8e2h6k0n4q8t",
    "object": "workflow",
    "name": "Order status",
    "description": "Collects an order number and looks it up.",
    "isActive": false,
    "isPublished": false,
    "publishedAt": null,
    "createdAt": "2026-09-12T08:15:02.000Z",
    "updatedAt": "2026-09-18T09:41:12.000Z"
  }
}

Get a workflow

GET/v1/workflows/:workflowId

The workflow with its draft definition and the publishedDefinition customers run.

Permission workflows:readCounts toward requests

Path parameters

workflowIdstringRequired

The workflow's id.

Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/workflows/jd2f6h0k4m8p2s6v0y4b8e2h6k0n4q8t" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "id": "jd2f6h0k4m8p2s6v0y4b8e2h6k0n4q8t",
    "object": "workflow",
    "name": "Order status",
    "description": "Collects an order number and looks it up.",
    "isActive": true,
    "isPublished": true,
    "publishedAt": "2026-09-18T09:41:12.000Z",
    "createdAt": "2026-09-12T08:15:02.000Z",
    "updatedAt": "2026-09-18T09:41:12.000Z",
    "definition": {
      "schemaVersion": 1,
      "name": "Order status",
      "nodes": [
        "…"
      ],
      "edges": [
        "…"
      ]
    },
    "publishedDefinition": {
      "schemaVersion": 1,
      "name": "Order status",
      "nodes": [
        "…"
      ],
      "edges": [
        "…"
      ]
    }
  }
}

Update a workflow

PATCH/v1/workflows/:workflowId

Changes the draft. Customers keep running the published version until you publish again.

Permission workflows:writeCounts toward requests, changes

Path parameters

workflowIdstringRequired

The workflow's id.

Body (JSON)

namestring

Up to 120 characters.

descriptionstring

Send null to clear it.

definitionobject

Replaces the whole draft.

Request
curl -X PATCH "https://nautical-gazelle-675.eu-west-1.convex.site/v1/workflows/jd2f6h0k4m8p2s6v0y4b8e2h6k0n4q8t" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Order status (v2)"
}'
Response
{
  "data": {
    "id": "jd2f6h0k4m8p2s6v0y4b8e2h6k0n4q8t",
    "object": "workflow",
    "name": "Order status (v2)",
    "description": "Collects an order number and looks it up.",
    "isActive": true,
    "isPublished": true,
    "publishedAt": "2026-09-18T09:41:12.000Z",
    "createdAt": "2026-09-12T08:15:02.000Z",
    "updatedAt": "2026-09-18T09:41:12.000Z"
  }
}

Publish a workflow

POST/v1/workflows/:workflowId/publish

Publishes the draft. By default it also becomes the live workflow and any other one is switched off.

Permission workflows:writeCounts toward requests, changes

Path parameters

workflowIdstringRequired

The workflow's id.

Body (JSON)

activateboolean

Set to false to publish without going live, for workflows used as components. Defaults to true.

Request
curl -X POST "https://nautical-gazelle-675.eu-west-1.convex.site/v1/workflows/jd2f6h0k4m8p2s6v0y4b8e2h6k0n4q8t/publish" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "id": "jd2f6h0k4m8p2s6v0y4b8e2h6k0n4q8t",
    "object": "workflow",
    "name": "Order status",
    "description": "Collects an order number and looks it up.",
    "isActive": true,
    "isPublished": true,
    "publishedAt": "2026-09-18T09:41:12.000Z",
    "createdAt": "2026-09-12T08:15:02.000Z",
    "updatedAt": "2026-09-18T09:41:12.000Z"
  }
}

Switch a workflow off

POST/v1/workflows/:workflowId/deactivate

New conversations go back to the assistant. Conversations already running finish as they are.

Permission workflows:writeCounts toward requests, changes

Path parameters

workflowIdstringRequired

The workflow's id.

Request
curl -X POST "https://nautical-gazelle-675.eu-west-1.convex.site/v1/workflows/jd2f6h0k4m8p2s6v0y4b8e2h6k0n4q8t/deactivate" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "id": "jd2f6h0k4m8p2s6v0y4b8e2h6k0n4q8t",
    "object": "workflow",
    "name": "Order status",
    "description": "Collects an order number and looks it up.",
    "isActive": false,
    "isPublished": true,
    "publishedAt": "2026-09-18T09:41:12.000Z",
    "createdAt": "2026-09-12T08:15:02.000Z",
    "updatedAt": "2026-09-18T09:41:12.000Z"
  }
}

Delete a workflow

DELETE/v1/workflows/:workflowId

Deletes a workflow and its run history. Switch a live workflow off first.

Permission workflows:writeCounts toward requests, changes

Path parameters

workflowIdstringRequired

The workflow's id.

Request
curl -X DELETE "https://nautical-gazelle-675.eu-west-1.convex.site/v1/workflows/jd2f6h0k4m8p2s6v0y4b8e2h6k0n4q8t" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "id": "jd2f6h0k4m8p2s6v0y4b8e2h6k0n4q8t",
    "object": "workflow",
    "deleted": true
  }
}

List runs

GET/v1/workflows/:workflowId/runs

Recent conversations this workflow ran, newest first.

Permission workflows:readCounts toward requests

Path parameters

workflowIdstringRequired

The workflow's id.

Query parameters

windowDaysinteger

How far back to look, 1 to 365. Defaults to 30.

outcomestring

Only runs that ended this way.

completedabandonedlive
limitinteger

Up to 200. Defaults to 50.

Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/workflows/jd2f6h0k4m8p2s6v0y4b8e2h6k0n4q8t/runs?limit=20" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": [
    {
      "id": "kr8f2v6n0q4u8y2c6g0k4o8s2w6a0e4i",
      "object": "workflow_run",
      "conversationId": "jx9a4k2m7p1q8r3s6t0v5w2y4z7b1c9d",
      "outcome": "completed",
      "status": "ended",
      "contactName": "Dilnoza Karimova",
      "stepCount": 6,
      "errorCount": 0,
      "lastNodeId": "end-1",
      "durationMs": 48210,
      "startedAt": "2026-09-18T09:41:07.000Z",
      "endedAt": "2026-09-18T09:41:12.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Get step statistics

GET/v1/workflows/:workflowId/stats

How many runs reached, stopped at, or failed on each step.

Permission workflows:readCounts toward requests

Path parameters

workflowIdstringRequired

The workflow's id.

Query parameters

windowDaysinteger

How far back to look, 1 to 365. Defaults to 30.

Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/workflows/jd2f6h0k4m8p2s6v0y4b8e2h6k0n4q8t/stats" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "object": "workflow_stats",
    "windowDays": 30,
    "totalRuns": 412,
    "completed": 351,
    "abandoned": 55,
    "live": 6,
    "nodes": [
      {
        "nodeId": "ask-order",
        "entered": 398,
        "stoppedHere": 31,
        "errors": 0
      }
    ]
  }
}

API reference

Webhooks

Have Osonflow call your server when something happens, such as a new conversation or message.

List webhooks

GET/v1/webhooks

Every event webhook. Signing secrets are only ever shown in part.

Permission webhooks:readCounts toward requests
Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/webhooks" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": [
    {
      "id": "kw5z9c3f7i1l5o9r3u7x1a5d9g3j7m1p",
      "object": "webhook",
      "url": "https://api.example.uz/osonflow/events",
      "description": "Sync conversations to our CRM",
      "provider": "webhook",
      "events": [
        "conversation.created",
        "message.received"
      ],
      "enabled": true,
      "secretPreview": "whsec_4f9a1c2e…",
      "createdAt": "2026-09-12T08:15:02.000Z",
      "updatedAt": "2026-09-18T09:41:12.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Create a webhook

POST/v1/webhooks

Osonflow will POST each chosen event to your URL. The response holds the full signing secret — the only time it is shown.

Permission webhooks:writeCounts toward requests, changes

Body (JSON)

urlstringRequired

A public https address.

eventsarrayRequired

Any of contact_session.created, conversation.created, conversation.status_changed, message.received, message.sent.

descriptionstring

A note for your team.

Request
curl -X POST "https://nautical-gazelle-675.eu-west-1.convex.site/v1/webhooks" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://api.example.uz/osonflow/events",
  "events": [
    "conversation.created",
    "message.received"
  ],
  "description": "Sync conversations to our CRM"
}'
Response
{
  "data": {
    "id": "kw5z9c3f7i1l5o9r3u7x1a5d9g3j7m1p",
    "object": "webhook",
    "url": "https://api.example.uz/osonflow/events",
    "description": "Sync conversations to our CRM",
    "provider": "webhook",
    "events": [
      "conversation.created",
      "message.received"
    ],
    "enabled": true,
    "secretPreview": "whsec_4f9a1c2e…",
    "createdAt": "2026-09-12T08:15:02.000Z",
    "updatedAt": "2026-09-18T09:41:12.000Z",
    "secret": "whsec_4f9a1c2e7b8d0f3a6c9e2b5d8f1a4c7e0b3d6f9a2c5e8b1d4f7a0c3e6b9d2f5a"
  }
}

Update a webhook

PATCH/v1/webhooks/:webhookId

Send only what you want to change.

Permission webhooks:writeCounts toward requests, changes

Path parameters

webhookIdstringRequired

The webhook's id.

Body (JSON)

urlstring

A public https address.

eventsarray

Replaces the whole list.

descriptionstring

A note for your team.

enabledboolean

Pause or resume deliveries.

Request
curl -X PATCH "https://nautical-gazelle-675.eu-west-1.convex.site/v1/webhooks/kw5z9c3f7i1l5o9r3u7x1a5d9g3j7m1p" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "enabled": false
}'
Response
{
  "data": {
    "id": "kw5z9c3f7i1l5o9r3u7x1a5d9g3j7m1p",
    "object": "webhook",
    "url": "https://api.example.uz/osonflow/events",
    "description": "Sync conversations to our CRM",
    "provider": "webhook",
    "events": [
      "conversation.created",
      "message.received"
    ],
    "enabled": false,
    "secretPreview": "whsec_4f9a1c2e…",
    "createdAt": "2026-09-12T08:15:02.000Z",
    "updatedAt": "2026-09-18T09:41:12.000Z"
  }
}

Delete a webhook

DELETE/v1/webhooks/:webhookId

Stops all deliveries to this URL.

Permission webhooks:writeCounts toward requests, changes

Path parameters

webhookIdstringRequired

The webhook's id.

Request
curl -X DELETE "https://nautical-gazelle-675.eu-west-1.convex.site/v1/webhooks/kw5z9c3f7i1l5o9r3u7x1a5d9g3j7m1p" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "id": "kw5z9c3f7i1l5o9r3u7x1a5d9g3j7m1p",
    "object": "webhook",
    "deleted": true
  }
}

Rotate the signing secret

POST/v1/webhooks/:webhookId/rotate-secret

Issues a new secret. Deliveries are signed with it immediately.

Permission webhooks:writeCounts toward requests, changes

Path parameters

webhookIdstringRequired

The webhook's id.

Request
curl -X POST "https://nautical-gazelle-675.eu-west-1.convex.site/v1/webhooks/kw5z9c3f7i1l5o9r3u7x1a5d9g3j7m1p/rotate-secret" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "id": "kw5z9c3f7i1l5o9r3u7x1a5d9g3j7m1p",
    "object": "webhook",
    "secret": "whsec_9b2e5d8a1c4f7b0e3d6a9c2f5b8e1d4a7c0f3b6e9d2a5c8f1b4e7d0a3c6f9b2e"
  }
}

List deliveries

GET/v1/webhooks/:webhookId/deliveries

Recent attempts to call this webhook, newest first, with your server's response.

Permission webhooks:readCounts toward requests

Path parameters

webhookIdstringRequired

The webhook's id.

Query parameters

limitinteger

Up to your largest page. Defaults to 20.

Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/webhooks/kw5z9c3f7i1l5o9r3u7x1a5d9g3j7m1p/deliveries?limit=20" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": [
    {
      "id": "k89s3v7y1b5e9h3k7n1q5t9w3z7c1f5i",
      "object": "webhook_delivery",
      "webhookId": "kw5z9c3f7i1l5o9r3u7x1a5d9g3j7m1p",
      "eventId": "evt_3b1c9a52-6f0e-4d8b-9a7c-2e4f6b8d0a1c",
      "eventType": "message.received",
      "status": "success",
      "attempt": 1,
      "responseStatus": 200,
      "error": null,
      "durationMs": 184,
      "createdAt": "2026-09-18T09:41:12.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

API reference

Analytics

Resolution rates, top questions, sentiment and lead numbers.

Get the overview

GET/v1/analytics/overview

How many conversations were resolved or handed over, the most common questions, sentiment, and the questions the assistant could not answer.

Permission analytics:readCounts toward requests

Query parameters

windowDaysinteger

How far back to look, 1 to 365. Defaults to 30.

Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/analytics/overview" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "object": "analytics_overview",
    "windowDays": 30,
    "totalConversations": 1204,
    "resolved": 902,
    "escalated": 211,
    "unanswered": 64,
    "resolutionRate": 75,
    "escalationRate": 18,
    "unansweredRate": 5,
    "averageTeamResponseMs": 312000,
    "minutesSaved": 5410,
    "topIntents": [
      {
        "label": "delivery_time",
        "count": 318
      }
    ],
    "sentimentMix": [
      {
        "label": "neutral",
        "count": 801
      }
    ],
    "urgencyMix": [
      {
        "label": "low",
        "count": 950
      }
    ],
    "unansweredQuestions": [
      {
        "question": "Do you ship to Kazakhstan?",
        "count": 9,
        "intent": "shipping_abroad"
      }
    ],
    "channels": [
      {
        "channel": "chat",
        "total": 1130,
        "resolved": 851,
        "escalated": 199,
        "resolutionRate": 75
      },
      {
        "channel": "voice",
        "total": 74,
        "resolved": 51,
        "escalated": 12,
        "resolutionRate": 69
      }
    ]
  }
}

List conversation insights

GET/v1/analytics/insights

The AI's reading of each conversation — intent, sentiment, urgency and a summary — most recently updated first.

Permission analytics:readCounts toward requests

Query parameters

limitinteger

How many items to return, from 1 up to your largest page. Defaults to 20.

cursorstring

The nextCursor from the previous page.

Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/analytics/insights?limit=20" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": [
    {
      "id": "kz0c4f8i2l6o0r4u8x2a6d0g4j8m2p6s",
      "object": "insight",
      "channel": "chat",
      "conversationId": "jx9a4k2m7p1q8r3s6t0v5w2y4z7b1c9d",
      "voiceConversationId": null,
      "contactId": "kd7f2m9qv1c8d4e6t3w5y0b2n7h1j9s4",
      "status": "resolved",
      "intent": "delivery_time",
      "sentiment": "neutral",
      "urgency": "low",
      "language": "uz",
      "summary": "Customer asked when their order arrives; the assistant gave delivery times.",
      "unanswered": false,
      "unansweredQuestion": null,
      "escalated": false,
      "resolved": true,
      "resolvedBy": "ai",
      "firstTeamResponseMs": null,
      "minutesSaved": 6,
      "updatedAt": "2026-09-18T09:41:12.000Z"
    }
  ],
  "hasMore": true,
  "nextCursor": "20"
}

Get lead numbers

GET/v1/analytics/leads

How many contacts you have, where they came from, and how many are waiting on you.

Permission analytics:readCounts toward requests
Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/analytics/leads" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "object": "lead_summary",
    "totalLeads": 842,
    "newcomers": 57,
    "withConversations": 790,
    "awaitingReply": 12,
    "withoutChats": 52,
    "channels": {
      "widget": 12,
      "voice": 30,
      "telegram": 210,
      "whatsapp": 95,
      "instagram": 61,
      "web": 434
    },
    "topReferrers": [
      {
        "label": "google.com",
        "count": 211
      }
    ],
    "topPages": [
      {
        "label": "/checkout",
        "count": 96
      }
    ]
  }
}

API reference

Voice calls

AI voice conversations held through the voice widget.

List voice conversations

GET/v1/voice-conversations

Most recent activity first.

Permission voice:readCounts toward requests

Query parameters

limitinteger

How many items to return, from 1 up to your largest page. Defaults to 20.

cursorstring

The nextCursor from the previous page.

Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/voice-conversations?limit=20" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": [
    {
      "id": "jv6b0e4h8k2n6q0t4w8z2c6f0i4l8o2r",
      "object": "voice_conversation",
      "provider": "openai_realtime",
      "status": "resolved",
      "contact": {
        "id": "kd7f2m9qv1c8d4e6t3w5y0b2n7h1j9s4",
        "name": "Anonymous voice visitor",
        "email": null,
        "isAnonymous": true
      },
      "lastMessagePreview": "Rahmat, hammasi tushunarli.",
      "linkedConversationId": null,
      "lastActivityAt": "2026-09-18T09:41:12.000Z",
      "endedAt": "2026-09-18T09:41:12.000Z",
      "createdAt": "2026-09-18T09:41:07.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}

Get a voice conversation

GET/v1/voice-conversations/:voiceConversationId

One call. linkedConversationId is set when the call was handed to your team in the inbox.

Permission voice:readCounts toward requests

Path parameters

voiceConversationIdstringRequired

The voice conversation's id.

Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/voice-conversations/jv6b0e4h8k2n6q0t4w8z2c6f0i4l8o2r" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": {
    "id": "jv6b0e4h8k2n6q0t4w8z2c6f0i4l8o2r",
    "object": "voice_conversation",
    "provider": "openai_realtime",
    "status": "resolved",
    "contact": {
      "id": "kd7f2m9qv1c8d4e6t3w5y0b2n7h1j9s4",
      "name": "Anonymous voice visitor",
      "email": null,
      "isAnonymous": true
    },
    "lastMessagePreview": "Rahmat, hammasi tushunarli.",
    "linkedConversationId": null,
    "lastActivityAt": "2026-09-18T09:41:12.000Z",
    "endedAt": "2026-09-18T09:41:12.000Z",
    "createdAt": "2026-09-18T09:41:07.000Z"
  }
}

Get a call transcript

GET/v1/voice-conversations/:voiceConversationId/messages

What was said, newest first.

Permission voice:readCounts toward requests

Path parameters

voiceConversationIdstringRequired

The voice conversation's id.

Query parameters

limitinteger

How many items to return, from 1 up to your largest page. Defaults to 20.

cursorstring

The nextCursor from the previous page.

Request
curl "https://nautical-gazelle-675.eu-west-1.convex.site/v1/voice-conversations/jv6b0e4h8k2n6q0t4w8z2c6f0i4l8o2r/messages?limit=20" \
  -H "Authorization: Bearer $OSONFLOW_API_KEY"
Response
{
  "data": [
    {
      "id": "k4t8x2b6f0j4n8r2v6z0d4h8l2p6t0x4",
      "object": "voice_message",
      "role": "assistant",
      "text": "Rahmat, hammasi tushunarli.",
      "createdAt": "2026-09-18T09:41:12.000Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
Get started

Faster answers for your customers. A quieter day for your team.