Ask Anvil

Answers to questions about automating PDFs, e-signatures, Webforms, and other paperwork problems.
E-signatures
Categories

How do I send automatic reminders to signers who haven't signed yet?

If signers forget about a signature request, the fix is one small scheduled job, not a timer per request. Store two fields on each request (when it was sent and how many reminders have gone out), then let a cron job decide who is due.

const DAY = 24 * 60 * 60 * 1000;
const REMINDER_DAYS = [2, 5, 9]; // days after the request was sent

function remindersDue(requests, now = new Date()) {
  const due = [];
  for (const req of requests) {
    if (req.status !== "pending") continue;           // signed, declined, voided
    if (req.expiresAt && now >= req.expiresAt) continue;

    const sent = req.sentAt.getTime();
    // Index of the latest reminder step whose time has passed.
    const step = REMINDER_DAYS.filter((d) => now.getTime() >= sent + d * DAY).length;

    if (step > req.remindersSent) {
      due.push({ id: req.id, email: req.signerEmail, step });
    }
  }
  return due;
}

// Run on a schedule (cron, queue worker). Record the step *before* sending
// so a crash or overlapping run cannot double-send.
async function runReminderJob(db, sendEmail, now = new Date()) {
  const pending = await db.findPendingRequests();
  for (const r of remindersDue(pending, now)) {
    const claimed = await db.markReminderSent(r.id, r.step); // conditional update
    if (claimed) await sendEmail(r.email, r.id);
  }
}

module.exports = { remindersDue, runReminderJob };

How it works

remindersDue counts how many entries in REMINDER_DAYS have passed since the request was sent. If that count is higher than remindersSent, the signer is owed a reminder. Because it compares counts instead of matching exact days, a job that misses a run (a deploy, an outage) sends one reminder on the next run instead of skipping the step or sending a burst of catch-up emails.

It skips anything that is no longer pending (signed, declined, voided) and anything past its expiry, so nobody gets nudged about a dead request.

markReminderSent must be a conditional update, for example UPDATE requests SET reminders_sent = $2 WHERE id = $1 AND reminders_sent < $2, returning whether a row changed. Claiming the step before sending means two overlapping job runs cannot both email the same signer. The trade-off: if the email call fails after the claim, that reminder is skipped. Log the failure, and either let the next step cover it or roll the counter back on a send error.

Caveats

Check your e-signature provider first. Some APIs already support reminders on the request itself; DocuSign's eSignature REST API, for example, lets you set reminder and expiration notifications on an envelope. If yours does, configure that instead of running your own job, so the reminder comes from the same sender and points to the same signing session.

Update status from completion webhooks, not from the reminder job. If the job reads a stale status, a signer who just finished can still get a nudge. Mark the request completed as soon as the completion event arrives, and keep the schedule short (three reminders here) so it does not read as spam.

Back to All Questions

The fastest way to build software for documents

Anvil Document SDK is a comprehensive toolbox for product teams launching document flows where PDF filling, signing, and complex conditional scenarios are necessary.
Explore Anvil
Anvil Webforms
Marketing Mode