Sari la conținut

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.

  • 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 din A-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"
}
}
  • code este stabil și făcut pentru programe: ia decizii după el, nu după title sau detail (care sunt pentru oameni, în engleză, și pot fi reformulate).
  • request_id face legătura cu jurnalele noastre.
  • error este 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).

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_cursor este null.

Nu construi și nu modifica singur cursoarele. Colecțiile mici, mărginite (planuri, versiuni PHP) vin complete.

Preluarea tuturor elementelor cu curl și jq:

Terminal window
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" ] || break
done

CLI-ul face același lucru cu --all (pluspanel accounts list --all -o json).

Filtrele sunt parametri de query, documentați pentru fiecare operație în referință. De exemplu, jurnalul de audit:

Terminal window
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"

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 și 429 nu 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ște 409 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.
Terminal window
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))
done
  • Fiecare GET care răspunde cu un corp JSON are un ETag puternic. Trimite-l înapoi în If-None-Match, iar o resursă neschimbată primește 304 Not Modified, fără corp.
  • Un PUT sau PATCH respectă 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 este 412 precondition_failed și nu se modifică nimic. Citește din nou, aplică din nou modificarea, reîncearcă. Un PUT sau PATCH a cărui cale nu are GET primește 412 când trimiți If-Match. CLI-ul îl trimite cu --if-match.
  • Operațiile care suportă acest mecanism sunt marcate x-pluspanel-etag: true în referință.
Terminal window
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.json

Este 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.

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: 600
RateLimit-Remaining: 598
RateLimit-Reset: 1
RateLimit-Policy: 600;w=60

Câ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.