How-to guide
How to email PDFs via webhook
Connect generated PDFs to an email or workflow system by sending completion data to a webhook endpoint.
What you'll need
Supplies
- Generated PDF workflow
- Webhook endpoint URL
- Recipient and message fields
Tools
- DocForge
- Webhook receiver
- Email service
Steps
- 1
Decide what the webhook should send
Decide the handoff contract before generating the batch. The email workflow needs enough information to send the right PDF to the right person and retry failures without confusion. Define payload fields such as `record_id,recipient_email,recipient_name,email_subject,email_body,document_name,pdf_url`. Keep `record_id` stable across systems, for example `INV-2026-0482` or `SHIP-10455-02`, so support can trace a single failed send later. Do not rely on the recipient name to identify the document; names repeat and spelling changes. Include a document-specific subject in the CSV, such as `Your invoice INV-2026-0482 from Acme Manufacturing Co.`. A common error is sending a test batch where `recipient_email` contains spaces or uppercase aliases copied from a CRM export. Trim whitespace and validate addresses before upload. Preview the PDF and the payload fields together so `{{ row.recipient_name }}` matches the person receiving the email, not only the person listed on the account.
- 2
Prepare the PDF generation source
Prepare the template and CSV as one delivery package. If the webhook email step needs a greeting, subject, and attachment name, those fields must exist in the source data before the batch runs. A practical CSV header set is `record_id,recipient_email,recipient_name,email_subject,email_body,document_name,invoice_number,due_date,total_due`. Use Liquid inside the PDF only for document content, such as `{{ row.invoice_number }}` and `{{ row.due_date | date: '%B %-d, %Y' }}`. Keep email routing fields in the CSV even if they do not appear on the PDF. Fix formula contamination before upload: spreadsheet exports often turn `document_name` into a formula-derived value or strip leading zeros from IDs. Convert formulas to plain values and confirm `INV-000482.pdf` stays intact. Preview a row where `email_body` contains a comma or line break, because badly exported CSV text can shift columns and send the right PDF to the wrong address.
- 3
Configure the webhook receiver
Configure the receiver to accept one event per generated document and log the fields your team will use during support. At minimum, log `record_id`, `recipient_email`, `document_name`, and the PDF reference. Return a clear success response only after the receiver has accepted the handoff. In the no-code or internal workflow, map fields deliberately: `recipient_email` goes to the email recipient, `email_subject` goes to the subject, and `pdf_url` or the attachment reference goes to the file step. Do not bury `record_id` inside freeform message copy; keep it as a searchable field. A common operational failure is duplicate sends after a retry. Prevent that by treating `record_id` plus `document_name` as the unique handoff key. Preview how missing optional copy behaves with Liquid such as `{{ row.email_body | default: 'Please find your document attached.' }}`. Test with a real-looking record like `INV-2026-0482` before using customer data.
- 4
Test one recipient before the batch
Run one row through the entire path before releasing the batch. Use an internal recipient first, but keep the row realistic: `record_id,recipient_email,recipient_name,email_subject,document_name` with values like `INV-2026-0482,ops-test@example.com,Jamie Park,Invoice INV-2026-0482,INV-2026-0482.pdf`. Confirm the PDF opens, the subject is correct, the attachment name is useful, and the body does not expose template placeholders. Then check the webhook logs for the same `record_id`. If the email arrives but the log has no document name, fix logging before scaling; failed retries become painful without traceability. Test one edge case as well: a recipient name with an apostrophe, a long subject, or a missing optional message. If `{{ row.recipient_name }}` or `{{ row.due_date }}` appears literally in the email or PDF, stop and fix the mapping. Once the single-record path is clean, run the larger batch and monitor row-level failures for retry.