Skip to content

API Reference

All automation endpoints are mounted at /api/automations/* and require authentication via Bearer token.

Base Path: /api/automations

Authentication: All endpoints require Authorization: Bearer <access_token> header.

Authorization: Most endpoints require owner or admin role on the podcast (not member).

Templates Endpoints

List Templates

Retrieves all templates for a podcast.

GET /api/automations/templates?podcast_id={id}

Query Parameters:

ParameterTypeRequiredDescription
podcast_idUUIDYesPodcast ID to filter by
limitnumberNoMax results (default: 100)
offsetnumberNoPagination offset (default: 0)
categorystringNoFilter by category

Response:

json
{
	"templates": [
		{
			"id": "uuid",
			"podcast_id": "uuid",
			"name": "Booking Confirmation",
			"description": "Sent when a booking is confirmed",
			"category": "booking",
			"subject": "Recording Confirmed: {episode_title}",
			"body_html": "<p>Hi {guest_first_name}...</p>",
			"body_text": "Hi {guest_first_name}...",
			"created_at": "2025-01-07T10:00:00Z",
			"updated_at": "2025-01-07T10:00:00Z"
		}
	],
	"total": 5
}

Create Template

Creates a new email template.

POST /api/automations/templates

Request Body:

json
{
	"podcast_id": "uuid",
	"name": "Booking Confirmation",
	"description": "Sent when a booking is confirmed",
	"category": "booking",
	"subject": "Recording Confirmed: {episode_title}",
	"body_html": "<p>Hi {guest_first_name}...</p>",
	"body_text": "Hi {guest_first_name}..."
}

Response: 201 Created

json
{
	"template": {
		/* template object */
	}
}

Get Template

Retrieves a single template by ID.

GET /api/automations/templates/:id

Response:

json
{
	"template": {
		/* template object */
	}
}

Update Template

Updates an existing template.

PUT /api/automations/templates/:id

Request Body: Same as create (all fields optional).

Response:

json
{
	"template": {
		/* updated template object */
	}
}

Delete Template

Deletes a template.

DELETE /api/automations/templates/:id

Response: 200 OK

json
{
	"success": true
}

Preview Template

Renders a template with sample data without saving. This endpoint does not require a template ID - it previews arbitrary content.

POST /api/automations/templates/preview

Request Body:

json
{
	"subject": "Recording Confirmed: {episode_title}",
	"body_html": "<p>Hi {guest_first_name}...</p>",
	"sample_data": {
		"guest_first_name": "John",
		"episode_title": "My Great Episode"
	}
}

Response:

json
{
	"preview": {
		"subject": "Recording Confirmed: My Great Episode",
		"body_html": "<p>Hi John...</p>",
		"body_text": "Hi John..."
	},
	"validation": {
		"is_valid": true,
		"used_tags": ["guest_first_name", "episode_title"],
		"invalid_tags": [],
		"missing_context": [],
		"errors": []
	}
}

Rules Endpoints

List Rules

Retrieves all automation rules for a podcast.

GET /api/automations/rules?podcast_id={id}

Query Parameters:

ParameterTypeRequiredDescription
podcast_idUUIDYesPodcast ID to filter by
is_enabledbooleanNoFilter by enabled status

Response:

json
{
    "rules": [
        {
            "id": "uuid",
            "podcast_id": "uuid",
            "name": "Booking Confirmation Workflow",
            "description": "Send confirmation when booking is confirmed",
            "trigger_type": "booking.confirmed",
            "trigger_config": {},
            "workflow_data": { "nodes": [...], "connections": [...] },
            "is_enabled": true,
            "is_paused": false,
            "created_at": "2025-01-07T10:00:00Z",
            "updated_at": "2025-01-07T10:00:00Z",
            "automation_actions": [
                {
                    "id": "uuid",
                    "action_type": "send_email",
                    "action_config": { "template_id": "uuid" }
                }
            ]
        }
    ],
    "total": 3
}

Create Rule

Creates a new automation rule.

POST /api/automations/rules

Request Body:

json
{
    "podcast_id": "uuid",
    "name": "Booking Confirmation Workflow",
    "description": "Send confirmation when booking is confirmed",
    "trigger_type": "booking.confirmed",
    "trigger_config": {},
    "workflow_data": {
        "nodes": [...],
        "connections": [...]
    },
    "is_enabled": true,
    "actions": [
        {
            "action_type": "send_email",
            "action_config": {
                "template_id": "uuid",
                "to": "guest"
            },
            "execution_order": 0
        }
    ]
}

Response: 201 Created

json
{
	"rule": {
		/* rule object with actions */
	}
}

Get Rule

Retrieves a single rule with its actions.

GET /api/automations/rules/:id

Response:

json
{
	"rule": {
		/* rule object with automation_actions */
	}
}

Update Rule

Updates an existing rule.

PUT /api/automations/rules/:id

Request Body: Same as create (all fields optional).

Response:

json
{
	"rule": {
		/* updated rule object */
	}
}

Delete Rule

Deletes a rule and all its actions/scheduled jobs.

DELETE /api/automations/rules/:id

Response: 200 OK

json
{
	"success": true
}

Toggle Rule

Enables or disables a rule.

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

Request Body (optional):

json
{
	"is_enabled": true
}

If no body provided, toggles the current state.

Response:

json
{
	"rule": {
		/* rule with updated is_enabled */
	}
}

Executions Endpoints

List Executions

Retrieves execution history for a podcast.

GET /api/automations/executions?podcast_id={id}

Query Parameters:

ParameterTypeRequiredDescription
podcast_idUUIDYesPodcast ID to filter by
rule_idUUIDNoFilter by rule
statusstringNoFilter by status
limitnumberNoMax results (default: 50)
offsetnumberNoPagination offset (default: 0)

Response:

json
{
	"executions": [
		{
			"id": "uuid",
			"rule_id": "uuid",
			"podcast_id": "uuid",
			"trigger_type": "booking.confirmed",
			"status": "completed",
			"context_data": {
				"guest_first_name": "John",
				"episode_title": "Great Episode"
			},
			"action_results": [
				{
					"action_type": "send_email",
					"status": "completed",
					"result": { "message_id": "..." }
				}
			],
			"error_message": null,
			"started_at": "2025-01-07T10:00:00Z",
			"completed_at": "2025-01-07T10:00:05Z",
			"created_at": "2025-01-07T10:00:00Z",
			"automation_rules": {
				"name": "Booking Confirmation Workflow"
			}
		}
	],
	"total": 100
}

Get Execution

Retrieves a single execution with full details.

GET /api/automations/executions/:id

Response:

json
{
	"execution": {
		/* execution object */
	}
}

Retry Execution

Retries a failed execution.

POST /api/automations/executions/:id/retry

Response:

json
{
	"execution": {
		/* new execution object with status: pending */
	}
}

Get Execution Stats

Retrieves aggregated execution statistics.

GET /api/automations/executions/stats?podcast_id={id}

Query Parameters:

ParameterTypeRequiredDescription
podcast_idUUIDYesPodcast ID
daysnumberNoDays to look back (default: 30)

Response:

json
{
	"stats": {
		"total": 150,
		"completed": 142,
		"failed": 5,
		"pending": 3,
		"by_trigger": {
			"booking.confirmed": 80,
			"time.before_recording": 50,
			"episode.published": 20
		},
		"by_action": {
			"send_email": 145,
			"send_webhook": 5
		}
	}
}

Error Responses

All endpoints return errors in a consistent format:

json
{
	"error": "Error message description"
}

Common HTTP Status Codes:

CodeMeaning
400Bad Request - Invalid input or missing required fields
401Unauthorized - Missing or invalid auth token
403Forbidden - User lacks permission for this podcast
404Not Found - Resource doesn't exist
409Conflict - Duplicate resource (e.g., idempotency key)
500Internal Server Error - Server-side error

TypeScript Client Usage

Using the Hono RPC client:

typescript
import { createApiClient } from '$api/client';
import { createClient } from '$lib/supabase/client';

// Get auth token
const supabase = createClient();
const {
	data: { session }
} = await supabase.auth.getSession();

// Create client
const client = createApiClient(fetch);

// List templates
const response = await client.api.automations.templates.$get(
	{ query: { podcast_id: 'uuid' } },
	{ headers: { Authorization: `Bearer ${session.access_token}` } }
);

// Create rule
const response = await client.api.automations.rules.$post(
	{
		json: {
			podcast_id: 'uuid',
			name: 'My Automation',
			trigger_type: 'booking.confirmed',
			workflow_data: { nodes: [], connections: [] }
		}
	},
	{ headers: { Authorization: `Bearer ${session.access_token}` } }
);

// Toggle rule
const response = await client.api.automations.rules[':id'].toggle.$post(
	{ param: { id: 'rule-uuid' } },
	{ headers: { Authorization: `Bearer ${session.access_token}` } }
);

Rate Limiting

The API implements rate limiting per podcast:

Action TypeLimitWindow
send_email1001 hour
send_webhook5001 hour
update_field2001 hour

When rate limited, the API returns:

json
{
	"error": "Rate limit exceeded for send_email. Try again in X minutes."
}

Internal documentation - Not for public distribution