Μετάβαση στο περιεχόμενο

Webhooks

Αντί να ρωτάτε συνεχώς το GET /api/v1/requests/{requestId} (polling), δηλώστε ένα δικό σας HTTPS endpoint. Η Novus το καλεί κάθε φορά που αλλάζει κάτι σε μια αίτησή σας.

Εγγραφή

POST /api/v1/webhooks

{
  "url": "https://to-logismiko-sas.gr/novus-events",
  "events": [
    "request.status_changed",
    "request.provisioned"
  ]
}
Πεδίο
url Το HTTPS endpoint σας
events Ποια events θέλετε, τουλάχιστον ένα (δείτε παρακάτω)

Η απάντηση (201) περιέχει endpointId, url, events, createdAt και ένα secret. Το secret επιστρέφεται μόνο αυτή τη φορά, οπότε αποθηκεύστε το: με αυτό επαληθεύετε την υπογραφή κάθε κλήσης.

Κλήση
GET /api/v1/webhooks Τα endpoints σας (χωρίς τα secrets)
DELETE /api/v1/webhooks/{endpointId} Σταματά τις ειδοποιήσεις προς αυτό το endpoint (204)
GET /api/v1/webhooks/deliveries?endpointId=…&page=1&pageSize=50 Ιστορικό αποστολών, για debugging: τι στάλθηκε και τι απαντήσατε (eventId, event, attempt, responseStatus, deliveredAt, succeeded). Φίλτρα endpointId, eventId.

Events

event Πότε
request.created Δημιουργήθηκε αίτηση
request.status_changed Άλλαξε το status μιας αίτησης. Το πιο χρήσιμο event.
request.provisioned Ο πελάτης ενεργοποιήθηκε (provisioning.status = COMPLETED) και μπορεί να εκδίδει
request.provisioning_failed Η ενεργοποίηση απέτυχε. Η Novus ειδοποιείται αυτόματα και το αναλαμβάνει. Μην ξαναστείλετε αίτηση.
client.statement_recalled Ανακλήθηκε η Δήλωση Παρόχου στην ΑΑΔΕ

Η κλήση που δέχεστε

POST https://to-logismiko-sas.gr/novus-events
Content-Type: application/json
X-Novus-Event-Id: evt_8f7b2c9a1d4e
X-Novus-Signature: sha256=4f1c…
{
  "event": "request.status_changed",
  "eventId": "evt_8f7b2c9a1d4e",
  "occurredAt": "2026-09-17T10:15:00Z",
  "data": {
    "requestId": "req_8f7b2c9a",
    "status": "APPROVED",
    "previousStatus": "UNDER_REVIEW",
    "message": "Η αίτηση εγκρίθηκε."
  }
}

Το webhook λέει «κάτι άλλαξε», δεν είναι η αλήθεια

Επιβεβαιώνετε πάντα με GET /api/v1/requests/{requestId} πριν κάνετε οτιδήποτε.

  • Απαντήστε 2xx όσο πιο γρήγορα γίνεται και κάντε τη δουλειά σας μετά. Οτιδήποτε άλλο ξαναδοκιμάζεται μετά από 1 λεπτό, 5 λεπτά, 30 λεπτά, 2 ώρες και 12 ώρες, και μετά εγκαταλείπεται.
  • Διπλά: το ίδιο event μπορεί να φτάσει ξανά σε retry, με το ίδιο X-Novus-Event-Id. Αγνοήστε όσα έχετε ήδη δει.
  • Υπογραφή: επαληθεύετε πάντα το X-Novus-Signature.

Επαλήθευση της υπογραφής

X-Novus-Signature = sha256= + το HMAC-SHA256 του ακριβούς body, με κλειδί το secret σας, σε πεζά hex. Υπολογίστε το πάνω στα bytes όπως τα λάβατε, πριν από οποιοδήποτε JSON parsing.

using System.Security.Cryptography;
using System.Text;

static bool IsValid(string rawBody, string header, string secret)
{
    using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
    var expected = "sha256=" + Convert.ToHexString(hmac.ComputeHash(Encoding.UTF8.GetBytes(rawBody))).ToLowerInvariant();
    return CryptographicOperations.FixedTimeEquals(Encoding.UTF8.GetBytes(expected), Encoding.UTF8.GetBytes(header ?? ""));
}
uses System.Hash, System.SysUtils;

function IsValid(const RawBody, Header, Secret: string): Boolean;
var
  Expected: string;
begin
  Expected := 'sha256=' + LowerCase(THash.DigestAsString(
    THashSHA2.GetHMACAsBytes(TEncoding.UTF8.GetBytes(RawBody), TEncoding.UTF8.GetBytes(Secret))));
  Result := SameStr(Expected, Header);
end;
const crypto = require('crypto');

function isValid(rawBody, header, secret) {
  const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody, 'utf8').digest('hex');
  return header && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(header));
}
import hmac, hashlib

def is_valid(raw_body: bytes, header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, header or "")