Automations API
Full API reference for the automation engine. For architecture and concepts, see Automation Engine Documentation.
Base Path: /api/automations
Authentication: All endpoints require Bearer token.
Sub-Routes
The automations API is organized into sub-routes:
| Sub-Route | Description |
|---|---|
/templates | Notification template CRUD |
/rules | Automation rule CRUD |
/executions | Execution history |
/scheduled-jobs | Scheduled job management |
Templates Endpoints
The notification_templates table backs both email automation templates and show-notes templates that auto-fill new episodes. The shape is shared; show-notes templates carry a target_section (shared / host_private / guest) and may be marked as the podcast default for that section.
List Templates
GET /api/automations/templates?podcast_id={id}Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
podcast_id | UUID | Yes | Podcast to list templates for |
category | string | No | Filter by category |
template_type | 'email' | 'show_notes' | No | Filter by type |
target_section | 'shared' | 'host_private' | 'guest' | No | Filter show-notes templates by section |
search | string | No | Substring match on name or subject |
limit | number | No (default 50, max 100) | Page size |
offset | number | No (default 0) | Page offset |
Response: See API Reference - Templates
Create Template
POST /api/automations/templatesRequest Body:
| Field | Type | Required | Description |
|---|---|---|---|
podcast_id | UUID | Yes | Owning podcast |
name | string (1–100) | Yes | |
description | string (≤500) | No | |
subject | string (1–200) | Yes | Title for show-notes; subject line for email |
body_html | string (1–50000) | Yes | Magic tags rendered at apply time |
body_text | string (≤50000) | No | Auto-generated from body_html if omitted |
category | string (≤50) | No | |
template_type | 'email' | 'show_notes' | No (default 'email') | |
target_section | 'shared' | 'host_private' | 'guest' | Iff template_type='show_notes' | Rejected for email templates |
set_as_default | boolean | No | Show-notes only. Persists the new template id to podcasts.default_<target_section>_template_id after creation. The DB trigger validates section + podcast match |
The response includes a non-fatal set_as_default_error field if the default-write step failed (e.g. concurrent template change).
Update Template
PUT /api/automations/templates/:idBody — same fields as create (all optional), with two extra rules:
target_sectionis immutable for show-notes templates. A change attempt returns400rather than letting the DB CHECK constraint fail later. Allowing it would orphan dependent FKs (podcasts.default_<section>_template_id,booking_links.show_notes_<section>_template_id,show_notes.created_from_guest_template_id).set_as_defaultis tri-state-aware:undefined→ don't touch the podcast defaulttrue→ set this template as the section defaultfalse→ clear the section default iff this template is currently it (so a concurrent edit doesn't get stomped)
Delete Template
DELETE /api/automations/templates/:idRejects with 409 Conflict if any automation_actions.action_config references the template id. (Show-notes references via podcasts.default_<section>_template_id, booking_links.show_notes_<section>_template_id, and show_notes.created_from_guest_template_id are FKs with ON DELETE SET NULL, so they don't block deletion — they just clear.)
For the runtime semantics of show-notes templates (resolution precedence, the guest-template snapshot, magic-tag rendering with unresolvedAs: 'literal'), see Show Notes Auto-Create & Per-Section Templates.
Rules Endpoints
List Rules
GET /api/automations/rules?podcast_id={id}Create Rule
POST /api/automations/rulesUpdate Rule
PUT /api/automations/rules/:idDelete Rule
DELETE /api/automations/rules/:idToggle Rule
Enable or disable a rule:
POST /api/automations/rules/:id/toggleManual Trigger
Manually trigger a rule for testing:
POST /api/automations/rules/:id/triggerRequest Body:
{
"episode_id": "uuid",
"booking_id": "uuid",
"guest_id": "uuid",
"context": {
"custom_field": "value"
}
}Response:
{
"execution": {
"id": "uuid",
"status": "pending",
"created_at": "2025-01-07T10:00:00Z"
}
}Executions Endpoints
List Executions
GET /api/automations/executions?podcast_id={id}Get Execution
GET /api/automations/executions/:idScheduled Jobs Endpoints
List Scheduled Jobs
GET /api/automations/scheduled-jobs?podcast_id={id}Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
podcast_id | UUID | Yes | Podcast to list jobs for |
status | string | No | Filter by status |
from_date | ISO 8601 | No | Jobs scheduled after this date |
to_date | ISO 8601 | No | Jobs scheduled before this date |
Response:
{
"jobs": [
{
"id": "uuid",
"rule_id": "uuid",
"podcast_id": "uuid",
"episode_id": "uuid",
"booking_id": "uuid",
"scheduled_for": "2025-01-14T09:00:00Z",
"status": "pending",
"trigger_type": "time.before_recording",
"trigger_config": {
"offset_minutes": 1440
},
"created_at": "2025-01-07T10:00:00Z",
"automation_rules": {
"name": "24h Recording Reminder"
}
}
],
"total": 5
}Cancel Scheduled Job
DELETE /api/automations/scheduled-jobs/:idResponse:
{
"success": true
}TypeScript Client Usage
import { createApiClient } from '$api/client';
const client = createApiClient(fetch);
// List templates
const templatesRes = await client.api.automations.templates.$get(
{ query: { podcast_id: 'uuid' } },
{ headers: { Authorization: `Bearer ${token}` } }
);
// Create rule
const ruleRes = await client.api.automations.rules.$post(
{
json: {
podcast_id: 'uuid',
name: 'Booking Reminder',
trigger_type: 'booking.confirmed',
workflow_data: { nodes: [], connections: [] }
}
},
{
headers: { Authorization: `Bearer ${token}` }
}
);
// Toggle rule
const toggleRes = await client.api.automations.rules[':id'].toggle.$post(
{ param: { id: 'rule-uuid' } },
{ headers: { Authorization: `Bearer ${token}` } }
);
// Manual trigger
const triggerRes = await client.api.automations.rules[':id'].trigger.$post(
{
param: { id: 'rule-uuid' },
json: { episode_id: 'uuid' }
},
{
headers: { Authorization: `Bearer ${token}` }
}
);
// List scheduled jobs
const jobsRes = await client.api.automations['scheduled-jobs'].$get(
{ query: { podcast_id: 'uuid', status: 'pending' } },
{ headers: { Authorization: `Bearer ${token}` } }
);