A flow is a JSON document with three lists: triggers, steps, and an optional settings block. The runner walks the steps by nextStepId. Conditions can fork to multiple branches; actions can pause and resume via the queue. This page is the canonical reference for the data shape and the CRUD endpoints.
The flow document
Every automation is wrapped in BaseModel\<T\>. The data payload is the workflow itself:
interface AutomationWorkflow {
id: string;
orgId: string;
name: string;
description?: string;
status: 'draft' | 'active' | 'inactive' | 'archived';
steps: AutomationStep[];
triggers: AutomationTrigger[];
settings?: WorkflowSettings;
errorHandling?: ErrorHandlingConfig;
createdBy: string;
}
interface AutomationStep {
id: string;
name: string;
enabled: boolean;
type: 'trigger' | 'action' | 'condition' | 'wait';
config: Record<string, any>;
nextStepId?: string;
description?: string;
}
interface AutomationTrigger {
id: string;
type: string; // e.g. 'contact_created', 'database_change'
name: string;
config: Record<string, any>;
enabled: boolean;
automationId: string;
}
A trigger fires → the runner builds an AutomationContext (orgId, executionId, variables, metadata) → it walks steps from the first one whose ID matches the trigger's resolved nextStepId, or the first step in the array.
Step config keys
Each step's config carries the type-specific parameters. The step-processor.service.ts reads config.actionType (or conditionType) to look up the registered handler.
{
"id": "step-2",
"type": "action",
"name": "Send welcome email",
"enabled": true,
"config": {
"actionType": "send_notification",
"deliveryType": "email",
"to": "{{contact.email}}",
"subject": "Welcome, {{contact.firstName}}",
"templateId": "welcome-template"
},
"nextStepId": "step-3"
}
{{...}} placeholders are resolved against context.variables. Triggers inject contact, event, and trigger data into variables before the first step runs. See the per-trigger payload shapes in triggers reference.
Create a flow
/automationJWTPass the wrapped BaseModel. The repository service generates pk/sk for you.
curl -X POST https://appengine.appmint.io/automation \
-H "Authorization: Bearer $JWT" \
-H "orgid: $ORG" \
-H "Content-Type: application/json" \
-d @flow.json
The response returns the saved automation with sk — that's the automationId for every other endpoint.
Activate it
/automation/start/:automationIdJWTstart flips data.status to active and registers all enabled triggers in the trigger registry so live events can match them. Without this call, triggers never fire — even if enabled is true.
curl -X POST https://appengine.appmint.io/automation/start/$AUTOMATION_ID \
-H "Authorization: Bearer $JWT" \
-H "orgid: $ORG" \
-H "Content-Type: application/json" \
-d '{"startedBy": "[email protected]"}'
To stop: POST /automation/stop/:automationId. That deregisters triggers and cancels pending queue jobs.
Run it manually
/automation/:automationId/executeJWTForces a single execution outside the trigger system. Good for ad-hoc runs and back-fills. Pass any variables your steps will read.
curl -X POST https://appengine.appmint.io/automation/$AUTOMATION_ID/execute \
-H "Authorization: Bearer $JWT" \
-H "orgid: $ORG" \
-H "Content-Type: application/json" \
-d '{
"executionId": "manual-001",
"variables": { "contact": { "id": "c-123", "email": "[email protected]" } },
"startStepId": "s1",
"executedBy": "[email protected]"
}'
| Field | Type | Description |
|---|---|---|
| executionId | string | Optional. Auto-generated if omitted. |
| variables | object | Seeds |
| startStepId | string | Skip past triggers and start at this step. |
| executedBy | string | Recorded in execution metadata. |
The response is an ExecutionResult with success, executionId, completedSteps, logs. The runner walks the graph synchronously until a step returns pauseExecution: true (delays, waits-for-reply). Paused steps schedule a resume job in the automation queue and the response returns immediately.
Update or delete
/automation/:automationIdJWTBaseModel. Updating an active automation does not auto-restart triggers — call start again if triggers changed.
/automation/:automationIdJWTstatus === 'active', then soft-deletes. Pass { "deletedBy": "[email protected]", "reason": "obsolete" }.
Settings worth knowing
interface WorkflowSettings {
maxExecutionTime?: number; // minutes
retryAttempts?: number;
retryDelay?: number; // seconds
timezone?: string;
businessHours?: BusinessHours; // skip steps outside business hours
circularPrevention?: CircularPreventionSettings;
}
circularPrevention is on by default. It blocks an automation from triggering itself, caps chain depth at 5, and skips re-runs against the same record within a 1 s cooldown. Bump maxDepth only when you have a deliberate fan-out chain.
Branching uses nextStepId, thenStepId, elseStepId, etc. Renaming a step ID without updating every reference will break the workflow at runtime — the runner will simply terminate when it can't find the next step.