Document Storage
Hyphen provides built-in document storage so workflows can operate on uploaded files rather than requiring data to be embedded in API payloads. Upload a CSV, PDF, or JSON file once, get back a document_id, and reference it anywhere in your workflow using the doc: prefix.
This keeps large file payloads out of workflow definitions. URL ingestion checks the destination before fetching the file, and workflow execution reads the stored copy.
Uploading Documents
Upload via multipart form data:
curl -X POST https://your-hyphen.example.com/documents \
-H "X-Org-Id: acme-corp" \
-F "file=@transactions_q4.csv" \
-F 'name=Q4 Transactions' \
-F 'tags=["finance", "quarterly"]' \
-F 'metadata={"department": "accounting"}' \
-F 'ttl_days=90'
Response:
{
"document": {
"id": "doc_a1b2c3d4e5f6",
"name": "Q4 Transactions",
"content_type": "text/csv",
"size_bytes": 245890,
"checksum_sha256": "e3b0c44298fc...",
"current_version": 1,
"status": "ready",
"tags": ["finance", "quarterly"],
"created_at": "2026-02-01T00:00:00Z"
}
}
Supported Content Types
| Content Type | Extensions | Max Size |
|---|---|---|
| CSV | .csv |
50 MB |
| JSON | .json |
50 MB |
| Plain text | .txt |
50 MB |
.pdf |
50 MB | |
| Excel | .xlsx |
50 MB |
| Images | .png, .jpg, .jpeg |
50 MB |
Deduplication
If you upload a file with the same SHA-256 checksum as an existing document in your organization, Hyphen returns 200 with the existing document and deduplicated: true. A newly stored document returns 201.
Upload from URL
For files already hosted elsewhere:
curl -X POST https://your-hyphen.example.com/documents/from-url \
-H "X-Org-Id: acme-corp" \
-H "Content-Type: application/json" \
-d '{
"url": "https://storage.example.com/reports/q4.csv",
"name": "Q4 Report",
"tags": ["finance"]
}'
Hyphen fetches the file, validates it, and stores it internally. The external URL is not accessed at workflow execution time.
Referencing Documents in Workflows
Use the doc: prefix anywhere you would normally pass inline data:
{
"type": "matcher",
"properties": {
"left": "doc:doc_a1b2c3d4e5f6",
"right": "doc:doc_x7y8z9w0v1u2",
"matchOn": ["invoice_id"],
"tolerance": 50,
"outputMatched": "matched",
"outputUnmatchedLeft": "unmatched_invoices"
}
}
At execution time, the engine retrieves the stored file and resolves it according to content type.
Resolution by Content Type
| Content Type | Resolves To | Example Use |
|---|---|---|
text/csv |
Array<Object> with header values as keys |
Matcher left/right, Loop items_path |
application/json |
Object or Array as-is |
Any property expecting structured data |
text/plain |
String |
LLM template input, text analysis |
application/pdf |
Text extraction result with document metadata; binary metadata if extraction fails | Agent objective or tool input |
image/* |
Binary metadata with the document ID, type, size, and version | Tool input for image processing |
Excel (.xlsx) |
Binary metadata with the document ID, type, size, and version | Tool input for spreadsheet processing |
CSV and JSON arrays resolve up to the configured extraction row limit, which defaults to 10,000 rows per document.
CSV parsing is strict. Use UTF-8, comma-delimited files with a header row and consistent columns. Validation errors identify the first inconsistent row found during upload.
{
"type": "loop",
"properties": {
"mode": "foreach",
"items_path": "doc:doc_customer_list",
"item_variable_name": "customer",
"actions_to_execute": [
{
"type": "custom-table",
"properties": {
"table": "welcome_email_queue",
"operation": "upsert",
"key_fields": ["email"],
"keys": ["email", "name", "status"],
"values": ["@customer.email", "@customer.name", "ready"]
}
}
]
}
}
The CSV resolves to an array, each row becomes @customer inside the loop, and each row is written to the delivery queue. A separate workflow can dispatch the queued emails through Gmail or Outlook with its own retry and evidence boundary.
Versioning
Upload a new version of an existing document without changing the document_id:
curl -X POST https://your-hyphen.example.com/documents/doc_a1b2c3d4e5f6/versions \
-H "X-Org-Id: acme-corp" \
-F "file=@transactions_q4_updated.csv" \
-F 'change_note=Updated with December corrections'
The current_version increments automatically. Workflows referencing doc:doc_a1b2c3d4e5f6 always get the latest version.
Pinning to a Version
For audit reproducibility, pin a specific version:
{
"left": "doc:doc_a1b2c3d4e5f6@2"
}
The @N suffix locks to version N. A pinned reference lets a run use the same dataset version during review or replay.
Reading Version History
Use the document audit endpoint for version history:
curl https://your-hyphen.example.com/documents/doc_a1b2c3d4e5f6/audit \
-H "X-Org-Id: acme-corp"
The audit stream includes version_created entries with version metadata.
Webhook Triggers
Register webhooks to trigger your own automation service when documents are uploaded:
{
"event": "document.uploaded",
"url": "https://automation.example.com/hyphen/hooks/document-uploaded",
"filter": {
"tags": ["invoices"],
"content_type": "text/csv"
},
"auto_payload": {
"workflow_id": "wfl-123e4567-e89b-12d3-a456-426614174000"
}
}
Your automation service can then call POST /workflows/:id/execute with:
{
"input": {
"invoices": "doc:<document_id>"
}
}
This enables the upload-to-execution pattern: data providers drop files, workflows run automatically. See Webhooks for full configuration.
Audit Trail
Every document action is tracked:
| Event | When |
|---|---|
uploaded |
New document created |
version_created |
New version uploaded |
downloaded |
File content retrieved |
metadata_updated |
Metadata changed |
deleted |
Document soft-deleted |
curl https://your-hyphen.example.com/documents/doc_a1b2c3d4e5f6/audit?limit=50 \
-H "X-Org-Id: acme-corp"
Storage Limits
| Limit | Default |
|---|---|
| Storage per organization | 10 GB |
| Maximum file size | 50 MB |
Check current usage:
curl https://your-hyphen.example.com/documents/storage-usage \
-H "X-Org-Id: acme-corp"
Common Patterns
Reconciliation with uploaded datasets. Upload invoices and payments as CSVs, reference both in a matcher step, investigate the exceptions, and write the result to a custom table.
Agent document processing. Upload a PDF and reference it in an agent objective or input. Hyphen extracts the text before the agent reasons over the content.
Scheduled reconciliation with versioned data. Upload a new version each month. A scheduled workflow can use the latest version, while a pinned reference supports historical comparison.
Webhook-driven ingestion. An external system uploads a CSV, the document.uploaded webhook fires, and your automation starts the right workflow. This is the Agent as Trigger pattern with document storage.
Document storage works with existing primitives. Matcher can consume doc: references, loop can iterate over uploaded CSV or JSON arrays, and agents can receive extracted PDF text. Documents use the same context resolution model as other workflow inputs.