Skip to content

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

json
{
  "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:

FieldEffect
publictrue lets actions outside the owner's organization attach this action to their own objects. Default is false.
continue_on_failWhen false, a failed action aborts the trigger chain on that event. Default is false.
dedup_modeper_action or per_attachment. Controls whether identical concurrent events deduplicate by action or per attached object. Default is per_attachment.
inputSchema definition used to validate input on each attachment.

Response:

json
{
  "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

json
{
  "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:

json
{
  "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