Automations
Build, try and submit your own automations, switch them, and read every run step by step.
TL;DRAI summary
This chapter explains how to set up automations that watch your vehicle records and automatically take actions, such as adding notes or updating prices. Cypress must approve and publish your automations before they run.
An automation watches your records and acts on them: a car listed 30 days gets a note or a new price, a deal that went quiet gets a task, a buyer's offer gets a follow-up. The API builds your organization's own automations with the rules the app uses, so an agent or an integration can propose one and try it on your cars. Cypress approves: nothing you write runs until a Cypress manager publishes it. Since 1.5.0.
| Scope | Grants |
|---|---|
automations:read | Your automations, their versions and graphs, where a draft stands with Cypress, their runs and every step, and validating an automation |
automations:write | Everything automations:read grants, plus creating, editing, saving drafts, dry runs, submitting, and switching automations on and off |
Neither is a default scope. Automations is a product Cypress switches on per
organization: until it is on, every call answers 403 AUTOMATIONS_NOT_ENABLED, asked before the scopes. Both an API key and a
Connect Cypress token are accepted.
You see your own automations only. Cypress's own automations, which run for every organization or the ones a Cypress manager chose, are never listed, and neither are their runs.
Read your automations
curl "$BASE/api/v1/automations?active=true" -H "authorization: Bearer $TOKEN"
curl "$BASE/api/v1/automations/$AUTOMATION_ID" -H "authorization: Bearer $TOKEN"An automation has at most two versions that matter: published, what runs,
and draft, the work in progress, with its review (where it stands with
Cypress). Reading one automation returns the graph of each (the nodes and
the edges between them, as the editor stores them); the list leaves the
graphs out. active says whether the published version runs on its trigger.
The node types, their settings and what each needs are in
GET /api/v1/automation/manifest.
Cypress's own automations (since 1.10.0). Every automation says whose
it is: owner is ORGANIZATION (yours) or CYPRESS. Cypress runs a few of its
own on every organization's cars (pricing, outlier alerts), and they can move
a car with nobody at your organization doing anything: ?owner=cypress lists
the ones that run on your records (switched on and published), ?owner=all
lists them with yours. Reading one by id shows what runs, its published
version and graph, with no draft and no runs (its runs are every
organization's). They are never yours to change: a write, a switch or a run
answers 404. The list without owner is yours alone, as before. Cypress's
own read even when your organization's Automations product is off (they run
on your cars either way); with it off, owner=all lists only Cypress's, and
your own answer 403 AUTOMATIONS_NOT_ENABLED as before.
The manifest: every action as an act
GET /api/v1/automation/manifest describes Cypress to an automation engine
that is not Cypress's (Himo, as Grove runs it): the records and their fields,
the triggers, the lookups, every node with its settings, and what your
credential can run. Since 1.5.0 every action an agent may run is an act:
the same action as the app's actions, with its
input as the node's settings, the refusal codes it may answer with, and how it
dry runs. Each act has its node (named after its MCP tool, in capitals), and
every read the API offers is a read.
An engine runs an act by calling the action's own REST route with your
credential: the path filled from the input (the record the run is about fills
its {id}), the rest as the body, ?dryRun=true on a dry run, and the step's
Idempotency-Key otherwise, so a retried step acts once. A refusal (4xx, its
code) is a step blocked for that reason; a 5xx is a failure. A write with no
dry run of its own is never called on a dry run. A person's verbs (accepting
below the floor, signing, approving a quarter) are not acts: an automation acts
as an agent does.
The answer is cut to your credential's scopes, carries an ETag, and answers
304 to If-None-Match.
Build one
curl -X POST "$BASE/api/v1/automations" \
-H "authorization: Bearer $TOKEN" -H "content-type: application/json" \
-d '{
"name": "Stale listing nudge",
"category": "LISTING",
"graph": {
"nodes": [
{ "id": "start", "type": "trigger", "position": { "x": 0, "y": 0 },
"data": { "nodeType": "ENTITY_TIME_ELAPSED", "label": "30 days listed",
"config": { "entity": "vehicle", "watchStatus": "LISTED", "duration": 30, "unit": "days" } } },
{ "id": "note", "type": "action", "position": { "x": 0, "y": 120 },
"data": { "nodeType": "CREATE_TASK", "label": "Leave a note",
"config": { "note": "Listed 30 days: review the price" } } }
],
"edges": [{ "id": "e1", "source": "start", "target": "note" }]
}
}'
curl -X PUT "$BASE/api/v1/automations/$AUTOMATION_ID/draft" \
-H "authorization: Bearer $TOKEN" -H "content-type: application/json" \
-d '{ "graph": { "nodes": [ … ], "edges": [ … ] } }'A new automation has one draft and runs nothing. PUT .../draft saves the
draft: it may be half wired, and the published version keeps running until
Cypress publishes the draft in its place. PATCH renames an automation or
changes its category or stages (the life stages it works in; Operation and
Planning need the Planning product). The stages belong to the automation, not
to a version: once Cypress has published one, they are part of what Cypress
approved, and Cypress changes them.
What an organization's automation may contain
Your automations may use every trigger, condition and control node, and the actions that work on your own records: labels, tasks, CRM tags, notifications to your own people, the three price nodes, status changes, sales channels, fleet, exports and email. A node outside that list is refused with its reason, for example:
- Delist takes a car off the market, withdraws its offers and tells the buyers, so a person does it in the app.
- Request a valuation is a paid request, and Cypress orders those.
- A notification goes to your own members, never to Cypress managers.
- A label is your own or one of the platform's shared labels; a chained automation is one of your own; a fixed email recipient is one of your members, or a contact of your book who may be mailed.
Saving or submitting an automation that breaks one of these answers 403 POLICY_DENIED with findings, one per node.
Validate
curl -X POST "$BASE/api/v1/automations/$AUTOMATION_ID/validate" -H "authorization: Bearer $TOKEN"Validate says what publishing would say, and writes nothing. errors would
stop Cypress publishing it: the rules the app checks before an automation runs
unattended (an export after a fan-out fetch, an automation that runs itself, a
sales-channel step with no channel, a planning-stock step on an automation
that works on planning stock) and the list above. warnings are the editor's
advice and never block: a node parked to one side, a setting left empty. Send
a graph in the body to judge it before saving.
Try it: dry runs
curl -X POST "$BASE/api/v1/automations/$AUTOMATION_ID/dry-run" \
-H "authorization: Bearer $TOKEN" -H "content-type: application/json" \
-d '{ "subjectIds": ["clx…", "clx…"] }'A dry run tries a version (the draft by default; "version": "published" for
the other) on your own records. Every step runs as the automation would and
writes nothing: its result says what it WOULD have done, and a step the
rules refuse shows as refused, with the reason. It answers 202 at once with
one run per record:
{ "automationId": "…", "version": 2,
"runs": [{ "id": "6f1c…", "subjectId": "clx…", "href": "/api/v1/automations/runs/6f1c…" }] }Read each run at its href. It reads QUEUED until the worker starts it,
then DRY_RUN once it is done; a run the worker does not go ahead with (a car
outside the automation's stages, a record that is gone) is DRY_RUN too,
with the reason in problems ("Not run: …").
- On what. Vehicles for a vehicle automation; contacts, businesses or deals of your book for a CRM automation (at least one). An automation that fetches its own cars, or runs on a schedule, may be tried with no record. At most 25 per call.
- As whom. As your organization's account admin (the earliest Fleet Manager or admin), exactly as the published automation will act. An automation switched off can still be tried.
Submit: Cypress approves
curl -X POST "$BASE/api/v1/automations/$AUTOMATION_ID/submit" -H "authorization: Bearer $TOKEN"Submitting asks Cypress to publish the draft. It is checked first as
publishing would check it (422 INVALID with findings when it could not be
published), then waits for a Cypress manager: the draft's review.state reads
SUBMITTED. The manager either:
- publishes it: it replaces the published version and runs on its trigger
while the automation is switched on (
AUTOMATION.PUBLISHED), or - sends it back with a note on what to change (
review.statereadsSENT_BACK, with thenote;AUTOMATION.SENT_BACK). Change the draft and submit it again.
Saving a submitted draft withdraws it from review: what Cypress would read is no longer what is there. Submit again when it is ready.
A published automation acts as your organization's account admin. It can do nothing that person could not do in the app: no price under the floor, the floor only for a Fleet Manager or an admin, a status move only as the app allows that role, email only to your own people and to contacts who may be mailed.
Switch on and off
curl -X POST "$BASE/api/v1/automations/$AUTOMATION_ID/deactivate" -H "authorization: Bearer $TOKEN"
curl -X POST "$BASE/api/v1/automations/$AUTOMATION_ID/activate" -H "authorization: Bearer $TOKEN"A switched-off automation runs nothing, and a run paused on a Wait step is cancelled when its wait ends. Switching it back on runs the version Cypress published.
- Who may. An API key acts as your organization's admin. An app acting for
a person needs that person to be a Fleet Manager or an admin. An agent
drafts, tries, submits and switches automations off; switching one back on
is a person's:
403 PEOPLE_ONLY. - Limits. At most 50 automations switched on and 200 in all (
409 CONFLICT, withlimitandcount). Your automations start at most 500 runs an hour together; past that, a run is skipped until the hour has room. - Your records only. Whatever a graph says, your automation reads and acts on your own cars and your own book: a fetch is limited to your stock, and a chain reaches only your own automations.
Every write takes ?dryRun=true (it checks everything and writes nothing:
not to be confused with POST .../dry-run, which tries the automation) and an
Idempotency-Key, and is recorded in your audit log with the key, app or
agent that made it.
Runs and steps
curl "$BASE/api/v1/automations/$AUTOMATION_ID/runs?status=PARTIAL" -H "authorization: Bearer $TOKEN"
curl "$BASE/api/v1/automations/runs/6f1c…" -H "authorization: Bearer $TOKEN"
curl "$BASE/api/v1/automations/runs/6f1c…/steps?limit=100" -H "authorization: Bearer $TOKEN"status | Meaning |
|---|---|
QUEUED | A dry run you asked for that the worker has not started |
RUNNING | Working, or paused on a Wait step until waitingUntil |
SUCCESS | Every step did what it was asked |
PARTIAL | It ran, and some steps were refused or failed: problems says which |
FAILED | It stopped on an error |
DRY_RUN | A finished dry run: its steps say what would have happened |
A run's steps come in the order they ran, each with its node, the record it
worked on, what it read and wrote, and, for a condition, its reasoning
(conditionTrace). A batch run takes a step per node per car, so page through
them with limit and offset.
Events
| Event | When | Body |
|---|---|---|
AUTOMATION.PUBLISHED | Cypress published a draft you submitted | automationId, automationName, version, publishedAt |
AUTOMATION.SENT_BACK | Cypress sent a submitted draft back | automationId, automationName, version, note, sentBackAt |
AUTOMATION.RUN.COMPLETED | A run of one of your automations finished: status is SUCCESS, PARTIAL (some steps were refused or failed: problems says which) or DRY_RUN (a dry run you asked the API for) | runId, automationId, automationName, version, status, dryRun, startedBy, subject, vehiclesEvaluated, vehiclesActioned, problems, durationMs, startedAt, completedAt |
AUTOMATION.RUN.FAILED | A run stopped on an error | The same, with the error in problems |
Each run is told once, when it is over: a run paused on a Wait step is told
when its last wait ends. The href reads the automation or the run back (a
run's steps are at stepsHref). Like TASK.*, they are opt-in: only a
Cypress-dialect subscription that lists them gets them, subscribing needs
automations:read, and nothing is sent while your Automations product is off.
Cypress's own automations never send these. A dry run someone at Cypress
starts is not sent; one you asked the API for is, so you can wait for it
instead of polling.