Developer API, MCP and trigger simulation

You can inspect your triggers, switch them on or off, read event history, and simulate a trigger from your own code or from an AI assistant. Workflow Trigger Extensions exposes a REST API and an MCP server, both managed on the Developer page.

Simulating a trigger

Testing a Flow workflow normally means making the real thing happen in your store - editing a product, placing an order, waiting for a poll. Simulation removes that wait: pick a trigger, point it at a resource, and it fires as if the real event had just happened.

Two ways to run one:

  • In the app. Open any event in Event History and choose Simulate again. It re-fires that exact event.
  • Over the API or MCP, with a resource ID or a past event.

Simulation is deliberately faithful. It does not build a shortcut payload - it goes through the same pipeline a real Shopify webhook does, so what your workflow receives is exactly what it would receive in production.

Dry run firstbash
curl -X POST https://your-app-url/api/v1/triggers/product-update-trigger/simulate \
  -H "Authorization: Bearer ftk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"resourceId":"gid://shopify/Product/123456789","dryRun":true}'

A dry run validates the trigger, resolves the topic, and shows you the payload - and fires nothing. Remove dryRun to fire it for real. You can also pass {"fromHistoryId":"456"} to re-fire a past event instead of building a new one.

Triggers driven by polling rather than a webhook cannot be simulated; they report simulatable: false in the trigger list.

API keys

Create a key on the Developer page and pick its access level:

  • Read only - list triggers and their state, read event history, stats and permissions.
  • Read & write - also switch triggers on and off.
  • Read, write & execute - also simulate triggers.

The full key is shown once, at creation. Keys are stored hashed and can be revoked at any time. Send it as a Bearer token:

Authorization: Bearer ftk_your_key_here

Simulation sits behind its own level for the reason above: it runs your automations for real, so a key used for everyday reads cannot fire them.

REST API

Method Path Level Purpose
GET /api/v1 none API index - confirms the API is up
GET /api/v1/me read Check auth and see your key's level
GET /api/v1/triggers read Every trigger, with its state for your store
GET /api/v1/triggers/:handle read One trigger in detail
PUT /api/v1/triggers/:handle write Switch one trigger on or off
PUT /api/v1/triggers write Switch many on or off in one call
POST /api/v1/triggers/:handle/simulate execute Simulate a trigger
GET /api/v1/history read List trigger events
GET /api/v1/history/:id read Get one event, with its payload
GET /api/v1/stats read Totals, success rate, status breakdown
GET /api/v1/permissions read Which data permissions you granted, and what each unlocks

GET /api/v1/triggers is the useful one for setup: for every trigger it tells you the permission it needs, whether that permission is granted, whether the trigger is switched on, and where in the app to manage it.

Switching triggers on and off

PUT is used rather than PATCH because the call is idempotent - replaying it after a timeout cannot double-apply anything, which matters when an assistant is driving the API.

Enable one triggerbash
curl -X PUT https://your-app-url/api/v1/triggers/order-tags-added-trigger \
  -H "Authorization: Bearer ftk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"enabled":true}'

Enabling never grants a permission. If the trigger needs one you have not granted, the call still succeeds and tells you exactly what is missing and where to grant it:

{
  "enabled": true,
  "scopeGranted": false,
  "missingScopeLabels": ["Order Data Access"],
  "permissionsUrl": "https://admin.shopify.com/store/.../app/permissions?permission=read_orders",
  "warning": "Enabled, but this trigger cannot fire until Order Data Access is granted."
}

That link opens the Permissions page scrolled straight to the card you need.

Add "sync": true to also start a data sync, so the trigger works on records that already exist rather than only on ones created from now on.

To switch several at once, PUT /api/v1/triggers accepts an explicit list or a whole category:

{ "enabled": true, "handles": ["order-tags-added-trigger", "order-note-changed-trigger"] }
{ "enabled": true, "category": "orders" }

MCP server

The MCP tab on the Developer page shows the server URL and a ready-to-copy connect command for Claude, Cursor, VS Code, Gemini CLI and others. Tools mirror the REST endpoints, and the tool set reflects your key's level - a read-only key does not see the write or execute tools at all.

Tool Level What it does
list_triggers read Every trigger and its state
list_history read Recent trigger events
get_event read One event including its payload
get_stats read Aggregate statistics
get_permissions read Granted permissions and what they unlock
get_trigger write One trigger in detail
set_trigger write Switch one trigger on or off
set_triggers_bulk write Switch several on or off at once
simulate_trigger execute Fire a trigger on demand

This is what makes an assistant genuinely useful for setup: it can list what exists, explain what a trigger needs, switch it on, fire a test event, and read back the result - without you leaving the conversation.

How your data is protected

Event payloads are returned with personal data masked: email addresses, phone numbers, card numbers and person-named fields become ***. Shopify resource IDs and business fields are left intact so the payload stays useful.

The masking works on field names and value patterns, so it is careful but not a guarantee - personal data inside a free-text field can still come through. Error messages are also stripped of internal diagnostic detail before they leave the server.

Next steps