Automations
Event-driven automations — events, conditions and actions, outgoing webhooks with signatures, the run log, and the endpoints to manage automations.
An automation runs actions when an event happens in the project and the event matches the automation's conditions. For example: when an order moves to "In production", write off its materials from the default warehouse and create a task for the workshop.
Every run is recorded in the automation's run log, whether it succeeded or failed.
Field types follow the field notation.
Access
Every endpoint on this page requires both the projects and automations OAuth scopes. The user must be a member
of the project, and the owner's subscription must be active. Otherwise the endpoint returns 404 RES_NOT_FOUND.
All endpoints require can_edit_project; the project owner bypasses this check. (The can_view_automations and
can_edit_automations permission bits are reserved and not checked yet.)
Automation Object
Automation Structure
| Field | Type | Description |
|---|---|---|
| uuid | uuid | Automation ID |
| name | string | Name, 1–255 characters. Shown in order journals next to what it did |
| is_active | boolean | Inactive automations never run |
| event | string | The event that triggers it |
| conditions | object | Event-specific filter, see Events |
| actions | array of action objects | Run in order |
| created_at | ISO8601 datetime | When the automation was created |
| updated_at | ISO8601 datetime | When the automation was last changed |
Action Structure
| Field | Type | Description |
|---|---|---|
| type | string | One of the action types |
| params | object | Action parameters, see Actions |
Example Automation
{
"uuid": "0199b07c-93a4-75b6-c7d8-e9f0a1b2c3d4",
"name": "Start production",
"is_active": true,
"event": "order_status_changed",
"conditions": {
"status_uuid": "0198f1c0-2d4a-7e11-8b3f-1a2b3c4d5e6f"
},
"actions": [
{
"type": "warehouse_spend_bom",
"params": {
"target": "materials",
"warehouse": "default"
}
},
{
"type": "create_task",
"params": {
"title": "Cut and assemble",
"assignee": "0198f0b2-3c4d-7e5f-8a6b-7c8d9e0f1a2b",
"due_in_days": 2
}
},
{
"type": "send_webhook",
"params": {
"url": "https://erp.acme.example/hooks/configo",
"secret": "s3cr3t"
}
}
],
"created_at": "2026-09-10T08:00:00.000Z",
"updated_at": "2026-09-10T08:00:00.000Z"
}
Events
| Event | Fires when | Conditions | Payload |
|---|---|---|---|
order_created |
A new order is saved — from the widget, the API, or the app | none | order_uuid, status_uuid (its initial status), entity_type: "order", entity_uuid |
order_status_changed |
An order is moved to another status (by a user, the API, or the app). Not when an order is created | status_uuid (required): the status the order moved to |
order_uuid, status_uuid, entity_type: "order", entity_uuid |
task_overdue |
An open task passes its due date. Checked every 15 minutes; fires once per due date | none | task_uuid, entity_type, entity_uuid (the task's link), order_uuid (if linked to an order) |
scheduled |
Every day at 09:00 server time; weekly on Mondays, monthly on the 1st |
frequency (required): daily, weekly or monthly |
frequency |
A condition matches when the payload has the same value under the same key.
Transactions. order_status_changed automations run inside the same transaction as the status change. If any of
them fails, for example a write-off without enough stock, the status change is rolled back and the API returns the
error (409 ORDER_INSUFFICIENT_STOCK). order_created automations run after the order is saved, each on its own: a
failure never undoes the order and shows up only in the run log. Time-driven events also run each automation on its
own, so one failure does not stop the others.
Actions
Which actions are available depends on the event. An action not offered under an event is rejected on save.
| Action | order_created |
order_status_changed |
task_overdue |
scheduled |
|---|---|---|---|---|
warehouse_spend_bom |
✓ | |||
warehouse_income_product |
✓ | |||
create_task |
✓ | ✓ | ✓ | ✓ |
change_responsible |
✓ | ✓ | ✓ | |
send_email |
✓ | ✓ | ✓ | |
send_webhook |
✓ | ✓ | ✓ | ✓ |
warehouse_spend_bom
Writes off stock for the order.
| Param | Type | Description |
|---|---|---|
| target? | string | materials (default): the materials of every order product, by bill of materials × quantity. products: the finished products themselves |
| warehouse | string | A warehouse uuid, or default for the project's default warehouse |
Fails, and with it the status change, if any item lacks stock. Movements are recorded in the warehouse history as
spend and in the order journal as automation_warehouse_movement.
warehouse_income_product
Puts the order's finished products into stock, e.g. when production is complete.
| Param | Type | Description |
|---|---|---|
| target? | string | products (the only option) |
| warehouse | string | A warehouse uuid, or default |
create_task
Creates a task, linked to the event's entity (the order, or the overdue task's order or contact).
| Param | Type | Description |
|---|---|---|
| title | string | Task title. Required |
| assignee? | string | A member uuid, or inherit (default) for the order's responsible member. Under scheduled and order_created, inherit is not available: give a member (a new order usually has no responsible yet) |
| due_in_days? | integer | Due date, days from now; ≥ 0. Default 1 |
change_responsible
Makes another member responsible for the order. Under task_overdue it works only for tasks linked to an order.
| Param | Type | Description |
|---|---|---|
| member | uuid | Project member. Required |
send_email
Sends an email template to the contact the event is about. The email is sent only after the transaction commits.
| Param | Type | Description |
|---|---|---|
| template | uuid | Email template. Required |
send_webhook
POSTs the event to your URL. The request is sent only after the transaction commits, so a rolled-back event is never reported.
| Param | Type | Description |
|---|---|---|
| url | string | http or https URL. Required |
| secret? | string | If set, requests are signed, see below |
See Webhooks.
Webhooks
Webhook Request
POST /hooks/configo HTTP/1.1
Host: erp.acme.example
Content-Type: application/json
User-Agent: ConfigoWebhook/1.0
X-Configo-Signature: sha256=5d41402abc4b2a76b9719d911017c592...
{
"event": "order_status_changed",
"project_uuid": "0198e7a0-0000-7000-8000-000000000001",
"automation": {
"uuid": "0199b07c-93a4-75b6-c7d8-e9f0a1b2c3d4",
"name": "Start production"
},
"payload": {
"order_uuid": "0199a4e2-7b1c-7c3e-9d2a-5f4e8b1c0a11",
"status_uuid": "0198f1c0-2d4a-7e11-8b3f-1a2b3c4d5e6f",
"entity_type": "order",
"entity_uuid": "0199a4e2-7b1c-7c3e-9d2a-5f4e8b1c0a11"
},
"sent_at": "2026-10-01T11:02:41.512Z"
}
payload is the event payload. The webhook carries IDs only; fetch the order or task through the API if
you need its details.
Verifying the signature
With a secret, X-Configo-Signature is sha256= followed by the hex HMAC-SHA256 of the raw request body with
the secret as key. Compute it over the bytes you received, before parsing the JSON, and compare in constant time:
import {createHmac, timingSafeEqual} from 'node:crypto'
function verify(rawBody, header, secret) {
const expected = 'sha256=' + createHmac('sha256', secret).update(rawBody).digest('hex')
return header && header.length === expected.length && timingSafeEqual(Buffer.from(header), Buffer.from(expected))
}
Delivery
- Respond with any
2xxstatus within 10 seconds. Anything else, a timeout, or a network error counts as a failure. - Failed deliveries are retried up to 5 attempts in total, with exponential backoff starting at 5 seconds.
- Redirects are not followed.
- URLs resolving to private, loopback, link-local or CGNAT addresses are refused.
- Delivery is at least once: deduplicate by
automation.uuid+payload+sent_atif that matters to you.
List Automations
GET /api/v1/projects/{project.uuid}/automations
Returns the project's automations, newest first.
Get Automation
GET /api/v1/projects/{project.uuid}/automations/{automation.uuid}
Create Automation
POST /api/v1/projects/{project.uuid}/automations
JSON Params
| Field | Type | Description |
|---|---|---|
| name | string | 1–255 characters |
| event | string | An event |
| conditions? | object | Default {}. Required keys depend on the event |
| actions? | array | Default [] |
| is_active? | boolean | Default true |
The automation is checked against the event catalog: the event must exist, required conditions must be set, every
action must be offered under the event, and every required parameter must be filled in. Parameters with a default
(warehouse: "default", assignee: "inherit") count as filled. References such as a member or template uuid are
checked only when the automation runs; a stale one shows up as a failed run.
Response
201 Created with {"uuid": "..."}.
Modify Automation
PATCH /api/v1/projects/{project.uuid}/automations/{automation.uuid}
Updates any of name, is_active, event, conditions, actions. At least one must be present. conditions and
actions replace the stored value. The result is checked against the catalog as on create. To switch an automation
off, send {"is_active": false}.
PUT requires name and event; the other fields are optional.
204 No Content.
Delete Automation
DELETE /api/v1/projects/{project.uuid}/automations/{automation.uuid}
Deletes the automation and its run log. 204 No Content.
List Automation Runs
GET /api/v1/projects/{project.uuid}/automations/{automation.uuid}/runs
Returns runs newest first, 50 per page (page? query param, from 1). Runs older than 30 days are deleted.
Run Structure
| Field | Type | Description |
|---|---|---|
| uuid | uuid | Run ID |
| status | string | success or failed |
| error | ?string | Why it failed: an error code such as ORDER_REQUIRED, WEBHOOK_URL_INVALID, or a JSON list of stock shortages |
| context | object | {event, payload}; deferred: true if an email or webhook failed to be queued after commit |
| created_at | ISO8601 datetime | When it ran |
A success run means the actions completed and emails and webhooks were queued. A webhook that later fails to deliver
does not change the run's status.
[
{
"uuid": "0199b1aa-0000-7000-8000-000000000001",
"status": "failed",
"error": "[{\"material_uuid\":\"0199a2c0-7a1b-7c2d-8e3f-a0b1c2d3e4f5\",\"needed\":12.5,\"available\":8,\"item_type\":\"material\",\"name\":\"Blackout 605\",\"unit\":\"sm\"}]",
"context": {
"event": "order_status_changed",
"payload": {
"order_uuid": "0199a4e2-7b1c-7c3e-9d2a-5f4e8b1c0a11",
"status_uuid": "0198f1c0-2d4a-7e11-8b3f-1a2b3c4d5e6f",
"entity_type": "order",
"entity_uuid": "0199a4e2-7b1c-7c3e-9d2a-5f4e8b1c0a11"
}
},
"created_at": "2026-10-01T11:02:41.000Z"
}
]
Errors
| Status | Code | When |
|---|---|---|
| 400 | REQ_VALIDATION_FAILED |
The body does not match the schema |
| 400 | REQ_NO_DATA_PROVIDED |
Modify: the body has no fields |
| 400 | AUTOMATION_INVALID |
The automation does not fit the event catalog; reason says why, see below |
| 403 | AUTH_INSUFFICIENT_SCOPE |
The token lacks projects or automations |
| 403 | AUTH_NO_PROJECT_ACCESS |
The user's role lacks can_edit_project |
| 404 | RES_NOT_FOUND |
The project or automation does not exist |
AUTOMATION_INVALID carries a reason object:
reason.kind |
Other fields | Meaning |
|---|---|---|
unknown_event |
event |
No such event |
condition_required |
condition |
A required condition is missing |
action_not_allowed |
event, action_type |
The action is not offered under this event |
param_required |
action_type, param |
A required action parameter is missing |
param_not_allowed |
action_type, param |
The value is not available under this event, e.g. assignee: "inherit" under scheduled or order_created |
{
"id": 4010,
"code": "AUTOMATION_INVALID",
"message": "The automation does not match the event catalog: unknown event, an action unavailable for it, or a missing required value.",
"reason": {
"kind": "action_not_allowed",
"event": "scheduled",
"action_type": "warehouse_spend_bom"
}
}