Convențiile API
Aceste reguli sunt valabile pentru fiecare operație din /api/v1. Referința API
documentează parametrii, corpurile și codurile de eroare ale fiecărei operații.
Noțiuni de bază
Secțiune intitulată „Noțiuni de bază”- URL de bază:
https://<panoul tău>/api/v1. API-ul se află la adresa panoului (inclusiv la adresa proprie a panoului unei organizații); nu există un host separat pentru API. - JSON la intrare și la ieșire (
Content-Type: application/json), UTF-8. Datele calendaristice sunt RFC 3339 în UTC (2026-10-19T09:12:44Z), id-urile sunt UUID-uri. - Doar HTTPS. Trimite
Authorization: Bearer <cheie>(autentificare). - Membrii necunoscuți din corpul unei cereri sunt refuzați (
422), ca greșelile de tastare să nu treacă neobservate. - Fiecare răspuns are antetul
X-Request-ID(poți trimite propriul id, cel mult 64 de caractere dinA-Z a-z 0-9 . _ -); menționează-l când contactezi suportul.
Erorile sunt „problem details” RFC 9457, cu tipul media
application/problem+json:
{ "type": "https://pluspanel.net/problems/domain_taken", "title": "Conflict", "status": 409, "detail": "shop.example.com is already used by another site on this server", "instance": "/api/v1/accounts/5a5a5a5a-1111-4222-8333-444455556666/sites", "code": "domain_taken", "request_id": "4b7c1e2a-9d3f-4e8a-b1c2-0f9e8d7c6b5a", "error": { "code": "domain_taken", "message": "shop.example.com is already used by another site on this server", "request_id": "4b7c1e2a-9d3f-4e8a-b1c2-0f9e8d7c6b5a" }}codeeste stabil și făcut pentru programe: ia decizii după el, nu dupătitlesaudetail(care sunt pentru oameni, în engleză, și pot fi reformulate).request_idface legătura cu jurnalele noastre.erroreste membrul de eroare original al API-ului, păstrat ca clienții existenți să meargă în continuare. Este depreciat: codul nou citește membrii de pe primul nivel. Rămâne pe toată durata v1.
Coduri de stare frecvente: 400 cerere malformată, 401 credențiale lipsă sau invalide, 403 nepermis
(rol sau scope), 404 inexistent sau invizibil pentru tine, 409 conflict cu starea curentă, 412
precondiție neîndeplinită, 422 validare eșuată, 429 limită de rată depășită, 5xx eroare pe server
(se poate reîncerca în siguranță cu același Idempotency-Key).
Paginare
Secțiune intitulată „Paginare”Colecțiile care pot crește fără limită sunt paginate cu un cursor opac:
limit: mărimea paginii, 1–200 (implicit 50);cursor:next_cursor-ul paginii anterioare;- răspunsul este
{"items": [...], "next_cursor": "…"}; pe ultima paginănext_cursorestenull.
Nu construi și nu modifica singur cursoarele. Colecțiile mici, mărginite (planuri, versiuni PHP) vin complete.
Preluarea tuturor elementelor cu curl și jq:
cursor=""while :; do page=$(curl -fsS -G "$PLUSPANEL_URL/api/v1/accounts" \ -H "Authorization: Bearer $PLUSPANEL_API_KEY" \ --data-urlencode "limit=200" ${cursor:+--data-urlencode "cursor=$cursor"}) jq -c '.items[]' <<<"$page" cursor=$(jq -r '.next_cursor // empty' <<<"$page") [ -n "$cursor" ] || breakdoneCLI-ul face același lucru cu --all (pluspanel accounts list --all -o json).
Filtrare
Secțiune intitulată „Filtrare”Filtrele sunt parametri de query, documentați pentru fiecare operație în referință. De exemplu, jurnalul de audit:
curl -fsS -G "$PLUSPANEL_URL/api/v1/audit" -H "Authorization: Bearer $PLUSPANEL_API_KEY" \ --data-urlencode "object_type=site" \ --data-urlencode "from=2026-10-01T00:00:00Z" --data-urlencode "to=2026-10-19T00:00:00Z"Idempotență
Secțiune intitulată „Idempotență”Fiecare POST care modifică ceva acceptă antetul Idempotency-Key (1–255 caractere ASCII tipăribile;
un UUID este o alegere bună). Folosește o cheie nouă pentru fiecare operație nouă și aceeași cheie când o
reîncerci.
- Rezultatul primei cereri este păstrat 24 de ore, per cheie și per credențială (cheia API, altfel utilizatorul).
- O reîncercare cu aceeași cheie, aceeași metodă, aceeași cale, același query și același corp reia răspunsul
păstrat, cu antetul
Idempotent-Replayed: true. Nimic nu se execută de două ori. - Aceeași cheie cu o cerere diferită primește
422 idempotency_key_reused. - O reîncercare cât timp prima cerere încă rulează primește
409 idempotency_in_progress: așteaptă și reîncearcă. - Răspunsurile care conțin un secret afișat o singură dată (o cheie API nouă, secretul de semnare al unui
webhook, un link de invitație, coduri de recuperare…) nu sunt păstrate niciodată. Reluarea lor primește
409 idempotency_replay_unavailable: prima cerere a reușit, dar secretul nu este afișat a doua oară. - Răspunsurile
5xx,408și429nu sunt păstrate, deci le poți reîncerca cu aceeași cheie. Nici un răspuns mai mare de 1 MiB nu este păstrat (reluarea lui primește409 idempotency_replay_unavailable). - O cheie malformată primește
400 idempotency_key_invalid. Pașii de autentificare și device flow ignoră antetul. CLI-ul îl trimite cu--idempotency-key.
key=$(uuidgen)for attempt in 1 2 3; do curl -fsS -X POST "$PLUSPANEL_URL/api/v1/accounts/$ACCOUNT_ID/sites" \ -H "Authorization: Bearer $PLUSPANEL_API_KEY" -H "Content-Type: application/json" \ -H "Idempotency-Key: $key" \ -d '{"primary_domain": "shop.example.com", "title": "Shop", "admin_email": "owner@example.com"}' \ && break sleep $((attempt * 5))doneETag-uri și precondiții
Secțiune intitulată „ETag-uri și precondiții”- Fiecare
GETcare răspunde cu un corp JSON are unETagputernic. Trimite-l înapoi înIf-None-Match, iar o resursă neschimbată primește304 Not Modified, fără corp. - Un
PUTsauPATCHrespectăIf-Match: dacă resursa s-a schimbat de când ai citit-o (GET-ul ei curent, citit cu propria credențială și cu permisiunile tale, are alt ETag), răspunsul este412 precondition_failedși nu se modifică nimic. Citește din nou, aplică din nou modificarea, reîncearcă. UnPUTsauPATCHa cărui cale nu areGETprimește412când trimițiIf-Match. CLI-ul îl trimite cu--if-match. - Operațiile care suportă acest mecanism sunt marcate
x-pluspanel-etag: trueîn referință.
etag=$(curl -fsS -D - -o site-security.json "$PLUSPANEL_URL/api/v1/sites/$SITE_ID/security" \ -H "Authorization: Bearer $PLUSPANEL_API_KEY" | awk 'tolower($1)=="etag:" {print $2}' | tr -d '\r')# … editezi site-security.json …curl -fsS -X PUT "$PLUSPANEL_URL/api/v1/sites/$SITE_ID/security" \ -H "Authorization: Bearer $PLUSPANEL_API_KEY" -H "Content-Type: application/json" \ -H "If-Match: $etag" --data @site-security.jsonEste concurență optimistă, nu o blocare: îi protejează pe clienții corecți să nu suprascrie unul modificările altuia, dar doi scriitori care se întrec în același moment pot trece amândoi.
Limite de rată
Secțiune intitulată „Limite de rată”Fiecare cheie API are un buget de cereri pe minut: 600 implicit, în toate edițiile; unei chei i se
poate da o limită mai mică. Bugetul se reumple continuu (token bucket). Fiecare răspuns la o cerere cu
cheie îți arată unde te afli (RateLimit-Reset este numărul de secunde până când bugetul e din nou
plin):
RateLimit-Limit: 600RateLimit-Remaining: 598RateLimit-Reset: 1RateLimit-Policy: 600;w=60Când bugetul este consumat, răspunsul este 429 rate_limited cu Retry-After (în secunde). Așteaptă
atât înainte să reîncerci și distribuie în timp lucrările mari, în loc să le trimiți în rafale.