A workflow has three things to verify: triggers fire on the events you expect, conditions branch the way you expect, and actions produce the right side effects. AppEngine gives you a manual-execute endpoint, validation utilities, and a queryable execution history.
Dry-run an automation
/automation/:automationId/executeJWTThe same endpoint that powers manual execution doubles as a dry-run tool. Pass synthetic variables and a startStepId to skip past the trigger and run a specific branch:
curl -X POST https://appengine.appmint.io/automation/$AUTOMATION_ID/execute \
-H "Authorization: Bearer $JWT" -H "orgid: $ORG" \
-d '{
"executionId": "test-001",
"startStepId": "branch-1",
"variables": {
"contact": { "id": "test", "email": "[email protected]", "country": "US", "tags": ["vip"] }
},
"executedBy": "[email protected]"
}'
The response is an ExecutionResult:
{
"success": true,
"executionId": "test-001",
"completedSteps": ["branch-1", "us-flow-step", "send-step"],
"totalSteps": 5,
"logs": [
{ "level": "info", "message": "Condition branch-1 → true", "stepId": "branch-1" },
{ "level": "info", "message": "Notification sent", "stepId": "send-step" }
],
"startedAt": "2026-04-25T...",
"completedAt": "2026-04-25T..."
}
Wait actions still pause the run. To skip a delay during testing, design the flow so the test startStepId lands after it, or temporarily set duration: 0.
Manual execution still runs every action's real side effect — emails are sent, records are created, webhooks fire. Use a test contact and a sandbox webhook receiver. There is no built-in mock mode.
Validate a workflow shape
/automation/ai/validateJWTStatic checks against the workflow JSON: orphaned step IDs, missing required config keys, unknown actionType, broken nextStepId links. Run before saving.
curl -X POST https://appengine.appmint.io/automation/ai/validate \
-H "Authorization: Bearer $JWT" -H "orgid: $ORG" \
-d '{"workflow": { /* the data payload */ }}'
Response:
{
"isValid": false,
"errors": [
{ "type": "broken_link", "message": "Step s2 references unknown nextStepId 's99'", "location": "steps[1]", "severity": "error" }
],
"suggestions": [
{ "type": "missing_branch", "message": "Condition has no elseStepId", "fix": { "elseStepId": null } }
]
}
Read execution history
/automation/execution/historyJWTReturns past runs with full step-level logs.
| Query param | Description |
|---|---|
automationId | Filter to one workflow |
limit | Page size (default 50) |
offset | Page offset |
curl "https://appengine.appmint.io/automation/execution/history?automationId=$ID&limit=20" \
-H "Authorization: Bearer $JWT" -H "orgid: $ORG"
Each item carries completedSteps, the full logs array, and the error if it failed. Use this to spot the step where the run died.
Inspect dashboard stats
/automation/dashboard/statsJWTAggregate counts: active vs draft, total executions, completion rate, recent activity feed. Good for a glance after a deploy.
/automation/status/:automationIdJWTPer-automation: totalExecutions, lastExecution, current status.
Debugging tips
- Check the trigger registered. After saving, call
POST /automation/start/:idand watch logs forRegistered automation triggers. Withoutstart, no trigger fires. - Pin the test data. Pass an explicit
executionIdandexecutedByso you can find the run in history later. - Look at the last completed step.
completedStepsends at the step that failed (or paused). The next step insteps[]is where to dig. - Watch for circular blocks. If a
data_updatedautomation never fires, check whether circular prevention is rejecting it — the guard logsTrigger blocked by circular preventionat debug level. - Pause on a wait? Wait actions return
pauseExecution: true. The execution showssuccess: truebut with fewercompletedStepsthantotalSteps. The remainder runs when the queue resumes the job.
The /automation/health endpoint is unauthenticated and returns the registry sizes (number of registered actions, triggers, conditions). Use it to verify the engine itself is alive.