Webhooks
EDI webhooks
Quick answer
What HTTP status should handlers return?
Event types
Typical events include document.validated, document.routed, document.partner_ack, and document.error. Payloads are documented in /docs.
- JSON envelopes with documentId + partnerId
- Retry with exponential backoff on 5xx
Signature headers
Every delivery carries X-SignalEDI-Signature (sha256=<hex>), X-SignalEDI-Timestamp (unix seconds), X-SignalEDI-Event, and X-SignalEDI-Delivery-ID. The signature is an HMAC-SHA256 over the string timestamp + '.' + rawBody, using your webhook signing secret.
- Reject deliveries whose X-SignalEDI-Timestamp skews more than ~5 minutes (replay protection)
- During key rotation a second X-SignalEDI-Signature-Legacy header lets you accept both secrets
Verification
Verify webhook signatures using the shared secret configured in the developer console. See the verify-signed-webhooks guide for code samples.
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"));
}Idempotency
Use X-SignalEDI-Delivery-ID to dedupe retries. Your handler should be safe to run twice for the same logical transition.
FAQ
Common questions
Return 2xx quickly after enqueueing async work. Timeouts cause retries — respond fast and process in a background queue.
Use a tunnel (ngrok, Cloudflare Tunnel) for your dev URL, register it in the console, and send sandbox documents to trigger events.