Automations API
Automations API
Automations execute automatic actions when events occur on your quotes. You can send emails, change statuses, fire webhooks, and more — all based on configurable conditions.
All endpoints require PRO plan and config:manage permission (Owner only).
List Automations
GET /api/automations
Lists all automations for the tenant, including execution counts.
Response 200 OK:
[
{
"id": "clxyz...",
"name": "Notify on acceptance",
"trigger": "QUOTE_ACCEPTED",
"isActive": true,
"conditions": [
{ "field": "total", "operator": "gt", "value": 5000 }
],
"actions": [
{ "type": "send_email", "to": "owner", "subject": "Quote accepted", "body": "..." }
],
"delayMinutes": 0,
"createdAt": "2026-04-01T10:00:00.000Z",
"_count": { "logs": 12 }
}
]Create Automation
POST /api/automations
Permission: config:manage | Plan: PRO
Request body:
{
"name": "Email on large quote sent",
"trigger": "QUOTE_SENT",
"conditions": [
{ "field": "total", "operator": "gt", "value": 10000 },
{ "field": "client.companyName", "operator": "is_not_empty" }
],
"actions": [
{
"type": "send_email",
"to": "client",
"subject": "Your quote is ready",
"body": "Thank you for your interest. Your quote is attached."
}
],
"delayMinutes": 5
}| Field | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Name (1-100 characters) |
| trigger | string | Yes | Event that triggers the automation (see table below) |
| conditions | array | No | Conditions that must be met (AND logic) |
| actions | array | Yes | At least one action to execute |
| delayMinutes | number | No | Minutes to delay before executing (default: 0) |
Response 201 Created: Returns the created automation.
Triggers (Events)
| Trigger | Description |
|---|---|
QUOTE_CREATED |
A new quote was created |
QUOTE_SENT |
A quote was sent to the client |
QUOTE_VIEWED |
The client viewed the quote in the portal |
QUOTE_ACCEPTED |
The client accepted the quote |
QUOTE_REJECTED |
The client rejected the quote |
QUOTE_WON |
The quote was marked as won |
QUOTE_LOST |
The quote was marked as lost |
QUOTE_EXPIRING |
The quote is about to expire (within 24 hours) |
Conditions
Conditions filter when the automation executes. All conditions must be met (AND logic).
{ "field": "total", "operator": "gt", "value": 5000 }| Field | Type | Required | Description |
|---|---|---|---|
| field | string | Yes | Field to evaluate (see available fields) |
| operator | string | Yes | Comparison operator |
| value | string/number | Depends | Value to compare (not required for is_empty/is_not_empty) |
Operators
| Operator | Description | Compatible types |
|---|---|---|
eq |
Equals | all |
neq |
Not equals | all |
gt |
Greater than | number |
lt |
Less than | number |
gte |
Greater than or equal | number |
lte |
Less than or equal | number |
contains |
Contains text | text |
starts_with |
Starts with | text |
is_empty |
Is empty (no value) | text |
is_not_empty |
Is not empty | text |
Available fields
Quote
| Field | Type | Operators |
|---|---|---|
status |
select | eq, neq |
currency |
select | eq, neq |
total |
number | eq, neq, gt, lt, gte, lte |
subtotal |
number | eq, neq, gt, lt, gte, lte |
tax |
number | eq, neq, gt, lt, gte, lte |
shippingCost |
number | eq, neq, gt, lt, gte, lte |
viewCount |
number | eq, neq, gt, lt, gte, lte |
quoteNumber |
number | eq, neq, gt, lt, gte, lte |
notes |
text | eq, neq, contains, starts_with, is_empty, is_not_empty |
Client
| Field | Type | Operators |
|---|---|---|
client.contactName |
text | eq, neq, contains, starts_with |
client.companyName |
text | eq, neq, contains, starts_with, is_empty, is_not_empty |
client.email |
text | eq, neq, contains, starts_with |
client.phone |
text | eq, neq, contains, is_empty, is_not_empty |
Product
Fields prefixed with items.* evaluate whether any product in the quote matches the condition.
| Field | Type | Operators |
|---|---|---|
items.productName |
text | eq, neq, contains, starts_with |
items.model |
text | eq, neq, contains, is_empty, is_not_empty |
items.unitPrice |
number | eq, neq, gt, lt, gte, lte |
items.quantity |
number | eq, neq, gt, lt, gte, lte |
items.discountPct |
number | eq, neq, gt, lt, gte, lte |
items.* fields use "any matches" logic — the condition is met if at least one product in the quote matches.
Actions
Each automation must have at least one action. They execute in order.
send_email
Sends an email.
{
"type": "send_email",
"to": "client",
"subject": "Your quote is ready",
"body": "We've prepared a personalized quote for you."
}| Field | Type | Required | Description |
|---|---|---|---|
| to | string | Yes | "client", "owner", or "custom" |
| customEmail | string | Conditional | Destination email (required if to is "custom") |
| subject | string | Yes | Email subject |
| body | string | Yes | Email body |
send_notification
Creates an in-app notification for the owner.
{
"type": "send_notification",
"title": "Quote accepted",
"body": "The client has accepted quote COT-0042"
}change_status
Changes the quote status.
{
"type": "change_status",
"newStatus": "SENT"
}Valid values: GENERATED, SENT, WON, LOST, ACCEPTED, REJECTED
add_label
Adds a label to the quote.
{
"type": "add_label",
"labelId": "clxyz..."
}create_reminder
Creates a follow-up reminder.
{
"type": "create_reminder",
"delayMinutes": 1440,
"message": "Follow up on this quote"
}fire_webhook
Sends a POST to an external URL with the quote data. Ideal for connecting with Zapier, Slack, or any external service.
{
"type": "fire_webhook",
"url": "https://hooks.zapier.com/hooks/catch/123456/abcdef/",
"headers": {
"Authorization": "Bearer my-secret-token"
},
"includeItems": true
}| Field | Type | Required | Description |
|---|---|---|---|
| url | string | Yes | Webhook URL (must be valid) |
| headers | object | No | Additional HTTP headers (e.g., Authorization) |
| includeItems | boolean | No | Include products in the payload (default: true) |
The payload sent has this structure:
{
"event": "QUOTE_SENT",
"timestamp": "2026-04-09T14:30:00.000Z",
"data": {
"quote": {
"id": "clxyz...",
"quoteNumber": 42,
"status": "SENT",
"total": 15000,
"subtotal": 13000,
"currency": "MXN",
"notes": "...",
"client": {
"id": "...",
"contactName": "María López",
"companyName": "Acme Corp",
"email": "maria@acme.com"
},
"items": [
{
"productName": "Premium service",
"model": "SRV-001",
"unitPrice": 13000,
"quantity": 1,
"discountPct": 0
}
]
}
}
}The webhook has a 5-second timeout. If the server doesn't respond in time, the action is logged as failed.
Get an Automation
GET /api/automations/:id
Permission: config:manage | Plan: PRO
Gets an automation with its last 10 executions.
Response 200 OK: Returns the automation with the logs array.
Update Automation
PATCH /api/automations/:id
Permission: config:manage | Plan: PRO
Updates an automation. All fields are optional. Includes isActive to enable or disable.
{
"isActive": false
}Response 200 OK: Returns the updated automation.
Delete Automation
DELETE /api/automations/:id
Permission: config:manage
Permanently deletes an automation and all its execution history.
Response 200 OK:
{
"ok": true
}This operation cannot be undone. All execution logs are also deleted.
Execution History
GET /api/automations/:id/logs
Permission: config:manage | Plan: PRO
Lists executions for an automation with pagination.
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | Page number |
| limit | number | 20 | Results per page (max 100) |
Response 200 OK:
{
"logs": [
{
"id": "clxyz...",
"automationId": "...",
"quoteId": "...",
"tenantId": "...",
"status": "COMPLETED",
"triggeredAt": "2026-04-09T14:30:00.000Z",
"completedAt": "2026-04-09T14:30:01.000Z",
"error": null
}
],
"total": 47,
"page": 1,
"totalPages": 3
}Possible statuses: TRIGGERED, COMPLETED, FAILED.