Robonote documentation
Connect Robonote to your phone system, CRM and data tools. Send us your calls, and get the analysis back where your team works.
Quickstart
Get from zero to your first analyzed call.
- Get accessAsk your Robonote contact to set up your company and give you access to the Client API.
- Open the API referenceGo to client-api.robonote.io. There you can see every endpoint, authorize with your credentials and try requests in the browser.
- Send a callSend a recording together with its details: the agent, the team, the customer and the date. Add your own custom fields if you need them. They come back with every result.
- Get the resultsAdd a webhook URL in Integrations in the Robonote platform, or ask for S3 exports. Results arrive as soon as each analysis step is done.
How it works
- Your systems send interactions to Robonote: calls, meetings, chats and emails, with their details.
- Robonote transcribes, translates and analyzes them: summaries, scores, events, flows and signals.
- The results go back to you by webhook (JSON) or as hourly CSV files in Amazon S3.
Each interaction keeps one ID from start to end, so you can always match results to the call in your own system.
Data in: Client API
Use the Client API to send calls, chats and emails to Robonote. The live reference, with every field and a "Try it out" button, is at client-api.robonote.io.
Authentication
Every request carries your company API token. You find it on the Integrations page in Robonote.
Authorization: Bearer <company-api-token>How imports work
Imports are queued and processed in the background. A 200 response with "result": "success" means the request was queued, not that the call was stored.
- Keep the
request_idfrom the response. Support will ask for it. - Confirm the upload with the status endpoints.
A queued request can still be rejected later, when:
- The API token does not belong to a company.
- Call, chat or email import is not turned on for your company.
- The same recording is already stored under another
call_id, or a chat or email with the same messages is already stored under another id. It is stored under your id as a duplicate and is not analyzed. The single-call status endpoint answersstatus: duplicatewith the original id. - The recording is shorter than 3 seconds, is not a valid audio file, or cannot be downloaded.
Rules for requests
| Rule | Details |
|---|---|
| Format | Send JSON with Content-Type: application/json. At most 4 MB. |
| Your own ids | Always send your own call_id, chat_call_id or email_call_id. Robonote does not return its own id, so without yours the status endpoints cannot be used. |
| One id space | Call, chat and email ids share one space per company. Never reuse an id across channels, or the later import overwrites the earlier one. |
| Id characters | Use only letters, digits, ., _ and -. Ids that differ only in other characters may collide. |
| Types | Send ids, names, statuses and dates as strings, and booleans as booleans. A number in time, agent_id, client_id or recording_file makes the whole body invalid (400 JSON body is not valid). |
| Times | Send all times in UTC as YYYY-MM-DD HH:MM:SS. Time zone offsets are not converted. |
| Fields | Send only the fields in the API reference. |
v1 and v2
Both versions accept the same payloads. They differ in one step.
| Version | What it does | Use it when |
|---|---|---|
| v2 (current) | Checks that recording_file can be reached before it accepts the call. If not, it answers 400 The file is not exist. Also adds email import. | Your recording links are public, or signed for both GET and HEAD. |
| v1 | Accepts any recording_file. An unreachable file only fails later, when Robonote downloads it. | Your links are pre-signed only for GET, or your server refuses HEAD requests. |
In both versions the recording is downloaded in the background after the request is accepted.
Endpoints
| Method | Path | What it does |
|---|---|---|
| POST | /api/v2/calls/api-import | Import a call |
| POST | /api/v2/chat-calls/api-import | Import a chat |
| POST | /api/v2/email-calls/api-import | Import an email conversation (v2 only) |
| GET | /api/v2/call/{callId} | Upload status of one call |
| GET | /api/v2/calls/uploaded | Upload status of several calls |
| POST | /api/v1/calls/api-import | Import a call, without checking the file first |
| POST | /api/v1/chat-calls/api-import | Import a chat |
| GET | /api/v1/call/{callId} | Upload status of one call |
| GET | /api/v1/calls/uploaded | Upload status of several calls |
Use the server address shown in the API reference. In the examples below it is $ROBONOTE_API.
Import a call
POST /api/v2/calls/api-import
Send the call with your own call_id and a link to the recording. The example shows the main fields. See the API reference for all fields.
export ROBONOTE_API="https://..." # server from the API reference
export ROBONOTE_TOKEN="<company-api-token>"
curl -X POST "$ROBONOTE_API/api/v2/calls/api-import" \
-H "Authorization: Bearer $ROBONOTE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"call_id": "crm-call-10482",
"recording_file": "https://files.example.com/calls/10482.mp3",
"time": "2026-11-05 14:32:10",
"agent_id": "agent-17",
"client_id": "client-5521"
}'import os, requests
API = os.environ["ROBONOTE_API"] # server from the API reference
TOKEN = os.environ["ROBONOTE_TOKEN"]
r = requests.post(
f"{API}/api/v2/calls/api-import",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"call_id": "crm-call-10482", # your own id, always a string
"recording_file": "https://files.example.com/calls/10482.mp3",
"time": "2026-11-05 14:32:10", # UTC
"agent_id": "agent-17",
"client_id": "client-5521",
},
timeout=30,
)
print(r.status_code, r.json()) # keep request_id for supportconst API = process.env.ROBONOTE_API; // server from the API reference
const TOKEN = process.env.ROBONOTE_TOKEN;
const res = await fetch(`${API}/api/v2/calls/api-import`, {
method: "POST",
headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json" },
body: JSON.stringify({
call_id: "crm-call-10482", // your own id, always a string
recording_file: "https://files.example.com/calls/10482.mp3",
time: "2026-11-05 14:32:10", // UTC
agent_id: "agent-17",
client_id: "client-5521",
}),
});
console.log(res.status, await res.json()); // keep request_id for supportResponse
{
"result": "success",
"request_id": "..."
}200 means queued. Check the status a little later to confirm the call was stored.Import a chat or email
POST /api/v2/chat-calls/api-import POST /api/v2/email-calls/api-import
Same request as a call import: same header, same 200 answer. Only the path, your own id field and the messages array change. There is no recording to fetch, so the text you send is what gets analyzed. Email import is only in v2.
curl -X POST "$ROBONOTE_API/api/v2/chat-calls/api-import" \
-H "Authorization: Bearer $ROBONOTE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"chat_call_id": "crm-chat-10482",
"chat_call_status": "ANSWERED",
"time": "2026-11-05 14:32:10",
"agent_id": "agent-17",
"agent_name": "Dana Levi",
"team_id": "team-4",
"team_name": "Service Desk",
"client_id": "client-5521",
"client_name": "Eva Richter",
"custom_data": { "case_reference": "CASE-4107" },
"chat_call": [
{ "is_agent": true, "is_client": false, "time": "2026-11-05 14:32:10",
"text": "Good morning, how can I help?" },
{ "is_agent": false, "is_client": true, "time": "2026-11-05 14:32:40",
"text": "The access point was replaced yesterday and both rooms work again." },
{ "is_agent": true, "is_client": false, "time": "2026-11-05 14:33:05",
"text": "Thank you for confirming. May I close the technical issue?" },
{ "is_agent": false, "is_client": true, "time": "2026-11-05 14:33:30",
"text": "Yes, but please keep my complaint about the first response open." }
]
}'curl -X POST "$ROBONOTE_API/api/v2/email-calls/api-import" \
-H "Authorization: Bearer $ROBONOTE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"email_call_id": "crm-email-10482",
"email_call_status": "email",
"time": "2026-11-05 09:15:00",
"agent_id": "agent-17",
"agent_name": "Dana Levi",
"team_id": "team-4",
"client_id": "client-5521",
"client_name": "Eva Richter",
"email_call": [
{ "is_agent": true, "is_client": false, "time": "2026-11-05 09:15:00",
"text": "<html><body><p>Hello,</p><p>Here is the quote you asked for.</p></body></html>" },
{ "is_agent": false, "is_client": true, "time": "2026-11-05 11:40:00",
"text": "<html><body><p>Thank you, I will review it with our owner this week.</p></body></html>" }
]
}'Every message carries the same four fields:
| Field | Details |
|---|---|
is_agent / is_client | Who wrote the message. Exactly one of the two is true. |
time | When the message was sent, UTC, YYYY-MM-DD HH:MM:SS. Send the messages in the order they happened. |
text | The message itself. Email bodies may carry their original HTML; Robonote strips it before the analysis. |
time is when the conversation started, and custom_data is a free-form object that comes back untouched on the result. Full field list in the API reference.Check the status
GET /api/v2/call/{callId} for one call, or GET /api/v2/calls/uploaded for several.
curl "$ROBONOTE_API/api/v2/call/crm-call-10482" \
-H "Authorization: Bearer $ROBONOTE_TOKEN"r = requests.get(
f"{API}/api/v2/call/crm-call-10482",
headers={"Authorization": f"Bearer {TOKEN}"},
timeout=30,
)
print(r.json())If the same recording was already stored under another id, the answer is status: duplicate with the original id. See the API reference for every status.
Data out: choose a method
Get your results through webhooks, hourly CSV files in Amazon S3, or both.
| Method | Format | How it works | Best for |
|---|---|---|---|
| Webhooks | JSON | Results are sent to your URL as each analysis step finishes. | CRM updates, automations, one call at a time |
| S3 exports | CSV | Robonote uploads hourly CSV batches to a bucket and gives your team access. | Data warehouses, reports, bulk processing |
Both need company-level setup. Results depend on which analysis runs for your company.
Webhooks
Each webhook is an HTTP POST with JSON for one interaction. Analysis steps can finish at different times, so you may get several updates for the same interaction.
Full webhook reference: webhooks.robonote.io
Webhook types
| Type | What you get |
|---|---|
| Transcription | Transcribed text, speakers and timestamps |
| Translation | Original and English text, with speakers and timestamps |
| Events | Detected events, with the related text and speaker roles |
| Flows | Flow tasks and their results |
| Summarization | Topic summaries and results, with names and groups |
| Signals | Signal results, with names and groups |
| Download Errors | Final recording download failures and some rejected interactions |
Every request includes
- Company and interaction IDs
- The analysis results
- Interaction, agent, team and customer details
- Your custom fields, when you sent them
Set up your endpoint
- In the Robonote platform, open Integrations.
- Choose a webhook type and enter your URL.
- Add authentication headers if your endpoint needs them.
- Turn it on. One URL per type. Several types can share a URL.
Example receiver
// A minimal webhook receiver (Node.js + Express)
import express from "express";
const app = express();
app.use(express.json({ limit: "5mb" }));
app.post("/robonote/webhook", (req, res) => {
// 1. Answer fast: Robonote waits up to 3 seconds.
res.sendStatus(200);
// 2. Do the real work after you answered.
setImmediate(() => {
const result = req.body; // one interaction, one analysis step
saveOrUpdate(result); // same interaction ID = update, not a new record
});
});
app.listen(3000);# A minimal webhook receiver (Python + Flask)
from threading import Thread
from flask import Flask, request
app = Flask(__name__)
@app.post("/robonote/webhook")
def robonote_webhook():
result = request.get_json(force=True) # one interaction, one analysis step
# Answer fast (within 3 seconds), then do the real work in the background.
Thread(target=save_or_update, args=(result,)).start()
return "", 200
if __name__ == "__main__":
app.run(port=3000)# Test your endpoint before you add it in Robonote
curl -X POST https://your-domain.com/robonote/webhook \
-H "Content-Type: application/json" \
-d '{"test": true}' \
-w "\nHTTP %{http_code} in %{time_total}s\n"Delivery rules
| Rule | What it means for you |
|---|---|
| Answer in 3 seconds | Return HTTP 2xx fast. Do longer work after you answer. |
| Best effort | Failed requests are logged, but not sent again. Log what you receive. |
| Updates | Reruns can send results again. There is no separate delivery ID or rerun label. Use your endpoint or custom headers to tell result types apart. |
| On and off | Messages already waiting may still arrive after you turn a webhook off. Turning one on does not send old results. |
Amazon S3 exports
Robonote stores CSV exports in its own AWS account and gives your team access. Ask your Robonote contact for permissions and the bucket details.
What each export includes
| File | Contents |
|---|---|
| Interactions | One row per interaction: IDs, time, language, status, length and Robonote link |
| Transcription | One row per transcript part: original and English text, speakers and timestamps |
| Summarization & Signals | Topic summaries and Signal results, with names, timestamps and groups |
| Events | Confirmed events, with scores, related text, categories and speaker roles |
| Flows | Flow task results: status, explanations, quotes and details |
| Download Errors | The latest failed recording download, with link, error type, code and message |
Every file includes the interaction details and your custom fields. No audio. Empty data types are left out.
How the hourly batch works
- Hourly. Batches start at minute 48 of every hour. Files appear after processing and upload.
- By analysis time. Selection uses the latest analysis time, so an older call that was analyzed again today can be in today’s batch.
- Window. The first export covers the past 24 hours. Each next one starts where the last one ended, up to 7 days.
- Download Errors. Selected by the original call time, so new failures on old calls can fall outside the window.
Files and updates
- Files are grouped by data type and time period. Robonote gives you the locations.
- A rerun creates new files. Old batches stay. Retrying a batch can replace its files.
- Files hold the latest results. Changing only the call details does not trigger an export.
- Files can appear at different times. No webhook announces a finished batch.
Reruns and updates
| Rerun | Webhooks | S3 exports |
|---|---|---|
| Full analysis | Results are sent again as each step runs | The call can be in a later batch after the analysis is done |
| Selected Signals, Topics or Flows | Only the selected results are sent again | Current results can be in a later batch |
Custom integrations
Using a phone system, dialer or CRM that is not covered here? Our team builds the integration for you, tests it, and stays with you after it goes live.
Support
Questions about the API or your setup? Write to [email protected].