Масова відправка документів на підписання з трекінгом та нагадуваннями

Коли потрібно розіслати тисячі документів на підписання в стислі терміни, ручна відправка перетворюється на хаос з помилками та затримками. Ми автоматизуємо цей процес: ви завантажуєте список, система генерує персоналізовані файли, надсилає їх через чергу завдань і відстежує статуси з нагадуваннями. Наша команда реалізує рішення під ключ — від налаштування до подальшої підтримки, щоб ви працювали надійно та без зриву термінів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Масова відправка документів на підписання з трекінгом та нагадуваннями
Середній
~3-5 днів
Часті запитання

Масова відправка документів на підписання з трекінгом та нагадуваннями

При злитті двох компаній потрібно розіслати 5000 трудових договорів за 48 годин. Ручна відправка потребує 10 співробітників на тиждень — з помилками, втраченими листами та нервотрепкою. Ми автоматизуємо цей процес: завантаження CSV, генерація персоналізованих PDF, відправка через чергу завдань, відстеження статусів та автоматичні нагадування. Нижче — архітектура, яка витримає навантаження 10K+ документів без падіння.

Які проблеми вирішуємо?

Генерація тисяч документів без зависання сервера. Синхронна генерація вб'є сервер — використовуємо чергу завдань на BullMQ. Кожен документ генерується окремим воркером, паралельність до 10. Навіть при піку в 5000 документів система залишається чуйною.

Rate limiting email-провайдерів. Resend дає 100 листів на секунду, Postmark — стільки ж. Для batch в 10K листів ми впроваджуємо throttling в черзі: відправка йде зі швидкістю 100–200 листів на хвилину, щоб не потрапити в блокування. Таймаути та повторні спроби — обов'язкові.

Відсутність підписання у частини отримувачів. Автоматичні нагадування через 2 та 4 дні. Налаштовувана кількість (до 3). Дашборд показує, хто не відкрив лист, і дозволяє надіслати нагадування вручну.

Як ми це робимо?

Типовий стек: PostgreSQL + Redis + BullMQ + Node.js (NestJS) + Puppeteer для PDF. Воркери розгорнуті в Docker на окремому інстансі. Код зберігається в GitLab CI, деплой на AWS ECS.

Кейс: для маркетплейсу впровадили масову відправку актів виконаних робіт. Щомісяця 15 000 актів. Раніше бухгалтерія витрачала 3 дні на розсилку через Outlook. Після впровадження — 30 хвилин. Помилки скоротилися з 5% до 0.2%.

Процес роботи

  1. Аналітика. Збираємо вимоги: обсяг, шаблони документів, інтеграції (CRM, бухгалтерія).
  2. Проектування. Схема БД, черга завдань, API, сторінка підписання.
  3. Реалізація. Розробка модулів: завантаження CSV, генерація PDF, відправка, дашборд.
  4. Тестування. Навантажувальне тестування з 10K документів, сценарії помилок.
  5. Деплой. На сервер клієнта або хмару. Налаштування CI/CD, моніторинг (Sentinel, Grafana).

Чому черга завдань краща за синхронну генерацію?

Синхронна генерація 5000 PDF викличе тайм-аут сервера. Черга BullMQ з воркерами (паралельність 10) обробляє документи поступово, не блокуючи запити. Redis зберігає прогрес. Якщо воркер впав — завдання автоматично перезапускається. Це дає надійність та масштабованість.

Як працює черга в коді?

const signingWorker = new Worker('document-signing', async (job) => {
  const { requestId } = job.data;
  const request = await db.signingRequests.findByPk(requestId);
  try {
    await db.signingRequests.update(requestId, { status: 'generating' });
    const pdfBytes = await documentGenerator.generate(
      request.template,
      request.templateData
    );
    const document = await documentStorage.store(pdfBytes, {
      batchId: request.batchId,
      requestId: request.id,
    });
    const signingUrl = `${process.env.APP_URL}/sign/${request.signingToken}`;
    await emailService.send({
      to: request.recipientEmail,
      subject: 'Документ ожидает вашей подписи',
      template: 'signing-invitation',
      data: {
        recipientName: request.recipientName,
        documentName: request.template.name,
        signingUrl,
        expiresAt: request.expiresAt,
      },
    });
    await db.signingRequests.update(requestId, {
      status: 'sent',
      documentId: document.id,
      sentAt: new Date(),
    });
    await db.signingBatches.increment(request.batchId, 'sent_count');
  } catch (error) {
    await db.signingRequests.update(requestId, {
      status: 'failed',
      errorMessage: error.message,
    });
    await db.signingBatches.increment(request.batchId, 'failed_count');
  }
}, {
  concurrency: 10,
  connection: redisConnection,
});

Обмеження та надійність

Email-провайдери блокують при перевищенні лімітів. Ми налаштовуємо чергу з паузою між відправками. Використовуємо виділені сервери відправки (SMTP-ретранслятори) та відстежуємо статуси листів (bounce, spam). Гарантуємо доставку 99%.

Автоматичні нагадування

Якщо отримувач не підписав, автоматика надсилає нагадування на 2-й та 4-й день. На 7-й день — позначка "прострочено" та сповіщення менеджеру. У дашборді можна надіслати повторний лист з новим посиланням. Налаштовуваний ліміт нагадувань — від 0 до 5.

async function sendSigningReminders() {
  const pending = await db.signingRequests.findAll({
    status: 'sent',
    reminderCount: { lt: 3 },
    sentAt: { lt: subDays(new Date(), 2) },
    expiresAt: { gt: new Date() },
  });
  for (const request of pending) {
    const lastReminderAt = request.lastReminderAt || request.sentAt;
    const daysSinceLastReminder = differenceInDays(new Date(), lastReminderAt);
    if (daysSinceLastReminder >= 2) {
      await emailService.sendReminder(request);
      await db.signingRequests.update(request.id, {
        reminderCount: request.reminderCount + 1,
        lastReminderAt: new Date(),
      });
    }
  }
}

Порівняння ручної та автоматичної розсилки

Параметр Ручна (10 співробітників) Автоматична (наше рішення)
Час на 5000 документів 40 людино-годин 2 години машинного часу
Помилки (одруки, втрата) 5–8% <0.5%
Вартість за одну розсилку значні витрати економія
Трекінг статусів Excel Дашборд в реальному часі

Також додаємо порівняння за швидкістю: автоматична розсилка в 20 разів швидша за ручну (40 годин проти 2).

Модель даних

CREATE TABLE signing_batches (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name VARCHAR(500),
  template_id UUID REFERENCES document_templates(id),
  initiated_by UUID REFERENCES users(id),
  total_count INT NOT NULL,
  sent_count INT DEFAULT 0,
  signed_count INT DEFAULT 0,
  failed_count INT DEFAULT 0,
  status VARCHAR(50) DEFAULT 'pending', -- pending → processing → completed / partially_failed
  deadline_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE signing_requests (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  batch_id UUID REFERENCES signing_batches(id),
  recipient_email VARCHAR(500) NOT NULL,
  recipient_name VARCHAR(500),
  recipient_phone VARCHAR(50),
  template_data JSONB NOT NULL,
  document_id UUID REFERENCES documents(id),
  signing_token UUID UNIQUE DEFAULT gen_random_uuid(),
  status VARCHAR(50) DEFAULT 'pending',
  sent_at TIMESTAMPTZ,
  opened_at TIMESTAMPTZ,
  signed_at TIMESTAMPTZ,
  reminder_count INT DEFAULT 0,
  expires_at TIMESTAMPTZ,
  error_message TEXT
);

Завантаження та валідація CSV

async function processBatchUpload(file: Express.Multer.File, templateId: string) {
  const records = await parseCSV(file.buffer, { headers: true });
  const template = await db.documentTemplates.findByPk(templateId);
  const requiredFields = extractTemplateVariables(template.content);
  const errors: ValidationError[] = [];
  const validRows: RecipientRow[] = [];
  records.forEach((row, index) => {
    const rowErrors = [];
    if (!row.email || !isValidEmail(row.email)) {
      rowErrors.push(`Строка ${index + 2}: некорректный email`);
    }
    for (const field of requiredFields) {
      if (!row[field]) {
        rowErrors.push(`Строка ${index + 2}: отсутствует поле "${field}"`);
      }
    }
    if (rowErrors.length > 0) {
      errors.push(...rowErrors);
    } else {
      validRows.push(row);
    }
  });
  return { valid: validRows, errors, totalRows: records.length };
}

Сторінка підписання за токеном

Отримувач переходить за посиланням https://app.example.com/sign/{token} — авторизація не потрібна, доступ тільки за токеном:

app.get('/sign/:token', async (req, res) => {
  const request = await db.signingRequests.findOne({
    signingToken: req.params.token,
    status: { not: ['expired', 'signed', 'declined'] },
  });
  if (!request) return res.redirect('/sign/invalid');
  if (request.expiresAt < new Date()) {
    await db.signingRequests.update(request.id, { status: 'expired' });
    return res.redirect('/sign/expired');
  }
  if (!request.openedAt) {
    await db.signingRequests.update(request.id, { openedAt: new Date() });
  }
  res.render('signing-page', { request, document: request.document });
});

Дашборд моніторингу batch

Прогрес-бар: відправлено/підписано/не відкрито/прострочено. Таблиця з фільтрами за статусом. Експорт у CSV. Кнопка «Надіслати нагадування» для вибраних отримувачів.

Орієнтовні строки

Етап Строки
Завантаження CSV, валідація, черга + генерація PDF + відправка 7–10 днів
Сторінка підписання за токеном, нагадування, дашборд 5–7 днів
Інтеграція з CRM / імпорт наявних шаблонів 3–5 днів додатково

Що входить в роботу?

Детальний список
  • Вихідний код на GitHub/GitLab (закритий репозиторій)
  • Інструкція з розгортання (Docker + docker-compose)
  • Документація по API (OpenAPI/Swagger)
  • Міграції БД
  • Навантажувальне тестування (звіт)
  • Гарантія 1 місяць на баги

Зв'яжіться з нами для оцінки вашого проєкту. Отримайте консультацію з автоматизації підписання документів.

Наші компетенції:

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1542
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1394
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1369
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1142
  • Розробка веб-сайту для компанії ФІКСПЕР
    Розробка веб-сайту для компанії ФІКСПЕР
    1060