Skip to content

Firebase Cloud Functions API

Backend endpoints for AI processing, video chat, and cloud operations.


Overview

On Book Pro uses Firebase Cloud Functions (2nd gen, Node 24) for server-side operations that require:

  • Heavy computation (AI script analysis, SVG generation)
  • Secure API keys (Daily.co, Postmark, Vertex AI)
  • Admin privileges (Firestore writes)
  • Event-driven processing (Storage triggers, Firestore triggers)

Architecture

functions/src/index.ts serves as a thin re-export hub (~67 lines). All business logic lives in domain-specific modules:

functions/src/
├── index.ts                      # Re-export hub + initializeApp()
├── video-chat.ts                 # Daily.co room/token management
├── svg-generation.ts             # Gemini-powered SVG asset generation
├── cache-utils.ts                # Vertex AI context caching + retry logic
├── json-utils.ts                 # JSON repair + concurrency utilities
├── script-import/
│   ├── types.ts                  # Shared types, constants, Document AI client
│   ├── storage.ts                # GCS persistence helpers
│   ├── ocr-parser.ts             # Document AI shard → page parsing
│   ├── process-ocr.ts            # startOcrJob, processOcrResult, repairScriptImport
├── debug/
│   ├── test-endpoints.ts         # triggerScriptAnalysis, testVertexAI
│   └── test-rate-limits.ts       # Rate limit testing utility
├── email/                        # 14 email Cloud Functions (see Email API)
├── activity-log-triggers.ts      # Firestore onWrite triggers for activity
├── auto-link-invitation.ts       # Auto-link invitation on auth
├── attendance-sign-in.ts         # recordAttendanceSignIn callable
├── calendar-feed.ts              # iCal feed generation
└── cleanup-trashed-files.ts      # Scheduled file cleanup

Deployment

bash
cd functions
npm run build    # Compiles TS + copies prompts to lib/
firebase deploy --only functions

Functions deploy to: us-central1


Script Import Pipeline

The script import pipeline converts PDF scripts into structured data via Document AI (OCR) and Gemini 2.5 Pro (semantic extraction). For detailed architecture, see Script Ingestion Pipeline.

startOcrJob

Starts Document AI batch processing when a PDF is uploaded with script import metadata.

Type: onObjectFinalized (Storage trigger) Source: functions/src/script-import/process-ocr.ts

Configuration:

  • region: us-central1

Trigger Condition: File in projects/{projectId}/files/ with metadata isScriptImport: 'true' and .pdf extension.

Flow:

  1. Validates project exists and uploadedBy metadata is present
  2. Starts Document AI LRO (Long Running Operation)
  3. Creates script_jobs document with status processing

processOcrResult

Processes OCR shards with Gemini when Document AI writes output to GCS.

Type: onObjectFinalized (Storage trigger) Source: functions/src/script-import/process-ocr.ts

Configuration:

  • region: us-central1
  • timeoutSeconds: 540 (9 minutes)
  • memory: 2GiB

Trigger Condition: JSON files written to ocr-results/{projectId}/. Only the coordinator shard (suffix -0.json) proceeds.

Flow:

  1. Polls Document AI operation status until complete (up to 5 min)
  2. Discovers and downloads all shards with retry for GCS eventual consistency
  3. Creates Vertex AI context cache with full OCR text
  4. Phase 1: Extracts script blocks in parallel 5-page batches (concurrency limit 3)
  5. Phase 2: Extracts structure, props, and sound cues sequentially
  6. Saves all results to GCS as JSON artifacts
  7. Updates script_jobs status to completed or partial
  8. Cleans up context cache

Progress Reporting: The pipeline writes granular milestones to the script_jobs Firestore doc at each stage (OCR complete, batch N/M, extracting structure, saving results), enabling real-time UI feedback.


repairScriptImport

Re-runs only the failed batches from a partial import.

Type: onCallSource: functions/src/script-import/process-ocr.ts

Configuration:

  • region: us-central1
  • timeoutSeconds: 540
  • memory: 2GiB

Request:

typescript
{
  projectId: string;
  jobId: string;
}

Response:

typescript
{
  success: boolean;
  repairedBatches: number;
  stillFailed: number;
  totalBlocks: number;
}

Error Codes:

  • unauthenticated — User not signed in
  • not-found — Job not found
  • permission-denied — User doesn't own this job
  • failed-precondition — No failed batches or job not in partial status

SVG Generation

generateSvgAsset

Generates a top-down architectural SVG asset for the Set Builder using AI. Includes a global Firestore cache to avoid redundant generations.

Type: onCallSource: functions/src/svg-generation.ts

Configuration:

  • timeoutSeconds: 60
  • memory: 512MiB

Request:

typescript
{
  prompt: string;   // e.g., "blue victorian sofa"
}

Response:

typescript
{
  id: string;
  prompt: string;
  svgCode: string;     // SVG markup
  createdAt: string;
  source: 'cache' | 'generated';
}

Example:

typescript
const generateAsset = httpsCallable(functions, 'generateSvgAsset');
const result = await generateAsset({ 
  prompt: 'blue victorian sofa'
});

// result.data.svgCode contains the SVG code
// result.data.source indicates if it came from cache

Video Chat

createDailyRoom

Creates a Daily.co video room for a project.

Type: onCallSource: functions/src/video-chat.tsSecret: DAILY_API_KEY

Request:

typescript
{
  projectId: string;
}

Response:

typescript
{
  roomUrl: string;
  roomName: string;
  expiresAt: string;  // ISO timestamp (2 hours from creation)
}

Permissions: Requires owner, stage_manager, or production_manager role.


getDailyToken

Generates a meeting token for a participant to join a Daily room.

Type: onCallSource: functions/src/video-chat.tsSecret: DAILY_API_KEY

Request:

typescript
{
  roomName: string;
  userName?: string;
}

Response:

typescript
{
  token: string;
}

Email Distribution Functions

For complete documentation of all email Cloud Functions (8 manual + 6 automated), see the dedicated Email API reference.

Quick Summary:

  • 8 Manual Functions (onCall): sendCallSheet, sendRehearsalReport, sendLineNotes, sendDeadlinePing, sendMeetingInvites, sendProjectInvite, sendCustomAnnouncement, sendBatchInvitations
  • 6 Automated Functions (Firestore/CRON triggers): notifyWelcome, notifyScheduleChange, notifyDeadlineApproaching, notifyProfileReminder, notifyMentionPost, notifyMentionComment
  • Shared module: functions/src/email/postmark-client.ts
  • Secret: POSTMARK_SERVER_TOKEN

Attendance

recordAttendanceSignIn

Records an actor's attendance sign-in for a scheduled event. Validates the sign-in token server-side, determines present/late status based on a 30-minute window, and writes to the Firestore attendance subcollection using the Admin SDK.

Type: onCallSource: functions/src/attendance-sign-in.ts

Request:

typescript
{
  projectId: string;
  token: string;       // Sign-in token from QR code / link
  actorId: string;     // ID of the actor signing in
}

Response:

typescript
{
  status: 'present' | 'late';   // Determined by 30-min window
  signedInAt: string;            // ISO timestamp
}

Error Codes:

  • invalid-argument — Missing required fields
  • not-found — Invalid token or event not found
  • failed-precondition — Sign-in window expired
  • already-exists — Actor already signed in for this event

Notes: This function does not require Firebase Auth — it validates via the sign-in token, allowing unauthenticated actors to sign in via QR code link. The function uses the Admin SDK to bypass Firestore security rules.


Utility Functions

File Cleanup

FunctionTypeDescription
cleanupOldTrashedFilesonScheduleScheduled cleanup of trashed files
manualCleanupTrashedFilesonCallManual trigger for file cleanup

Source: functions/src/cleanup-trashed-files.ts

Calendar

FunctionTypeDescription
getCalendarFeedonRequestGenerates iCal feed for a project

Source: functions/src/calendar-feed.ts

Activity Log Triggers

FunctionTypeDescription
logPropActivityonDocumentWrittenTracks prop changes
logCostumeActivityonDocumentWrittenTracks costume changes
logBlockingActivityonDocumentWrittenTracks blocking changes
logScheduleActivityonDocumentWrittenTracks schedule changes
logActActivityonDocumentWrittenTracks act changes
logSceneActivityonDocumentWrittenTracks scene changes
logCharacterActivityonDocumentWrittenTracks character changes

Source: functions/src/activity-log-triggers.ts

Invitations

FunctionTypeDescription
autoLinkInvitationonDocumentCreatedAuto-links invitation when user signs up

Source: functions/src/auto-link-invitation.ts

Debug / Test

FunctionTypeDescription
triggerScriptAnalysisonRequestRe-triggers script analysis for an existing file
testVertexAIonRequestTests Vertex AI connectivity

Source: functions/src/debug/test-endpoints.ts


Testing Locally

Emulator Setup

bash
# Install Firebase emulators
firebase init emulators

# Start emulators
firebase emulators:start

Connect Client to Emulator

typescript
// src/config/firebase.ts
if (import.meta.env.DEV) {
  connectFunctionsEmulator(functions, 'localhost', 5001);
}

Authentication

All onCall functions require Firebase Auth:

typescript
import { onCall, HttpsError } from 'firebase-functions/v2/https';

export const myFunction = onCall(async (request) => {
  // Check authentication
  if (!request.auth) {
    throw new HttpsError('unauthenticated', 'User must be signed in');
  }
  
  const userId = request.auth.uid;
  // ... function logic
});

Security Best Practices

1. Validate Input

typescript
if (!request.data.projectId || typeof request.data.projectId !== 'string') {
  throw new HttpsError('invalid-argument', 'projectId is required');
}

2. Check Permissions

typescript
// Verify user has project access
const memberDoc = await db
  .collection('projects').doc(projectId)
  .collection('members').doc(request.auth.uid)
  .get();

if (!memberDoc.exists) {
  throw new HttpsError('permission-denied', 'User is not a project member');
}

Monitoring

Cloud Functions Logs

bash
# View all logs
firebase functions:log

# Filter by function
firebase functions:log --only processOcrResult
firebase functions:log --only generateSvgAsset

Error Reporting

typescript
import * as logger from 'firebase-functions/logger';

export const myFunction = onCall(async (request) => {
  try {
    // ... logic
  } catch (error) {
    logger.error('Function failed', { error, userId: request.auth?.uid });
    throw new HttpsError('internal', 'An error occurred');
  }
});

Further Reading


Last updated: February 12, 2026 (Added recordAttendanceSignIn Cloud Function)