> ## Documentation Index
> Fetch the complete documentation index at: https://docs.salesive.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Workflow actions and triggers

> Add your own steps and events to Salesive workflows. Merchants who install your app see them in the workflow builder under your app's name.

Salesive workflows are the store's automations: "when an order is paid, wait a day, then
email the customer". Your app can plug into them in two ways:

* **Actions** are steps your app performs. A merchant adds "Send an SMS (Your App)" to a
  workflow, maps values into its inputs, and when a run reaches the step Salesive sends a
  signed request to your app. What you answer is available to the steps after it.
* **Triggers** are events your app reports. A merchant starts a workflow from "Loyalty tier
  changed (Your App)", and your server fires it with one API call.

Both appear only on stores that have your app installed.

## Declaring them

Open your app in **Apps > Developer**, then fill in **Workflow steps** with JSON:

```json theme={null}
{
  "actions": [
    {
      "id": "send_sms",
      "label": "Send an SMS",
      "description": "Texts a phone number",
      "path": "/salesive/workflow/send-sms",
      "inputSchema": {
        "type": "object",
        "properties": {
          "to": { "type": "string", "title": "Phone number" },
          "message": { "type": "string", "title": "Message", "maxLength": 640 },
          "priority": { "type": "string", "enum": ["normal", "high"] }
        },
        "required": ["to", "message"]
      }
    }
  ],
  "triggers": [
    {
      "id": "tier_changed",
      "label": "Loyalty tier changed",
      "payloadSchema": {
        "type": "object",
        "properties": {
          "customerId": { "type": "string" },
          "tier": { "type": "string" },
          "points": { "type": "integer" }
        },
        "required": ["customerId", "tier"]
      }
    }
  ]
}
```

Rules:

* Up to 20 actions and 20 triggers. An `id` is lower case letters, numbers, `_` or `-`,
  starts with a letter, at most 48 characters, and is unique in its list. `label` is
  required (80 characters), `description` is optional (300).
* `path` is a path on the **host of your webhook URL** for the install's environment, for
  example `/salesive/workflow/send-sms`. No host, query string or `..`.
* Schemas are a small part of JSON Schema: an object with flat `properties` (at most 30) of
  type `string`, `number`, `integer` or `boolean`. Each may have `title`, `description`,
  `enum`, `maxLength` (strings), `minimum` and `maximum` (numbers), and `required` lists
  the properties that must be set. Nested objects, arrays and `$ref` are not supported.
* Declaring actions needs a webhook URL, because that is where actions are sent and its
  secret is what signs them.

Changes take effect immediately on every store that has your app.

## Receiving an action

```http theme={null}
POST https://your-app.example.com/salesive/workflow/send-sms
Content-Type: application/json
User-Agent: Salesive-Workflows/1.0
X-Salesive-Topic: workflow/action
X-Salesive-Shop-Id: 665f0c...
X-Salesive-App-Client-Id: app_0f3c...
X-Salesive-Event-Id: wf:665f...:evt-1:n1
X-Salesive-Action-Id: send_sms
X-Salesive-Hmac-SHA256: <base64 signature>
```

```json theme={null}
{
  "id": "wf:665f...:evt-1:n1",
  "type": "workflow.action",
  "actionId": "send_sms",
  "shopId": "665f0c...",
  "inputs": { "to": "+2348000000000", "message": "Thanks for your order!" },
  "run": {
    "workflowId": "6660aa...",
    "runId": "wf:6660aa...:evt-1",
    "stepId": "n1",
    "iteration": null,
    "test": false,
    "trigger": { "id": "evt-1", "topic": "orders/paid", "resourceId": "6661bb...", "occurredAt": "2026-11-05T10:00:00.000Z" }
  },
  "occurredAt": "2026-11-05T10:00:01.000Z"
}
```

* **Verify the signature** exactly as for [webhooks](https://docs.salesive.com/apps-api/webhooks.md): base64
  HMAC-SHA256 of the raw body with your webhook signing secret. A development install uses
  your development webhook URL and its own secret; production uses the production pair.
* **Inputs** have already been checked against your `inputSchema`, and numbers and booleans
  arrive as JSON numbers and booleans.
* **`run`** tells you where the call comes from. It carries ids and the trigger's topic,
  never the trigger's data or customer details: you receive what the merchant mapped into
  your inputs, nothing more. `test` is true for a test run from the builder.
* **Be idempotent.** A call that times out or gets a 5xx is retried (3 attempts in all),
  always with the same `id` and `X-Salesive-Event-Id`. Each pass of a "For each" loop has
  its own id (`iteration` is set). Ignore an id you have already handled.

### Answering

Answer within 10 seconds with a 2xx. To hand values to later steps, return JSON with an
`outputs` object:

```json theme={null}
{ "outputs": { "messageId": "sms_123", "segments": 1 } }
```

Later steps read them as `{{nodes.<step>.outputs.messageId}}`, or `{{vars.<name>.messageId}}`
when the merchant saves the result under a name.

| Your answer | What happens |
| - | - |
| 2xx | The step succeeds. |
| 4xx (not 429) | The step fails at once, showing your `error` or `message` field to the merchant. Use it for inputs you cannot act on. |
| 429, 5xx, timeout | Retried with backoff, then the step fails. |
| 3xx | The step fails. Redirects are never followed. |

Your address must be a public `https` host on port 443 (or `http` on 80). Private and
local addresses are refused, so to test a development install from your machine use a
public tunnel (for example `cloudflared` or `ngrok`) as your development webhook URL.

## Firing a trigger

Your app needs the `WRITE_WORKFLOW_TRIGGERS` scope. Then:

```bash theme={null}
curl -X POST https://api.salesive.com/api/v1/workflow-triggers/tier_changed \
  -H "Authorization: Bearer $APP_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: tier-665f-gold" \
  -d '{ "customerId": "665f...", "tier": "gold", "points": 1200 }'
```

```json theme={null}
{ "status": 202, "success": true, "message": "Accepted", "data": { "eventId": "tier-665f-gold", "topic": "app_0f3c.../tier_changed" } }
```

* Workflows of **the store your token belongs to** that start from this trigger run, with
  your payload as `{{trigger.data.*}}`. No other store is affected.
* The body must match `payloadSchema` exactly: required fields present, declared types,
  no extra fields. A mismatch is a `400` listing the fields.
* `Idempotency-Key` (optional, `[A-Za-z0-9._:-]`, up to 128 characters) makes a retry safe:
  the same key never starts the same workflow twice.
* The answer is `202` whether or not any workflow uses the trigger.

| Status | Meaning |
| - | - |
| 202 | Accepted. |
| 400 | The payload does not match `payloadSchema`. |
| 403 | Missing `WRITE_WORKFLOW_TRIGGERS`, or not an app token. |
| 404 | Your app does not declare this trigger. |
| 413 | The payload is over 64 KB. |
| 429 | More than 120 triggers a minute for this store. Retry after a minute. |

## When a merchant uninstalls your app

Your tokens stop working, so your triggers cannot fire. Workflows that use your actions or
triggers are paused, and the builder shows your steps as unavailable until the merchant
reinstalls your app or removes them. Workflows containing app steps cannot be sold as
marketplace templates.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.