Automation API
Rule-Based Automation
Create rules, inspect executions, trigger webhooks, and manage approvals
PrimeCal automation is built around user-owned rules with triggers, conditions, and actions. This page documents the full non-admin automation surface directly from the controller and DTOs.
Authentication and Permissions
- All rule-management routes require authentication.
POST /api/automation/webhook/:tokenis explicitly public via@Public().- Rules are scoped to the authenticated user.
- Sensitive rules can require explicit approval before execution.
- The controller uses the API validation pipe for create and update operations.
Endpoint Reference
| Method | Path | Purpose | Request or query | Auth | Source |
|---|---|---|---|---|---|
POST | /api/automation/rules | Create a rule. | Body: rule create payload | JWT or user API key | automation/automation.controller.ts |
GET | /api/automation/rules | List rules with pagination and optional enabled filter. | Query: page,limit,enabled | JWT or user API key | automation/automation.controller.ts |
GET | /api/automation/rules/:id | Get one rule. | Path: id | JWT or user API key | automation/automation.controller.ts |
PUT | /api/automation/rules/:id | Update a rule. | Path: id, body: partial rule payload | JWT or user API key | automation/automation.controller.ts |
DELETE | /api/automation/rules/:id | Delete a rule. | Path: id | JWT or user API key | automation/automation.controller.ts |
POST | /api/automation/rules/:id/execute | Run a rule immediately. | Path: id | JWT or user API key | automation/automation.controller.ts |
GET | /api/automation/rules/:id/audit-logs | List audit logs for one rule. | Path: id, query from AuditLogQueryDto | JWT or user API key | automation/automation.controller.ts |
GET | /api/automation/audit-logs/:logId | Get one audit log entry. | Path: logId | JWT or user API key | automation/automation.controller.ts |
GET | /api/automation/rules/:id/stats | Get execution statistics for a rule. | Path: id | JWT or user API key | automation/automation.controller.ts |
POST | /api/automation/webhook/:token | Trigger a webhook-backed rule. | Path: token, JSON payload | Public | automation/automation.controller.ts |
POST | /api/automation/rules/:id/webhook/regenerate | Regenerate the rule's webhook token. | Path: id | JWT or user API key | automation/automation.controller.ts |
POST | /api/automation/rules/:id/webhook/rotate-secret | Rotate the webhook signing secret. | Path: id | JWT or user API key | automation/automation.controller.ts |
POST | /api/automation/rules/:id/approve | Approve a sensitive rule. | Path: id, body: note | JWT or user API key | automation/automation.controller.ts |
GET | /api/automation/smart-values/:triggerType | List smart values for a trigger type. | Path: triggerType | JWT or user API key | automation/automation.controller.ts |
Request Shapes
List and approval queries
ListAutomationRulesQueryDto.page: optional int, minimum1, default1ListAutomationRulesQueryDto.limit: optional int,1..100, default20ListAutomationRulesQueryDto.enabled: optional booleanApproveAutomationRuleDto.note: optional string, max 500 chars
Rule definition
CreateAutomationRuleDto
name: required,1..200charsdescription: optional, max 1000 charstriggerType: required enumtriggerConfig: optional objectisEnabled: optional booleanconditionLogic: optional enumAND|ORconditions: optional array, max 10 itemsactions: required array,1..5items
UpdateAutomationRuleDto keeps the same structure but makes all fields optional.
Trigger types
event.createdevent.updatedevent.deletedevent.starts_inevent.ends_inrelative_time_to_eventcalendar.importedscheduled.timewebhook.incomingtask.status_changed— fires when a task'sstatuschanges. Not yet selectable from the rule builder UI; create rules using it directly through this API.task.scheduled_start— fires when a task (including one instantiated from a routine template) reaches its scheduled start time. Also API-only for now. See Smart-Home Automation for the "routine task drives a smart-home device" pattern this enables.notification.dispatched— fires whenever a notification is dispatched to the rule owner on any channel (event reminders, reservation alerts, another rule'ssend_notificationaction, etc.). Use thenotification.type/notification.channelcondition fields to narrow it. See the Trigger Reference for a worked smart-home example.
Relative time trigger config
The relative-time trigger config has nested validation for:
eventFilter.calendarIdseventFilter.titleContainseventFilter.descriptionContainseventFilter.tagseventFilter.labelseventFilter.isAllDayOnlyeventFilter.isRecurringOnlyreferenceTime.base:start|endoffset.direction:before|afteroffset.value: int>= 0offset.unit:minutes|hours|days|weeksexecution.runOncePerEventexecution.fireForEveryOccurrenceOfRecurringEventexecution.skipPastexecution.pastDueGraceMinutes:0..60execution.schedulingWindowDays:1..730
Conditions
CreateConditionDto
field: required enumoperator: required enumvalue: required string, max 1000 charsgroupId: optional stringlogicOperator: required enumAND|OR|NOTorder: optional number
Current condition fields:
event.titleevent.descriptionevent.locationevent.notesevent.durationevent.is_all_dayevent.colorevent.statusevent.calendar.idevent.calendar.namewebhook.datanotification.type/notification.channel/notification.data.*— only resolvable onnotification.dispatchedrules; see Condition Reference.
Current operators include:
contains,not_contains,matches,not_matchesequals,not_equalsstarts_with,ends_withis_empty,is_not_emptygreater_than,less_thangreater_than_or_equal,less_than_or_equalis_true,is_falsein,not_in,in_list,not_in_list
Actions
CreateActionDto
actionType: required enumactionConfig: optional objectorder: optional number
Current action types:
set_event_coloradd_event_tagsend_notificationupdate_event_titleupdate_event_descriptioncancel_eventmove_to_calendarcreate_taskwebhookinstantiate_routine— starts a routine template right now, the same as the manual "Start now" endpoint.actionConfig: { routineTemplateId: number }. Enforces the same ownership/group-readability check asPOST /api/routine-templates/:id/instantiate, so a rule can never instantiate a template its owner can't see. Not yet in the action picker UI; see Routine Templates API and Smart-Home Automation.
Webhook action config
The webhook action's actionConfig also accepts, beyond url:
method: optional enumGET|POST|PUT|PATCH|DELETE, defaultPOSTauthType: optional enumnone|bearer|basic|header, defaultnoneauthToken: required whenauthTypeisn'tnoneauthHeaderName: required whenauthTypeisheader(the header the token is sent under)includeEventData,headers,customPayload,timeoutMs,maxResponseBytes,signingSecret: unchanged from the original webhook action
This generalization is what lets the webhook action call a smart-home device's REST API (e.g. a Home Assistant switch.turn_on service call) instead of only posting to a webhook receiver. See Smart-Home Automation for a full example.
Example Calls
Create a rule
curl -X POST "$PRIMECAL_API/api/automation/rules" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Highlight school pickups",
"triggerType": "event.created",
"conditionLogic": "AND",
"conditions": [
{
"field": "event.title",
"operator": "contains",
"value": "pickup",
"logicOperator": "AND"
}
],
"actions": [
{
"actionType": "set_event_color",
"actionConfig": { "color": "#f59e0b" }
}
]
}'
Run a rule now
curl -X POST "$PRIMECAL_API/api/automation/rules/14/execute" \
-H "Authorization: Bearer $TOKEN"
Trigger a webhook rule
curl -X POST "$PRIMECAL_API/api/automation/webhook/$WEBHOOK_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"payload": {
"source": "school-system",
"message": "Late pickup today"
}
}'
Read smart values
curl "$PRIMECAL_API/api/automation/smart-values/event.created" \
-H "Authorization: Bearer $TOKEN"
Instantiate a routine when a webhook arrives
curl -X POST "$PRIMECAL_API/api/automation/rules" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Smart alarm starts Morning Routine",
"triggerType": "webhook.incoming",
"actions": [
{
"actionType": "instantiate_routine",
"actionConfig": { "routineTemplateId": 42 }
}
]
}'
Call a smart-home device's REST API on a task'''s scheduled start
curl -X POST "$PRIMECAL_API/api/automation/rules" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Start laundry via Home Assistant",
"triggerType": "task.scheduled_start",
"actions": [
{
"actionType": "webhook",
"actionConfig": {
"url": "https://homeassistant.local:8123/api/services/switch/turn_on",
"method": "POST",
"authType": "bearer",
"authToken": "YOUR_HOME_ASSISTANT_LONG_LIVED_TOKEN",
"customPayload": { "entity_id": "switch.washing_machine" }
}
}
]
}'
Response and Behavior Notes
DELETE /api/automation/rules/:idreturns204 No Content.POST /api/automation/rules/:id/executereturns a message and updated execution count.POST /api/automation/rules/:id/webhook/regeneratereturns the newwebhookToken.POST /api/automation/rules/:id/webhook/rotate-secretreturns the newwebhookSecretandgraceUntil.- Public webhook execution uses the raw body and headers when evaluating the rule.
- The
webhookaction defaults toPOSTwhenmethodis omitted, for backward compatibility with rules saved before the field existed. - The
instantiate_routineaction returns the created task ids and count in its audit log entry; if the referencedroutineTemplateIdisn't readable by the rule's owner, the action fails (silently, per the standard automation error-handling model) and is logged in the audit trail rather than throwing during rule save.
Best Practices
- Keep actions narrow and deterministic. Rules with too many side effects become hard to debug.
- Use smart values and the catalog returned by
GET /api/automation/smart-values/:triggerTypeinstead of hard-coding tokens. - Prefer
GET /api/automation/rules/:id/audit-logsand/statswhen troubleshooting before editing the rule itself. - Regenerate webhook tokens if a URL leaks. Rotate webhook secrets if the signing secret leaks.
- When building UI, treat relative-time triggers as a first-class subtype because their config is far richer than basic event triggers.
- Store smart-home
authTokenvalues as carefully as any other credential — they travel with the rule if it's ever cloned or exported.