Storeware Docs

Automations

Triggers your app fires and actions your app does, inside the rules a merchant builds.

A merchant's automations are rules the shop runs by itself: when something happens, check a few things, then do some steps. Your app can add to both ends. A trigger is something your app says happened, such as a customer reaching a loyalty tier. An action is something your app does when a rule reaches that step, such as adding points.

Both are declared in your manifest. They appear in a shop once the merchant has installed the release that declares them, under your app's name, in the admin and in what the merchant assistant reads.

[[automations.triggers]]
key = "tier_reached"
label = "A customer reaches a tier"
description = "Fired when a customer's points take them into a new tier."

[[automations.triggers.fields]]
key = "tier"
label = "The tier"
type = "text"

[[automations.triggers.fields]]
key = "points"
label = "Their points"
type = "number"

[[automations.actions]]
key = "add_points"
label = "Add points"
url = "https://loyal.example.com/storeware/actions/add-points"

[[automations.actions.fields]]
key = "points"
label = "Points"
type = "number"
required = true

[[automations.actions.fields]]
key = "reason"
label = "Reason"
type = "text"

A trigger or action key is lower case letters and underscores, up to 32 characters, with no digits. A field key is lower case letters, digits and underscores. You may declare up to 20 triggers and 20 actions, each with up to 20 fields.

Trigger field typeWhat you sendA condition can test
textA string, up to 2000 charactersis, is not, one of, contains
numberA numberis, more than, at least, less than
booleantrue or falseis, is not
dateAn ISO 8601 date, such as 2026-11-07is, is not, known, not known
urlAn http or https address, up to 2000 charactersis, is not, one of, contains

Action fields are text (one line), multi_line_text, number, boolean, url (https) and select, which needs options.

Firing a trigger

Call POST /api/v1/app/events with your app's access token, naming the trigger by its key and sending its fields as data:

await fetch('https://api.storeware.ai/api/v1/app/events', {
  method: 'POST',
  headers: { authorization: `Bearer ${accessToken}`, 'content-type': 'application/json' },
  body: JSON.stringify({ trigger: 'tier_reached', data: { tier: 'gold', points: 1200 } }),
});

It answers 202 with the trigger as a rule names it, app:<your slug>:tier_reached, and the data that was kept. The shop's rules on that trigger run shortly after.

  • The trigger must be declared in the release the shop has installed. Anything else is refused with 400 app_trigger_not_declared.
  • A field you did not declare is dropped. A declared field of the wrong type is refused with 400 app_trigger_data_invalid, naming the field. A field you leave out, or send as null, is read as unknown.
  • data may be at most 16 KB.
  • Each installation may fire 60 triggers a minute and 1000 an hour. Past that the answer is 429 with Retry-After.

A trigger is not sent to any app's webhooks, your own included. It exists for the shop's automations alone.

A merchant writes a condition on your trigger's fields as data.<key>, so data.tier is gold or data.points is at least 1000.

Doing an action

When a rule reaches your action, Storeware sends a signed POST to the action's url:

{
  "storeId": "…",
  "automationId": "…",
  "runId": "…",
  "step": { "id": "bonus", "index": 1 },
  "action": "add_points",
  "fields": { "points": 50, "reason": "Order 1042" },
  "event": {
    "id": "…",
    "topic": "order.paid.v1",
    "fields": { "order.id": "…", "order.number": "1042" }
  }
}

fields are what the merchant filled in. Text may name a field of the event in braces, such as Order {order.number} or Welcome to {data.tier}, and arrives filled in. event is the event that started the rule, or null for a rule run on a schedule or by hand. Its fields use the names conditions use. For a platform event they are sent only when your app's scopes cover that event, so an app without order access is told the order's id and topic and nothing else. For an app's trigger they are the data that app sent.

Verify the request with verifyActionRequest. It is signed with your installation's secret as the kind action, so a webhook or a block request replayed at this address fails.

import { verifyActionRequest, IDEMPOTENCY_HEADER } from '@storeware/plugin-sdk/actions';

export async function POST(request: Request) {
  const rawBody = await request.text();
  const action = await verifyActionRequest({
    secret: installationSecret,
    rawBody,
    signatureHeader: request.headers.get('storeware-signature'),
  });
  const key = request.headers.get(IDEMPOTENCY_HEADER); // `<runId>:<step index>`

  if (!(await alreadyDone(key))) await addPoints(action.storeId, action.fields);
  return new Response(null, { status: 204 });
}

Answer with any 2xx once it is done. Any other status, no answer within 10 seconds, or an answer over 16 KB fails the step, and the merchant reads why on the rule's runs. A rule that fails ten times in a row switches itself off. The same run and step can reach you twice after a retry, with the same idempotency-key both times, so do the work once per key.

Backtests never call your app. When a merchant tries a rule against the last 30 days, your action is shown as what would have happened.

When your app is removed

If a merchant uninstalls your app, every rule that starts from one of your triggers or uses one of your actions is switched off, and the merchant is told an app it used was removed. Installing your app again and switching the rule back on is all it takes. A rule is also refused, when it is saved or switched on, if it names a trigger, action or field the installed release does not declare, so removing one from a new release stops rules from being switched on with it.

On this page