Skip to main content

EDI webhooks: verify signed deliveries

Verify SignalEDI webhook signatures with HMAC-SHA256, timestamp tolerance, and replay protection — Node and Python examples included.

Quick answer

What is an EDI webhook?

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.

More common questions →

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.

Common questions

© 2026 SignalEDI Inc. All rights reserved.