Quickstart
Create a trigger, capture a real payload, and run a workflow from it.
This walks the whole path once, in the order that avoids guesswork: create the URL, send a real request to it without running anything, then build the handler from the payload you captured.
1. Create the trigger
Go to Events → Triggers and choose Create Trigger.
| Field | Notes |
|---|---|
| Trigger Name | For you. Shown in the triggers table, the monitor and the activity breakdown. |
| Trigger Code | Generated for you. It is the public part of the URL and must be unique across the platform. |
| Authentication | Optional. See Protecting the URL below. |
Save it, then copy the URL from the table. It looks like this:
https://service.ensembleapp.dev/api/trigger/AbC123XyZCopy the URL from the triggers table rather than assembling it by hand. It points at the API server, which is a different host from the app you are reading it in.
2. Capture a real payload
You could write handler parameters from the provider's documentation, but the field names and nesting are easier to get right from an actual request. Send one to the inspect endpoint, which records the request and runs nothing:
curl -X POST https://service.ensembleapp.dev/api/trigger/inspect/AbC123XyZ \
-H 'Content-Type: application/json' \
-d '{ "event": "order.created", "order": { "id": "A-1001", "total": 49.5 },
"customer": { "email": "sam@example.com" } }'Better still, point the real provider at the inspect URL for one event. That gives you the payload they actually send, headers included, rather than the one their docs describe.
The request now appears under Recent Requests on the trigger's monitor page, with its body and parameters.
3. Decide how events are classified
If this trigger only ever does one thing, skip this step: leave the trigger with no event type path, leave your handler's event type empty, and every request runs it.
If one URL carries several kinds of event — most providers send order.created and
order.refunded to the same endpoint — set the trigger's event type path to an
expression over the payload:
${body.event}Read Event types before setting this. The matching rule has one edge that surprises people, and it decides whether your handler ever runs.
The event type path is currently settable only through the API, not the trigger form:
curl -X PUT https://service.ensembleapp.dev/api/event-triggers/42 \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{ "eventTypePath": "${body.event}" }'4. Create the handler
Go to Events → Handlers, select your trigger, and choose Create New Event Handler.
| Field | What to put in it |
|---|---|
| Name | For you. It is what the outcome log names when something fails. |
| Handler Type | Workflow. See the note on orchestration in Handlers. |
| Workflow | The workflow to start. |
| Event Type | order.created, to match what step 3 classifies. Empty only if the trigger has no event type path. |
| User Expression | Who the workflow runs as. ${body.customer.email} resolves a tenant user by email. |
| Workflow Parameters | JSON mapping the payload into the workflow's inputs. |
Workflow parameters are JSON whose values are expressions over the same payload:
{
"orderId": "${body.order.id}",
"orderTotal": "${body.order.total}",
"customerEmail": "${body.customer.email}"
}5. Fire it for real
Post the same payload to the live URL, without inspect:
curl -X POST https://service.ensembleapp.dev/api/trigger/AbC123XyZ \
-H 'Content-Type: application/json' \
-d '{ "event": "order.created", "order": { "id": "A-1001", "total": 49.5 },
"customer": { "email": "sam@example.com" } }'A successful call answers with one result per handler that ran:
{
"success": true,
"message": "Trigger executed successfully",
"data": {
"triggerCode": "AbC123XyZ",
"triggerName": "Orders",
"handlersExecuted": 1,
"results": [
{
"handlerId": 7,
"handlerName": "Fulfil order",
"handlerType": "workflow",
"status": "success",
"result": { "state": "started", "workflowId": "wf-fulfil", "runId": "..." }
}
]
}
}"state": "started" is the important word. The workflow has been accepted, not finished.
status: "success" on the handler means it was dispatched without error.
Reading a failure
The response status tells you which stage rejected the request.
| Status | Meaning | Where to look |
|---|---|---|
404 Trigger not found | The code is wrong, or the trigger is archived. | The triggers table, with Show archived on. |
401 | Basic auth is configured and the credentials did not match. | The trigger's authentication settings. |
404 No handlers found for this trigger | The request arrived and was classified, and nothing matched. | Event types. This is the common one. |
200 with a handler status: "error" | A handler was matched but failed to start. | The error field on that result, and the monitor. |
500 | The platform failed. | Server logs. |
An archived trigger answers 404 exactly as an unknown code does. Archiving is how you
retire a URL; it keeps the request history and stops accepting new events.
Protecting the URL
A trigger URL is public and unauthenticated by default. Anyone who learns the code can post to it, and the trigger code alone is the only thing standing between the internet and your workflows.
Set Authentication to Basic and give it a username and password, then have the caller
send them:
curl -X POST https://service.ensembleapp.dev/api/trigger/AbC123XyZ \
-u 'webhook-user:the-password' \
-H 'Content-Type: application/json' \
-d '{ "event": "order.created" }'OAuth 2 client credentials
The alternative to a shared password. The caller exchanges a client secret for a short-lived token and sends that instead:
# Once an hour, or whenever the token expires
TOKEN=$(curl -s -X POST https://service.ensembleapp.dev/oauth/token \
-d grant_type=client_credentials \
-d client_id=cli_... \
-d client_secret=cls_... | jq -r .access_token)
curl -X POST https://service.ensembleapp.dev/api/trigger/AbC123XyZ \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{ "event": "order.created" }'Create the client under Credentials, on the API key you want it to act as. Then set the
trigger's Authentication to OAuth.
API key
The simplest of the three. Pick one of your API keys when configuring the trigger, and the caller sends it:
curl -X POST https://service.ensembleapp.dev/api/trigger/AbC123XyZ \
-H 'x-api-key: sk_...' \
-H 'Content-Type: application/json' \
-d '{ "event": "order.created" }'All three modes are scoped to one trigger. API key and OAuth are bound to a specific key when you configure the trigger: only that key, or an OAuth client belonging to it, is accepted. A key issued for something else cannot fire it, and neither can a client on a different key.