PDF extraction
How to automate PDF workflows with n8n webhooks
Use n8n Webhook and HTTP Request nodes to upload PDFs to okraPDF, register lifecycle webhooks, and route ready or failed events.
n8n is a practical choice when you want workflow automation but still want code-level control. It can run in n8n Cloud or self-hosted, and the HTTP Request node makes it easy to wire APIs before a native node exists.
This is API-feasible today. n8n has a Webhook node with test and production URLs, and an HTTP Request node that can call REST APIs and import cURL examples. Those two nodes cover both directions of the workflow.
The examples below intentionally use built-in nodes only. That keeps the templates importable before okraPDF has a verified n8n community node available in every hosted workspace.
Use two workflows
Use one workflow to upload PDFs and another workflow to react to completed processing.
| Workflow | Trigger | Main action |
|---|---|---|
| Upload workflow | Drive, Gmail, S3, form, or manual trigger | HTTP Request to POST /v1/upload |
| Ready workflow | n8n Webhook node | Route document.processed or document.failed |
This split avoids a common mistake: treating upload response as final extracted data. Upload starts processing. The ready webhook is the durable handoff.
In this pattern, okraPDF is the document processing step and n8n owns the routing logic.
For direct field-extraction workflows, such as Gmail attachments that need OCR
and tracking-number extraction, a single n8n workflow can also upload the binary
file, start /v1/parse, poll the JSON job result, and write directly to Sheets.
Keep that pattern JSON-only in n8n Cloud; do not write intermediate results to a
local /files path.
Workflow 1: send PDFs into okraPDF
Start with your source node:
- Google Drive Trigger
- Gmail Trigger
- IMAP Email
- S3-compatible storage
- Manual Trigger for testing
- Webhook for uploads from your app
Add an IF node to keep only PDFs. Check MIME type, extension, or both.
Then add an HTTP Request node:
- Method: POST.
- URL:
https://api.okrapdf.com/v1/upload. - Authentication: header with your API key.
- Body: binary file or source URL, depending on the source node.
- Response: keep document ID, job ID, hosted URL, and poll URL.
Write the document ID back to your tracking system. That ID becomes the join key when the ready event arrives.
Workflow 2: receive lifecycle events
Create a new workflow:
- Add a Webhook node.
- Set HTTP method to POST.
- Copy the Production URL.
- Register that URL with
POST /v1/webhooksor the webhook settings page. - Subscribe to
document.processedanddocument.failed.
In n8n, add an IF node:
- If
{{$json.body.type}}equalsdocument.processed, continue to row writing. - If it equals
document.failed, continue to exception handling.
Importable n8n templates
The importable workflows live in the standalone public okrapdf/examples GitHub repo. The current sample pack includes:
BINARY_UPLOAD_CONTRACT.md: exact HTTP Request node binary upload shapes for Gmail, Drive, Webhook, and Form PDFs.okrapdf-binary-upload-contract-fixtures.json: importable fixture branches for/v1/files+/v1/parseand/v1/upload.okrapdf-gmail-scanned-pdf-tracking-to-sheets.json: Gmail PDF attachments -> OCR parse API -> polling -> Google Sheets row with tracking-number candidates.okrapdf-google-drive-to-sheets.json: Google Drive folder -> upload API ->document.processedwebhook -> Google Sheets row.okrapdf-lifecycle-router.json: lifecycle webhook router for ready and failed PDFs that already enter from another source.n8n/README.md: setup notes, credential placeholders, and webhook registration commands.VALIDATION.md: static validation status for the sample workflows and helper scripts.
The lifecycle templates include a manual registration path that calls /v1/webhooks with the n8n Production Webhook URL. Keep that registration step separate from the upload flow so you can rotate webhook URLs without changing your document intake logic. The Gmail scanned-PDF template uses polling instead of lifecycle webhooks because it needs the parse result JSON before writing a row.
For okraPDF calls, use an HTTP Header Auth credential and set each HTTP Request node to Authentication: Generic Credential Type and Generic Auth Type: HTTP Header Auth. If the credential is attached but authentication is left as None, n8n will not send the API key header.
Verify signatures
For higher-trust workflows, add a Code node after the Webhook node to verify the HMAC signature. Use the raw request body and the subscription secret. If verification fails, stop the workflow.
If your n8n setup does not preserve raw body access easily, keep the webhook URL private and limit what the workflow can do. For financial or customer data, prefer signature verification.
Write rows
For Google Sheets:
- Add a Set node to flatten fields.
- Add a Google Sheets node.
- Append one row per document with job ID, file ID, status, hosted URL, and direct PDF URL.
For databases:
- Use Postgres, MySQL, or HTTP Request.
- Upsert on the document ID.
- Store the hosted PDF URL.
For Slack:
- Add a Slack node or HTTP Request to Slack webhook.
- Format a short human-readable message.
- Link back to the hosted PDF or review queue.
Error handling
Create an exception branch for document.failed. Store:
- Event type
- Document ID
- Filename
- Source system
- Error message
- Timestamp
- Hosted URL if available
Notify a human only for actionable failures. For skipped non-PDF files, log them but avoid noisy alerts.
Production hardening
Use environment credentials in n8n rather than hardcoded API keys. Make the API key workflow-specific, not a shared admin key.
Add idempotency. n8n workflows can retry, and webhooks can redeliver. Before writing a destination row, look up the document ID and event type.
Version workflows. When a PDF schema changes, clone the workflow, test it with fixture PDFs, then activate the new version.
Use extraction-specific workflow routes for invoice rows or schema outputs, and treat the generic lifecycle webhook as the durable “PDF is ready or failed” signal. Per-facet extraction events belong to a separate pipeline event namespace when that public surface ships.