Skip to main content
client.triggers covers the whole surface: the type catalog, instance lifecycle, destinations, the event log, and a live stream for local development. Concepts and the wire contract are in Triggers.

Create and enable

The connection is optional for types that need none. Omit both connection_id and connection_name for a connection-less type (e.g. engini_schedule, http_webhook) and the server supplies its own. For a type whose application does need one, pass it - the server does not guess. The CLI matches this from 0.23.0 - see the CLI reference.

Destinations

destination.put() returns one of two shapes. Setting the account default creates a destination the first time — and that response carries the signing secret. Re-pointing an existing default returns the bare destination with no secret. Test for the secret’s presence; do not assume either shape.
Rotating a secret and bringing back an auto-disabled destination are both one call:
Rotation has no overlap window. Deliveries signed with the old secret stop verifying the moment rotate_secret returns. Deploy the new secret to your receiver straight away, then replay anything that failed in between - see Debug and replay a failed delivery.

Sending a test event

POST /v1/triggers/{triggerId}/test pushes one synthetic, correctly-signed event through the real delivery path to the trigger’s destination and returns what the destination answered. It is API-only for now: neither SDK (Python 0.24.0, TypeScript 0.24.0) nor the CLI (0.24.0) wraps it yet, so call it over HTTP.
  • A broken receiver is a successful test. A 5xx or unreachable destination still returns 200 from this endpoint, with delivered: false and the reason in error.
  • Nothing is stored. A test event never appears in the event log or the stream, and never counts against deduplication. Its id starts te_test_ and its body carries "test": true - both inside the signature, so a receiver can tell a test from real traffic.
  • Omit the body to get a default payload. The call is limited to 10 per minute per account, and answers 409 if the destination has auto-disabled.

Verifying a delivery

verify_webhook(payload, signature_header, secret) / verifyWebhook(payload, signatureHeader, secret) raises on any rejection - it does not return a boolean. That is deliberate: an ignored False is a webhook endpoint that accepts forgeries, and a raise cannot be ignored by accident. On success it returns the parsed event body.
Three ways to get this wrong, all of which produce a MAC that never matches:
  • Passing the secret without its esec_ prefix. The key is the whole string.
  • Passing a re-serialised body. Frameworks that parse JSON for you must be configured to hand over the raw bytes.
  • Widening the replay window. It defaults to 300 seconds either side of t (tolerance= / { tolerance } to change it); the default is the recommendation.
A full receiver - raw body, verification, deduplication, fast acknowledgement - is in the recipe Receive trigger events on a webhook.

Reading events

events.list is not identical in the two languages. Python takes no top and always auto-paginates to the full set. TypeScript takes top and pages. Code that assumes one shape will not port directly.

Streaming, for local development

subscribe is a second read path over the same events, not a delivery mechanism. Production delivery is a destination plus signature verification. Use the stream while you are building, to watch events arrive without exposing a public URL.
delivery on the stream is always the literal "pending". The stream reports dispatch, before anything has tried to POST. Real delivery state comes only from events.get(id).
Every stream ends — the gateway caps one connection at one hour of wall-clock time, an absolute cap the 20-second heartbeat does not defer, and the cut arrives with no goodbye frame. Both SDKs resume automatically with ?since=<last event id>, so the seam neither duplicates nor skips. Pass auto_resume=False / autoResume: false if you would rather handle it yourself. TypeScript only, since @engini/sdk 0.23.0: subscribe({ ..., signal }) takes an AbortSignal.
TypeScript
Aborting stops the subscription immediately, including mid-read on an idle connection - the generator returns normally rather than throwing, and the in-flight connection is aborted (not merely abandoned), so no lingering socket keeps the process alive after you stop iterating. The CLI’s engini triggers listen wires its own Ctrl-C handling to this same signal (see the machine contract). There is no Python equivalent - stop a Python stream by breaking out of the for loop. Two endings stop hard rather than resuming: A gap is surfaced, never papered over with a silent restart from “now” — the whole point of the cursor is that you get to decide what to do about missed events.

Typed errors

Each refines the status-mapped error it sits under, so existing catch blocks keep working.

Runnable examples

The Python SDK ships a worked set under python/examples/triggers/: the catalog and create flow, destinations, a webhook receiver that verifies signatures, subscribing to the stream, and reading events and recovering from a failure. There is no TypeScript equivalent yet; the flows translate directly using the method names above. For task-shaped walkthroughs in every language, see the Cookbook’s React to events recipes, starting with Receive trigger events on a webhook.