Agents

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=25 and 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"
  }'

idstringAssistant ID (read-only).
namestringDisplay name. Required on create.
instructionsstringThe system prompt: who the assistant is and what it should do.
first_messagestringWhat the assistant says when the call is answered.
language, timezonestringConversation language and the timezone used for dates.
extensionstringThe PBX extension routed to this assistant. Unique per organization (409 on conflict).
stt_provider, stt_modelstringSpeech-to-text engine.
llm_provider, llm_model, llm_temperaturestring / numberLanguage model and its temperature.
tts_provider, tts_model, tts_voicestringText-to-speech engine and voice.
allow_interruptionsbooleanWhether the caller can interrupt the assistant.
end_call_enabled, voicemail_hangupbooleanLet the assistant end the call; hang up on voicemail.
record_callsbooleanRecord calls handled by this assistant.
max_call_duration, silence_timeoutinteger (s)Hard cap on call length; hang-up delay on silence.
tool_idsstring[]Tools attached to the assistant. On update the list replaces the current attachments.
is_activebooleanInactive assistants do not answer calls.
created_at, updated_atstringTimestamps (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_idstringThe assistant that will speak. Required.
to_numberstringThe number to dial, as your PBX expects it. Required.
pbx_idstringWhich 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" }
400Bad requestThe request cannot be applied as sent.
401UnauthorizedMissing, invalid or revoked API key.
402Payment requiredThe balance or plan does not allow the action (for example placing a call).
404Not foundThe object does not exist in your organization.
409ConflictA unique value is already used (for example an extension).
422Validation errorA field is missing or has the wrong type; detail lists the fields.

call.startedeventThe assistant answered and the conversation began.
call.endedeventThe call finished; data holds the final call with transcript, duration and charge.
call.failedeventThe call could not be completed.
balance.loweventThe 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}