# Export to Callsheet Pro: Developer API

Push call sheets from any tool straight into a Callsheet Pro account. No PDF, no import step: send the data and the day appears in the app on the account owner's phone within seconds.

You don't have to read this page. The prompt below does the work for you.

## Quick start: paste one prompt

No coding knowledge needed. Three steps:

1. Copy the prompt block below.
2. Paste it into the AI tool that builds your software (Claude Code, Codex, Cursor, or similar) and let it work.
3. When it asks for your API key: open the Callsheet Pro app, go to Settings, then API Access, and copy the key. It's a Pro feature.

That's it. Your tool gets an Export to Callsheet Pro action, and sheets you export land straight in the app.

```
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. The user finds their
key in the Callsheet Pro app under Settings, API Access (Pro feature).

Body: JSON object { "externalId"?: string, "day": object }.
- externalId: strongly recommended. Use this project's stable id for the
  callsheet (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 callsheet 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 callsheet as plain text>" } instead of
"day". That costs the user 1 AI parse 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
re-copy it from Settings), 403 PRO_REQUIRED (subscription lapsed), 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://share.callsheetpro.app/developers#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://share.callsheetpro.app/developers
```

## Full API reference

Everything from here down is reference for engineers, and for curious AI agents, who want to build or check the integration by hand. If you pasted the prompt above, you're done.

## Get your API key

1. Open the Callsheet Pro app (iOS, Android, or app.callsheetpro.app).
2. Go to Settings, then API Access. This is a Pro feature.
3. Copy your personal key. It starts with `csp_`.

The key writes into your own account only. Rotate it from the same screen if it ever leaks; the old key stops working immediately.

### Store the key safely

- **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 sheets into that account.

## 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 callsheet day. The response tells you whether it was created or updated.

## Structured mode (recommended)

Send the day as JSON in a `day` object. This is instant, exact, and does not cost any AI parse credits.

```json
{
  "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.

The update contract:

- Always send the full day, every time. Fields you omit are cleared. An omitted `crew` list empties the crew section.
- The API owns the callsheet content (title, times, locations, crew, schedule, scenes, transport, cast, and so on). Sending an update overwrites those with what you send.
- The user's personal data in the app always survives updates: private notes, work log, 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 AI turns it into a structured day. Costs the account owner 1 parse credit per call (same as scanning a PDF in the app) and is rate limited to one call per 15 seconds.

```json
{
  "externalId": "your-stable-id-for-this-sheet",
  "input": "CALL SHEET - Night Shift - Tuesday, July 14, 2026\nGeneral call 07:00 ... (any format, max 100,000 characters)"
}
```

Send exactly one of `day` or `input`, never both.

## Field reference (day object)

Top-level fields:

| 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 below. |
| crew | CrewMember[] | See below. |
| schedule | ScheduleItem[] | See below. |
| scenes | Scene[] | See below. |
| transport | TransportRow[] | See below. |
| cast | CastMember[] | See below. |
| emergency | object | `hospitalName`, `hospitalAddress`, `hospitalPhone`, `firstAid`, `counselor`. |
| invoiceEmail / invoiceCompany / invoiceDetails / projectNumber | string | Billing info for crew invoices. |
| gearAndSettings | object | `gear`: string[], `settings`: [{ label, value }], `warning`: string. |

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 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

Success:

```json
{ "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 handler look like:

```json
{ "error": { "code": "INVALID_DAY", "message": "how to fix it", "issues": ["field: problem"], "hint": "docs url" } }
```

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`. Clients should 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 rotated. Copy the current key from Settings, API Access. |
| 403 | PRO_REQUIRED | The account's Pro subscription lapsed. Renew, then retry. |
| 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. |
| 429 | RATE_LIMITED | Too fast. Send requests sequentially and retry after a short wait. |
| 429 | DAILY_LIMIT_REACHED | 500 requests per day used. Retry after midnight UTC. |
| 429 | PARSE_LIMIT_REACHED | Monthly AI parse allowance used. Switch to structured mode (free) or wait for the monthly reset. |
| 503 | UPSTREAM_BUSY | AI parser busy (loose mode only). Retry in a minute; no credit was charged. |

## curl example

```bash
curl -X POST "https://us-central1-callsheet-pro.cloudfunctions.net/api/v1/days" \
  -H "Authorization: Bearer $CALLSHEET_PRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "sheet-2026-07-14",
    "day": {
      "productionTitle": "Night Shift",
      "dateISO": "2026-07-14",
      "generalCallTime": "07:00"
    }
  }'
```

## The official button

Put this in your tool's interface on the action that sends a sheet to Callsheet Pro. Use it as-is; don't change the colors or the wording.

![Export to Callsheet Pro button, dark](https://share.callsheetpro.app/assets/export-button-dark@2x.png)
![Export to Callsheet Pro button, yellow](https://share.callsheetpro.app/assets/export-button-yellow@2x.png)

Downloads:

- Dark: [SVG](https://share.callsheetpro.app/assets/export-button-dark.svg), [PNG](https://share.callsheetpro.app/assets/export-button-dark.png), [PNG 2x](https://share.callsheetpro.app/assets/export-button-dark@2x.png)
- Yellow: [SVG](https://share.callsheetpro.app/assets/export-button-yellow.svg), [PNG](https://share.callsheetpro.app/assets/export-button-yellow.png), [PNG 2x](https://share.callsheetpro.app/assets/export-button-yellow@2x.png)

Or render it natively with plain HTML (no image, no font files needed):

```html
<!-- 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)  -->
```


Questions or a use case the API does not cover yet? Email support@earlystudios.nl.
