Automations
Build automations from your app, ChatGPT or Claude — start from a recipe or a list of steps, test, then switch on.
An automation does something for you when a trigger fires: "when a post fails, alert me", "every morning, have my Trend Scout find a topic and my Designer make a carousel". They are the same automations you see on the Automations page in the app, and you can open and edit any of them there.
With the API you can:
- list the building blocks (step types) and ready-made recipes,
- create an automation from a recipe, from a list of steps, or from a full graph,
- test it, switch it on or off, run it now, and see its runs,
- change or delete it.
Scopes: automations:read to read, automations:write for everything that creates,
changes, tests or runs. Both are opt-in.
The safe order: draft → test → switch on
Every automation you create through the API starts as a draft. A draft never runs on its own.
- Create it (
POST /v1/automationsorPOST /v1/automations/from-recipe). - Test it (
POST /v1/automations/{id}/test). Steps that would change something are only previewed. - Show the user what it will do, and switch it on (
POST /v1/automations/{id}/activate) only when they agree.
Endpoints
| Method | Path | Scope | What it does |
|---|---|---|---|
GET | /v1/automation-step-types | automations:read | Triggers, actions and logic you can use, with their settings |
GET | /v1/automation-recipes | automations:read | Ready-made automations and the blanks to fill |
GET | /v1/automations | automations:read | Your automations (status filter; up to 100, most recently changed first) |
POST | /v1/automations | automations:write | Create a draft from steps or graph |
POST | /v1/automations/from-recipe | automations:write | Create a draft from a recipe |
GET | /v1/automations/{id} | automations:read | One automation, in plain words and as a graph |
PATCH | /v1/automations/{id} | automations:write | Change name, description or steps |
DELETE | /v1/automations/{id} | automations:write | Delete it and its run history |
POST | /v1/automations/{id}/activate | automations:write | Switch it on |
POST | /v1/automations/{id}/pause | automations:write | Switch it off |
POST | /v1/automations/{id}/test | automations:write | Test run with sample data |
POST | /v1/automations/{id}/run | automations:write | Run it once for real |
GET | /v1/automations/{id}/runs | automations:read | Recent runs (limit up to 100) |
GET | /v1/automation-runs/{id} | automations:read | One run, step by step |
Tests are limited to 20 requests per minute per key and live runs to 10 per minute, on top of the general rate limit.
Step types
GET /v1/automation-step-types lists every building block you can use through the API:
- Triggers start an automation: a schedule, a button press (
trigger.manual), a workspace event such as "Post failed", a new article in a feed, a new comment, and more. - Actions do something: run an agent, create a draft post, make a design, send a notification, tag a person, and more.
- Logic:
logic.if(branch on a value) andlogic.delay(wait).
Here is the If step from that list:
{
"data": [
{
"key": "logic.if",
"kind": "logic",
"name": "If condition",
"description": "Branches the workflow: continues on the true or false path.",
"fields": [
{ "key": "left", "type": "text", "label": "Value", "required": true, "multiple": false },
{
"key": "operator",
"type": "select",
"label": "Operator",
"required": true,
"multiple": false,
"options": [
{ "value": "equals", "label": "equals" },
{ "value": "not_equals", "label": "does not equal" },
{ "value": "contains", "label": "contains" },
{ "value": "gt", "label": "greater than" },
{ "value": "lt", "label": "less than" },
{ "value": "exists", "label": "exists" },
{ "value": "not_exists", "label": "does not exist" }
]
},
{ "key": "right", "type": "text", "label": "Compare to", "required": false, "multiple": false }
],
"outputs": ["true", "false"],
"costsCredits": false,
"activateInApp": false
}
]
}fieldsare the step's settings. Put them in the step'sparams. Text settings can use values from earlier steps:{{trigger.…}}and{{nodes.<id>.…}}.costsCredits: truemeans the step uses AI credits when it runs (running an agent, making a design, sorting inbox messages).activateInApp: truemarks a step that messages customers with no human check. See What stays in the app.
Recipes
Recipes are the quickest start. GET /v1/automation-recipes lists them with their blanks:
the things you choose, usually which agent to run.
curl -X POST "https://api.nimply.io/v1/automations/from-recipe" \
-H "Authorization: Bearer $NIMPLY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"recipeId": "daily-trend-carousel",
"values": {
"scoutInstanceId": "3f0c2a5e-1b7d-4c8e-9a6f-2d4b8e1c7a90",
"designerInstanceId": "8e1c7a90-2d4b-4c8e-9a6f-3f0c2a5e1b7d"
}
}'Agent blanks take an agent id from GET /v1/agents. The agent must be made
from one of the blank's templateIds. Blanks you leave out stay empty: the draft is created,
but it can't be switched on until they are filled (with PATCH or in the app).
Build from steps
For your own automation, send a list of steps: the trigger first, then the actions. Without
edges, the steps run one after another in the order you send them.
curl -X POST "https://api.nimply.io/v1/automations" \
-H "Authorization: Bearer $NIMPLY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Monday planning reminder",
"steps": [
{ "type": "trigger.schedule", "name": "Every Monday at 9", "params": { "cron": "0 9 * * 1" } },
{ "type": "action.notify", "params": { "target": "inapp", "message": "Time to plan the week" } }
]
}'Schedules use five-field cron, in UTC.
Branch with If
To branch, give the steps your own ids and add edges. Each edge out of an If step needs a
sourceHandle of "true" or "false":
{
"name": "Failure triage",
"steps": [
{ "id": "trigger", "type": "trigger.event", "params": { "event": "post.failed" } },
{
"id": "check",
"type": "logic.if",
"name": "Auth error?",
"params": { "left": "{{trigger.error}}", "operator": "contains", "right": "token" }
},
{
"id": "reconnect",
"type": "action.notify",
"params": { "target": "inapp", "message": "{{trigger.channel.name}} needs reconnecting." }
},
{
"id": "generic",
"type": "action.notify",
"params": { "target": "inapp", "message": "Post failed on {{trigger.channel.name}}: {{trigger.error}}" }
}
],
"edges": [
{ "source": "trigger", "target": "check" },
{ "source": "check", "target": "reconnect", "sourceHandle": "true" },
{ "source": "check", "target": "generic", "sourceHandle": "false" }
]
}Or send the full graph
If a list can't express your layout, send graph instead of steps: { "nodes": [...], "edges": [...] }, the same shape GET /v1/automations/{id} returns. Each node needs an id
and a type; name, params and a canvas position are optional. Send steps or graph,
not both.
The automation
Both ways return 201 with the draft:
{
"id": "9d2f4b1a-6c3e-4f8a-b5d7-1e0a2c4b6d8f",
"name": "Monday planning reminder",
"description": null,
"status": "DRAFT",
"pausedReason": null,
"managedBy": null,
"trigger": { "type": "trigger.schedule", "summary": "On a schedule (0 9 * * 1 UTC)" },
"steps": [
{
"id": "n2",
"type": "action.notify",
"name": "Send notification",
"summary": "target: inapp · message: Time to plan the week"
}
],
"graph": {
"nodes": [
{
"id": "n1",
"type": "trigger.schedule",
"name": "Every Monday at 9",
"position": { "x": 80, "y": 200 },
"params": { "cron": "0 9 * * 1" }
},
{
"id": "n2",
"type": "action.notify",
"position": { "x": 420, "y": 200 },
"params": { "target": "inapp", "message": "Time to plan the week" }
}
],
"edges": [{ "id": "e1", "source": "n1", "target": "n2", "sourceHandle": "main" }]
},
"tags": [],
"version": 1,
"activateInApp": false,
"lastRunAt": null,
"nextRunAt": null,
"createdAt": "2026-10-29T09:00:00.000Z",
"updatedAt": "2026-10-29T09:00:00.000Z",
"appUrl": "https://app.nimply.io/workspace/…/automations/9d2f4b1a-…"
}trigger.summary and each step's summary describe the automation in plain words. Show them
to the user before you switch it on.
Draft, test, switch on
Test runs the automation once with sample data, or with your own triggerData
(available to the steps as {{trigger.…}}):
curl -X POST "https://api.nimply.io/v1/automations/9d2f4b1a-6c3e-4f8a-b5d7-1e0a2c4b6d8f/test" \
-H "Authorization: Bearer $NIMPLY_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'Steps that would change something (create a post, send a message, notify) are only previewed.
Steps that use AI can still use credits. The answer is 200 with the finished test run.
Switch on with POST /v1/automations/{id}/activate. Nimply checks everything first: one
trigger, every step connected, required settings filled, a valid schedule. If something is
wrong you get 422 INVALID_GRAPH, with one problem per entry in errors, so you can fix them
all at once:
{
"type": "https://developer.nimply.io/docs/errors#unprocessable-entity",
"title": "Unprocessable Entity",
"status": 422,
"detail": "Step \"n2\" (Send notification): missing required field \"Message\" (message)",
"code": "INVALID_GRAPH",
"errors": ["Step \"n2\" (Send notification): missing required field \"Message\" (message)"],
"instance": "/v1/automations/9d2f4b1a-6c3e-4f8a-b5d7-1e0a2c4b6d8f/activate"
}Once on, it runs by itself every time its trigger fires. Switching on an automation that starts on a new comment also turns on the Inbox for the channels it watches.
Switch off with POST /v1/automations/{id}/pause.
Run now with POST /v1/automations/{id}/run. This is a real run, not a test, whether or
not the automation is switched on. It works only for automations started by hand or on a
schedule; one that starts on an event returns 422 RUN_NEEDS_EVENT (test it instead). The
answer is 202 with the run; read it with GET /v1/automation-runs/{id}. Live runs count
toward your plan's monthly runs.
Change an automation
PATCH /v1/automations/{id} changes name, description (null clears it), or what it does
(steps + edges, or graph). New steps replace all the old ones.
Every automation has a version that goes up each time its steps change. Send the version
you read (or an If-Match: "3" header). If someone changed it in the meantime, nothing is
saved and you get 409 VERSION_CONFLICT: read it again, then retry.
curl -X PATCH "https://api.nimply.io/v1/automations/9d2f4b1a-6c3e-4f8a-b5d7-1e0a2c4b6d8f" \
-H "Authorization: Bearer $NIMPLY_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Monday planning nudge", "version": 1 }'An automation that is switched on must stay ready to run, so a change to its steps is checked the same way as switching on.
Managed automations are read-only
Some automations belong to something else. Their managedBy tells you:
agent: it follows an agent, for example the schedule of a LinkedIn Ghostwriter. Change, pause or remove the agent instead.nimply: Nimply runs it for a feature you switch on in the app (Smart inbox triage).
You can read them, but changing, testing, running or deleting one returns
409 MANAGED_AUTOMATION.
What stays in the app
Some things can't be done through the API at all:
- Web requests to outside addresses (
action.http_request). - Stored secrets (
{{secret:…}}). - Webhook triggers (
trigger.webhook), whose secret URL starts the automation.
These step types aren't listed, and sending one returns 422 STEP_NOT_ALLOWED. You can add
them yourself on the Automations page. An automation that already has one (built in the app)
can still be read, but testing, running or switching it on through the API returns
422 SET_IN_APP.
Two steps message customers with no human check: Reply in inbox (action.inbox_reply)
and Reply & DM by keyword (action.comment_dm, comment-to-DM). You can build and test
automations with them through the API. Switching them on, or running them live, happens only in
the Nimply app, where the person sees what they agree to. Such automations have
activateInApp: true. Trying returns 422 SET_IN_APP with appUrl, the link to the
automation in the app.
Runs
{
"id": "1c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
"automationId": "9d2f4b1a-6c3e-4f8a-b5d7-1e0a2c4b6d8f",
"mode": "TEST",
"status": "SUCCEEDED",
"triggerType": "manual",
"creditsUsed": 0,
"error": null,
"startedAt": "2026-10-29T09:00:00.000Z",
"finishedAt": "2026-10-29T09:00:01.000Z",
"steps": [
{ "nodeId": "n1", "type": "trigger.schedule", "status": "SUCCEEDED", "error": null },
{ "nodeId": "n2", "type": "action.notify", "status": "SUCCEEDED", "error": null }
]
}modeisTESTorLIVE.statusisRUNNING,WAITING(at a delay step),SUCCEEDED,FAILED,CANCELEDorSKIPPED.- Each step reports only its
statusanderror. What a step received or produced is never returned, because it can hold customer messages and other private data. Open the run in the app to see more.
Limits
- At most 30 steps (and 40 connections) per automation.
- At most 20 automations switched on per workspace (
403 ACTIVE_AUTOMATION_LIMIT). - Your plan sets how many automations you can have (
403 AUTOMATION_QUOTA_EXCEEDED) and how many live runs you get each month. - Live runs are also capped at 60 an hour and 5 at once per workspace (
429).
Who can do what
Keys act for a person: the user who approved the connection (OAuth), or the person who made the API key. That person's workspace role decides what the call may do:
| Action | Roles |
|---|---|
| Read step types, recipes, automations and runs | Any member |
| Create, change, test | Owner, admin, editor |
| Switch on, switch off, run now, delete | Owner, admin |
Otherwise the call fails with 403 and code INSUFFICIENT_ROLE.
Errors
| Status | code | Meaning |
|---|---|---|
403 | INSUFFICIENT_ROLE | The person the key acts for needs a higher workspace role |
403 | AUTOMATION_QUOTA_EXCEEDED | Your plan allows no more automations |
403 | ACTIVE_AUTOMATION_LIMIT | 20 automations are already switched on; pause one first |
403 | — | The key or connection is missing an automations:* scope |
404 | RECIPE_NOT_FOUND | No recipe with that id |
409 | VERSION_CONFLICT | It changed since you read it; read it again, then retry |
409 | MANAGED_AUTOMATION | It belongs to an agent or a Nimply feature and is read-only |
422 | STEP_NOT_ALLOWED | A step type or stored secret the API can't add |
422 | INVALID_GRAPH | Not ready (or not valid); errors lists every problem |
422 | INVALID_INPUT | Send steps or graph, not both; edges only with steps |
422 | INVALID_VALUES | An unknown recipe blank, or an agent that isn't in this workspace or doesn't fit the blank |
422 | SET_IN_APP | Only the Nimply app can switch this on or run it; see appUrl |
422 | RUN_NEEDS_EVENT | It starts on an event, so it can't be run by hand; test it instead |
429 | — | Too many requests, or the hourly / at-once run caps; retry after Retry-After seconds |
From ChatGPT, Claude and other assistants
The MCP server (version 0.7.0 and later) has matching tools:
list_automation_step_types, list_automation_recipes, list_automations, get_automation,
create_automation, create_automation_from_recipe, update_automation,
set_automation_status, test_automation, run_automation, get_automation_run and
delete_automation. Ask things like:
- "Every Monday, make a trend carousel with my Trend Scout and Designer."
- "Alert me in Slack when a post fails."
- "Pause my planning reminder."
The assistant creates a draft, tests it, and switches it on only after you confirm.
Connections made before these tools existed don't have automation access yet. Reconnect Nimply in your assistant and approve the automation permissions.