Skip to content
Display settings
Reading preferences

Saved only in this browser.

Start free

eSignature Expiration and Reminders via API: A Deadline Ledger Design for Backend Teams

Executive brief

For teams evaluating esignature api integration

Use this guide to frame compliance risk, signing workflow fit, buyer readiness, implementation effort, and cost before choosing an eSignature path.

  • Clarifies where electronic signatures can reduce approval delays.
  • Connects the topic to relevant eSignly plans, API options, and security controls.
  • Helps decision makers compare legal, operational, and adoption tradeoffs.
View related solutionCompare plans
eSignature Expiry & Reminder API Design for Backends
eSignature Expiry & Reminder API Design for Backends

Short answer: treat every deadline action in an eSignature integration (a reminder, an extension, an expiry, a void) as a durable, tenant-scoped operation record. Bind it to the approved payload with a hash. Claim it atomically before any worker acts. Read the authoritative signing state before every consequential change. When a call times out, reconcile before you try again. This article calls that arrangement a Deadline Ledger. It is a proposed design for this article. It is not a described eSignly product feature or an established eSignly method.

Deadlines look like the easy part of an eSignature integration. The signature itself, with its identity checks, evidence and audit trail, gets most of the design attention. Deadlines get a cron job and a boolean. Then a signer completes a document seconds before an expiry job runs. A deploy replays the scheduler. An extension call times out and the user clicks twice. None of these is a signature-law problem. They are concurrency problems that happen to have legal and customer-facing consequences.

This guide is for senior backend developers and solution architects who own the code between a CRM, contract system or product workflow and an eSignature API. It covers:

  1. the three clocks that exist in every signing workflow
  2. why cron-and-flag implementations fail
  3. a ledger design with illustrative SQL and worker logic
  4. a mandatory go/no-go control list
  5. failure scenarios, legal and privacy scope, and a rollout order that limits risk

For related designs, see our guides on reconcile-first recovery after API timeouts and safely voiding, correcting and re-sending requests. This article covers the timer-driven part of that problem space.

Key Takeaways

  1. Three clocks, three owners: the business deadline, the platform-enforced expiry and your scheduler's time are different things. Most deadline bugs come from treating them as one.
  2. Operation identity first: every reminder, extension, expiry or void gets a durable, tenant-scoped operation key bound to a hash of the approved payload. Reusing a key with a different payload is rejected.
  3. Atomic claim, then authoritative read: a worker claims work in a single database statement, then reads the real signing state before it mutates anything.
  4. Unknown is not success and not failure: a timeout means completion could not be confirmed. Reconcile first. Never retry a consequential change blindly.
  5. Acknowledge only what is durable: deduplication, state change, cancellation of obsolete timers and outbox recording happen in one transaction before you return a success status to a webhook sender.
  6. Go/no-go, not a score: the control list below is all-required. Passing it does not certify your system as production-ready or legally compliant. It only removes known classes of failure.
  7. Law is not your scheduler: whether a late signature binds anyone depends on contract terms, jurisdiction and facts. Have counsel define that policy. Your code should record what happened accurately.

Why Signing Deadlines Break eSignature Integrations

Key takeaway: a signing workflow contains three independent clocks. The defects appear where the clocks disagree and no one owns the disagreement.

Consider the actors in one signing request. A signer acts on a human schedule, often at the last minute. The eSignature platform enforces its own rules about when a request can still be signed and when reminders go out. Your application runs jobs on its own infrastructure. Your business has a commercial deadline that may not match either. Each can be correct on its own terms, and the system can still end in a contradiction.

ClockOwnerExample (illustrative)What goes wrong if confused
Business deadlineContract owner, sales or legal operationsA pricing offer is valid through a stated date and time zone.The system enforces the wrong cutoff, or enforces it in the wrong time zone.
Platform expiryThe eSignature providerThe request stops accepting signatures at a stored timestamp.Your local record says open while the platform says closed, or the reverse.
Scheduler timeYour infrastructureA job fires three days before expiry to send a reminder.Clock skew, replays and missed runs produce early, late or duplicate actions.

Four structural reasons make this hard, and none of them is a skill problem:

  1. Human timing clusters at boundaries. Signers disproportionately act near deadlines, which is exactly when your expiry and reminder jobs are most active.
  2. Event delivery is not ordered or exactly-once. Design for at-least-once delivery and out-of-order arrival even when a vendor describes its delivery as reliable. eSignly's public materials describe signed webhook events and at-least-once delivery. That is a vendor statement, and your handler should assume duplicates and reordering regardless.
  3. Timers outlive the facts that created them. A reminder scheduled yesterday can fire after the signer finished, the request was voided, or a corrected request replaced it.
  4. Several systems send messages. Platform auto-reminders, your own reminders, a CRM sequence and a support tool can all contact the same signer about the same document.

A fifth reason is easy to miss. Deadline actions are mutations on the target system. A void or an extension changes what a counterparty can do. Treat them with the same discipline as creating a request, not as housekeeping.

How Most Teams Build It, and Why It Fails

Key takeaway: the common pattern is a polling job plus a status flag. It works until two things happen at nearly the same time, which at scale is routine.

The typical first implementation has four parts. A nightly or hourly cron selects rows where the local status is still sent and a due date has passed, then calls an expire or void endpoint. A second job emails or calls a reminder endpoint for everything still pending. An admin screen lets a user extend a deadline, and the handler retries on timeout. A webhook handler flips the local status when completion events arrive.

PatternHidden failure modeVisible consequence
Cron reads local status, then actsLocal status lags the platform. A completion webhook is delayed, dead-lettered or not yet processed.The job expires or voids a request that was just signed.
Reminder job sends to all pending rowsNo per-send identity and no claim. A restart, a second replica or a replay sends again.Signers get several reminders. Support and sales see complaints.
Extension handler retries on timeoutThe first call may have succeeded. The retry applies a second change or targets a request that has since been superseded.The deadline ends up wrong, and no one can say who approved it.
Webhook handler returns success, then does workA crash after the success response but before the state write loses the event.The completion is never recorded. Later timers act on stale facts.
Deadline stored as local timeDaylight-saving shifts and the server's zone setting move the cutoff.A deadline ends an hour early or late, or on the wrong calendar day.
Platform and app both own remindersNo one decides which system sends.Double reminders, or none because each assumes the other is sending.

The shared flaw is that reading state and then writing is treated as one safe step. It is not. Between the read and the write, a signer can sign, a webhook can arrive, another worker can claim the same row, or an operator can void the request. A read-compare-then-write sequence is not a compare-and-swap unless the target performs the comparison itself. We return to this in the control list.

The second flaw is that the approach has no vocabulary for uncertainty. Code that has only success and failure must collapse a timeout into one of them. Both guesses are wrong some of the time.

A Proposed Design: The Deadline Ledger

Key takeaway: the ledger gives every timer-driven action an identity, a state and an owner. It acts only on confirmed facts, and it can say unknown out loud. This is a proposed design offered for your evaluation. It is not a standard and not a claim about how any vendor's platform works internally.

The six rules

  1. Durable, tenant-scoped operation identity. The identity is the pair (tenant, operation key). The key is derived from opaque identifiers, such as the internal request reference, the action kind and an approval or sequence identifier. Do not build it from signer email addresses or names. Identifiers can themselves be sensitive data.
  2. Immutable approved payload, bound by hash. When a person or policy approves an action (for example, extend to this exact timestamp), canonicalize the payload and store its hash with the operation. Reusing the same key with a different hash is rejected, never overwritten.
  3. Atomic claim. A worker takes work with a single database statement that both selects and marks the row, with a lease. Two workers cannot claim the same operation.
  4. Authoritative read before consequential mutation. The worker asks the platform for the request's current state. It does not rely on the local mirror.
  5. Explicit uncertain state. A timeout, a connection reset or an unreadable response moves the operation to needs_reconcile. It never moves to done or to a retryable failed.
  6. Terminal outcomes with an owner. Every operation ends as done, obsolete or failed_terminal. Quarantined items name a human or team that decides.

Operation states

StateMeaningAllowed next states
scheduledApproved and waiting for its due timeclaimed, obsolete
claimedA worker holds a lease and is workingdone, obsolete, needs_reconcile
needs_reconcileCompletion could not be confirmed (unknown)done, obsolete, scheduled (same operation, bounded attempts), quarantined
quarantinedFacts conflict or input is unrecognized. A named owner must decide.done, obsolete, failed_terminal (by decision)
doneEffect confirmed against authoritative statenone
obsoleteFacts changed and the action is no longer needed (for example, already signed)none
failed_terminalConfirmed not applied and no further automatic attempts allowednone

Legitimate later operations

Idempotency must not block real business changes. A second extension a week later is a new approved action, so it receives a new approval identifier and therefore a new operation key. The same approval identifier carrying a different target date is a conflict and returns an error. This is how the ledger separates the same operation retried from a different operation that looks similar.

Flow overview

INBOUND EVENT (webhook or poll result)   verify authenticity -> ONE transaction:      inbox dedup + monotonic state update + mark obsolete timers + outbox row   -> only then return success to the sender  SCHEDULED WORK   atomic claim (lease) -> authoritative read of signing state     terminal already?        -> obsolete     facts differ from plan?  -> quarantined (named owner)     unreadable?              -> needs_reconcile     else apply change (conditional if target supports it, else serialized per request)       timeout / unknown      -> needs_reconcile (never blind retry)       confirmed              -> done  RECONCILER   read authoritative state -> done | obsolete | reschedule same operation (bounded) | quarantined

The vendor-side mutation is the hard part. If the platform's API offers a conditional update (for example, apply only if the current expiry equals a value you supply), use it. If it does not, you cannot build a true compare-and-swap from the outside. Serialize all deadline mutations for one request through a single claimed operation at a time, read state immediately before and after, and document the residual window. In that window a signer or another system can change the request between your read and your write. Do not describe that design as race-free.

Illustrative Implementation: Claims, Reconciliation and Verified Acknowledgment

Key takeaway: the code below is illustrative. It shows the invariants in a Postgres-style schema and Python-style pseudocode. The vendor calls are placeholders, not sourced eSignly endpoints. For real endpoints and parameters, use the eSignly API overview and the current API documentation for your plan.

Confirm in that documentation which deadline-related parameters your plan exposes (reminders, status checks and signed webhooks are listed on eSignly's public API pages), and whether any idempotency or conditional-update mechanism exists. The design here assumes none, so the ledger carries the identity itself. HTTP-level idempotency headers, such as the one described in an IETF HTTP API working group draft, apply to HTTP request retries. They only help if the server you call supports them, and they do not replace your own durable operation record.

Schema and registration

CREATE TABLE deadline_op (   tenant_id     text        NOT NULL,   op_key        text        NOT NULL,   -- opaque: request_ref + kind + approval_id   kind          text        NOT NULL,   -- reminder | extend | expire | void   request_ref   text        NOT NULL,   -- internal opaque id, not signer PII   payload_hash  text        NOT NULL,   -- hash of canonical approved payload   expected_from text,                   -- e.g. current expiry the approver saw   due_at        timestamptz NOT NULL,   -- stored in UTC   state         text        NOT NULL DEFAULT 'scheduled',   attempts      int         NOT NULL DEFAULT 0,   claimed_by    text,   lease_until   timestamptz,   PRIMARY KEY (tenant_id, op_key) );  def register_op(db, tenant_id, op_key, kind, request_ref, approved_payload, due_at):     h = sha256(canonical_json(approved_payload))     with db.transaction():         inserted = db.one(             'INSERT INTO deadline_op (tenant_id, op_key, kind, request_ref, payload_hash, due_at) '             'VALUES (%s,%s,%s,%s,%s,%s) '             'ON CONFLICT (tenant_id, op_key) DO NOTHING RETURNING payload_hash',             (tenant_id, op_key, kind, request_ref, h, due_at))         if inserted is None:             existing = db.one('SELECT payload_hash FROM deadline_op '                               'WHERE tenant_id=%s AND op_key=%s', (tenant_id, op_key))             if existing.payload_hash != h:                 raise Conflict('operation key reused with a different approved payload')             return 'duplicate_ok'     return 'registered'

Atomic claim and lease handling

-- Claim: select and mark in one statement UPDATE deadline_op SET state = 'claimed', claimed_by = :worker,     lease_until = now() + interval '2 minutes', attempts = attempts + 1 WHERE (tenant_id, op_key) IN (   SELECT tenant_id, op_key FROM deadline_op   WHERE state = 'scheduled' AND due_at <= now()   ORDER BY due_at   FOR UPDATE SKIP LOCKED   LIMIT 20) RETURNING ;  -- Expired lease on a side-effecting operation = UNKNOWN, not 'retry' UPDATE deadline_op SET state = 'needs_reconcile' WHERE state = 'claimed' AND lease_until < now();

The SKIP LOCKED locking option is documented for queue-like tables, and the PostgreSQL manual notes it gives an inconsistent view of the data. That is acceptable for claiming work and unsuitable for general queries. See the PostgreSQL SELECT documentation for the exact semantics. If you use a different datastore, use its equivalent single-step conditional claim.

Note the second statement. A worker that died mid-call may or may not have applied the change. Returning that row to scheduled would be a blind retry. It goes to needs_reconcile instead.

Worker and reconciler logic

def execute(op):     truth = vendor_get_status(op.request_ref)     # placeholder: authoritative read     if truth is None or truth.unreadable:         return park(op, 'needs_reconcile')        # uncertain input: do not act     if truth.is_terminal:                         # signed, voided or expired already         return finish(op, 'obsolete')     if op.kind in ('extend', 'expire', 'void') and truth.expiry != op.expected_from:         return park(op, 'quarantined')            # facts differ from what was approved     try:         vendor_apply(op)                          # placeholder: conditional if supported     except (Timeout, ConnectionReset):         return park(op, 'needs_reconcile')        # never blind retry     confirm = vendor_get_status(op.request_ref)     return finish(op, 'done') if matches_target(confirm, op) else park(op, 'needs_reconcile')  def reconcile(op):     truth = vendor_get_status(op.request_ref)     if truth is None or truth.unreadable:  return bounded_retry_or_terminal(op)     if truth.is_terminal:                  return finish(op, 'obsolete')     if matches_target(truth, op):          return finish(op, 'done')     if truth.expiry == op.expected_from:   return reschedule_same_op(op)  # attempts bounded     return park(op, 'quarantined')

Reminders deserve a short note. A reminder is a communication, not a change to the agreement, so its consequence is lower. Even so, apply a minimum interval between reminders for one signer and prefer skipping a reminder over sending a duplicate when the outcome is unknown. If your provider exposes a last-reminder timestamp, use it to reconcile. If it does not, accept that the ledger can only bound duplicates, not eliminate them.

Verified acknowledgment for inbound events

def handle_webhook(raw_body, headers):     if not verify_signature(raw_body, headers):     # authenticity first         return 401     event = parse(raw_body)     if event is None or unknown_request(event):         quarantine_input(raw_body); return 202      # recorded for review, not dropped     try:         with db.transaction():                      # ONE transaction             fresh = insert_inbox_if_new(event.tenant_id, event.event_id)             if fresh:                 apply_monotonic_state(event)        # never move backward from terminal                 mark_obsolete_timers(event.tenant_id, event.request_ref)                 insert_outbox(event)                # downstream handoff recorded         return 200                                  # durable before acknowledging     except Exception:         return 500                                  # sender retries; nothing was lost

The acknowledgment is the contract. Returning a success status means the event is durably recorded and the dependent work is scheduled or recoverable. If you cannot guarantee that, return an error and let the sender retry. A signature-verified webhook also proves provenance, not completeness. For consequential actions, the worker still reads authoritative state. Our guide on webhook dead-letter, replay and privacy design covers the failure path in depth, and the completion handoff guide covers what happens once a signature is confirmed.

Time handling in one paragraph

Store every instant in UTC. Serialize it in the unambiguous RFC 3339 timestamp format. Keep the signer-facing local deadline (for example, end of day in a named time zone) as a separate field with its zone identifier, and compute the UTC instant once, at approval time, from the business rule. Never recompute it from the server's local settings.

Race Conditions and the Mandatory Go/No-Go Controls

Key takeaway: list the races explicitly, then require every control. A weighted score can hide a missing control behind several strong ones, so this is a gate, not a grade.

The races to design against

RaceWhat happensSafe handling
Signature arrives as the expiry job runsThe job's read is stale by seconds. The platform may accept or reject the late action.Authoritative read immediately before the change. Treat the platform's response as final and record it. Do not overwrite it locally.
Reminder queued, signer then signsThe timer fires after completion.The inbound-event transaction marks scheduled timers obsolete. The worker re-checks state before sending.
Extension call times out, user clicks againTwo requests could apply, or the second could target a superseded request.Same approval identifier gives the same operation key. Reconcile by reading the current expiry before any further attempt.
Completion event arrives after an expiry eventOut-of-order delivery looks like a contradiction.Monotonic state rules plus an authoritative read. Disagreements go to quarantine, not to a silent overwrite.
Deploy or failover replays the schedulerTwo schedulers see the same due rows.Atomic claim with leases. Duplicate scheduling attempts collide on the primary key.
Daylight-saving change or zone confusionAn end-of-day deadline moves by an hour or a day.UTC instants, a stored zone identifier, and computation at approval time.
Clock skew between your host and the platformYour local now disagrees with the platform's.Use the platform's reported expiry as truth for platform behavior. Use your clock only to decide when to look.

Mandatory go/no-go controls (all required)

This list is a pre-production gate for the deadline subsystem only. Every row must have a documented pass. It does not certify the system as production-ready or legally compliant, and it does not replace your own security review, testing or legal advice.

#ControlPass conditionEvidence to produce
1Tenant-scoped operation identityUniqueness is on (tenant, key). One tenant's key can never collide with another's.Schema and a cross-tenant collision test
2Payload-bound identityThe key is bound to a hash of the approved payload. Reuse with different content is rejected.Test showing a 409-style rejection
3Atomic claimSelecting and marking happen in one step. Concurrent workers never both act.Concurrency test with several workers
4Authoritative readConsequential changes follow a read of platform state, not the local mirror.Code path review and a stale-mirror test
5No blind retryA timeout or lost lease moves to needs_reconcile. Only reconcile may reschedule.Failure-injection results
6Conditional mutation or disclosed limitationThe target performs the comparison, or the serialization approach and its residual window are written down.Design note signed off by the owner
7Atomic inbound recordingDedup, state update, timer cancellation and outbox row commit together before success is returned.Crash-point test between each step
8Terminal failure pathEvery operation ends in done, obsolete or failed_terminal. Quarantine names an owner and an alert.Runbook and an alert test
9Uncertain-input handlingUnknown IDs, bad signatures and unparseable bodies are rejected or quarantined. They are never treated as success.Fuzz and replay tests
10Distinct later operationsA new approval produces a new key. A changed payload under an old approval is refused.Two-extension scenario test
11Single reminder ownerOne system owns reminders per workflow. The other is disabled or documented.Configuration record
12Privacy ownershipJob payloads and logs hold opaque references. Signing links and signer identifiers are excluded or redacted. A named owner sets retention.Log sample review and a retention schedule
13UTC and explicit zonesInstants are UTC. Local deadlines carry a zone identifier. Conversion happens once.DST-boundary test

The failure-injection plan for backend teams gives test cases that can produce much of the evidence in the right-hand column.

Why This Fails in the Real World

Key takeaway: intelligent teams still ship these defects, because deadline logic looks low-risk, belongs to no single team, and passes every test that runs one request at a time. The scenarios below are hypothetical illustrations of common patterns. They are not reports of specific customers.

Scenario A: the reminder storm (illustrative)

A platform team moves its scheduler to a new container platform. During cutover, the old and new schedulers run together for a few hours. Each selects pending requests due a reminder and sends. Signers receive duplicates. Meanwhile the eSignature platform's own automatic reminders, which a product manager had enabled earlier, are also running. Support gets complaints and sales loses trust in the workflow.

Why capable teams miss it: reminders feel harmless, so they skip design review. No one owned the question which system sends reminders? The test environment had one scheduler and a handful of requests, so duplicates never appeared. The gap is governance and process, not individual skill.

Scenario B: the false expiry (illustrative)

A contract is signed at the very end of the business day. The completion webhook hits a transient error in the receiving service and is queued for retry. At midnight an expiry job reads the local mirror, sees sent, and calls the platform to close the request. In a well-behaved platform this call fails because the document is already complete. The job then logs an error and, following its error branch, marks the local record expired. Later the retried webhook arrives and flips it to completed. Downstream systems that reacted to expired have already canceled provisioning and notified the customer.

Why capable teams miss it: the local mirror was treated as truth, error branches were written as if an error meant not done, and state transitions were not monotonic. No one tested the case where an inbound event and a timer collide.

Scenario C: the double extension (illustrative)

An account manager extends a deadline for a slow counterparty. The call times out. The UI shows an error, so the manager clicks again. The first call had in fact succeeded. The second applies a different relative extension (plus seven days from a new base). The request now ends two weeks past what anyone approved. The audit record shows two changes but no clear approval for the second.

Why capable teams miss it: the extension was expressed as a relative offset rather than an approved absolute target, and nothing bound the user's intent to a durable operation. A timeout was interpreted as failure.

What these share

  1. An ownership gap: no single owner for reminders, expiry policy or reconciliation.
  2. A single-request test culture: concurrency, replay and crash-point scenarios are not in the standard test plan.
  3. A two-valued logic: success or failure, with no unknown.
  4. A mirror-as-truth assumption: local status is trusted beyond its freshness.

For the broader recovery perspective after a request goes wrong, see the reconcile-first guide to timeouts and unknown outcomes.

Legal Scope, Privacy, Trade-offs and a Lower-Risk Rollout

Key takeaway: your scheduler should record accurately and enforce the policy that counsel and the business define. It should not decide legal effect. Deadline data also contains personal identifiers and bearer-like links, so privacy ownership has to be explicit.

Legal scope: what this article does and does not say

In the United States, the federal ESIGN Act provides, in general terms, that a signature or contract may not be denied legal effect solely because it is electronic. See 15 U.S.C. § 7001 for the text and its conditions. That general rule says nothing about your expiry and reminder logic. Whether a signature that arrives after a stated deadline binds a party depends on the contract's terms, the rules on offer and acceptance, the jurisdiction and the facts. Industry or statutory timelines (for example, regulated notices) can add separate duties. Those are reader-specific legal obligations. Ask counsel to define:

  1. which deadline is the business or contractual cutoff, and in which time zone
  2. whether a late signature is refused, accepted with review, or handled case by case
  3. who may approve an extension, and how that approval is evidenced

Then encode that policy as data, such as an approved payload and an approval identifier. Do not bury it in a job. The audit record for an extension or expiry should show who approved what, when, and what the platform's state was before and after. For more on audit evidence, see eSignly's audit trail resource.

Privacy ownership

  1. Identifiers are data. Signer email addresses, phone numbers and names are personal data. Keep them out of operation keys, job payloads and logs. Use opaque internal references and look up contact details only at the moment of need.
  2. Signing links are credentials in practice. Anyone holding a live signing link may be able to use it, depending on your authentication settings. Never write links to logs, tickets or analytics.
  3. Name the retention owner. Ledger rows, inbox records and quarantine payloads need a retention schedule set by someone accountable. Sizing that schedule against your own legal, regulatory and contractual retention duties is your responsibility.
  4. Vendor scope. eSignly's security and compliance pages describe the platform's controls and attestations. Those are vendor statements about the vendor's service. They support your program, but they do not make your own integration, logs or retention practices compliant. Request current reports and read their scope.

Trade-offs to accept deliberately

ChoiceBenefitCost
Authoritative read before each consequential changePrevents acting on stale stateExtra API calls, added latency and rate-limit consumption
Quarantine on conflicting factsPrevents silent wrong decisionsRequires a human queue and an on-call owner
Skip a reminder rather than risk a duplicateProtects signer experienceA signer may occasionally miss a nudge
A ledger table and inbox/outbox recordsAuditability and recoverabilityMore schema, more storage, and a retention policy to maintain
Serializing per request when the target lacks conditional updatesNarrows the race windowA residual window remains. It must be disclosed, not denied.

A lower-risk rollout order

Order the work by consequence, lowest first:

  1. Shadow mode. Run the ledger and workers without calling the platform. Compare what they would do against current behavior.
  2. Reminders. Pick one reminder owner (platform or application). Enable the ledger for it with a minimum interval.
  3. Read-only reconciliation. Run the reconciler against live state, with alerts on any mismatch.
  4. Expiry and void. Add them with the go/no-go controls proven by failure-injection tests.
  5. Extensions. Enable last, with an approval step and an absolute target in the approved payload.

To exercise these flows, you can start with eSignly's API plans, which list a free plan with a limited monthly number of signing requests. Review the API plan page for current limits before you test.

2027 Update: more callers, same invariants

Heading into 2027, more deadline actions will be triggered by workflow engines and AI agents calling tools on a user's behalf, not only by humans clicking buttons. Agents retry, rephrase and run in parallel, which makes a durable operation identity and an approved payload hash more important, not less. If you expose signing actions to agents through a tool protocol, see eSignly's MCP guide for what is documented, and apply the same ledger rules to every caller. The principles in this article (three clocks, atomic claims, authoritative reads, reconcile before retry, privacy ownership) do not depend on which interface issues the call. They apply whichever tools or vendors you use.

Test deadline flows before your signers do.

Reminders, status checks and signed webhooks are where timing bugs hide. Exercise them on a free API plan first.

Start free, then run your failure-injection plan against real signing requests.

Free Trial

Conclusion: Five Actions Before You Ship Deadline Logic

Deadline handling is a small surface with a large blast radius. These steps keep it controlled.

  1. Name the three clocks in your design document. Write down the business deadline, the platform expiry and your scheduler's role. Give each an owner, and decide in writing which system sends reminders.
  2. Give every deadline action a durable identity. Use a tenant-scoped key, bind it to a hash of the approved payload, and reject reuse with different content. Issue a new key only for a genuinely new approval.
  3. Read before you write, and reconcile before you retry. Claim work atomically, read authoritative state before any consequential change, and send every ambiguous outcome to reconciliation. Treat unknown as its own state.
  4. Make acknowledgment mean durability. Commit deduplication, state change, timer cancellation and the outbox row together before returning success to a sender. Give quarantined items a named owner.
  5. Run the go/no-go list as a gate. Produce the evidence for every control, including privacy ownership and disclosure of any residual race window. Have counsel confirm the policy for late signatures and extensions.

About eSignly and this review

eSignly is an eSignature SaaS and API provider operating since 2014, with online, API and on-premises options. Its public security and compliance pages state alignment with ISO 27001, SOC 2 Type II, HIPAA, GDPR, 21 CFR Part 11 and PCI DSS. These are vendor statements, and their scope should be confirmed against current reports and your own obligations. Platform capabilities support your compliance program. They do not replace it. This article was reviewed by the eSignly Expert Team. It is general technical guidance, not legal advice, and the code is illustrative and not an official API contract.

Frequently Asked Questions

How should an eSignature integration handle a signature that arrives right as a request expires?

Let the platform decide, and record what it decides. Read the authoritative signing state immediately before any expiry or void call. Treat the platform's response as final, and do not overwrite it with your local mirror. If the facts disagree with what your plan expected, move the operation to quarantine for a named owner.

Whether a late signature has legal effect is a contract and jurisdiction question for counsel, not for the scheduler.

Is it safe to retry an extension or void call after a timeout?

Not blindly. A timeout means completion could not be confirmed. It does not mean the call failed. Mark the operation as needs_reconcile, then read the authoritative state. If the state matches the target, mark it done. If it still matches the pre-change value, reschedule the same operation within a bounded number of attempts. If it matches neither, quarantine it.

Who should send reminders: the eSignature platform or my application?

Choose one owner per workflow. Platform reminders are simpler and avoid extra integration code. Application reminders give you control over timing, tone and cross-system context. Running both without coordination produces duplicates. If you choose application reminders, use a ledger with a minimum interval between sends and prefer skipping to duplicating when the outcome is unknown.

Does an idempotency key header solve duplicate deadline actions?

Only partly, and only where the server you call supports it. An HTTP idempotency header addresses retried HTTP requests. Your own durable operation record must still cover the full workflow: scheduling, claiming, reconciliation and distinguishing legitimate later operations. Confirm in the provider's current documentation whether any such mechanism exists.

How do I avoid time zone and daylight-saving errors in signing deadlines?

Store every instant in UTC and serialize it in an unambiguous format such as RFC 3339. Store the business rule (for example, end of day in a named time zone) separately with its zone identifier. Compute the UTC instant once, at approval time, and test the daylight-saving boundaries explicitly.

What should a webhook handler do before returning a success status?

It should verify authenticity and then, in one transaction, record the event for deduplication, apply a monotonic state update, mark obsolete timers and write the outbox row. Return success only after that commit. If any step fails, return an error so the sender retries. Record unrecognized or malformed input for review instead of dropping it or treating it as success.

What personal data can end up in deadline and reminder jobs?

Signer email addresses, phone numbers, names and signing links are common examples. Identifiers can themselves be sensitive, and a live signing link may function like a credential. Use opaque references in keys and payloads, redact logs, and name an owner for retention.

Does passing the go/no-go control list make my integration compliant or production-ready?

No. The list removes known classes of timing and concurrency failure in the deadline subsystem. It does not certify your system, and it is not a score. Your security review, load testing, privacy assessment and legal review remain separate and required.

Building deadline logic around an eSignature API?

Review the API overview, confirm the reminder and status endpoints available on your plan, and prove the controls with real requests.

Try eSignly's API plan and run your first deadline test today.

Free Trial
Related solution

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.

Explore related solutionCompare plans
Editorial review

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.

Reviewed byeSignly content, product, and conversion review team
Reviewed2026-11-02
FocuseSignature API integration

For regulated, high-volume, or customer-facing workflows, validate legal duties, plan assumptions, and integration requirements with your internal stakeholders before rollout.