עבריתLog in

REST API & keys

Authenticate with an API key, then run a full call in code: create, follow, consult, instruct and cancel.

API keys

  1. Go to oneli.io/app/connect and create a key in the API keys card (מפתחות API).
  2. Give it a name (1 to 60 characters) and pick an expiry: 90 days (default), 1 year, or no expiry.
  3. The key, which starts with oli_, is shown once only. Store it in an environment variable or a secrets manager, for example ONELI_API_KEY. We keep only a hash of it.
  • The key list shows the last four characters, when each key was created, last used and expires. You can revoke a key at any time.
  • Keys are created only on the site, by a signed-in person. There is no endpoint to create a key with a key.
  • An expired or revoked key gets 401 UNAUTHENTICATED. Up to 20 active keys and connected agents per account (429 TOO_MANY_AGENTS).

A key can call only these endpoints. Everything else (billing, profile, plans, keys) returns 403 INSUFFICIENT_SCOPE:

MethodPath
GET/v1/me · /v1/wallet
GET/v1/call-tasks · /v1/call-tasks/:id · /v1/call-tasks/:id/events
POST/v1/call-tasks · /v1/call-tasks/:id/instructions · /v1/call-tasks/:id/cancel
POST/v1/consultations/:id/response
GET · PUT · DELETE/v1/decision-webhook
…Your agents: settings, knowledge, calls, outbound calls and WhatsApp
API keys belong on the server. The authenticated API has no CORS, so browser calls are blocked, and a key in client-side code is visible to anyone who opens the page. If a key leaks, revoke it right away at /app/connect and create a new one.

Base URL and requests

Base URL
https://api.oneli.io
  • Every request carries Authorization: Bearer oli_….
  • Request bodies are JSON with Content-Type: application/json, up to 100 KB.
  • Times are ISO 8601 in UTC. Phone numbers come back in international form without +, for example 972501234567.
  • Errors come back as {"error":"CODE","message":"…"}. See Errors & limits.

The examples below use a small helper:

# Every request: the key in the Authorization header, JSON bodies.
export ONELI_API_KEY="oli_..."
export API="https://api.oneli.io"

The call lifecycle

Create a task, follow its events until the status is final (completed, failed, cancelled or expired), answer consultations as they arrive, and send instructions or cancel at any point.

1. Create a task

Replace 05XXXXXXXX with a real number. idempotency_key is required: sending the same key again returns the same task with created: false and never places a second call. More fields in the reference.

curl -X POST $API/v1/call-tasks \
  -H "Authorization: Bearer $ONELI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "idempotency_key": "order-1234-call",
    "to_number": "05XXXXXXXX",
    "goal": "לבדוק אם הזמנה 1234 מוכנה לאיסוף ועד מתי אפשר להגיע",
    "instructions": "לדבר בקצרה ובנימוס. אם אין תשובה ברורה, לבקש שעה לחזור.",
    "constraints": { "max_price_ils": 50 },
    "max_duration_s": 180
  }'

2. Poll events

Events are numbered in sequence. Send after with the last next_cursor to get only what is new. Polling every 2–3 seconds is enough and stays well under the rate limit. On a busy line or no answer the task goes back to queued with a task.retry_scheduled event, so keep polling until a final status.

# Events after the last sequence you have seen (start at 0). Repeat with after=<next_cursor>.
curl "$API/v1/call-tasks/$TASK_ID/events?after=0&limit=100" \
  -H "Authorization: Bearer $ONELI_API_KEY"

# The task itself: status, outcome, summary, revision
curl $API/v1/call-tasks/$TASK_ID -H "Authorization: Bearer $ONELI_API_KEY"

3. Answer a consultation

When the voice agent needs a decision, pending_consultation is filled (and a consultation.requested event arrives). There are only a few seconds until deadline_at, so answer fast. The first answer wins. ask_user is always allowed: the voice agent does not commit and the decision comes back to you. To answer automatically without polling, see the decision webhook.

curl -X POST $API/v1/consultations/con_3d48e1a72832d769/response \
  -H "Authorization: Bearer $ONELI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "decision": "approve", "reason": "בתוך התקציב" }'

# → { "status": "accepted" }  (or "expired" / "conflict" with the decision that won)

4. Send an instruction

During an active call the instruction reaches the voice agent and applies from her next turn. expected_revision prevents two senders from colliding: if the task changed in the meantime you get 409 REVISION_CONFLICT. An instruction for a task that is still queued is recorded and bumps the revision, but it is not passed to the call; to change a call that has not started, cancel it and create a new task.

# expected_revision = task.revision from GET /v1/call-tasks/:id
curl -X POST $API/v1/call-tasks/$TASK_ID/instructions \
  -H "Authorization: Bearer $ONELI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "instruction": "לשאול גם אם אפשר לאסוף מחר אחרי 18:00",
    "expected_revision": 1,
    "idempotency_key": "order-1234-instr-1"
  }'

# → { "command_id": "cmd_…", "revision": 2, "status": "pending" }

5. Cancel

A queued task is cancelled at once. During an active call a hang-up is sent and the final status follows in the events. Cancelling a finished task returns 409 CALL_NOT_ACTIVE. Cancel is never rate limited.

curl -X POST $API/v1/call-tasks/$TASK_ID/cancel \
  -H "Authorization: Bearer $ONELI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "user_changed_plans" }'

# queued → { "status": "cancelled" }   during the call → { "status": "cancel_requested" }

Your agents over the API

Everything the chat assistant on the site does with your agents is also available with an API key and on the MCP server: build and edit an agent, its knowledge bank, read its calls, start an outbound call and run its WhatsApp. So you can build your own product on OneLi: a CRM, a lead system, or an AI agent that drives your voice agent. Each route needs a scope; new keys and connections get them all.

MethodPathScopeWhat it does
GET/v1/voice-agents · /v1/voice-agents/:idagents:readYour agents, and every setting of one agent with what blocks going live.
GET/v1/voice-agent-templates · /v1/voice-agent-voices · /v1/voice-agent-appsagents:readTemplates, tools, languages and models; voices; the apps you connected.
POST/v1/voice-agentsagents:writeA new agent from a template (template and name).
PUT/v1/voice-agents/:idagents:writeSaves the name and the whole config (read it first, send it all back).
POST/v1/voice-agents/:id/deploy · /pause · /playgroundagents:writePuts it live, pauses it, or a text test conversation.
DELETE/v1/voice-agents/:idagents:deleteDeletes an agent: its number is freed and its monthly payment cancelled.
GET · POST/v1/voice-agents/:id/kb · /kb/searchagents:readThe knowledge bank: list, and search the way a call does.
POST · PUT · DELETE/v1/voice-agents/:id/kb · /kb/:itemagents:writeAdd (text, a link or an objection), update and remove.
GET/v1/voice-agents/:id/calls · /calls/:task_idcalls:readThe agent’s calls, and one call with its transcript and the Supervisor’s notes.
POST · GET/v1/voice-agents/:id/outbound-calls · /outbound-calls/:callcalls:createcalls:readThe agent calls someone now (to, variables, Idempotency-Key), and the call’s status.
GET/v1/voice-agents/:id/whatsapp · /threads · /threads/:tid/messages · /templates · /automationswhatsapp:readThe agent’s WhatsApp status, chats and messages, templates and rules.
POST/v1/voice-agents/:id/whatsapp/send · /threads/:tid/messageswhatsapp:sendSend from the agent’s number: to and text, or to with template_id and params.
POST · PATCH · DELETE/v1/voice-agents/:id/whatsapp/templates · /automations · /whatsappwhatsapp:writeTemplates, rules and auto-reply.

The agent calls a lead

The agent must be live. The fields in variables fill the {{variables}} in its instructions. The same Idempotency-Key never dials twice. The full conversation shows in the agent’s Calls tab and at GET …/calls.

curl
curl -X POST https://api.oneli.io/v1/voice-agents/va_…/outbound-calls \
  -H "Authorization: Bearer $ONELI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: lead-42" \
  -d '{"to":"0521234567","variables":{"name":"Dana","interest":"kitchen renovation"}}'

WhatsApp from the agent’s number

Free text goes through only within 24 hours of that person’s last message; outside the window send the template_id of an approved template with params. A send from the API pauses the agent’s auto-reply in that chat, like a reply typed on the site.

curl
curl -X POST https://api.oneli.io/v1/voice-agents/va_…/whatsapp/send \
  -H "Authorization: Bearer $ONELI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to":"0521234567","text":"Thanks for the call! Here are the details you asked for: …"}'
These stay on the site only: taking a phone number, payments, the agent’s contacts, recordings, connecting a WhatsApp number or an app (done in the browser), and the voice test. For MCP, the same things are tools.

Integration recipes

The short way to let your agent place calls: wrap POST /v1/call-tasks and GET /v1/call-tasks/:id as two tools. This works with OpenAI function calling, LangChain tools and any framework that takes JSON Schema. Frameworks with MCP support can connect straight to the MCP server with your API key, without writing tools.

JavaScript (framework-neutral)
// Two tools for your agent. Map them to your framework's tool format.
export const tools = [
  {
    name: "place_phone_call",
    description: "Phone an Israeli number in Hebrew through OneLi to reach a goal. Returns a task_id.",
    parameters: {
      type: "object",
      properties: {
        to_number: { type: "string", description: "Israeli number, e.g. 03-1234567 or 050-1234567" },
        goal: { type: "string", description: "What the call should achieve (max 500 characters)" },
        instructions: { type: "string", description: "How to conduct the call (optional)" },
        max_price_ils: { type: "number", description: "Above this price the voice agent consults you" },
      },
      required: ["to_number", "goal"],
    },
  },
  {
    name: "get_call_result",
    description: "Status, outcome and summary of a OneLi call task.",
    parameters: { type: "object", properties: { task_id: { type: "string" } }, required: ["task_id"] },
  },
];

export async function runTool(name, args) {
  if (name === "place_phone_call") {
    const { task } = await oneli("POST", "/v1/call-tasks", {
      idempotency_key: crypto.randomUUID(),
      to_number: args.to_number,
      goal: args.goal,
      instructions: args.instructions,
      ...(args.max_price_ils != null ? { constraints: { max_price_ils: args.max_price_ils } } : {}),
    });
    return { task_id: task.id, status: task.status };
  }
  if (name === "get_call_result") {
    const { task, pending_consultation } = await oneli("GET", `/v1/call-tasks/${args.task_id}`);
    return { status: task.status, outcome: task.outcome, summary: task.summary, pending_consultation };
  }
}

In n8n, Make and Zapier: an HTTP Request node or module with these settings, followed by a wait and reading the result:

HTTP Request
Method:  POST
URL:     https://api.oneli.io/v1/call-tasks
Headers: Authorization: Bearer oli_...        (store it as a credential)
         Content-Type: application/json
Body:    {
           "idempotency_key": "lead-{{ id }}",
           "to_number": "{{ phone }}",
           "goal": "לאשר את פרטי הפנייה ואת מועד הפגישה"
         }

Full guides for each framework: