Skip to content

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-RouteDescription
/templatesNotification template CRUD
/rulesAutomation rule CRUD
/executionsExecution history
/scheduled-jobsScheduled 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:

ParameterTypeRequiredDescription
podcast_idUUIDYesPodcast to list templates for
categorystringNoFilter by category
template_type'email' | 'show_notes'NoFilter by type
target_section'shared' | 'host_private' | 'guest'NoFilter show-notes templates by section
searchstringNoSubstring match on name or subject
limitnumberNo (default 50, max 100)Page size
offsetnumberNo (default 0)Page offset

Response: See API Reference - Templates

Create Template

POST /api/automations/templates

Request Body:

FieldTypeRequiredDescription
podcast_idUUIDYesOwning podcast
namestring (1–100)Yes
descriptionstring (≤500)No
subjectstring (1–200)YesTitle for show-notes; subject line for email
body_htmlstring (1–50000)YesMagic tags rendered at apply time
body_textstring (≤50000)NoAuto-generated from body_html if omitted
categorystring (≤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_defaultbooleanNoShow-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/:id

Body — same fields as create (all optional), with two extra rules:

  • target_section is immutable for show-notes templates. A change attempt returns 400 rather 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_default is tri-state-aware:
    • undefined → don't touch the podcast default
    • true → set this template as the section default
    • false → clear the section default iff this template is currently it (so a concurrent edit doesn't get stomped)

Delete Template

DELETE /api/automations/templates/:id

Rejects 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/rules

Update Rule

PUT /api/automations/rules/:id

Delete Rule

DELETE /api/automations/rules/:id

Toggle Rule

Enable or disable a rule:

POST /api/automations/rules/:id/toggle

Manual Trigger

Manually trigger a rule for testing:

POST /api/automations/rules/:id/trigger

Request Body:

json
{
	"episode_id": "uuid",
	"booking_id": "uuid",
	"guest_id": "uuid",
	"context": {
		"custom_field": "value"
	}
}

Response:

json
{
	"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/:id

Scheduled Jobs Endpoints

List Scheduled Jobs

GET /api/automations/scheduled-jobs?podcast_id={id}

Query Parameters:

ParameterTypeRequiredDescription
podcast_idUUIDYesPodcast to list jobs for
statusstringNoFilter by status
from_dateISO 8601NoJobs scheduled after this date
to_dateISO 8601NoJobs scheduled before this date

Response:

json
{
	"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/:id

Response:

json
{
	"success": true
}

TypeScript Client Usage

typescript
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}` } }
);

Internal documentation - Not for public distribution