Base URLhttps://agents.logic-leap.ai/api/v1
- All requests and responses are JSON over HTTPS.
- IDs are 32-character hex strings. Timestamps are ISO-8601 in UTC. Money is a JSON number in USD.
- Paginated lists accept
?page=1&page_size=25and return{"items": [...], "total": n, "page": 1, "page_size": 25}. - Every request is scoped to the organization that owns the API key.
curl https://agents.logic-leap.ai/api/v1/assistants \
-H "Authorization: Bearer ll_YOUR_API_KEY"
Keep keys on your server. A key is shown in full only once, when it is created, and can be revoked at any time from the dashboard.
GET/assistantsList assistants
POST/assistantsCreate an assistant (
name required)GET/assistants/{id}Retrieve one
PATCH/assistants/{id}Update any subset of fields
DELETE/assistants/{id}Delete
curl -X POST https://agents.logic-leap.ai/api/v1/assistants \
-H "Authorization: Bearer ll_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Front desk",
"instructions": "You are the receptionist of Acme Clinic. Greet the caller, find out what they need and book or transfer accordingly.",
"first_message": "Thank you for calling Acme Clinic, how can I help you?",
"language": "en",
"extension": "7001"
}'
| id | string | Assistant ID (read-only). |
| name | string | Display name. Required on create. |
| instructions | string | The system prompt: who the assistant is and what it should do. |
| first_message | string | What the assistant says when the call is answered. |
| language, timezone | string | Conversation language and the timezone used for dates. |
| extension | string | The PBX extension routed to this assistant. Unique per organization (409 on conflict). |
| stt_provider, stt_model | string | Speech-to-text engine. |
| llm_provider, llm_model, llm_temperature | string / number | Language model and its temperature. |
| tts_provider, tts_model, tts_voice | string | Text-to-speech engine and voice. |
| allow_interruptions | boolean | Whether the caller can interrupt the assistant. |
| end_call_enabled, voicemail_hangup | boolean | Let the assistant end the call; hang up on voicemail. |
| record_calls | boolean | Record calls handled by this assistant. |
| max_call_duration, silence_timeout | integer (s) | Hard cap on call length; hang-up delay on silence. |
| tool_ids | string[] | Tools attached to the assistant. On update the list replaces the current attachments. |
| is_active | boolean | Inactive assistants do not answer calls. |
| created_at, updated_at | string | Timestamps (read-only). |
GET/callsList calls, newest first (paginated)
GET/calls/{id}One call with transcript and usage
{
"id": "5f0c…",
"agent_id": "9b1e…",
"agent_name": "Front desk",
"direction": "inbound", // inbound | outbound | web
"from_number": "+966500000000",
"to_number": "7001",
"status": "completed", // ringing | in_progress | completed | failed | rejected
"ended_reason": "",
"started_at": "2026-01-15T09:30:02Z",
"ended_at": "2026-01-15T09:32:41Z",
"duration_seconds": 159,
"rate_per_minute": 0.12,
"charge_amount": 0.318,
"transcript": [
{ "role": "agent", "text": "Thank you for calling Acme Clinic, how can I help you?", "ts": "2026-01-15T09:30:03Z" },
{ "role": "user", "text": "I'd like to book an appointment.", "ts": "2026-01-15T09:30:08Z" }
],
"summary": "Caller booked an appointment for tomorrow at 4 pm.",
"recording_url": null,
"created_at": "2026-01-15T09:30:00Z"
}
Transcript roles are user, agent, tool and system. List responses return the same objects without transcript, summary, recording_url and usage.
POST/callsPlace an outbound call
curl -X POST https://agents.logic-leap.ai/api/v1/calls \
-H "Authorization: Bearer ll_YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"assistant_id": "9b1e…", "to_number": "+966500000000"}'
# → {"call_id": "5f0c…"}
| assistant_id | string | The assistant that will speak. Required. |
| to_number | string | The number to dial, as your PBX expects it. Required. |
| pbx_id | string | Which linked PBX places the call. Optional. |
The call is dialled through your own PBX. If the balance cannot cover a call the API answers 402. Follow the call with GET /calls/{id} or with webhooks.
GET/balanceWallet balance and current rate
{ "balance": 42.5, "currency": "USD", "price_per_minute": 0.12 }
{ "detail": "رسالة الخطأ / Error message" }
| 400 | Bad request | The request cannot be applied as sent. |
| 401 | Unauthorized | Missing, invalid or revoked API key. |
| 402 | Payment required | The balance or plan does not allow the action (for example placing a call). |
| 404 | Not found | The object does not exist in your organization. |
| 409 | Conflict | A unique value is already used (for example an extension). |
| 422 | Validation error | A field is missing or has the wrong type; detail lists the fields. |
| call.started | event | The assistant answered and the conversation began. |
| call.ended | event | The call finished; data holds the final call with transcript, duration and charge. |
| call.failed | event | The call could not be completed. |
| balance.low | event | The wallet dropped below your low-balance threshold. |
POST https://your-server.example/webhooks/logic-leap
Content-Type: application/json
X-LL-Signature: sha256=3f5a…
{
"event": "call.ended",
"org_id": "c41d…",
"created_at": "2026-01-15T09:32:41Z",
"data": { "id": "5f0c…", "status": "completed", "duration_seconds": 159, "charge_amount": 0.318, "transcript": [ … ] }
}
For call.* events data is the call object described above. Answer with any 2xx within 10 seconds; do slow work after responding. Deliveries are logged in the dashboard, where you can also send a test event.
Node.js
import crypto from "node:crypto";
import express from "express";
const app = express();
const SECRET = process.env.LL_WEBHOOK_SECRET;
// Verify against the raw bytes, before any JSON parsing.
app.post("/webhooks/logic-leap", express.raw({ type: "application/json" }), (req, res) => {
const expected = "sha256=" + crypto.createHmac("sha256", SECRET).update(req.body).digest("hex");
const received = req.get("X-LL-Signature") || "";
const ok = expected.length === received.length &&
crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received));
if (!ok) return res.status(401).end();
const event = JSON.parse(req.body.toString("utf8"));
if (event.event === "call.ended") {
console.log("call", event.data.id, "lasted", event.data.duration_seconds, "seconds");
}
res.status(204).end();
});
Python
import hashlib, hmac, json, os
from fastapi import FastAPI, Header, HTTPException, Request
app = FastAPI()
SECRET = os.environ["LL_WEBHOOK_SECRET"].encode()
@app.post("/webhooks/logic-leap")
async def logic_leap_webhook(request: Request, x_ll_signature: str = Header("")):
body = await request.body() # raw bytes, exactly as received
expected = "sha256=" + hmac.new(SECRET, body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, x_ll_signature):
raise HTTPException(status_code=401)
event = json.loads(body)
if event["event"] == "balance.low":
... # notify your team
return {"ok": True}