Most eSignature integrations are tested on the happy path: a signer completes a document, a webhook arrives, a record flips to "executed." The path that decides whether your integration survives an audit, an outage, or a privacy review is the other one: an event that can never be processed, arrives for a record you do not recognize, contradicts your current state, or carries signer data that ends up in a queue nobody owns.
This article answers one question for the engineer or platform lead who carries the pager: how should an eSignature integration handle webhook events that fail permanently, arrive out of order, or cannot be trusted, without losing signed-document evidence, applying a stale event, or leaking signer personal data?
It is a deliberate follow-on to eSignly's existing guidance on API timeouts and unknown outcomes and on idempotency, retries, and webhook resilience. Those pieces cover how to send safely and receive reliably. This one covers what happens after reliability has run out: terminal failure, dead letters, replay, and who owns the data while it waits.
The design below is a proposed design, not an established eSignly method and not a statement of how any specific vendor behaves. It assumes the safest general posture for any webhook source: delivery is at-least-once, ordering is not guaranteed, and the sender's retry behavior must be confirmed in its current API reference. All code is illustrative and is not an official eSignly API contract.
Key Takeaways
- Acknowledge only what you have durably recorded. A 2xx response should mean "verified and committed to our database," never "received and logged somewhere."
- Dedupe, record, and schedule in one atomic write. If the inbox row is also the work item, there is no gap between "we saw it" and "we will act on it."
- A dead-letter queue is a holding area, not an archive. Retention clocks, redrive behavior, and access rules need an owner before the first failure.
- Never replay blindly. A replayed event must pass through the same verification, tenant-scoped dedupe, and conditional mutation as a live one, after reconciling against authoritative state.
- Unknown means unconfirmed. It is neither success nor verified failure, and it needs a deadline and an owner.
- Identifiers are data too. Envelope IDs, document IDs, and signer emails can identify a person or a contract. Keep them out of logs, alerts, and test fixtures by default.
- Use a go/no-go gate, not a score. Every control in the readiness table must pass for consequential workflows. This is a design-review gate, not a compliance certification.
Why Some eSignature Events Can Never Be Processed Cleanly
Key takeaway: Retrying fixes transient failures only. Several other failure classes will never succeed on retry, and treating them all the same is how teams lose events or loop forever.
An eSignature webhook is a message about something that happened on another system's clock. Your system receives it later, possibly twice, possibly after you have moved on, and possibly before you finished recording the thing it describes. That mismatch is structural, not a bug in any vendor.
The signing platform's integrations page describes the basic mechanic: you configure a webhook URL, and the platform sends a POST request with a JSON payload when events such as a document being sent, viewed, signed, or declined occur (see eSignly's integrations page). What that page does not promise, and what no webhook design should assume, is exactly-once or strictly ordered delivery. Build as if neither exists.
Once you accept that, a handful of failure classes appear. They need different handling, which is why a single "retry then dead-letter" rule is not enough.
Six failure classes and what each one means
| Class | Example | Will retry fix it? | Correct disposition |
|---|---|---|---|
| Transient dependency failure | Your database or CRM API is down for minutes | Yes | Retry with backoff and jitter, capped attempts |
| Unknown reference | "Completed" event arrives for a sign request your system has no record of, perhaps because your create call timed out | Sometimes | Hold for a bounded time, then reconcile against provider state |
| Out-of-order or stale event | A "viewed" event arrives after "completed," or a "signed" event arrives after the contract was voided internally | No | Compare to current state; apply, discard as stale, or escalate |
| Schema or contract violation | A new field type or missing required field breaks your parser | No, until code changes | Quarantine, fix the code, replay after approval |
| Conflicting duplicate | Same event ID, different payload content | No | Quarantine as an anomaly; never overwrite the first record |
| Authenticity failure | Signature does not verify, or timestamp is outside your window | No | Reject at the edge; do not enqueue the payload |
Notice that only the first row is helped by the retry machinery most teams build first. The other five are decisions, and decisions need an explicit owner and a durable record of the outcome.
How Most Teams Handle Failed Webhook Events, and Why It Breaks
Key takeaway: The common patterns optimize for getting a 200 back quickly. None of them answers what happens to the event afterward, which is the part auditors and customers eventually ask about.
Teams rarely design failure handling up front. It accretes: a try/catch, a log line, a retry loop, then a queue, then a dead-letter queue added after the first incident. Each step is reasonable. The result is a system where nobody can say what state a given event is in.
Common approaches compared
| Approach | What it does | Where it fails |
|---|---|---|
| Log and return 200 | Always acknowledges, writes failures to application logs | The sender stops retrying while your only copy lives in a log that rotates. The event is effectively lost, and the log may now hold signer data. |
| Return 500 on any error | Lets the sender retry everything | Poison events retry until the sender gives up. You depend on the sender's schedule and ceiling, which you do not control. |
| In-process retry loop | Retries inside the request handler | Slow responses cause sender-side timeouts and duplicate deliveries. Worker restarts erase retry state. |
| Queue plus DLQ, no policy | Moves failing messages to a dead-letter queue after N attempts | The DLQ becomes a landfill. Retention expires silently, and redrive happens by ad hoc script. |
| Manual database patch | An engineer updates the record by hand after reading the payload | No dedupe, no audit record of why, and no check that the event is still current. |
The queue-retention trap, with a sourced example
Dead-letter queues carry their own clocks. In Amazon SQS standard queues, a message's expiration is based on its original enqueue timestamp, and that timestamp is unchanged when the message moves to a dead-letter queue. AWS's own documentation gives the arithmetic: if a message spends one day in the source queue and the dead-letter queue's retention is four days, it is deleted from the dead-letter queue after three more days. AWS therefore recommends setting dead-letter retention longer than the source queue's (Amazon SQS dead-letter queue documentation).
Two more behaviors from the same documentation matter for replay. A redrive task resets the retention period, and redriven messages receive a new message ID and enqueue time (SQS dead-letter queue redrive). The practical consequence: your idempotency key must be the provider's event identity, never the queue's message ID, or a redriven event will look new. SQS also does not support filtering or modifying messages during redrive, so any "fix then replay" step has to happen outside the queue.
These details are specific to SQS. Other brokers differ, which is exactly why retention and redrive semantics belong in your design document and not in tribal knowledge.
A Proposed Design: The Five-Lane Event Disposition Model
Key takeaway: Give every verified event exactly one durable disposition: apply, hold, retry, reconcile, or quarantine. Acknowledge only after the record exists.
The following is a proposed design for teams building or reviewing an eSignature webhook receiver. It is not a proprietary eSignly framework and is not a description of any production system. Its purpose is to make the question "what happened to this event?" answerable with a database query.
The flow
Provider POST | v [1 Verify authenticity: signature over the raw body, timestamp window] |-- fail --> reject (4xx), increment counter, store no payload v [2 One transaction: insert inbox row (tenant_id, provider_event_id, payload hash, state=RECEIVED, next_attempt_at=now)] |-- commit fails --> 5xx (sender may redeliver) |-- same event id, same hash --> duplicate: 2xx, no new work |-- same event id, different hash --> write CONFLICT record, 2xx after commit v 2xx acknowledgment (a durable record now exists) | v [3 Worker claims row with a lease, then classifies] |-- APPLY : conditional update succeeds --> APPLIED |-- HOLD : reference not known yet --> HELD (deadline) |-- RETRY : dependency down --> backoff + jitter, capped |-- RECONCILE : uncertain or contradictory input --> read authoritative state |-- QUARANTINE : terminal (schema, conflict, stale) --> QUARANTINED, alert without payloadVerified acknowledgment: what each response should mean
| Situation | Response | Reasoning |
|---|---|---|
| Signature or timestamp check fails | 4xx (for example 401 or 403) | The sender is not authenticated. Do not store the body. Count it for security monitoring. |
| Verified, durably committed | 2xx | Per RFC 9110, 2xx indicates the request was successfully received and accepted. Here it additionally means a durable record exists. |
| Verified, but your database write failed | 5xx | You have not recorded it, so you must not claim you did. Let the sender's redelivery policy help, and confirm that policy in its documentation. |
| Verified, committed, but business logic will never accept it | 2xx after the quarantine record commits | Retrying cannot help. Keep the evidence in your own store rather than depending on the sender's retry window. |
Transport authenticity has limits. A valid signature or a TLS connection tells you who sent the bytes. It does not tell you the signer's identity was adequately verified, and it does not tell you the event is current. Those are separate questions handled in your identity and reconciliation steps.
Why the inbox row is also the work item
A frequent bug is a dual write: insert into a dedupe table, then publish to a queue. If the process dies between the two, the event is recorded as seen and never worked. The reverse order produces duplicate work. Making the inbox row carry its own state and next_attempt_at means dedupe, durable recording, and scheduling are a single atomic write. A poller or scheduler claims rows from that table. If you prefer a broker, use a transactional outbox so the publish is derived from the committed row and is recoverable after a crash.
Illustrative inbox schema and claim logic
The SQL below is a PostgreSQL-flavored sketch. It illustrates the invariants: tenant-scoped identity, a payload hash that detects changed-content reuse, an atomic claim that works under concurrent workers, and a conditional mutation on the target record.
CREATE TABLE webhook_inbox ( tenant_id text NOT NULL, provider_event_id text NOT NULL, payload_sha256 bytea NOT NULL, payload_ref text NOT NULL, -- pointer to encrypted raw body state text NOT NULL DEFAULT 'RECEIVED', attempts int NOT NULL DEFAULT 0, next_attempt_at timestamptz NOT NULL DEFAULT now(), lease_until timestamptz, hold_deadline timestamptz, received_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (tenant_id, provider_event_id) ); -- Receiver: dedupe + record + schedule in one statement INSERT INTO webhook_inbox (tenant_id, provider_event_id, payload_sha256, payload_ref) VALUES ($1, $2, $3, $4) ON CONFLICT (tenant_id, provider_event_id) DO NOTHING; -- 0 rows inserted: load existing row, compare payload_sha256. -- Equal: duplicate, acknowledge. Different: write a conflict record, then acknowledge. -- Worker: atomic claim safe under concurrent workers UPDATE webhook_inbox SET state = 'PROCESSING', attempts = attempts + 1, lease_until = now() + interval '2 minutes' WHERE (tenant_id, provider_event_id) IN ( SELECT tenant_id, provider_event_id FROM webhook_inbox WHERE (state IN ('RECEIVED','RETRY') AND next_attempt_at <= now()) OR (state = 'PROCESSING' AND lease_until < now()) ORDER BY received_at LIMIT 10 FOR UPDATE SKIP LOCKED) RETURNING ; -- Target-side conditional mutation, not read-compare-then-write UPDATE contract SET status = 'EXECUTED', executed_event_id = $1 WHERE tenant_id = $2 AND envelope_ref = $3 AND status IN ('SENT','PARTIALLY_SIGNED'); -- 0 rows affected: do not guess. Read current state and classify as -- ALREADY_APPLIED, STALE, or CONFLICT.Two notes on this sketch. First, the hash should cover the canonical event content, not delivery metadata such as a per-attempt timestamp, or legitimate redeliveries will look like conflicts. Check what your provider actually varies between attempts. Second, "read the row, compare, then write" in application code is not compare-and-swap, because another worker can change the row in between. The WHERE status IN (...) clause makes the database enforce the condition atomically. If your target is a system you cannot condition (a CRM API without conditional updates, for example), disclose that limitation and serialize writes per record, such as one worker per contract ID.
Why This Fails in the Real World
Key takeaway: Capable teams fail here because failure handling is owned by no one. The scenarios below are illustrative composites, not customer results.
These are illustrative scenarios built from common failure patterns. They are not drawn from any named customer or eSignly deployment, and the figures are invented to show mechanics.
Scenario 1: The dead-letter queue that expired its own evidence
A team routes failed webhook messages to a dead-letter queue after several attempts, and sets a short retention on it to keep costs down. A downstream CRM outage lasts most of a weekend. On Monday, an engineer sees alarms clear and assumes the backlog drained. Several "completed" events had already aged out, because retention was measured from the original enqueue time, not from the moment they hit the DLQ. The contracts were signed on the provider's side; the company's order system never learned it.
Why intelligent teams still fail: the alert fired on queue depth, not on message age. Nobody owned the question "how long can an unprocessed event live?" The fix is a design rule: retention for dead letters is longer than the source queue, age-of-oldest alarms exist, and the inbox table (not the broker) is the system of record for event state.
Scenario 2: The replay that resurrected a voided contract
A parsing bug quarantines a batch of "signed" events. After the fix, an engineer redrives everything. One event belongs to a contract that sales operations voided and reissued two days earlier. Replay applies the old signature to the superseded record, and a provisioning job grants access under terms that no longer exist.
Why it happens: replay ran through a faster path than live traffic and skipped the state check. Nobody had written down that "replay" is a privileged operation with preconditions. The fix: replay re-enters the same classifier, the conditional mutation rejects a record whose state has advanced, and stale events default to discard-or-escalate, never apply.
Scenario 3: The alert that carried the signer's email
To make on-call triage easier, the failure handler posts the full payload to a team chat channel. The payload includes signer email addresses and document titles. The channel has broad membership, a long retention default, and external guests. No one intended a disclosure; the alert template simply predated the privacy review.
Why it happens: debugging convenience and data protection were owned by different groups with no shared checklist. The fix is to alert on event ID, tenant, state, and error class only, and to fetch the payload through an access-controlled tool when an engineer needs it.
The common thread
- Failure handling was built reactively, after the first incident.
- The message broker was treated as the record of truth rather than a transport.
- Replay and data retention were treated as engineering conveniences instead of governed operations.
- No single role could answer "what happened to event X?" in minutes.
Replay, Reconciliation, and Uncertain Input Without Double Effects
Key takeaway: Replay is a controlled re-entry, not a shortcut. When the outcome is unknown, reconcile against authoritative state first; do not retry a consequential mutation blindly.
Replay decision matrix
| Replay situation | Precondition | Default action | Approval |
|---|---|---|---|
| Event never applied because a dependency was down | Inbox state is RETRY or PROCESSING with expired lease | Re-enter classifier; conditional mutation decides | Automatic, rate-limited |
| Event already APPLIED | Inbox state is APPLIED | No-op; record that a duplicate was suppressed | None; cannot be forced |
| Quarantined for schema violation, code since fixed | Fix deployed and tested against redacted fixtures | Dry-run, then single or small-batch replay | Engineering owner |
| Target record has moved on (voided, superseded, amended) | Current state differs from event's assumed state | Reconcile; default to discard-as-stale or escalate | Business process owner |
| Conflicting duplicate (same ID, different content) | Hash mismatch recorded | Never auto-apply; investigate | Security and engineering |
| Reproducing a bug in a non-production environment | Redacted or synthetic payload | Do not copy production payloads | Privacy owner if exceptions are requested |
Reconcile-first for uncertain input
Uncertain input includes an event that references an unknown envelope, an event that contradicts current state, and an outbound call whose response you never received. In each case, the right move is to read the authoritative state from the provider (a read-only status lookup, as supported by the API you use) and compare it to your record before you mutate anything. Confirm in the current eSignly API documentation which status-retrieval operations exist and what they return, then build the reconcile step on those, not on assumptions.
Treat the result as one of three outcomes: confirmed (authoritative evidence supports applying the event), refuted (authoritative evidence shows it is stale or wrong), or unknown (you could not obtain evidence). Unknown is not success and it is not verified failure. Give it a state of its own, an owner, a deadline, and an escalation path. The companion article on timeouts and unknown outcomes covers the outbound side of this in more depth.
Outbound operations: identity, payload hash, and legitimate later operations
When your system initiates a consequential action, such as sending a contract for signature, bind a durable operation identity to the exact approved payload. The sketch below shows the idea.
CREATE TABLE outbound_operation ( tenant_id text NOT NULL, operation_id text NOT NULL, -- stable business intent, e.g. contract + version payload_sha256 bytea NOT NULL, -- hash of the approved, immutable payload state text NOT NULL DEFAULT 'CLAIMED', provider_ref text, created_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (tenant_id, operation_id) ); -- Claim atomically; only one worker proceeds INSERT INTO outbound_operation (tenant_id, operation_id, payload_sha256) VALUES ($1, $2, $3) ON CONFLICT (tenant_id, operation_id) DO NOTHING; -- 0 rows: load existing. Same hash: resume or reconcile that operation. -- Different hash: reject as changed-payload key reuse. Never overwrite.The rules this encodes:
- Tenant-scoped identity. Two tenants can legitimately generate the same internal ID. The key is the pair, never the ID alone.
- Immutable payload hash. Reusing an operation ID with different content is a defect or an attack, not a retry.
- Atomic claim. The unique constraint decides which concurrent worker owns the work.
- Legitimate later operations. A re-send after a decline, or a new contract version, is a new operation with a new identity derived from the new business intent. Do not suppress it as a duplicate.
- Ambiguous timeout. Move to reconcile, not retry. Resend only after the authoritative state shows no matching request exists, or when the provider's documented idempotency mechanism guarantees safety. If your provider does not document one, enforce it yourself with the ledger above and verify behavior before relying on it.
Privacy and Retention Ownership for Dead Letters and Replay Stores
Key takeaway: Dead-letter and replay stores hold signer data longer than anyone planned. Assign retention, access, and erasure ownership before launch, and treat identifiers as potentially personal data.
Webhook payloads often contain more than status. Depending on your configuration and the provider's payload design, they can include signer names, email addresses, document titles, timestamps, and IP-related details. Even "just IDs" can be personal or confidential data when they can be linked back to a person or a contract. Plan on the assumption that anything in the payload may be sensitive, then verify the actual fields for your integration.
Handling by data element
| Element | Why it matters | Default handling |
|---|---|---|
| Signer email or name | Directly identifies a person | Keep in encrypted raw-payload store only; never in logs, alerts, or tickets |
| Envelope or document ID | Linkable to a person or deal through your own systems | Allowed in operational telemetry with access control; treat as internal identifiers |
| Document titles and names | May contain client names or deal details | Exclude from alert text; fetch on demand under access control |
| IP address or device data, if present | Can be personal data and evidentiary detail | Store only where required; apply the same retention as the evidence record |
| Webhook secrets and signature headers | Allow forgery if exposed | Never log; rotate on suspicion; store in a secrets manager |
| Payload hash and event ID | Low sensitivity, high operational value | Safe default for logs and alerts |
The OWASP Logging Cheat Sheet lists categories of data that generally should not be logged in the clear, including credentials and sensitive personal data, and recommends masking or removing them. For a framework on identifying and protecting personally identifiable information, see NIST SP 800-122. Both are guidance, not law, and neither tells you what your own legal obligations are.
Who owns what
| Decision | Typically owned by | Why |
|---|---|---|
| Mechanics: queue configuration, redrive tooling, alarms | Engineering or platform | They operate the system |
| Retention schedule for quarantined and raw payloads | Privacy or data protection lead, with Legal | Balances minimization against evidence retention |
| Whether a quarantined event can be applied or discarded | Business process owner | They own the contract's consequences |
| Access to raw payloads | Security, with named approvers | Least privilege and auditability |
| Erasure requests versus legal holds | Legal and privacy | Engineering should not decide this at 2 a.m. |
Scope your claims carefully
Three distinctions keep this section honest. First, vendor statements are not your obligations met: eSignly states that it maintains ISO 27001, SOC 2 Type II, HIPAA, GDPR, 21 CFR Part 11, and PCI DSS alignment, and publishes information on its security page and compliance page. Treat those as vendor statements, request current reports and data processing terms, and note that a vendor's attestation covers the vendor's systems, not your webhook receiver, your queue, or your chat channel. Second, compliance-support features are not compliance: audit trails and access controls help, but whether your use satisfies GDPR, HIPAA, or FDA 21 CFR Part 11 depends on your role, your data, and your validation work. Third, regulated records may constrain deletion. If an event is part of an electronic record under rules such as 21 CFR Part 11, your retention and deletion design needs Legal and quality input, not just an engineering default. This article is not legal advice.
Go/No-Go Readiness Gate and What to Verify With Your Provider
Key takeaway: Every control below must pass before a consequential workflow, such as contract execution or provisioning, depends on webhook-driven state. One failure is a no-go. There is no score to average away a gap.
Mandatory go/no-go controls
Use this as a design-review gate. It does not certify production readiness or compliance. It identifies gaps that commonly turn into incidents.
| # | Control (all required) | Evidence to show | No-go if |
|---|---|---|---|
| 1 | Authenticity verified over the raw body before any storage | Test with tampered body and stale timestamp; both rejected | Verification runs after parsing or after enqueue |
| 2 | 2xx returned only after a durable commit | Fault-injection test: kill the database mid-request, observe 5xx | Acknowledgment precedes persistence |
| 3 | Dedupe key is tenant-scoped provider event identity | Schema and unit test with two tenants sharing an ID | Key is a queue message ID or a bare ID |
| 4 | Dedupe, record, and scheduling are atomic or explicitly recoverable | Crash test between steps shows no lost or double-run event | Separate dual write with no reconciler |
| 5 | Changed-payload reuse of an event or operation ID is rejected and recorded | Test with same ID, different hash | Second payload overwrites or is silently ignored |
| 6 | Concurrent workers cannot process the same item | Load test with lease-based claim | Claim relies on read-then-update in application code |
| 7 | State mutation is conditional on the target side | Zero-row-affected path is classified, not retried blindly | Read-compare-then-write with no serialization or disclosure |
| 8 | Terminal states exist and are reachable (QUARANTINED, DISCARDED_STALE) | Runbook plus a drill that reaches each state | Only success and retry exist |
| 9 | UNKNOWN has an owner and a deadline | Query lists unknown items with age and assignee | Unknown is logged and forgotten |
| 10 | Dead-letter retention exceeds source retention; age alarms exist | Configuration export and alarm test | Alarm is on depth only |
| 11 | Replay passes through the live classifier and requires approval by type | Replay drill with a stale event is discarded or escalated | Replay uses a bypass script |
| 12 | No signer data in logs, alerts, or non-production fixtures | Log scan and alert template review | Payloads appear in chat or tickets |
| 13 | Retention, access, and erasure owners are named | Written policy with owners | Ownership is "engineering" by default |
Questions to confirm with any eSignature provider
Do not assume answers. Check the current API reference and, for contractual points, the agreement itself. eSignly's published integration documentation describes webhook configuration and event types such as sent, viewed, signed, and declined. Other behaviors below should be verified in the current API reference before you build on them.
| Question | Why it changes your design |
|---|---|
| How are webhooks authenticated (signature scheme, header name, timestamp)? | Determines control 1 and your replay-attack window |
| Is the event ID stable across redeliveries of the same event? | Required for control 3 |
| What is the retry schedule, the ceiling, and how are non-2xx responses treated? | Sets your acknowledgment strategy and how long you can rely on redelivery |
| Is ordering guaranteed? | If not, you need the Hold and Reconcile lanes |
| Which status-lookup endpoints exist for reconciliation? | Enables read-only authoritative comparison |
| Can missed events be resent on demand? | Determines whether provider-side replay is an option |
| Which personal data fields appear in payloads, and can they be minimized? | Drives section six handling |
| Is an idempotency mechanism documented for create operations? | If not, your outbound ledger is mandatory |
| Which source IP ranges or network controls are documented? | Optional defense in depth; do not rely on it alone |
For related depth on signature verification and trust boundaries, see eSignly's guide to webhook security. If a failure has already damaged your evidence chain, the emergency playbook for recovering non-repudiation covers recovery.
Want to test this design against a real sandbox?
Start with the free API plan, wire up a webhook receiver, and run the readiness gate against live event types before you commit.
Try eSignly's API and run your failure drills first.
Start Free2026 Update: What to Re-verify Each Year
The principles here are stable, but the details around them move. Once a year, and after any provider or infrastructure change, re-check the following:
- The provider's current webhook documentation: signature scheme, retry behavior, event types, and payload fields.
- Your queue or broker's retention, redrive, and visibility behavior, using its current documentation rather than memory.
- Current vendor attestation reports and data processing terms.
- Your own drill results for controls 2, 4, 6, and 11. A control that has not been exercised recently is an assumption.
Conclusion: Decide Before the Incident Does
A webhook receiver is finished when you can explain, for any event, what it was, what you did with it, and why. These actions turn that standard into a plan:
- Run the thirteen-control gate against your current receiver and mark each as pass or fail with evidence. Treat any fail as a blocker for consequential workflows.
- Move event state into your own database. Make the inbox row the record of truth and the broker a transport, then add disposition states for hold, reconcile, and quarantine.
- Write the replay policy as a table like the one above, with approval roles, and remove any bypass scripts.
- Name the privacy and retention owners for raw payloads and quarantined items, and strip signer data from logs and alerts.
- Schedule a failure drill: a stale event, a conflicting duplicate, and a database outage. Record what actually happened, then fix the gaps.
About the eSignly Expert Team
eSignly has provided online eSignature software and APIs since 2014 and serves more than 100,000 users. The company states that it maintains ISO 27001, SOC 2 Type II, HIPAA, GDPR, 21 CFR Part 11, and PCI DSS alignment; readers should review the current details on its security and compliance pages and request supporting documentation as part of vendor diligence. This article was reviewed by the eSignly Expert Team. It offers engineering guidance, is not legal advice, and does not certify any specific implementation as compliant.
Frequently Asked Questions
Should a webhook receiver return 2xx for an event it cannot process?
It depends on why processing failed.
- If authenticity checks fail, return a 4xx and do not store the payload.
- If you could not durably record a verified event, return a 5xx so you are not claiming receipt.
- If the event is verified and durably recorded but business logic will never accept it, return 2xx after the quarantine record commits, since retrying will not help.
Confirm how your provider treats each status code in its current documentation.
What is the difference between a dead-letter queue and an inbox table?
A dead-letter queue holds messages a consumer could not process, with broker-specific retention and redrive rules. An inbox table is your application's durable record of every verified event and its state. The proposed design uses the inbox as the system of record and treats the queue as transport, so retention expiry in a broker cannot erase the only evidence of an event.
Can I safely replay a webhook event from a dead-letter queue?
Only if replay re-enters the same path as live traffic: verification, tenant-scoped dedupe on the provider event ID, and a conditional mutation that checks current state. Replaying through a bypass script risks applying stale events, for example to a voided or superseded contract. Redrive can also change message IDs, so dedupe on the provider's event identity, not the queue's.
How do I handle an outbound API call that timed out?
Do not retry blindly if the call creates something consequential, such as a signature request. Record the operation in a ledger with a tenant-scoped ID and an immutable payload hash, then query the provider's authoritative state to learn whether the request exists. Resend only when evidence shows it does not, or when a documented provider idempotency mechanism makes resending safe.
Are envelope IDs and document IDs sensitive?
They can be. Identifiers that can be linked to a person, a customer, or a contract may count as personal or confidential data depending on your context and applicable law. Treat them as internal identifiers with access control. The default-safe values for logs and alerts are event ID, tenant, state, and payload hash.
Does a vendor's SOC 2 or ISO 27001 certification cover my webhook pipeline?
No. A vendor's attestation addresses the vendor's own systems and controls within its stated scope. Your receiver, queue, logging, alerts, and replay tooling are your responsibility. Use vendor reports as diligence inputs, and review your own pipeline against your legal and security requirements.
How long should I keep quarantined webhook payloads?
There is no universal number. Retention should come from your privacy and legal owners, balancing data minimization against evidence needs and any regulated-record obligations. Set it explicitly per lane, keep it longer than the source queue's retention, and document who can approve exceptions or legal holds.
Build the integration you can defend after a bad night.
Webhook failures are inevitable. Lost evidence, stale replays, and leaked signer data are not. Explore eSignly's API, security, and integration documentation, then pressure-test your design with real events.
Create a free account and run your first API signing flow.
Free TrialResource Esignature Api Guide
This article is most relevant for CTOs and developers who need to roll out a practical signing workflow. Use the related eSignly path to compare plans, API options, compliance fit, and implementation next steps.
Reviewed for electronic signature decision makers
This guide is reviewed for clarity, legal and operational relevance, service alignment, and practical conversion path before being connected to an eSignly plan or API workflow.
For regulated, high-volume, or customer-facing workflows, validate legal duties, plan assumptions, and integration requirements with your internal stakeholders before rollout.

