Skip to content

Convenții API ​

Tot ce este la fel pe fiecare endpoint, ca paginile de referință să nu repete. Citește asta o dată.

URL de bază ​

Fiecare cale din referință este relativă la un URL de bază care se termină deja în /api/v1.

Exemplele din referință folosesc $CALLAGENT_API_URL pentru bază și $CALLAGENT_API_KEY pentru cheie, așa că fiecare dintre ele rulează exact așa cum e scris odată ce acestea două sunt exportate:

bash
export CALLAGENT_API_URL="http://localhost:8000/api/v1"
export CALLAGENT_API_KEY="ca_sk_your_private_key_here"

Autentificare ​

O cheie bearer pe fiecare request:

http
Authorization: Bearer ca_sk_…

Cheile vin cu două prefixe — ca_sk_ (privată, server-side) și ca_pk_ (publică, sigură în browser, schimbată la POST /client-tokens pe un JWT de scurtă durată). Scope-urile, restricțiile de origin și de IP, rotația și client token-urile sunt toate pe Autentificare.

Tenancy ​

Organizația este dedusă din cheie. Nu există niciun parametru organization_id nicăieri în acest API, deci nu ai ce să falsifici. O resursă care aparține altei organizații întoarce 404 — identic cu un id care nu a existat niciodată, pentru că un 403 ar confirma că resursa există.

Paginare ​

Endpoint-urile de listare întorc un paginator Laravel:

json
{
  "data": [ … ],
  "links": { "first": "…", "last": "…", "prev": null, "next": "…" },
  "meta": { "current_page": 1, "per_page": 25, "total": 412, "last_page": 17, "…": "…" }
}

Urmărește links.next în loc să incrementezi un număr de pagină.

Doi parametri pentru dimensiunea paginii

Parametrul nu este același peste tot, iar pagina de referință a fiecărui endpoint îl numește pe cel pe care îl citește:

  • limit pe suprafața Engage — campanii, liste de suprimare, contacte.
  • per_page pe Platform și Billing — chei API, webhooks, clienți, facturi.

Ambii au implicit 25 și se plafonează la 100. Trimiterea celui greșit nu este o eroare; este ignorat în tăcere și primești 25 de rânduri.

Denumirea câmpurilor ​

API-ul este snake_case (first_message_mode, phone_number_id). Pachetul @callagent/contracts și fișierele YAML de config-as-code sunt camelCase (firstMessageMode, phoneNumberId), iar CLI-ul convertește între ele.

Spațiile de chei care îți aparțin — metadata, variable_values, headers HTTP, parameters de tool — sunt transmise mai departe neatinse, așa că o cheie de metadata order_id rămâne order_id.

Trei locuri unde pe fir e camelCase oricum

Sunt reale și actuale. Trimite ce spune pagina de referință, nu ce implică convenția:

  • POST /telephony/calls primește un body integral camelCase — assistantId, phoneNumberId, customSipHeaders, idempotencyKey și restul — în timp ce întoarce snake_case. POST /calls este echivalentul snake_case al aceleiași operațiuni.
  • POST /campaigns/{id}/pause și /cancel citesc endLiveCalls. Restul ambelor body-uri (reason, farewell) este snake_case.
  • Fiecare endpoint de scriere din telefonie — numere de telefon, trunchiuri SIP, gateway-uri, DID-uri, rute inbound și outbound, conturi de telefonie — validează chei de request camelCase (friendlyName, outboundCallerIdNumber, inboundSourceAcl) și întoarce snake_case. Extensiile de contact centre sunt excepția și sunt snake_case peste tot.

A doua capcană de denumire, care nu ține de case: POST /campaigns/{id}/contacts primește numărul fiecărui contact ca phone, în timp ce lista de contacte și exportul CSV îl întorc ca phone_number.

Erori ​

O singură formă peste tot, cu cheia pe error.type:

json
{
  "error": {
    "type": "validation_error",
    "message": "The request payload failed validation.",
    "errors": { "name": ["The name field is required."] },
    "documentation_url": "https://docs.callagent.dev/api/errors#validation",
    "request_id": "01JQ…"
  }
}

Ramifică pe statusul HTTP și pe error.type. error.code este un al doilea vocabular, mai mare, care apare la eșecurile de provider, la evenimentele de apel și în payload-urile de webhook — vezi erori, care explică diferența și le listează pe amândouă.

Trimite X-Request-Id pe un request și îți este returnat în corpul erorii. Aceea este metoda sigură de a corela un eșec cu propriile tale loguri, pentru că request_id lipsește din răspunsurile 2xx și din erorile ridicate de middleware-ul de autentificare.

Idempotență ​

Un singur endpoint respectă header-ul

Idempotency-Key este citit de POST /calls și de nicăieri altundeva. Trimiterea lui către orice alt endpoint nu are niciun efect, iar un POST reîncercat acolo va acționa de două ori.

Două endpoint-uri primesc în schimb o cheie de idempotență ca câmp în body: POST /calls (idempotency_key, care are prioritate față de header) și POST /billing/credits (idempotency_key, transmis mai departe către Stripe). POST /telephony/calls primește idempotencyKey, camelCase.

http
Idempotency-Key: 6f1c0a5e-…

Rejucarea unei chei pe POST /calls întoarce apelul original, tot cu 201, în loc să formeze numărul a doua oară. Nu există expirare — cheia trăiește cât rândul apelului — și nici amprentare a request-ului, așa că rejucarea unei chei cu un body diferit întoarce în tăcere primul apel în loc să raporteze un conflict. Folosește o cheie derivată din propria ta unitate idempotentă de lucru.

Peste tot în rest, fă retry-urile sigure verificând înainte să creezi, sau folosind un PUT acolo unde există unul.

Rate limits ​

300 de request-uri pe minut per cheie API implicit, variind în funcție de plan, cu bugetul pe fiecare răspuns:

http
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 287

Un 429 poartă Retry-After în secunde. Numerele reale, plafoanele per plan și un helper de retry sunt pe rate limits.

Versionare ​

Calea poartă versiunea (/api/v1); documentul OpenAPI poartă o versiune de specificație datată, care este trimisă și la livrările de webhook ca X-CallAgent-Api-Version.

Cum se citește referința generată ​

Paginile de referință sunt generate din docs/api/openapi.json, pe care control plane-ul îl exportă din propriile rute, form request-uri și resurse API. Din asta decurg patru lucruri, de care paginile țin cont:

  1. Fiecare endpoint listat există. O verificare reconciliază specificația cu tabela de rute live la fiecare rulare și eșuează la o operațiune pe care nu o servește nicio rută, sau la o rută pe care nu o documentează nicio operațiune. Nu se documentează nimic ce nu a fost confirmat.

  2. Linia „Scope” este ce impune handler-ul, nu ce ar trebui să impună. Scope-urile sunt verificate per endpoint, nu printr-un middleware general, iar destul de multe endpoint-uri nu impun niciunul. Acolo unde o pagină spune asta, este un fapt despre cod. Vezi observația de pe Autentificare.

  3. Tabelul „Errors” este ce poate atinge efectiv handler-ul. Exportatorul deduce eșecurile din type hints și ratează tot ce este ridicat înăuntrul unui serviciu, așa că acele tabele sunt scrise pornind de la controllere. 401 și 429 sunt omise din fiecare dintre ele pentru că sunt globale — orice endpoint le poate întoarce.

  4. Array-urile fără tip sunt obiecte JSON. Exportatorul tipizează un cast PHP array ca array JSON fără schemă de element. În fiecare astfel de caz modelul declară array<string, mixed> — un array asociativ, care se codifică drept obiect JSON. De aceea referința randează acele câmpuri ca object (transcriber, model, voice, blocurile *_plan, metadata). Formele autoritative pentru ele sunt schemele zod din @callagent/contracts, rezumate pe Assistants.

Cum rămâne onest

node scripts/check-api-drift.mjs reexportă specificația din rute, o compară cu documentul comis și le reconciliază pe amândouă cu php artisan route:list. Eșuează la o specificație învechită, la o rută nedocumentată sau la o rută documentată care nu există.

Documentul brut ​

Descarcă openapi.json — OpenAPI 3.1, același fișier pe care îl citește orice generator de SDK. Indexul de referință poartă numărul curent de căi, operațiuni și scheme.

Scrisă după cod. Ce nu este încă implementat este marcat ca atare, niciodată descris ca funcțional.