CrunchFile API
Ask an employee, driver, contractor or client for a document from your own system — a POD, a timesheet, a signed work order, a receipt. CrunchFile texts them a link; they photograph the paperwork; the straightened, cleaned PDF comes back to you by webhook and a download link. No app or account on their side.
Get a key
Sign in, open Dashboard → Document Requests → Settings & API, and create an API key. Keys look like cf_live_… and are shown once. Send one on every call:
Authorization: Bearer cf_live_your_keyBase URL: https://getcrunchfile.com/api/v1. Requests and responses are JSON. Limit: 120 calls per minute per account.
Create a request
POST /api/v1/requests
{
"recipient_name": "Marcus Lee",
"recipient_phone": "+12145550101",
"document_type": "POD", // any short name: POD, Timesheet, Work order, Receipt, Signed contract, ID…
"reference": "Load 48213", // shown to them and on the file name (job #, invoice #, client)
"message": "Signed by the receiver, every page", // optional
"external_id": "load-48213", // your own id, echoed back
"send_sms": true // text the link now (needs texting set up on your account)
}Response 201:
{
"request": {
"id": "2f6c…",
"url": "https://getcrunchfile.com/r/Qk3v…", // the link the driver opens
"status": "open", // open → received (or cancelled)
"document_type": "POD",
"reference": "Load 48213",
"external_id": "load-48213",
"sms_status": "sent",
"files": [],
"created_at": "2026-10-09T21:14:03Z",
"expires_at": "2026-11-08T21:14:03Z"
}
}If you send texts or emails from your own system, leave out send_sms and put url in your message. A request stays open for 30 days and can take up to 10 uploads.
Other endpoints
| Call | What it does |
|---|---|
GET /api/v1/requests?status=received&limit=50 | List requests, newest first. |
GET /api/v1/requests/:id | One request with its files. |
POST /api/v1/requests/:id/text | Text (or re-text) the link to them. |
POST /api/v1/requests/:id/cancel | Close the link. |
GET /api/v1/requests/:id/files/:fileId | A download link for one upload (15 minutes). Add ?redirect=1 to be sent straight to the file. |
Webhook: know the moment someone uploads
Set a webhook URL in Settings & API. When someone uploads, CrunchFile POSTs to it (retrying twice over a minute on failure):
POST https://your-system.example.com/crunchfile
X-CrunchFile-Event: request.received
X-CrunchFile-Signature: t=1760044443,v1=5f1a…
{
"event": "request.received", // request.received on the first upload, request.updated after that, test from the dashboard
"created_at": "2026-10-09T21:20:43Z",
"request": {
"id": "2f6c…", "status": "received", "external_id": "load-48213", "reference": "Load 48213",
"files": [
{ "id": "9a1e…", "file_name": "POD - Load 48213 - Marcus Lee 2026-10-09.pdf", "file_size": 184233, "page_count": 2,
"download_url": "https://…" // valid for 7 days
}
]
}
}Verify the signature: take t from the header, compute HMAC-SHA256 of {t}.{raw body} with your signing secret, and compare it to v1. Reject anything older than a few minutes.
// Node
const crypto = require("crypto");
const [t, v1] = req.headers["x-crunchfile-signature"].split(",").map((p) => p.split("=")[1]);
const expected = crypto.createHmac("sha256", SECRET).update(`${t}.${rawBody}`).digest("hex");
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));Errors
Errors are { "error": { "code": "…", "message": "…" } } with the matching HTTP status: 401 invalid_api_key, 400 invalid_request, 404 not_found, 429 rate_limited, 501 not_configured (texting not set up).
Questions
Email [email protected]. If you're building for a fleet, a field-service team, a clinic or an accounting firm and need something the API doesn't do yet, say so — this is early and shaped by what people need.