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

# Database Triggers

> Change data capture with polling

Database triggers watch external database tables for changes and automatically execute functions when new or updated rows are detected. This is poll-based Change Data Capture — no setup required on the source database.

**How it works:**

1. A separate CDC service polls the configured table at a fixed interval
2. It queries rows where `poll_column > last_bookmark` (e.g., `updated_at > '2026-03-01T10:00:00'`)
3. If new rows are found, they are batched into a single function call
4. The bookmark advances to the highest value in the batch
5. On first activation, the bookmark is set to `MAX(poll_column)` — no backfill of existing data

**Key properties:**

| Property                               | Description                                                                 |
| -------------------------------------- | --------------------------------------------------------------------------- |
| `name`                                 | Unique trigger name (per user)                                              |
| `database_connection_id`               | Which database connection to poll                                           |
| `schema_name`                          | Database schema (default: `public`)                                         |
| `table_name`                           | Table to watch                                                              |
| `operations`                           | `["INSERT"]`, `["UPDATE"]`, or both                                         |
| `function_namespace` / `function_name` | Function to execute when changes are detected                               |
| `poll_column`                          | Monotonically increasing column used as bookmark (e.g., `updated_at`, `id`) |
| `poll_interval_seconds`                | How often to poll (1–3600, default: `10`)                                   |
| `batch_size`                           | Max rows per poll (1–10000, default: `100`)                                 |
| `is_active`                            | Enable/disable without deleting                                             |
| `last_poll_value`                      | Current bookmark (managed automatically)                                    |
| `error_message`                        | Last error, if any (visible in UI)                                          |

The `poll_column` must be a column whose value only increases — timestamps, auto-increment IDs, or sequences. The column type is detected automatically and comparisons are cast to the correct type.

**Function input payload:**

When changes are detected, the target function receives all new rows in a single call:

```json theme={null}
{
  "table": "public.orders",
  "operation": "CHANGE",
  "rows": [
    {"id": 123, "status": "paid", "amount": 99.50, "updated_at": "2026-03-02T10:30:00Z"},
    {"id": 124, "status": "pending", "amount": 45.00, "updated_at": "2026-03-02T10:30:01Z"}
  ],
  "poll_column": "updated_at",
  "count": 2,
  "timestamp": "2026-03-02T10:30:05Z"
}
```

If a poll returns zero rows, no function call is made.

**Error handling:** On failure, the trigger logs the error to `error_message` and retries with exponential backoff (up to 60 seconds). The trigger continues retrying until deactivated or the issue is resolved.

**Limitations:**

* Cannot detect `DELETE` operations (poll-based limitation)
* Changes are detected with a delay equal to the poll interval
* The `poll_column` must never decrease — resetting it will cause missed or duplicate rows

**Endpoints:**

```
POST   /api/v1/database-triggers              # Create trigger
GET    /api/v1/database-triggers              # List triggers
GET    /api/v1/database-triggers/{name}       # Get trigger
PATCH  /api/v1/database-triggers/{name}       # Update trigger
DELETE /api/v1/database-triggers/{name}       # Delete trigger
```

**Declarative configuration:**

```yaml theme={null}
databaseTriggers:
  - name: "customer_changes"
    connectionName: "prod_database"
    tableName: "customers"
    operations: ["INSERT", "UPDATE"]
    functionName: "sync/process_customer"
    pollColumn: "updated_at"
    pollIntervalSeconds: 10
    batchSize: 100
```

***
