Recoma
API v1

Integrează Recoma în site-ul tău

Un cod de reducere pe care îl poate da oricine îți aduce clienți — un influencer, un partener, un client mulțumit. Tu îl validezi la checkout și ne spui când s-a plasat comanda. Noi ținem socoteala: cine a adus vânzarea și cât are de încasat.

Toată integrarea are trei apeluri. Restul acestei pagini sunt detalii.

Pornire rapidă

Creezi o cheie în aplicație, la Setări → API. Cheia se vede o singură dată, la creare — noi păstrăm doar un hash, deci nu ți-o putem arăta a doua oară (și nici altcineva care ar ajunge la baza noastră de date).

Cheia se folosește doar de pe server. Orice ajunge în sursa paginii este public, iar o cheie acolo îi permite unui străin să înregistreze comenzi în contul tău.

Prima verificare — confirmă că merge cheia și îți spune în ce monedă lucrăm:

curl
curl https://api.recoma.ro/v1/me \
  -H "Authorization: Bearer rk_live_..."
200 OK
{
  "seller": {
    "id": "…",
    "business_name": "Mobilier Fagul",
    "currency": "RON",
    "timezone": "Europe/Bucharest"
  },
  "key": { "prefix": "rk_live_7f3aQ2xd", "scopes": ["read", "redeem", "write"] }
}

Fluxul complet, în trei pași:

CândCe apeleziCe obții
Clientul scrie codul în coșPOST /v1/codes/{cod}/validatereducerea de aplicat, sau motivul pentru care nu se aplică
Comanda a fost plasatăPOST /v1/codes/{cod}/redeemcomisionul calculat și înregistrat
Comanda a fost returnatăPOST /v1/redemptions/{id}/voidcomisionul se întoarce, folosirea se eliberează

Chei și scope-uri

Autentificarea este un header Authorization: Bearer rk_live_…. Fiecare cheie are unul sau mai multe scope-uri; dă-i fiecărei integrări strictul necesar.

ScopeCe permite
readlistarea și citirea codurilor și a folosirilor
redeemînregistrarea unei comenzi și anularea ei
writecrearea și modificarea codurilor

O cheie revocată și una care nu a existat niciodată primesc exact același răspuns. E intenționat: altfel API-ul ar confirma care dintre încercările cuiva au fost cândva reale.

Autentificările eșuate se numără separat, per adresă IP — douăzeci pe minut. Traficul reușit nu se pune niciodată în contul adresei, deci mai multe magazine din spatele aceleiași conexiuni nu își împart o limită.

Limită: 120 de cereri pe minut per cheie. Peste ea primești 429 și un header Retry-After.

Validare cod

Nu consumă nimic. Poți să o apelezi oricât — inclusiv la fiecare tastă, în limita ratei.

curl
curl https://api.recoma.ro/v1/codes/MARIA10/validate \
  -H "Authorization: Bearer rk_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "order_value": 1000 }'
200 OK
{
  "code": "MARIA10",
  "valid": true,
  "reason": null,
  "state": "active",
  "display_text": "10% la orice comandă",
  "discount": { "kind": "percent", "value": 10, "min_order_value": 200,
                "max_discount_amount": 150 },
  "currency": "RON",
  "remaining": 498,
  "referrer": { "name": "Maria Popescu" },
  "applies_to": { "order_value": 1000, "discount_amount": 100,
                  "net_amount": 900, "free_shipping": false }
}
Validate răspunde 200 și când răspunsul e „nu” — inclusiv pentru un cod care nu există. O casetă de cupon la checkout adună greșeli de tastare toată ziua, iar dacă fiecare ar fi un 404, monitorizarea ta s-ar umple de evenimente care nu sunt erori. Citește valid și reason.

Motive posibile: not_found, paused, archived, scheduled, expired, exhausted, below_minimum, customer_limit_reached.

order_value este valoarea produselor înainte de reducere, fără transport. Codul se caută iertător: mări a10, maria10 și MARIA10 duc toate la același cod.

Pentru free_shipping, discount_amount este 0 — nu știm cât încasezi tu pe livrare, iar o presupunere ar ajunge într-un calcul de comision. Acționează pe free_shipping.

Înregistrare comandă

Se apelează după ce comanda e plasată. Consumă o folosire, calculează comisionul și e ceea ce ajunge în extrasul lunar al celui care a recomandat.

curl
curl https://api.recoma.ro/v1/codes/MARIA10/redeem \
  -H "Authorization: Bearer rk_live_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1042" \
  -d '{ "order_value": 1000, "order_ref": "#1042" }'
201 Created
{
  "id": "9c1f…",
  "code": "MARIA10",
  "order_ref": "#1042",
  "status": "confirmed",
  "order_value": 1000,
  "discount_amount": 100,
  "net_amount": 900,
  "currency": "RON",
  "commission_percent": 4,
  "commission_amount": 36,
  "redeemed_at": "2026-07-29T09:14:22Z"
}

Comisionul se calculează pe net_amount — cât ai încasat efectiv pe produse, după reducere. Nu pe prețul de listă: altfel ai plăti comision din bani care nu au intrat niciodată.

Dacă un cod nu se poate aplica, primești 409 cu motivul exact: code_exhausted, code_expired, code_paused, below_minimum, customer_limit_reached.

customer_ref este necesar doar pentru codurile care limitează folosirile per client. Trimite ce ai tu — un email, un id intern. Noi stocăm doar un digest cu sare, nu valoarea: limita funcționează fără ca noi să ajungem să deținem lista ta de clienți.

Anulare / retur

Comanda a fost returnată sau anulată. Comisionul se inversează, folosirea se dă înapoi codului, iar order_ref redevine disponibil — dacă acel client comandă din nou, este o vânzare nouă și o poți înregistra.

curl
curl https://api.recoma.ro/v1/redemptions/9c1f.../void \
  -H "Authorization: Bearer rk_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "reason": "retur" }'

Se refuză după ce luna de decontare s-a închis (month_locked). Redeschiderea se face în doi, din aplicație — nu de aici.

Administrare coduri

Poți crea și modifica coduri și din API, nu doar din aplicație — util dacă vrei un cod pentru fiecare influencer, generat din propriul tău panou.

POST /v1/codes
{
  "code": "MARIA10",
  "label": "Maria — Instagram",
  "display_text": "10% la orice comandă",
  "discount": {
    "kind": "percent",
    "value": 10,
    "min_order_value": 200,
    "max_discount_amount": 150
  },
  "commission_percent": 4,
  "max_per_customer": 1,
  "payee": {
    "name": "Maria Popescu",
    "handle": "@maria.deco",
    "email": "maria@example.com"
  }
}

payee este cine câștigă din cod. Nu are nevoie de cont: îi scrii numele și codul funcționează. Mai târziu o poți invita, iar când acceptă, tot ce a adus codul până atunci trece în contul ei — istoricul o urmează.

Codul este unic în contul tău, nu global: dacă altă firmă folosește deja numele la care te gândeai, el rămâne disponibil pentru tine.

Un cod nu poate fi redenumit. E tipărit, postat și spus cu voce tare; dacă s-ar putea schimba, s-ar rupe în tăcere peste tot unde există deja. Arhivează-l și creează altul.

Codurile au și o stare derivată. status e ce a ales un om (active, paused, archived); state e ce este adevărat acum și include scheduled, expired, exhausted. Ia deciziile după state.

Erori

Orice eșec are aceeași formă:

409 Conflict
{
  "error": {
    "type": "conflict",
    "code": "code_exhausted",
    "message": "This code has reached its maximum number of uses."
  }
}

Ramifică pe code — este stabil și nu se va reformula niciodată. type este categoria generală, pentru tratare generică. message este în engleză, scris pentru tine, și se poate schimba oricând: nu-l arăta niciodată clientului. Textul pentru cumpărător îl scrii tu, în limba lui.

HTTPtypeCând
400invalid_requestlipsește sau e greșit un câmp; `param` spune care
401authentication_errorcheie lipsă, greșită sau revocată
403permission_errorcheia nu are scope-ul necesar
404not_foundobiectul nu există în acest cont
409conflictexistă, dar starea lui refuză operațiunea
429rate_limitprea multe cereri
503service_unavailablenu s-a decis nimic — reîncearcă
500server_errorproblema e la noi

Lista completă a codurilor, cu ce înseamnă fiecare, e în docs/openapi.yaml.

Reîncercări

503 temporarily_unavailable înseamnă că cererea nu a ajuns la o concluzie — o contenție, un timeout, o clipă în care baza de date nu a răspuns. Nu e un refuz și nu e o problemă a cererii tale. Reîncearc-o, cu același Idempotency-Key.

Asta e situația pentru care există header-ul. Un 5xx pe redeem e ambiguu — s-a înregistrat comanda sau nu? Cu cheia de idempotență poți afla în siguranță: dacă apucase să se înregistreze, primești înapoi exact aceeași înregistrare, cu "replayed": true. Fără ea, singura variantă e să ghicești.

500 internal_error e la noi. Reîncercarea e la fel de sigură, dar rareori ajută imediat.

Tot ce e în zona 4xx înseamnă că trebuie schimbată cererea. Reîncercată neschimbată, va eșua identic.

Idempotență

Trimite un header Idempotency-Key la fiecare redeem. Orice șir stabil între 8 și 200 de caractere — numărul comenzii e o alegere bună.

O cerere reluată cu aceeași cheie returnează înregistrarea originală, cu "replayed": true, în loc să calculeze un al doilea comision. Corpul reluării este ignorat. Statusul este 200, nu 201 — tratează-l la fel.

Ca plasă de siguranță, același order_ref pe același cod este oricum refuzat. Ești acoperit în ambele feluri, dar header-ul e cel care îți permite să reîncerci fără să te gândești.

Cazul pe care îl previne: apelul tău a expirat, dar a ajuns la noi. Fără cheie, reîncercarea plătește comision de două ori pentru aceeași comandă.

Următorul pas

Ghiduri concrete pentru WooCommerce și Shopify. Specificația completă OpenAPI 3.1 este în depozitul de cod, la docs/openapi.yaml.

Nu ai încă un cont? Creează unul gratuit — cheile se generează din Setări → API.