Bind an Action to a Trigger
An action is a Tengo script that runs when a trigger fires on a resource it is bound to. The flow has two writes: create the action with its code and trigger, then attach it to one or more objects. See Action and the trigger lists on Booking and Checkin.
For drafting actions through natural language, see Generate Action.
1. Create the Action
POST /action
{
"name": "Notify booker when meeting starts",
"description": "Sends a Slack DM to the booker 5 minutes before the booking starts.",
"trigger": "booking_created",
"owner_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"code": "/* tengo source */"
}name, trigger, code, and owner_id are required. Optional fields:
| Field | Effect |
|---|---|
public | true lets actions outside the owner's organization attach this action to their own objects. Default is false. |
continue_on_fail | When false, a failed action aborts the trigger chain on that event. Default is false. |
dedup_mode | per_action or per_attachment. Controls whether identical concurrent events deduplicate by action or per attached object. Default is per_attachment. |
input | Schema definition used to validate input on each attachment. |
Response:
{
"data": {
"id": "33333333-4444-5555-6666-777777777777",
"name": "Notify booker when meeting starts",
"description": "Sends a Slack DM to the booker 5 minutes before the booking starts.",
"code": "/* tengo source */",
"public": false,
"trigger": "booking_created",
"created_by": "e992bfc1-0336-42c5-bd0a-4f4804a9fd24",
"owner_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"input": {},
"continue_on_fail": false,
"dedup_mode": "per_attachment",
"created_at": "2026-05-15T10:00:00.000Z",
"updated_at": "2026-05-15T10:00:00.000Z"
},
"request_id": "c1d2e3f4-a5b6-7890-cdef-123456789012",
"count": 1
}Capture data.id as action_id.
2. Attach the Action to an Object
POST /action/{action_id}/object
{
"object_type": "public.bookable",
"object_id": "c3d4e5f6-a7b8-9012-cdef-345678901234",
"input": { "channel": "#room-events" }
}object_type, object_id, and input are all required. reason and priority are optional. input here is the per-attachment input: the action reads it as ctx.input on every run.
Response:
{
"data": {
"id": "44444444-5555-6666-7777-888888888888",
"action_id": "33333333-4444-5555-6666-777777777777",
"object_id": "c3d4e5f6-a7b8-9012-cdef-345678901234",
"object_type": "public.bookable",
"input": { "channel": "#room-events" },
"reason": null,
"priority": 0,
"created_by": "e992bfc1-0336-42c5-bd0a-4f4804a9fd24",
"owner_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"created_at": "2026-05-15T10:01:00.000Z",
"updated_at": "2026-05-15T10:01:00.000Z"
},
"request_id": "d2e3f4a5-b6c7-8901-def0-234567890123",
"count": 1
}The convenience routes mounted on each parent scope wrap the same call: POST /bookable/{id}/action, POST /location/{id}/action, POST /organization/{id}/action, POST /bookable_type/{id}/action, POST /capability/{id}/action, POST /code/{id}/action. Use whichever matches the place you want the action to live.
Running
Once attached, the action fires automatically on every matching trigger event on that object. Triggers from a parent scope (a type, a location, an organization) also flow down to actions attached at any descendant level.
Updating and Removing
PUT /action/{id} rewrites the action body and code. PUT /action/{id}/object/{object_id} updates an attachment's input or priority. DELETE /action/{id}/object/{object_id} removes the attachment without touching the action. DELETE /action/{id} removes the action and every attachment.
See Also
- Generate Action for drafting through AI.
- Generate Action Code for refining a draft.
- Action for the runtime model.