Javni API in webhooki
Povežite ProEntry s svojo spletno stranjo, Zapierjem ali internim sistemom. Prek REST API-ja berete delovni čas, storitve, račune in rezervacije ter ustvarjate povpraševanja v CRM; z izhodnimi webhooki pa vas ProEntry sam obvesti o novih povpraševanjih, rezervacijah in plačilih.
API dostop je na voljo v paketih Pro, Ekipa in Premium.
Avtentikacija
API ključ ustvarite v aplikaciji: Nastavitve → API dostop. Ključe lahko ustvarja in prekliče lastnik ali vodja podjetja; na podjetje je lahko aktivnih največ 10 ključev. Ključ ima obliko pe_live_ + 32 znakov in se v celoti prikaže samo enkrat, ob kreaciji — shranite ga varno, kasneje je viden le začetek ključa.
Ključ pošljete z vsakim zahtevkom na enega od dveh načinov (Bearer ima prednost):
Authorization: Bearer pe_live_...
# ali
X-Api-Key: pe_live_...Vsak ključ dobi ob kreaciji podmnožico scope-ov (npr. invoices:read) — endpoint brez ustreznega scope-a vrne 403 insufficient_scope. Preklican ključ ali deaktivirano podjetje vrne 401 invalid_key.
Endpointi
Osnovni URL: https://<project>.supabase.co/functions/v1/api-v1 (v primerih spodaj $BASE). Uspešen odgovor ima obliko {"data": [...]}; seznami podpirajo paginacijo z ?limit= (1–100, privzeto 50) in ?offset=.
| Metoda | Pot | Scope | Opis |
|---|---|---|---|
| GET | /work-hours | work_hours:read | Delovni čas podjetja (7 vrstic, day_of_week 0 = ponedeljek … 6 = nedelja). |
| GET | /services | services:read | Aktivne storitve: id, name, price, duration_minutes. |
| POST | /inquiries | inquiries:write | Ustvari povpraševanje v CRM (brez captche — zahtevek je avtenticiran s ključem). |
| GET | /invoices | invoices:read | Glave dokumentov (brez postavk): številka, datumi, znesek, status, stranka, plačilni podatki. Filtri: status, document_type, from/to, external_ref. |
| POST | /orders | orders:write | Prodajno naročilo iz zunanjega sistema (spletna trgovina): najde ali ustvari stranko, po želji projekt, in izda predračun s sklicem SI00 in UPN QR; strežniški PDF (pdf_url, 30 dni) in ob send_email pošiljanje stranki. Zahteva funkcijo paketa ORDERS_API. |
| GET | /bookings | bookings:read | Javne rezervacije s podatki o stranki in storitvah. Filtra from/to (po datumu rezervacije). |
Primer: delovni čas
curl -H "Authorization: Bearer pe_live_..." \
"$BASE/work-hours"
# Odgovor:
{"data": [{"day_of_week": 0, "is_open": true, "open_time": "08:00:00",
"close_time": "20:00:00", "break_start": "12:00:00", "break_end": "13:00:00"}]}Primer: novo povpraševanje
Obvezni polji sta name in description, plus vsaj eden od phone / email. Opcijsko: serviceType, source in gdprConsent (ob true se zabeleži čas privolitve).
curl -X POST -H "Authorization: Bearer pe_live_..." \
-H "Content-Type: application/json" \
-d '{"name":"Janez Novak","phone":"041123456","description":"Ponudba za ograjo","gdprConsent":true}' \
"$BASE/inquiries"
# Odgovor (201):
{"data": {"id": "...", "created_at": "..."}}Primer: naročilo iz spletne trgovine → predračun
Strežnik zneske preračuna sam (odjemalčevi totals so le kontrola), stranko poišče po DDV številki ali e-pošti, predračun dobi številko iz vašega številčenja. Isti Idempotency-Key z istim telesom vrne isti dokument (brez podvajanja). Celotna pogodba: docs/specs/2026-09-orders-api.md.
curl -X POST -H "Authorization: Bearer pe_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-8f1c2d3e-v1" \
-d '{
"external_ref": "8f1c2d3e", "external_source": "trgovina",
"client": { "name": "Avtohiša Novak d.o.o.", "customer_type": "business",
"vat_number": "SI12345678", "email": "narocila@novak.si",
"address": "Cesta 1", "postal_code": "1000", "city": "Ljubljana" },
"items": [ { "description": "Kartonska podloga", "quantity": 100, "unit": "kos",
"unit_price": 0.85, "vat_rate": 22, "item_type": "material" } ],
"create_project": true
}' \
"$BASE/orders"
# Odgovor (201):
{"data": {"invoice_id": "...", "document_number": "PF-2026/09/012", "status": "issued",
"issue_date": "2026-09-15", "due_date": "2026-09-23", "valid_until": "2026-09-30",
"totals": {"subtotal": 85.00, "total_vat": 18.70, "total": 103.70, "currency": "EUR"},
"payment": {"iban": "SI56...", "reference": "SI00 0202-6090-12",
"purpose": "Plačilo po predračunu PF-2026/09/012", "amount": 103.70,
"upn_qr_payload": "UPNQR\n..."}}}Rate limiti
60 / min
Minutni limit na ključ. Ob prekoračitvi: 429, Retry-After: 60, koda rate_limited.
1000 / dan
Dnevni limit na ključ. Ob prekoračitvi: 429, Retry-After: 3600, koda rate_limited_day.
V kvoto se štejejo samo postreženi zahtevki — zavrnjeni z 429 se ne štejejo.
Webhooki
ProEntry lahko vaš sistem obvešča o dogodkih s HTTP POST klicem na vaš https:// URL (privatni in lokalni naslovi so blokirani). Ob kreiranju naročnine prejmete secret oblike pe_whsec_<32 hex znakov> — prikazan je samo enkrat in ga kasneje ni mogoče ponovno prebrati. Vaš endpoint mora v 10 sekundah vrniti status 2xx; redirectom ne sledimo.
Dogodki
| Dogodek | Opis |
|---|---|
| inquiry.created | Novo povpraševanje (ime, kontakt, opis, vir). |
| booking.created | Nova javna rezervacija (termin, storitve, cena, stranka). |
| booking.cancelled | Preklic rezervacije (termin, čas in razlog preklica). |
| invoice.paid | Račun označen kot plačan (številka, znesek, stranka, datum plačila). Samo fiskalni dokumenti — ne za predračune/ponudbe. |
| proforma.paid | Predračun v celoti plačan (evidentiran prejeti avans): številka, external_source/external_ref, znesek, valuta, paid_at, način plačila, advance_payment_id. |
| proforma.created | Nov predračun (tudi iz POST /orders): številka, external_source/external_ref, znesek, roki, source_channel. |
| ping | Testni dogodek ob preizkusu naročnine — podpisan enako kot pravi dogodki. |
Oblika dostave
Vsaka dostava je POST z JSON ovojnico. Isti dogodek lahko ob retryju prejmete večkrat — deduplicirajte po id dostave. Vrstni red dostav ni zagotovljen.
POST <vaš URL>
Content-Type: application/json
X-ProEntry-Event: inquiry.created
X-ProEntry-Delivery: 9c5e6d1e-...
X-ProEntry-Signature: t=1755856800,v1=5f8a...
User-Agent: ProEntry-Webhooks/1.0
{
"id": "9c5e6d1e-...", // unikaten ID dostave (za deduplikacijo)
"event": "inquiry.created", // tip dogodka
"created_at": "2026-08-22T10:00:00Z",
"data": { ... } // payload dogodka
}Preverjanje podpisa
Header X-ProEntry-Signature ima obliko t=<unix_ts>,v1=<hex>, kjer je v1 HMAC-SHA256 s secretom nad nizom "<unix_ts>.<surovo telo zahtevka>". Podpis vedno preverite nad surovim telesom (pred JSON parse), primerjajte s konstantno-časovno primerjavo in zavrnite zahtevke, kjer je razlika med t in trenutnim časom večja od npr. 5 minut (zaščita pred replay napadi).
const crypto = require("node:crypto");
function verifyProEntrySignature(rawBody, signatureHeader, secret, toleranceSec = 300) {
const parts = Object.fromEntries(
signatureHeader.split(",").map((p) => p.split("=", 2))
);
const ts = parseInt(parts.t, 10);
if (!ts || !parts.v1) return false;
if (Math.abs(Date.now() / 1000 - ts) > toleranceSec) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${ts}.${rawBody}`)
.digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(parts.v1, "hex");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
// Express: uporabite express.raw({ type: "application/json" }), da dobite surovo telo
app.post("/webhooks/proentry", express.raw({ type: "application/json" }), (req, res) => {
const ok = verifyProEntrySignature(
req.body.toString("utf8"),
req.get("X-ProEntry-Signature") || "",
process.env.PROENTRY_WEBHOOK_SECRET
);
if (!ok) return res.status(401).end();
const event = JSON.parse(req.body);
// ... obdelava; deduplicirajte po event.id
res.status(200).end();
});Retry politika
- Dostave obdeluje worker, ki teče vsako minuto — prva dostava tipično do ~1 minuto po dogodku.
- Neuspela dostava (ne-2xx, timeout, napaka povezave) se ponovi z eksponentnim backoffom
min(1 min × 2^poskus, 1 h): ~2 min, 4 min, 8 min, 16 min, 32 min, nato 1 h. - Največ 8 poskusov na dostavo; potem je dostava označena kot neuspešna in se ne ponavlja več.
- Po 20 zaporednih neuspelih poskusih se naročnina samodejno deaktivira. Po odpravi napake jo znova vklopite; dogodki, nastali med deaktivacijo, se ne dostavijo za nazaj.
Napake
Vsaka napaka API-ja vrne JSON telo z opisom in strojno berljivo kodo:
{"error": "<sporočilo>", "code": "<koda>"}| Status | Koda | Kdaj |
|---|---|---|
| 401 | invalid_key | Manjkajoč, neveljaven ali preklican ključ; tudi deaktivirano podjetje. |
| 403 | insufficient_scope | Ključ nima scope-a, ki ga endpoint zahteva. |
| 404 | not_found | Neznana pot. |
| 405 | method_not_allowed | Napačna HTTP metoda za endpoint. |
| 429 | rate_limited / rate_limited_day | Presežen minutni oz. dnevni limit — glej Rate limiti. |
| 400 | validacijske kode | Neveljavno telo zahtevka (npr. manjkajoča obvezna polja pri POST /inquiries; validation_error s poljem field pri POST /orders). |
| 402 | iban_missing | POST /orders: podjetje nima vpisanega IBAN-a, zato predračuna s plačilnimi podatki ni mogoče izdati. |
| 403 | upgrade_required | POST /orders: paket ne vključuje funkcije ORDERS_API. |
| 409 | idempotency_conflict / external_ref_exists | POST /orders: isti Idempotency-Key z drugim telesom oz. odprt dokument za to naročilo že obstaja (telo vsebuje invoice_id). |
| 422 | totals_mismatch / vat_invalid | POST /orders: podani zneski odstopajo od strežniškega izračuna oz. DDV stopnje niso skladne z ZDDV-1. |
| 503 | not_ready | Endpoint na tem okolju še ni omogočen — poskusite kasneje. |
| 500 | server_error | Napaka na strežniku — zahtevek ponovite kasneje. |