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
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
5xxor unreachable destination still returns200from this endpoint, withdelivered: falseand the reason inerror. - 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
409if 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.
- 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.
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.
?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
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 underpython/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.