Skip to main content

Async polling and webhooks

DGII confirms e-CFs asynchronously via a trackId. ERPly Pro abstracts this pattern for you.

Polling

curl https://sandbox.api.erply.pro/v1/invoices/{docId}/status \
-H "Authorization: Bearer $ERPLYPRO_API_KEY"
{
"docId": "01HW9X4G…",
"trackId": "20260501-DGII-9988",
"status": "accepted",
"dgii": { "approvedAt": "2026-05-01T17:42:11Z" }
}

Webhook

ERPly Pro sends a POST to the tenant's configured webhook:

POST /tu-webhook HTTP/1.1
Content-Type: application/json
X-ErplyPro-Event: invoice.accepted
X-ErplyPro-Signature: t=1714583811,v1=4f9d…
X-ErplyPro-Delivery: 01HW9X4G…

Verify the signature (constant-time) using the shared secret:

import hmac, hashlib, time

def verify(body: bytes, header: str, secret: bytes, *, max_skew=300) -> bool:
parts = dict(p.split("=") for p in header.split(","))
t = int(parts["t"])
if abs(time.time() - t) > max_skew:
return False
expected = hmac.new(secret, f"{t}.".encode() + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts["v1"])

Retries

ERPly Pro retries the webhook with exponential back-off (1s/4s/16s/64s/256s) up to 5 attempts. If they all fail, the event is parked in a DLQ and the operator is paged via PagerDuty.