Document extraction to API
Receive a PDF (base64) by webhook, parse it, extract structured fields with a JSON schema, reshape them and POST the record to your API.
Drag to pan, pinch to zoom. This is the real graph the template installs.
Purpose
Receive a PDF (base64) by webhook, parse it, extract structured fields with a JSON schema, reshape them and POST the record to your API.
How it works
What it does
- A webhook receives
{file_content: <base64 pdf>, source?}. - Parse PDF turns it into text (
full_text). - Extract invoice fields —
ai.extractwith a strict JSON schema; the model output is validated against it. - Shape the record —
data.maprenames the extracted fields to your API's shape. - POST to your API with exponential-backoff retry.
Nodes 5
- 01Document receivedtrigger.webhookTrigger
- 02Parse PDFfiles.parse_pdfData
- 03Extract invoice fieldsai.extractAI
- 04Shape the recorddata.mapData
- 05POST to your APIhttp.requestHTTP
View template JSON
{
"format": "mantle-workflow/v1",
"id": "ai-document-extraction",
"name": "Document extraction to API",
"graph": {
"nodes": [
{
"id": "webhook",
"type": "trigger.webhook",
"label": "Document received",
"config": {
"slug": "document-in",
"input_schema": {
"required": [
"file_content"
],
"properties": {
"file_content": {
"type": "string",
"description": "Base64 PDF"
},
"source": {
"type": "string"
}
}
}
},
"worker_type": "generic-worker",
"position": {
"x": 60,
"y": 220
}
},
{
"id": "parse_pdf",
"type": "files.parse_pdf",
"label": "Parse PDF",
"config": {
"content_encoding": "base64"
},
"worker_type": "generic-worker",
"position": {
"x": 380,
"y": 220
}
},
{
"id": "ai_extract",
"type": "ai.extract",
"label": "Extract invoice fields",
"config": {
"model": "claude-sonnet-4-5",
"temperature": 0,
"text": "{{ steps.parse_pdf.full_text }}",
"system_prompt": "Extract the invoice fields exactly as printed. Use null for anything absent. Never invent values.",
"schema": {
"type": "object",
"required": [
"invoice_number",
"total"
],
"properties": {
"invoice_number": {
"type": "string"
},
"vendor": {
"type": [
"string",
"null"
]
},
"issue_date": {
"type": [
"string",
"null"
],
"description": "YYYY-MM-DD"
},
"due_date": {
"type": [
"string",
"null"
],
"description": "YYYY-MM-DD"
},
"currency": {
"type": [
"string",
"null"
]
},
"total": {
"type": [
"number",
"null"
]
}
}
}
},
"worker_type": "ai-worker",
"position": {
"x": 700,
"y": 220
}
},
{
"id": "map_fields",
"type": "data.map",
"label": "Shape the record",
"config": {
"mappings": {
"externalRef": "steps.ai_extract.response.invoice_number",
"vendorName": "steps.ai_extract.response.vendor",
"amount": "steps.ai_extract.response.total",
"currency": "steps.ai_extract.response.currency",
"dueDate": "steps.ai_extract.response.due_date",
"source": "steps.webhook.source"
}
},
"worker_type": "generic-worker",
"position": {
"x": 1020,
"y": 220
}
},
{
"id": "post_record",
"type": "http.request",
"label": "POST to your API",
"config": {
"method": "POST",
"url": "https://api.example.com/invoices",
"headers": {
"Content-Type": "application/json"
},
"body_from_input": true,
"timeout_seconds": 30
},
"worker_type": "generic-worker",
"position": {
"x": 1340,
"y": 220
},
"retry": {
"max_attempts": 3,
"backoff": "exp",
"initial_ms": 1000,
"max_ms": 15000
}
}
],
"edges": [
{
"from": "webhook",
"to": "parse_pdf"
},
{
"from": "parse_pdf",
"to": "ai_extract"
},
{
"from": "ai_extract",
"to": "map_fields"
},
{
"from": "map_fields",
"to": "post_record"
}
]
}
}Requirements
The connections and permissions each step needs, from the node contracts in the catalog.
- AnthropicAPI key
Used by Extract invoice fields.
Credential: {"api_key"}
- The provider adapter is chosen from the connection; `model` must belong to it.
Setup
- Choose an AI connection on Extract invoice fields; edit the schema for your document type.
- Set the destination
url(or switch the node to an HTTP connection +path) on POST to your API. - Publish and POST a base64 PDF to the webhook to test; the run's step outputs show each stage.
Sample input and output
Synthetic examples: node-contract samples or placeholders, never customer data.
Input
The fields the template declares or reads from the request body. Values are placeholders.
{
"file_content": "<file_content>",
"source": "<source>"
}Output
POST to your API http.request
{
"body": null,
"headers": {},
"status_code": 0
}Failure handling
What the graph does when a step fails, read from its retry settings, error branches and error policies.
- POST to your API: up to 3 attempts, exponential backoff from 1 s up to 15 s.
- POST to your API: the request times out after 30 s.
- A step that fails, after any retries, fails the run. The failed step is kept with its input and error.
See it run, then make it yours.
Templates install as drafts. Edit any step, and Workflow Health re-checks the graph on every save.