Ove upute objašnjavaju kako primati webhooke iz sustava SUPER. Namijenjene su developerima koji izrađuju prijemni endpoint na strani klijenta.
Webhook je HTTP POST zahtjev koji SUPER šalje na vaš URL kada se nešto dogodi. Ne morate periodički ispitivati (pollati) naš API. Kada se dogodi događaj, mi pozivamo vaš endpoint.
Endpoint kreirate u SUPER web aplikaciji. Zadajete:
https i javno dostupan),whsec_. Kopirajte ga i pohranite na sigurno mjesto. Ne možete ga ponovno vidjeti. Ako ga izgubite, rotirajte ključ u aplikaciji i dobit ćete novi.
invoice.receivedTrenutno je dostupan jedan događaj:
invoice.received - novi ulazni račun je dostupan za vašu tvrtku.Webhook ne sadrži cijeli račun. Javlja vam da je račun stigao i daje vam njegov invoiceGuid. Zatim pozovete GetInvoice u našem API-ju s tim invoiceGuid i pročitate cijeli račun.
Svaki webhook POST ima ova zaglavlja:
| Zaglavlje | Značenje |
|---|---|
X-Super-Webhook-Id | Jedinstveni id ove isporuke. Ista vrijednost kao id u tijelu. Koristite ga za otkrivanje duplikata. |
X-Super-Webhook-Event | Tip događaja, npr. invoice.received. |
X-Super-Webhook-Timestamp | Vrijeme kada smo kreirali zahtjev, kao Unix timestamp u sekundama (UTC). |
X-Super-Webhook-Signature | Potpis zahtjeva. Koristite ga za provjeru da zahtjev zaista dolazi od nas. |
Tijelo je uvijek JSON. Content-Type je application/json.
{
"id": "0f8c2a1e-4b7d-4a9e-9c3f-2b1d5e6a7c88",
"type": "invoice.received",
"createdUtc": "2026-07-03T09:15:42.1234567Z",
"companyGuid": "8159a7b9-0770-4d27-afe3-e6eb0466d857",
"data": {
"invoiceGuid": "63c1964d-0d83-4f8d-b01d-57c94ba453fc"
}
}
Nazivi polja su camelCase.
| Polje | Značenje |
|---|---|
id | Jedinstveni id ove isporuke. Isti kao zaglavlje X-Super-Webhook-Id. |
type | Tip događaja, npr. invoice.received. |
createdUtc | Kada smo izgradili payload (UTC). |
companyGuid | GUID vaše tvrtke na koju se događaj odnosi. |
data | Podaci događaja. Za invoice.received sadrži invoiceGuid. |
data.invoiceGuid | GUID novog računa. Pozovite GetInvoice s ovom vrijednošću i pročitajte cijeli račun. |
Kada u aplikaciji pritisnete "Pošalji testni događaj", šaljemo zahtjev istog oblika, ali je data objekt drugačiji:
{
"id": "...",
"type": "invoice.received",
"createdUtc": "...",
"companyGuid": "...",
"data": {
"test": true
}
}
Dakle, pravi događaj ima data.invoiceGuid. Testno slanje ima data.test = true.
Uvijek provjerite potpis prije nego što povjerujete zahtjevu. Time dokazujete da zahtjev dolazi od nas i da nije mijenjan.
Kako mi gradimo potpis:
X-Super-Webhook-Timestamp (Unix sekunde)."{timestamp}.{body}". body je točno onaj sirovi (raw) JSON koji smo poslali.whsec_ ključ kao tajnu.sha256=.Za provjeru napravite iste korake sa sirovim tijelom koje ste primili, pa usporedite svoj rezultat sa zaglavljem X-Super-Webhook-Signature.
CryptographicOperations.FixedTimeEquals.C# primjer:
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
public static bool IsValidWebhook( string secret, string timestampHeader, string signatureHeader, string rawBody )
{
// 1) Odbij stare zahtjeve (zaštita od replaya).
if ( !long.TryParse( timestampHeader, NumberStyles.Integer, CultureInfo.InvariantCulture, out var unixSeconds ) )
{
return false;
}
var sentAt = DateTimeOffset.FromUnixTimeSeconds( unixSeconds );
if ( DateTimeOffset.UtcNow - sentAt > TimeSpan.FromMinutes( 5 ) )
{
return false;
}
// 2) Ponovno izračunaj potpis nad "{timestamp}.{body}".
var payload = $"{timestampHeader}.{rawBody}";
using var hmac = new HMACSHA256( Encoding.UTF8.GetBytes( secret ) );
var hash = hmac.ComputeHash( Encoding.UTF8.GetBytes( payload ) );
var expected = "sha256=" + Convert.ToHexString( hash ).ToLowerInvariant( );
// 3) Usporedba u konstantnom vremenu.
return CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes( expected ),
Encoding.UTF8.GetBytes( signatureHeader ) );
}
Vratite 2xx status kod što brže možete (200, 201, 202 i 204 svi vrijede). Time nam javljate da je isporuka uspjela.
Ako vratite bilo koji drugi status kod, ili je vaš endpoint spor ili nedostupan, isporuku smatramo neuspjelom i ponavljamo je.
Ako isporuka ne uspije, ponavljamo je. Nakon svakog pokušaja čekamo dulje:
| Nakon neuspjelog pokušaja | Čekamo |
|---|---|
| 1 | 1 minutu |
| 2 | 5 minuta |
| 3 | 30 minuta |
| 4 | 2 sata |
| 5 | 6 sati |
Nakon 6 neuspjelih pokušaja isporuka prelazi u status FailedFinal (trajno neuspješno). Tu isporuku više ne ponavljamo automatski.
Ako jedan endpoint 5 puta zaredom završi u FailedFinal, gasimo taj endpoint. Zapišemo razlog na endpoint i pošaljemo email vašoj tvrtki. Dok je endpoint ugašen, ne šaljemo mu nove webhooke. Ponovno ga uključite u aplikaciji; time se resetira i brojač grešaka.
Uspješna isporuka resetira brojač na nulu.
Isti webhook id može stići više puta. To se može dogoditi nakon ponavljanja, ili kada netko u aplikaciji pritisne "Pošalji ponovno". Ručno ponovno slanje koristi isti id; ne stvara novi.
Zato svaki id obradite samo jednom. Prije obrade provjerite jeste li taj id već obradili. Ako jeste, odgovorite 2xx i ne radite ništa drugo. Tako vaša strana ostaje ispravna i kada webhook stigne dvaput.