Webhook security
EDI webhooks: verify signed deliveries
Quick answer
What is an EDI webhook?
Why signatures matter
Webhook endpoints are public URLs. SignalEDI signs every versioned document lifecycle event, including document.accepted.v1 and document.acknowledgement_received.v1.
Signing scheme
Headers: X-SignalEDI-Timestamp and X-SignalEDI-Signature (sha256=<hex>). Signed string is `${timestamp}.${rawBody}` using your webhook signing secret.
- Reject timestamps older than five minutes
- Legacy secrets honored during seven-day rotation grace
Node verification
Read the raw request body before JSON parsing — re-serialized JSON will not match the signature.
import crypto from "node:crypto";
function verifySignalEdiWebhook(input: {
rawBody: string;
signatureHeader: string;
timestampHeader: string;
secret: string;
}): boolean {
const provided = input.signatureHeader.replace(/^sha256=/i, "").toLowerCase();
const expected = crypto
.createHmac("sha256", input.secret)
.update(`${input.timestampHeader}.${input.rawBody}`, "utf8")
.digest("hex");
// Use a timing-safe compare so signature checks do not leak partial matches.
return crypto.timingSafeEqual(Buffer.from(provided, "hex"), Buffer.from(expected, "hex"));
}Python verification
Use hmac.compare_digest. Return 401 for failed verification.
import hmac
import hashlib
def verify_signaledi_webhook(raw_body: str, signature: str, timestamp: str, secret: str) -> bool:
provided = signature.removeprefix("sha256=").lower()
expected = hmac.new(
secret.encode(),
f"{timestamp}.{raw_body}".encode(),
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(provided, expected)Receive an EDI document event safely
For a versioned document event, read X-SignalEDI-Event, X-SignalEDI-Delivery-ID and Signal-Environment alongside the timestamp and signature. Verify the raw bytes, confirm the expected environment, then persist the event in durable storage before acknowledging it with a 2xx response. A webhook acknowledgement confirms your endpoint received the event; it does not establish partner acceptance, payment or business completion.
- Use X-SignalEDI-Delivery-ID to recognize repeat deliveries before applying a business change.
- If durable storage is unavailable, report the failure rather than acknowledging an event you cannot recover.
- Follow the event's documented document status and acknowledgement details; HTTP success is a separate layer of evidence.
- Legacy events use raw body signing without a timestamp header. Use the documented legacy contract rather than applying the versioned timestamp scheme to every event.
FAQ
Common questions
An EDI webhook delivers a document lifecycle event to your HTTPS endpoint, so your application can react to validation, delivery or acknowledgement status without relying only on polling. Verify the event, persist it and process repeat deliveries idempotently.
X-SignalEDI-Signature, X-SignalEDI-Timestamp, X-SignalEDI-Event, and X-SignalEDI-Delivery-ID. Verify before parsing JSON.
During the grace window, deliveries signed with the previous secret may include X-SignalEDI-Signature-Legacy — accept either until grace expires.