> ## Documentation Index
> Fetch the complete documentation index at: https://docs.heylua.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Subscribe to integration events

> Wake the agent, or call your own webhook or trigger, when something changes in a connected integration

After this guide, a connected [integration](/concepts/integrations) delivers an event such as a new Linear issue either to the agent as a background turn or to a webhook or [trigger](/concepts/triggers) URL you choose.

*Verified against lua-cli 3.33.0.*

**Before you begin**

* A connection at agent scope (see [Connect a Unified.to integration](/integrations/connect)); personal connections have no integration webhooks.
* Its connection ID from `lua integrations list`.
* For `--hook-url`, a deployed [webhook](/build/handle-a-webhook) or [trigger](/build/create-a-trigger) URL.

<Steps>
  <Step title="List the events a type offers">
    Each event is `<object>.<event>`; `[virtual]` events are polled by Unified.to at an interval you choose, `[native]` ones are pushed by the provider.

    ```bash theme={null}
    lua integrations webhooks events --integration linear --ci
    ```

    ```text Output theme={null}
    ============================================================
    📡 Available Trigger Events for integration linear
    ============================================================

      task_task.created [virtual]
        A new Linear issue was created.
        Filters: parent_id, project_id, status, updated_gte, user_id

      task_task.updated [virtual]
        An existing Linear issue was updated.
        Filters: parent_id, project_id, status, updated_gte, user_id

    …
    ────────────────────────────────────────────────────────────
    Total: 4 event(s) available
    ```

    `--connection <id>` lists the events of an existing connection instead; `--json` prints `{ source, events, defaults }`.
  </Step>

  <Step title="Create the subscription">
    Name the connection, the event, and the destination; for a virtual event add `--interval` in minutes (minimum 1; the interactive menu offers 1 to 2880). Without `--hook-url` the CLI asks where to send events, so a headless run passes the agent's own address explicitly.

    ```bash theme={null}
    lua integrations webhooks create --connection 6aa3c41eecbe5884460c797c \
      --object task_task --event created \
      --hook-url https://api.heylua.ai/webhook/unifiedto/data --interval 5 --ci
    ```

    The same subscriptions can be made at connect time with `lua integrations connect … --triggers task_task.created,task_task.updated` (add `--hook-url <url>` for your URL); a virtual event subscribed this way polls every minute.
  </Step>

  <Step title="Decide what receives the event">
    With the agent's address as the destination, each delivery starts a background turn on the channel `integration:<type>` (`integration:linear`), as the user who connected the integration. The model receives the message `[Webhook Trigger] <event description>` and a context block holding the event as JSON; `data` holds the changed objects in Unified.to's model for that object type, each with its `id`.

    ```json theme={null}
    {
      "source": "unified",
      "integration": "linear",
      "objectType": "task_task",
      "event": "created",
      "friendlyDescription": "A new Linear issue was created.",
      "connectionId": "6aa3c41eecbe5884460c797c",
      "webhookType": "virtual",
      "data": [
        {
          "id": "9f2c1e4a-7b3d-4c58-a1e6-0d2f5b8c9e11",
          "name": "Checkout button unresponsive on Safari",
          "status": "OPENED",
          "url": "https://linear.app/acme/issue/ENG-142",
          "created_at": "2026-09-12T09:14:03.000Z"
        }
      ]
    }
    ```

    The block is cut at 50,000 characters; what happens next is up to the persona and the agent's tools. Delivery is at most once: the platform acknowledges every event on receipt and never retries or deduplicates, so a redelivered event wakes the agent again. With `--hook-url`, Unified.to posts directly to your webhook or trigger URL, with the changed objects in `data` and the subscription's `integration_type`, `object_type`, `event`, and `connection_id` under `webhook`.
  </Step>

  <Step title="Pause, resume, or delete">
    Pause one subscription by ID or every subscription of a connection; resume the same way. Delete is by ID only.

    ```bash theme={null}
    lua integrations webhooks pause --webhook-id <id> --reason "quiet hours" --ci
    lua integrations webhooks resume --connection-id 6aa3c41eecbe5884460c797c --ci
    lua integrations webhooks delete --webhook-id <id> --ci
    ```
  </Step>

  <Step title="Verify">
    List the subscriptions: each prints as `<object>.<event> (poll|push, agent trigger|custom URL)` with its ID, and a paused one carries `(paused by you)`.

    ```bash theme={null}
    lua integrations webhooks list --ci
    ```

    Then make the change in the provider (create an issue) and, for a virtual event, wait one interval. `lua logs --type user_message --limit 20` lists the wake-up turn as `[Webhook Trigger] <event description>` with `Channel: integration:<type>`; a custom URL delivery shows in your webhook's logs (see [Logs and debugging](/ship/logs-and-debugging)).
  </Step>
</Steps>

## Options you may need

### The two `triggers` commands

`lua integrations triggers <action>` is an alias of `lua integrations webhooks <action>`, and the CLI calls a subscription a trigger elsewhere too: the `--triggers` flag on `connect`, the `Available Trigger Events` heading of `events`, the `agent trigger` destination in `list`, and the `[Webhook Trigger]` prefix on the message the agent receives. The top-level `lua triggers` command manages platform triggers, which are unrelated: pasteable URLs that invoke the agent on demand (see [Create a trigger](/build/create-a-trigger)). The [comparison table](/concepts/webhooks#webhooks-triggers-and-integration-webhooks) sets the three side by side.

### Start a workflow once per event

For a durable record, point `--hook-url` at a trigger whose `transform` returns `{ startWorkflow, idempotencyKey }`: the run is created in the delivery request, and a repeated post with the same object IDs returns the same run instead of starting another.

```ts src/triggers/linear-issue.trigger.ts theme={null}
import { defineTrigger } from 'lua-cli';
import { z } from 'zod';

export default defineTrigger({
  name: 'linear-issue',
  description: 'Start the triage workflow for each Linear issue Unified.to reports',
  inputSchema: z.object({
    data: z.array(z.object({ id: z.string() }).passthrough()),
    webhook: z.object({ object_type: z.string(), event: z.string() }),
  }),
  transform: (ctx) => {
    const ids = ctx.body.data.map((item: { id: string }) => item.id).join(',');
    return {
      startWorkflow: {
        name: 'triage-issue',
        input: { issues: ctx.body.data },
        idempotencyKey: `${ctx.body.webhook.object_type}:${ctx.body.webhook.event}:${ids}`,
      },
    };
  },
});
```

Unified.to signs each post with `sig256` over a secret only the platform holds, so your destination can't verify it, and the post carries no `x-lua-signature`: a `LuaWebhook` with `secret` set answers it 401. Prefer a trigger URL, whose token is the credential (`lua triggers rotate-token` replaces it), to an unsigned webhook URL, which anyone knowing the agent ID and webhook name can call.

### Filters

`--json` on `events` lists each event's `availableFilters`, but the CLI has no flag to set them: a subscription made with the CLI receives every occurrence of the event.&#x20;

## If it isn't working

<AccordionGroup>
  <Accordion title="Event 'task_task.deleted' is not supported.">
    The object and event pair isn't offered for that connection. Run `lua integrations webhooks events --connection <id>` and use one of the listed pairs.
  </Accordion>

  <Accordion title="✖ error: Interactive prompt required but --ci flag is set.">
    `create` needed a prompt: `--connection`, `--object`, `--event`, or `--hook-url` is missing, or the event is virtual and `--interval` is missing. Pass them all.
  </Accordion>

  <Accordion title="The agent doesn't react">
    `lua integrations webhooks list` shows the subscription as `(paused by you)` or `(credit-suspended)`, or the polling interval hasn't elapsed. Resume it with `lua integrations webhooks resume --webhook-id <id>`; a credit-suspended subscription resumes only after credits are added. If it is active, `lua logs --type agent_error --limit 20` shows a delivered turn that failed, `Channel: integration:<type>` under the entry; `--type agent_response` shows one that ran. No `--type` filters by channel.
  </Accordion>
</AccordionGroup>

## Next steps

<Columns cols={2}>
  <Card title="Handle a webhook" href="/build/handle-a-webhook">Receive a `--hook-url` delivery in your own code.</Card>
  <Card title="About triggers" href="/concepts/triggers">Platform triggers, and how they differ from integration webhooks.</Card>
  <Card title="Manage integration MCP tools" href="/integrations/mcp">The tools a woken agent can call.</Card>
  <Card title="lua integrations reference" href="/reference/cli/integrations">Every `webhooks` option.</Card>
</Columns>
