Ensemble Docs

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.

FieldNotes
Trigger NameFor you. Shown in the triggers table, the monitor and the activity breakdown.
Trigger CodeGenerated for you. It is the public part of the URL and must be unique across the platform.
AuthenticationOptional. See Protecting the URL below.

Save it, then copy the URL from the table. It looks like this:

https://service.ensembleapp.dev/api/trigger/AbC123XyZ

Copy 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.

FieldWhat to put in it
NameFor you. It is what the outcome log names when something fails.
Handler TypeWorkflow. See the note on orchestration in Handlers.
WorkflowThe workflow to start.
Event Typeorder.created, to match what step 3 classifies. Empty only if the trigger has no event type path.
User ExpressionWho the workflow runs as. ${body.customer.email} resolves a tenant user by email.
Workflow ParametersJSON 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.

StatusMeaningWhere to look
404 Trigger not foundThe code is wrong, or the trigger is archived.The triggers table, with Show archived on.
401Basic auth is configured and the credentials did not match.The trigger's authentication settings.
404 No handlers found for this triggerThe 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.
500The 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.

On this page