Send call sheets into Callsheet Pro
Your own tools can push a shoot day straight into a Callsheet Pro account. No PDF, no import step: send the data, and the day is on the phone within seconds.
Three ways in. Pick yours.
Tap Share on the PDF and pick Callsheet Pro. Built into the iOS and Android apps. No key, no API.
Forward it to your personal address from Settings, Email Forwarding. The attachment is read automatically.
Scheduling software, a production office system, a spreadsheet script, a Shortcut. That is what this page is for.
Get your key in 3 steps
Every Callsheet Pro account can make a key, Free or Pro. You never need to email anyone.
- Open Callsheet Pro and go to Settings. On the phone: the gear at the top right. On the web app: Settings in the sidebar.
- Scroll to API key and tap Create API key. It sits right under Email Forwarding.
- Tap the copy button and paste the key where your tool wants it. The key starts with
csp_. For safety it is shown only this once; if you lose it, tap Make a new key.
The key writes into your own account only. Treat it like a password: anyone holding it can add and change call sheet days in your account, and nothing else.
Try it in one minute
You need a Mac, Linux or Windows terminal with curl (every Mac has it). Put your key in place of csp_YOUR_KEY and run:
curl -X POST "https://us-central1-callsheet-pro.cloudfunctions.net/api/v1/days" \
-H "Authorization: Bearer csp_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"externalId": "my-first-test",
"day": {
"productionTitle": "API test",
"dateISO": "2026-10-20",
"generalCallTime": "07:00",
"locations": [{ "name": "Studio A", "address": "Kleine Weg 1, Amsterdam", "callTime": "07:00" }]
}
}'
You get back {"result":"created", ...}. Open Callsheet Pro: there is a production called API test with one day. That is the whole trick.
Now change the call time and send it again:
curl -X POST "https://us-central1-callsheet-pro.cloudfunctions.net/api/v1/days" \
-H "Authorization: Bearer csp_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"externalId": "my-first-test",
"day": {
"productionTitle": "API test",
"dateISO": "2026-10-20",
"generalCallTime": "08:00",
"locations": [{ "name": "Studio A", "address": "Kleine Weg 1, Amsterdam", "callTime": "08:00" }]
}
}'
This time the answer says "result":"updated", and the same day in the app now reads 08:00 instead of a second copy appearing. The externalId is what makes that happen: same id, same day.
Done testing? In Callsheet Pro, open All Days and swipe the day to the left to delete it (or tap Edit list, then the trash icon next to it). On the web app the trash icon sits on each row of All Days; tap it twice.
Got a 401? The key is wrong or was replaced. Make a new one in Settings, API key, and paste it again.
Got a 422? The issues list in the answer names exactly which field to fix.
Build an iOS Shortcut
A Shortcut turns any text on your iPhone into a shoot day: a call sheet pasted into a WhatsApp message, the body of an email, a note. Select the text, tap Share, pick the Shortcut, and the day appears in Callsheet Pro. Each send reads the text with the same parser as a PDF upload and spends one upload from your monthly allowance.
Have the call sheet as a PDF? You do not need a Shortcut: tap Share on the PDF and pick Callsheet Pro.
- Open the Shortcuts app and tap the plus at the top right. Tap the name at the top, call it Send to Callsheet Pro.
- Add the action Get Contents of URL. Tap Search Actions at the bottom, type the name, tap it. Then tap the blue URL word and paste this:
https://us-central1-callsheet-pro.cloudfunctions.net/api/v1/days
- Expand the action (tap the small arrow) and fill it in:
- Method: POST
- Headers: add
Authorizationwith the valueBearer csp_YOUR_KEY(the word Bearer, a space, then your key), and addContent-Typewith the valueapplication/json - Request Body: JSON. Tap Add new field, choose Text, type
inputas the key, then tap the value, tap Select Variable and pick Shortcut Input
- Set up the input. Picking Shortcut Input adds a Receive ... from ... step at the top. Tap Continue under If there's no input and choose Get Clipboard, so a copied call sheet works too. To get the Shortcut into the Share menu, tap the i (Details) at the bottom and switch on Show in Share Sheet.
- Add the action Show Content (on older iOS it is called Show Result). It shows the answer:
createdon success, or the message that says what went wrong. - Try it. Copy a call sheet text, tap the play button at the bottom right. The first time, iOS asks whether the Shortcut may send text to
us-central1-callsheet-pro.cloudfunctions.net: tap Always Allow. After a few seconds the answer appears, and the day is in Callsheet Pro. From then on: select text in a message or email, tap Share, pick Send to Callsheet Pro.
One send per 15 seconds, and each send spends one upload from your monthly allowance, like scanning a PDF in the app. If the text is not a call sheet at all, the answer says PARSE_FAILED and nothing is added.
Endpoint
POST https://us-central1-callsheet-pro.cloudfunctions.net/api/v1/days
Authorization: Bearer csp_YOUR_KEY
Content-Type: application/json
One call sends one shoot day. The response tells you whether it was created or updated. This is the only route; there is no way to read, list or delete days through the API.
Structured mode (recommended)
Send the day as JSON in a day object. Instant, exact, and free of upload credits.
{
"externalId": "your-stable-id-for-this-sheet",
"day": {
"productionTitle": "Night Shift",
"dateISO": "2026-07-14",
"generalCallTime": "07:00",
"wrapTime": "19:00",
"locations": [
{ "name": "Studio A", "address": "Kleine Weg 1, Amsterdam",
"callTime": "07:00", "parking": "Lot B" }
],
"crew": [
{ "role": "Gaffer", "name": "A. Person",
"phone": "+31600000000", "callTime": "07:00" }
],
"schedule": [
{ "time": "08:00", "description": "First setup", "teams": [] }
]
}
}
Required: productionTitle, and a date (dateISO as YYYY-MM-DD, preferred, or date as free text). Everything else is optional.
Updates (upsert by externalId)
externalId is your tool's own stable id for the sheet (max 128 characters). Re-send the same externalId and the existing day in the app is updated in place instead of duplicated. This is how live changes work: when the sheet changes in your tool, just send it again.
- Always send the full day, every time. Fields you omit are cleared. An omitted
crewlist empties the crew section. - The API owns the call sheet content (title, times, locations, crew, schedule, scenes, transport, cast, and so on). An update overwrites those with what you send.
- The user's personal data in the app always survives updates: private notes, My Wrap, attached documents, pinned notes, custom crew order, and how the day is grouped into a production.
Without externalId, every call creates a new day.
Loose mode
If you cannot produce our JSON, send any text instead and our parser turns it into a structured day. Costs the account owner one upload credit per call (same as scanning a PDF in the app) and is limited to one call per 15 seconds.
{
"externalId": "your-stable-id-for-this-sheet",
"input": "CALL SHEET - Night Shift - Tuesday, July 14, 2026\nGeneral call 07:00 ..."
}
Send exactly one of day or input, never both. input is capped at 100,000 characters. The API accepts no files: a PDF has to become text before it is sent, or go through Share in the app.
Field reference (day object)
| Field | Type | Notes |
|---|---|---|
productionTitle | string | Required. Days with the same title group into one production in the app. |
seriesName | string | Optional series name. |
episodeInfo | string | For example "S2E05". |
dayNumber | number | Shoot day number. |
dateISO | string | YYYY-MM-DD. Preferred date field. |
date | string | Human-readable date, fallback when dateISO is absent. |
country | string | ISO country code of the shoot, used for phone number formatting. |
generalCallTime | string | For example "07:00". |
wrapTime | string | Expected wrap. |
weather | string | Free text. |
sunrise / sunset | string | For example "05:42". |
warnings | string[] | Safety or general warnings shown prominently. |
locations | Location[] | See shapes below. |
crew | CrewMember[] | See shapes below. |
schedule | ScheduleItem[] | See shapes below. |
scenes | Scene[] | See shapes below. |
transport | TransportRow[] | See shapes below. |
cast | CastMember[] | See shapes below. |
emergency | object | hospitalName, hospitalAddress, hospitalPhone, firstAid, counselor. |
invoiceEmail and friends | string | invoiceCompany, invoiceDetails, projectNumber: billing info for crew invoices. |
gearAndSettings | object | gear: string[], settings: [{ label, value }], warning: string, groups: [{ title, gear, settings }]. Send groups when the technical block is printed under two or more subheadings (broadcast/OB sheets: TECHNIEK, AUDIO, EVS, VIDEOSIGNALEN); gear and settings are then derived from it and anything you put in them is ignored. A single group is folded into the flat arrays. |
Object shapes:
- Location:
name,address,callTime,parking,contactName,contactPhone - CrewMember:
role,name,phone(E.164 preferred),email,callTime,callLocation,team - ScheduleItem:
time,endTime,description,teams(string[]),sceneNumber - Scene:
number,description,cast(string[]),scheduledTime,locationName(must exactly match a locations[].name to link) - TransportRow:
kind,label,driver,driverPhone,passengers(string[]),time,from,to,ref - CastMember:
role,name,phone,pickupTime,makeupTime,wardrobeTime,callTime,onSetTime,note
All string fields accept null or can be omitted; they default to empty.
Responses and errors
{ "result": "created", "dayId": "...", "dayIds": ["..."],
"externalId": "...", "warnings": [] }
201 for created, 200 for updated. warnings lists non-fatal problems (for example a scene pointing at an unknown location). Fix them when convenient; the day was saved.
Errors produced by the API look like:
{ "error": { "code": "INVALID_DAY", "message": "how to fix it",
"issues": ["field: problem"], "hint": "https://developer.callsheetpro.app" } }
An HTTP platform or proxy can reject malformed HTTP or malformed application/json before the handler runs; in that case the response may be a plain 400. Check the HTTP status and content type before decoding the JSON error envelope.
| HTTP | code | Meaning and fix |
|---|---|---|
| 400 | INVALID_JSON / INVALID_BODY | Malformed body. Send a JSON object with exactly one of day or input. |
| 401 | MISSING_AUTH / INVALID_API_KEY | Key absent, wrong, or replaced. Make a new one in Callsheet Pro under Settings, API key, and update the tool. |
| 404 | NOT_FOUND | The only route is POST /v1/days. |
| 405 | METHOD_NOT_ALLOWED | Send a POST. |
| 413 | PAYLOAD_TOO_LARGE | Body over 1 MB or input over 100,000 characters. Send one day per request. |
| 422 | INVALID_DAY | Day rejected; the issues array lists exactly what to fix. |
| 422 | PARSE_FAILED | Loose mode could not produce a usable day. Check the optional issues array, improve the input, or use structured mode. |
| 422 | RESPONSE_TRUNCATED | The call sheet was too long to finish parsing in one response. Split it into one day per request; no credit was charged. |
| 422 | FILE_TOO_LARGE | Input too large for the parser. Send less text, or split it per day; no credit was charged. |
| 422 | PDF_PASSWORD_PROTECTED | The PDF is password protected. Remove the password first; no credit was charged. |
| 422 | IMAGE_TOO_LARGE | The image is larger than the parser accepts. Downscale it and retry; no credit was charged. |
| 422 | PDF_NOT_VALID | Not a readable PDF. Re-export it and retry; no credit was charged. |
| 422 | PDF_TOO_MANY_PAGES | The PDF has more pages than the parser accepts. Send the call sheet pages only; no credit was charged. |
| 429 | RATE_LIMITED | Too fast. Send requests one after another and retry after a short wait. |
| 429 | DAILY_LIMIT_REACHED | 500 requests today used. Retry after midnight UTC. |
| 429 | PARSE_LIMIT_REACHED | Monthly upload allowance used (loose mode). Switch to structured mode, which is free, or wait for the monthly reset. |
| 503 | UPSTREAM_BUSY | Parser busy (loose mode only). Retry in a minute; no credit was charged. |
| 500 | INTERNAL | Our fault. Retry once; if it persists, email support@earlystudios.nl. |
Limits
- 500 requests per account per day, counted in UTC.
- One request at a time: leave at least 300 ms between calls, and never send in parallel.
- 1 MB per request, and
inputat most 100,000 characters. - Loose mode: one call per 15 seconds, one upload credit per call from the account's monthly allowance (3 on Free, 100 on Pro). Structured mode spends no credits.
- One key per account. Making a new one replaces the old one.
Your key: safety and replacing it
- Single-account backend: store the key in a server-side secret or environment variable.
- Multi-user product: collect and encrypt one key per Callsheet Pro user in your backend, then use that user's key for their exports.
- User-owned desktop or mobile app: use the operating system's secure credential storage.
- Never log, commit, or embed a key in a public website bundle. Treat it like a password: anyone holding it can write call sheet days into that account.
Lost or leaked? In Callsheet Pro, Settings, API key, tap Make a new key. The old key stops working the same second; give the new one to the tools that need it. We store only a hash of your key, so nobody, including us, can read it back: that is why it is shown once.
Deleting your account deletes the key with it.
Prompt for coding tools
Building the integration with a coding tool (Claude Code, Codex, Cursor, or similar)? Paste this prompt and let it work.
Add an "Export to Callsheet Pro" feature to this project.
API: POST https://us-central1-callsheet-pro.cloudfunctions.net/api/v1/days
Auth: header "Authorization: Bearer <key>". The key starts with csp_. For a
single-account server integration, load it from an environment variable (for
example CALLSHEET_PRO_API_KEY). For a multi-user product, collect one key per
Callsheet Pro user and store it encrypted in your backend. Never hardcode,
log, commit, or ship keys in a public frontend bundle. Every Callsheet Pro
user makes their own key in the app: Settings, API key, Create API key.
Body: JSON object { "externalId"?: string, "day": object }.
- externalId: strongly recommended. Use this project's stable id for the
call sheet (max 128 chars).
Re-sending the same externalId updates the day in the app instead of
duplicating it, so exports are safe to repeat after every change.
- day: the call sheet as JSON. Required: productionTitle, and dateISO
("YYYY-MM-DD") or date (free text). Optional fields: seriesName,
episodeInfo, dayNumber, country, generalCallTime, wrapTime, weather,
sunrise, sunset, warnings (string[]),
locations [{name, address, callTime, parking, contactName, contactPhone}],
crew [{role, name, phone, email, callTime, callLocation, team}],
schedule [{time, endTime, description, teams (string[]), sceneNumber}],
scenes [{number, description, cast (string[]), scheduledTime, locationName}],
transport [{kind, label, driver, driverPhone, passengers, time, from, to, ref}],
cast [{role, name, phone, pickupTime, makeupTime, wardrobeTime, callTime,
onSetTime, note}], emergency {hospitalName, hospitalAddress, hospitalPhone,
firstAid, counselor}, invoiceEmail, invoiceCompany, invoiceDetails,
projectNumber.
Every scene.locationName should exactly match a locations[].name.
Always send the FULL day on every export: omitted lists are cleared.
Alternative when structured mapping is not feasible: send
{ "externalId": string, "input": "<the call sheet as plain text>" } instead of
"day". That costs the user 1 upload credit per call and allows one call per
15 seconds, so prefer "day".
Responses: 201 created / 200 updated, body { result, dayId, warnings }.
Surface warnings to the user. API-handler errors are
{ error: { code, message, issues } }; the message says how to fix. Handle:
401 invalid key (tell the user to make a new key in Callsheet Pro under
Settings, API key, and paste it again), 422 INVALID_DAY (fix the listed
issues), 429 (back off and retry sequentially, never parallel), 503 (retry
once after a minute).
If this project has a user interface, use the official "Export to Callsheet
Pro" button for the export action: ready-made HTML snippet and image assets
at https://developer.callsheetpro.app/#button
Verify the integration by sending a minimal day and confirming a 201 created
or 200 updated response. Then tell the user to open the Callsheet Pro app:
the day appears there automatically within seconds, grouped under its
production title.
Full docs: https://developer.callsheetpro.app (plain text:
https://developer.callsheetpro.app/developers.md)
The official button
Put this in your tool's interface on the action that sends a sheet to Callsheet Pro. Use it as-is; do not change the colors or the wording.
Downloads: dark SVG / PNG / PNG 2x, yellow SVG / PNG / PNG 2x.
Or render it natively with plain HTML (no image, no font files needed):
<!-- Export to Callsheet Pro button (dark). Attach your export handler. -->
<button type="button" style="display:inline-flex; align-items:center; gap:8px;
height:44px; padding:0 20px; background:#000; border:1px solid #46484d;
border-radius:12px; cursor:pointer;
font-family:Inter, system-ui, -apple-system, 'Segoe UI', sans-serif;">
<span style="font-size:14px; color:#aaabb0;">Export to</span>
<span style="font-size:15px; font-weight:800; letter-spacing:-0.02em;
color:#f6f6fc;">CALLSHEET<span style="color:#facc15;">PRO</span></span>
</button>
<!-- Yellow variant: swap the three style blocks for -->
<!-- button: background:#facc15; border:none; -->
<!-- "Export to" span: color:rgba(0,0,0,0.78); -->
<!-- wordmark span: color:#000; (and drop the inner PRO span) -->
