API reference
Deployment
1. Deploy as Web App
- Open Google Apps Script editor (Extensions > Apps Script)
- Click Deploy > New deployment
- Select Web app
- Configure:
- Execute as: Me (your account)
- Who has access: Anyone
- Click Deploy
- Copy the Web app URL (looks like
https://script.google.com/macros/s/XXXXX/exec)
2. Configure API Security (Recommended)
IMPORTANT: For production use, configure API authentication:
- In Apps Script editor, go to Project Settings (gear icon)
- Scroll down to Script Properties
- Click Add script property
- Add:
- Property:
API_SECRET - Value: A strong random string (e.g.,
sk_live_abc123xyz789...)
- Property:
- Click Save
Once configured, all API requests (except ?action=status) require authentication.
3. Test the deployment
curl "https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/exec?action=status"Expected response:
{ "success": true, "system": "Worship Service Production System", "version": "1.0.0", "timestamp": "2025-01-15T10:30:00.000Z", "endpoints": { "POST": ["tc_in", "tc_out", "status_update", ...], "GET": ["status", "posts", "schedule", ...] }}API Endpoints
GET Endpoints
All GET requests use query parameters.
Authentication: Add api_key=YOUR_SECRET to query params (except for status).
| Action | Description | Parameters | Auth Required |
|---|---|---|---|
status | System status and available endpoints | - | No |
posts | Get all posts for a program | program (1-4) | Yes |
schedule | Get recording schedule | day (day1/day2/day3, optional) | Yes |
post | Get specific post | post_id | Yes |
current | Get currently recording post | - | Yes |
clip_counter | Get next clip number | - | Yes |
Examples:
# Get system status (no auth required)curl "https://YOUR_URL/exec?action=status"
# Get all posts for Program 1 (with auth)curl "https://YOUR_URL/exec?action=posts&program=1&api_key=YOUR_SECRET"
# Get schedule for Day 1 (with auth)curl "https://YOUR_URL/exec?action=schedule&day=day1&api_key=YOUR_SECRET"
# Get specific post (with auth)curl "https://YOUR_URL/exec?action=post&post_id=P1:5&api_key=YOUR_SECRET"
# Get currently recording post (with auth)curl "https://YOUR_URL/exec?action=current&api_key=YOUR_SECRET"POST Endpoints
All POST requests use JSON body with action field.
Authentication: Include api_key in the JSON body.
| Action | Description | Required Fields |
|---|---|---|
tc_in | Log timecode IN | post_id, tc_in (optional) |
tc_out | Log timecode OUT | post_id, tc_out (optional) |
set_recording | Set post as recording | post_id |
mark_recorded | Mark post as recorded | post_id |
mark_approved | Mark post as approved | post_id |
status_update | Update post status | post_id, status |
get_posts | Get posts for program | program_nr |
get_next | Get next post to record | program_nr, recording_day (optional) |
get_schedule | Get full schedule | recording_day (optional) |
increment_clip | Increment clip counter | - |
Examples:
# Log TC-IN for a post (with auth)curl -X POST "https://YOUR_URL/exec" \ -H "Content-Type: application/json" \ -d '{"action": "tc_in", "post_id": "P1:5", "tc_in": "01:23:45:00", "api_key": "YOUR_SECRET"}'
# Mark post as recorded (with auth)curl -X POST "https://YOUR_URL/exec" \ -H "Content-Type: application/json" \ -d '{"action": "mark_recorded", "post_id": "P1:5", "api_key": "YOUR_SECRET"}'
# Get next post to record (with auth)curl -X POST "https://YOUR_URL/exec" \ -H "Content-Type: application/json" \ -d '{"action": "get_next", "program_nr": 1, "recording_day": "day1", "api_key": "YOUR_SECRET"}'Bitfocus Companion Integration
Setup
- Install Generic HTTP module in Companion
- Create a new connection with your Web App URL
Button Examples
Button: “TC IN” (Start recording)
Action: HTTP POST Request
URL: https://script.google.com/macros/s/YOUR_ID/execMethod: POSTContent-Type: application/jsonBody:{ "action": "tc_in", "post_id": "$(internal:custom_current_post)", "tc_in": "$(vmix:timecode)", "operator": "Companion", "api_key": "YOUR_SECRET"}Button: “TC OUT” (Stop recording)
Action: HTTP POST Request
URL: https://script.google.com/macros/s/YOUR_ID/execMethod: POSTContent-Type: application/jsonBody:{ "action": "tc_out", "post_id": "$(internal:custom_current_post)", "tc_out": "$(vmix:timecode)", "api_key": "YOUR_SECRET"}Button: “Mark Recorded”
{ "action": "mark_recorded", "post_id": "$(internal:custom_current_post)", "api_key": "YOUR_SECRET"}Button: “Mark Approved”
{ "action": "mark_approved", "post_id": "$(internal:custom_current_post)", "api_key": "YOUR_SECRET"}Button: “Next Post”
{ "action": "get_next", "program_nr": 1, "recording_day": "day1", "api_key": "YOUR_SECRET"}Companion Variables
Set up custom variables in Companion:
current_post: Currently selected post ID (e.g., “P1:5”)current_program: Current program number (1-4)recording_day: Current recording day (day1/day2/day3)api_key: Your API secret (store securely!)
vMix Integration
Using vMix Scripting
' VB.NET script for vMixDim url As String = "https://script.google.com/macros/s/YOUR_ID/exec"Dim postId As String = "P1:5"
' TC-INDim json As String = "{""action"":""tc_in"",""post_id"":""" & postId & """,""tc_in"":""" & API.GetTimecode() & """}"API.HTTPPost(url, json, "application/json")
' TC-OUTDim jsonOut As String = "{""action"":""tc_out"",""post_id"":""" & postId & """,""tc_out"":""" & API.GetTimecode() & """}"API.HTTPPost(url, jsonOut, "application/json")BMD HyperDeck Integration
With Companion
Use Companion’s HyperDeck module to:
- Get current timecode from HyperDeck
- Send to Google Sheets API via HTTP module
Trigger on Record Start:
{ "action": "tc_in", "post_id": "$(internal:custom_current_post)", "tc_in": "$(hyperdeck:timecode)", "clip_nr": "$(hyperdeck:clip_id)"}Trigger on Record Stop:
{ "action": "tc_out", "post_id": "$(internal:custom_current_post)", "tc_out": "$(hyperdeck:timecode)", "clip_nr": "$(hyperdeck:clip_id)"}Response Format
All responses are JSON with this structure:
Success Response
{ "success": true, "message": "Operation completed", "post_id": "P1:5", "status": "recorded", ...additional fields...}Error Response
{ "success": false, "error": "Error description", "valid_statuses": ["scheduled", "recording", "recorded", "approved"]}Status Values
| Key | Display Name | Colour | Description |
|---|---|---|---|
scheduled | Scheduled | White | Not yet recorded |
recording | Recording | Yellow | Currently recording |
recorded | Recorded | Light Green | Recorded, needs approval |
approved | Approved | Dark Green | Approved and done |
Post Object Structure
{ "post_id": "P1:5", "program_nr": 1, "sort_order": 50, "type": "sermon", "title": "Sermon on Hope", "duration_sec": 420, "duration_formatted": "00:07:00", "people_ids": "PXYZ123,PABC456", "location": "pulpit", "recording_day": "day1", "recording_time": "", "status": "scheduled", "notes": "Additional lighting required"}Workflow Example
Typical Recording Session
-
Before session: Get schedule
Terminal window curl "https://URL/exec?action=schedule&day=day1" -
Select first post: Get next
{ "action": "get_next", "recording_day": "day1" } -
Start recording: TC-IN
{ "action": "tc_in", "post_id": "P1:1", "tc_in": "01:00:00:00" } -
Stop recording: TC-OUT
{ "action": "tc_out", "post_id": "P1:1", "tc_out": "01:07:30:00" } -
Review and approve:
{ "action": "mark_approved", "post_id": "P1:1" } -
Repeat for next post
Rate Limits
API Rate Limiting (Built-in)
The API has built-in rate limiting:
- 60 requests per minute per client
- Identified by
client_idparameter (optional) - Returns
429 Too Many Requestsequivalent when exceeded
{ "success": false, "error": "Rate limit exceeded. Maximum 60 requests per minute.", "retry_after_seconds": 60}Google Apps Script Limits
Google Apps Script has execution limits:
- Trigger executions: 90 min/day (consumer), 6 hr/day (Workspace)
- URL Fetch calls: 20,000/day
- Script runtime: 6 minutes max per execution
For high-volume use, consider batching requests or using Google Cloud Functions.
Troubleshooting
”API key required” error
You need to include your API key in requests. Either:
- Add
api_key=YOUR_SECRETto query parameters (GET) - Add
"api_key": "YOUR_SECRET"to JSON body (POST)
If you haven’t set up API_SECRET, the API will be unprotected (not recommended for production).
”Invalid API key” error
The provided API key doesn’t match the one configured in Script Properties. Double-check your API_SECRET value.
”Rate limit exceeded” error
You’ve exceeded 60 requests per minute. Wait 60 seconds and try again. For high-frequency use, batch requests or use client_id parameter to identify different clients.
”API not enabled” error
The old API_CONFIG.ENABLED check has been removed. If you see this, you may have an old version of the code.
CORS errors
Deploy the web app with “Anyone” access. Google Apps Script handles CORS automatically.
Timeout errors
Google Apps Script has a 30-second timeout for web requests. Keep payloads small.
Post not found
Ensure post_id format is correct: P{program}:{number} (e.g., “P1:5”, “P2:10”)
Support
- Issues: https://github.com/FiLORUX/the-public-service/issues
- Documentation: See README.md and DEPLOYMENT.md