Triggers¶
Overview¶
Triggers (function-triggers) are the execution control layer in Centrali. A function contains the code; a trigger decides when and how that code runs.
Start here when you need something to happen after data arrives, on a schedule, on demand, or through a synchronous custom API. A single function can have multiple triggers — for example, the same order-processing function might run on new records (event-driven) and also be invocable manually for retries (on-demand).
Think of the execution model like this:
- Function = the code that runs
- Trigger = what starts that code, and whether it runs async or sync
- Automation = a multi-step workflow that coordinates one or more functions
Note
If you are setting up async inbound webhooks from Stripe, GitHub, Clerk, or another external system, start with Webhook Ingestion. This page explains the broader execution model around triggers, functions, endpoints, and automations.
The Execution Model in One Screen¶
Use this mental model before picking a trigger type:
- HTTP trigger: async inbound entry surface for webhook-style traffic
- Endpoint: synchronous request/response API surface
- Event-driven trigger: react when records change
- Scheduled trigger: run on time-based schedules
- On-demand trigger: invoke manually through the API or SDK
If one function is not enough, move up to Automations for multi-step workflows with branching and delays.
Trigger Types¶
Centrali supports five trigger types:
| Type | How It Fires | Best For |
|---|---|---|
| Event-driven | Automatically when records change | Workflows, notifications, data sync |
| Scheduled | On a time-based schedule | Reports, cleanup, periodic sync |
| On-demand | Manual API call or SDK invocation | Admin tools, testing, one-off tasks |
| HTTP | External HTTP request to a URL (fire-and-forget) | Webhooks from third parties (Stripe, GitHub, etc.) |
| Endpoint | HTTP request that returns function output | Custom APIs, form validators, data transformations |
Understanding Parameter Shapes
The #1 source of confusion with triggers is what data your function actually receives. The shape of executionParams is different for every trigger type — and for event-driven triggers, it varies by event. See the Trigger Parameters & Payload Shapes guide for complete examples of every shape.
Event-Driven Triggers¶
Event-driven triggers fire automatically when record events occur in your workspace. They are the most common trigger type.
Supported Events¶
Per-record events — fire once per record:
| Event | When It Fires |
|---|---|
record_created | After a new record is created |
record_updated | After an existing record is updated |
record_deleted | After a record is deleted (soft or hard) |
record_restored | After a deleted record is restored |
record_expired | When a record's TTL expires |
record_reverted | After a record is reverted to a previous version |
Aggregate events — fire once per bulk operation with all affected record IDs:
| Event | When It Fires |
|---|---|
records_bulk_created | After a bulk create operation |
records_bulk_updated | After a bulk update operation |
records_bulk_deleted | After a bulk delete operation |
There are also failure events (record_created_failed, record_updated_failed, record_deleted_failed, records_bulk_created_failed, records_bulk_updated_failed, records_bulk_deleted_failed) for error-handling workflows.
Bulk vs Batch: Choosing the Right Path¶
When working with multiple records, you choose between two paths that determine which events fire:
| Path | HTTP API | Compute Function API | Events | Best For |
|---|---|---|---|---|
| Bulk | POST /records/bulk | api.bulkCreateRecords() | 1 aggregate event | Processing all records together (indexing, batch notifications) |
| Batch | POST /records/batch | api.batchCreateRecords() | N per-record events | Processing each record individually (validation, per-record alerts) |
Both paths are available from both interfaces. Pick the path that matches your function's processing logic — not the interface you happen to be calling from.
How It Works¶
- You create a trigger linked to a function, specifying the event type and collection (via
recordSlug) - When a matching record event occurs, Centrali automatically executes your function
- The event data (record ID, record data, before/after values for updates) is passed as
executionParams
Example: Send notification on new order¶
curl -X POST "$CENTRALI_URL/data/workspace/$WORKSPACE/api/v1/function-triggers" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "notify-on-new-order",
"description": "Send Slack notification when a new order is created",
"functionId": "YOUR_FUNCTION_ID",
"executionType": "event-driven",
"triggerMetadata": {
"event": "record_created",
"recordSlug": "orders",
"params": {
"slackWebhookUrl": "https://hooks.slack.com/services/..."
}
}
}'
Accessing Event Data¶
In your function, the event payload is available via executionParams:
async function run() {
const { event, recordId, recordSlug, data } = executionParams;
if (event === "record_created") {
// data is the full record
api.log({ message: `New ${recordSlug} created`, recordId });
}
if (event === "record_updated") {
// data contains { before, after }
const statusChanged = data.before.data.status !== data.after.data.status;
if (statusChanged) {
api.log({ message: "Status changed", from: data.before.data.status, to: data.after.data.status });
}
}
return { success: true };
}
For full payload documentation, see the Event Payloads Reference.
When to Use Event-Driven Triggers¶
- Sending notifications when data changes
- Keeping derived data in sync (e.g., recalculating totals when line items change)
- Cascading updates across collections
- Audit logging
- Integrating with external systems in real time
Scheduled Triggers¶
Scheduled triggers run your function automatically based on time. There are three schedule types.
Interval¶
Run a function repeatedly every N seconds.
curl -X POST "$CENTRALI_URL/data/workspace/$WORKSPACE/api/v1/function-triggers" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "sync-inventory",
"functionId": "YOUR_FUNCTION_ID",
"executionType": "scheduled",
"triggerMetadata": {
"scheduleType": "interval",
"interval": 300
}
}'
Common intervals: 60 (every minute), 300 (5 minutes), 3600 (hourly), 86400 (daily).
Cron¶
Run on a cron schedule. Uses the standard 5-field format: minute hour day month weekday.
curl -X POST "$CENTRALI_URL/data/workspace/$WORKSPACE/api/v1/function-triggers" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "daily-report",
"functionId": "YOUR_FUNCTION_ID",
"executionType": "scheduled",
"triggerMetadata": {
"scheduleType": "cron",
"cronExpression": "0 9 * * 1-5",
"timezone": "America/New_York"
}
}'
| Expression | Description |
|---|---|
0 9 * * * | Every day at 9:00 AM |
0 9 * * 1-5 | Weekdays at 9:00 AM |
*/15 * * * * | Every 15 minutes |
0 0 1 * * | First day of each month at midnight |
Once¶
Run a single time at a specific datetime. The scheduledAt value must be in the future.
curl -X POST "$CENTRALI_URL/data/workspace/$WORKSPACE/api/v1/function-triggers" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "launch-campaign",
"functionId": "YOUR_FUNCTION_ID",
"executionType": "scheduled",
"triggerMetadata": {
"scheduleType": "once",
"scheduledAt": "2026-03-01T09:00:00Z",
"timezone": "America/New_York"
}
}'
Pause and Resume¶
Scheduled triggers can be paused and resumed without deleting them:
# Pause
curl -X PATCH "$CENTRALI_URL/data/workspace/$WORKSPACE/api/v1/function-triggers/TRIGGER_ID/pause" \
-H "Authorization: Bearer $TOKEN"
# Resume
curl -X PATCH "$CENTRALI_URL/data/workspace/$WORKSPACE/api/v1/function-triggers/TRIGGER_ID/resume" \
-H "Authorization: Bearer $TOKEN"
When to Use Scheduled Triggers¶
- Generating periodic reports
- Data cleanup and archival
- Syncing data with external systems
- Cache warming or invalidation
- Health checks and monitoring
On-Demand Triggers¶
On-demand triggers execute only when you explicitly invoke them via the API or SDK.
Creating and Executing¶
# Create the trigger with static configuration
curl -X POST "$CENTRALI_URL/data/workspace/$WORKSPACE/api/v1/function-triggers" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "manual-export",
"functionId": "YOUR_FUNCTION_ID",
"executionType": "on-demand",
"triggerMetadata": {
"params": {
"format": "csv",
"batchSize": 1000
}
}
}'
# Execute it with runtime parameters
curl -X POST "$CENTRALI_URL/data/workspace/$WORKSPACE/api/v1/function-triggers/TRIGGER_ID/execute" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"recordType": "orders",
"startDate": "2026-01-01"
}'
Using the SDK¶
const result = await centrali.triggers.invoke('manual-export-trigger-id', {
payload: {
recordType: 'orders',
startDate: '2026-01-01'
}
});
Two Types of Parameters¶
On-demand triggers distinguish between static and runtime parameters:
triggerParams— Set when creating the trigger. Good for API keys, configuration, and defaults.executionParams— Passed at execution time. Good for per-invocation data like record IDs or filters.
async function run() {
// Static config from trigger setup
const format = triggerParams.format; // "csv"
const batchSize = triggerParams.batchSize; // 1000
// Runtime data from this specific execution
const recordType = executionParams.recordType; // "orders"
const startDate = executionParams.startDate; // "2026-01-01"
// ... process data
return { success: true };
}
When to Use On-Demand Triggers¶
- Admin tools and dashboards
- Data migrations
- Testing and debugging functions
- User-initiated actions (export, recalculate)
- Retry workflows
HTTP Triggers¶
HTTP triggers create a public URL that queues your function when called. This is ideal for receiving webhooks from third-party services.
In the docs flow, HTTP triggers belong to the Receive path because they are the async ingest surface.
Creating an HTTP Trigger¶
curl -X POST "$CENTRALI_URL/data/workspace/$WORKSPACE/api/v1/function-triggers" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "stripe-webhook",
"functionId": "YOUR_FUNCTION_ID",
"executionType": "http-trigger",
"triggerMetadata": {
"path": "/payments/stripe",
"validateSignature": true,
"signingSecret": "whsec_abc123",
"signatureHeaderName": "stripe-signature",
"extractionPattern": "v1=([^,]+)",
"timestampExtractionPattern": "t=(\\\\d+)",
"hmacAlgorithm": "sha256",
"hmacEncoding": "hex",
"secretEncoding": "raw"
}
}'
The trigger URL will be:
Accessing the HTTP Payload¶
The parsed request body is available as executionParams.payload:
async function run() {
const event = executionParams.payload;
// Signature validation is configured on the trigger.
// Invalid requests never reach this function.
api.log({ message: 'Webhook received', type: event?.type });
return { success: true };
}
For provider-specific signature settings and the end-to-end accept-and-store pattern, see Webhook Ingestion.
When to Use HTTP Triggers¶
- Payment gateway callbacks (Stripe, PayPal)
- CI/CD pipeline notifications (GitHub, GitLab)
- Form submissions from external sites
- Third-party service integrations
- IoT device events
Endpoint Triggers¶
Endpoint triggers turn your function into a custom API endpoint. Unlike HTTP triggers (fire-and-forget), endpoint triggers wait for the function to complete and return its output directly in the HTTP response.
In the docs flow, endpoints belong to the React path. They are still externally callable, but they behave like synchronous custom APIs rather than queued webhook ingestion.
How It Works¶
- You create a trigger with
executionType: "endpoint"and a unique path - Centrali generates an endpoint URL:
https://api.centrali.io/data/workspace/YOUR_WORKSPACE/api/v1/endpoints/YOUR_PATH - When a request hits that URL, the function executes synchronously and the response contains the function's return value
Creating an Endpoint¶
curl -X POST "$CENTRALI_URL/data/workspace/$WORKSPACE/api/v1/function-triggers" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "price-calculator",
"functionId": "YOUR_FUNCTION_ID",
"executionType": "endpoint",
"triggerMetadata": {
"path": "calculate-price",
"allowedMethods": ["POST"],
"timeoutMs": 10000,
"auth": { "mode": "bearer" }
}
}'
Invoking the Endpoint¶
curl -X POST "$CENTRALI_URL/data/workspace/$WORKSPACE/api/v1/endpoints/calculate-price" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "items": [{ "sku": "WIDGET-1", "quantity": 3 }] }'
The response body is whatever your function returns:
Returning a Response¶
Endpoint triggers return your function result inline to the caller. Keep the response shape explicit and stable:
async function run() {
const payload = executionParams.payload ?? executionParams;
if (!payload.items || payload.items.length === 0) {
return { error: "No items provided" };
}
const total = calculateTotal(payload.items);
return {
subtotal: total,
currency: "usd"
};
}
For exact endpoint request and response behavior, see the Triggers API.
Configuration Options¶
| Field | Type | Default | Description |
|---|---|---|---|
path | string | (required) | URL path segment. Must be URL-safe (alphanumeric, hyphens, underscores). Unique per workspace. |
allowedMethods | string[] | ["POST"] | HTTP methods the endpoint accepts: GET, POST, PUT, DELETE, PATCH. |
timeoutMs | number | 30000 | Maximum execution time in milliseconds. Range: 1,000–30,000. |
auth | object | { mode: "bearer" } | Authentication mode. See below. |
params | object | {} | Static parameters passed to your function on every invocation. |
Authentication Modes¶
| Mode | Config | How to Call |
|---|---|---|
| Bearer (default) | { "mode": "bearer" } | Authorization: Bearer YOUR_JWT |
| API Key | { "mode": "apiKey" } | X-API-Key: GENERATED_KEY (returned on creation) |
| HMAC | { "mode": "hmac" } | X-Signature: HMAC_SHA256_SIGNATURE |
| Public | { "mode": "public" } | No authentication required |
| Multi-mode | { "modes": ["bearer", "apiKey"] } | Any configured mode succeeds |
Public Endpoints
Public endpoints are accessible without authentication. Only use this for truly public APIs. public cannot be combined with other modes.
When using API Key mode, the system generates a key automatically and returns it in the creation response. Store it securely — it cannot be retrieved again.
Error Responses¶
| Status | When |
|---|---|
| 401 | Authentication required but no valid credentials |
| 403 | Valid credentials but insufficient permissions |
| 404 | No endpoint found for this path, or trigger is disabled |
| 405 | HTTP method not in the endpoint's allowedMethods |
| 500 | Function threw an unhandled exception |
| 503 | No compute worker available (request is not queued) |
| 504 | Function exceeded its configured timeout |
Endpoint vs HTTP Trigger¶
| Endpoint | HTTP Trigger | |
|---|---|---|
| Response | Function output returned inline | 202 Accepted with execution ID |
| Execution | Synchronous (blocks until done) | Asynchronous (queued) |
| Timeout | 1–30 seconds (configurable) | Up to 5 minutes |
| No workers | Immediate 503 error | Queued for later |
| Auth | Bearer, API Key, HMAC, Public | HMAC signature validation |
| Best for | Custom APIs, validations, transformations | Webhooks, background processing |
When to Use Endpoint Triggers¶
- Building custom REST APIs backed by your functions
- Form validation or submission handlers
- Webhook responders that need to return a result
- Data transformation or calculation endpoints
- Any logic where the caller needs the function's output
Encrypted Parameters¶
Sensitive values like API keys and webhook secrets can be encrypted at rest. Add "encrypt": true to any parameter:
{
"triggerMetadata": {
"params": {
"publicSetting": "visible-in-api",
"apiKey": {
"value": "sk_live_abc123",
"encrypt": true
}
}
}
}
Encrypted parameters:
- Are stored using AES-256-GCM encryption
- Appear as encrypted objects in API responses (you cannot read the plaintext back)
- Are automatically decrypted before your function executes
- Are available as plain values in
triggerParams
Multiple Triggers Per Function¶
A single function can have multiple triggers. This is useful when the same logic needs to run in different contexts:
Function: "process-order"
├── Trigger 1: event-driven (record_created on "orders")
├── Trigger 2: on-demand (manual retry from admin panel)
├── Trigger 3: http-trigger (incoming webhook from partner API)
└── Trigger 4: endpoint (GET /order-status — returns order details inline)
Each trigger can have different triggerMetadata.params, so the same function can behave differently depending on how it was invoked. Check executionParams to determine the source:
async function run() {
if (executionParams.event) {
// Triggered by a record event
api.log({ message: 'Event-driven execution' });
} else if (executionParams.headers) {
// Triggered by HTTP webhook
api.log({ message: 'HTTP trigger execution' });
} else {
// On-demand or scheduled
api.log({ message: 'Manual/scheduled execution' });
}
return { success: true };
}
Choosing the Right Trigger Type¶
| Scenario | Trigger Type |
|---|---|
| "When a new user signs up, send a welcome email" | Event-driven (record_created) |
| "Every night at midnight, generate a report" | Scheduled (cron) |
| "Every 5 minutes, sync data from external API" | Scheduled (interval) |
| "Admin clicks 'Export' button in dashboard" | On-demand |
| "Stripe sends a payment confirmation" | HTTP trigger |
| "Run a one-time data migration next Tuesday" | Scheduled (once) |
| "When an order is updated, recalculate totals" | Event-driven (record_updated) |
| "Frontend calls an API to calculate shipping cost" | Endpoint |
| "External service needs a computed response" | Endpoint |
Best Practices¶
-
Name triggers descriptively — Use names like
notify-on-new-orderordaily-sales-report, nottrigger-1. -
Encrypt sensitive parameters — Always use
{ "value": "...", "encrypt": true }for API keys, secrets, and credentials. -
Use on-demand triggers for retry — Pair event-driven triggers with an on-demand trigger on the same function so you can manually retry failures.
-
Keep event-driven functions fast — They run on every matching event. Offload heavy processing to scheduled or on-demand triggers.
-
Use cron over interval for specific times — If you need "every day at 9 AM", use cron (
0 9 * * *). Interval-based triggers drift over time and don't respect timezones. -
Set timezones explicitly — Cron and once triggers default to UTC. Always set
timezoneif your schedule is business-hours dependent. -
Monitor execution logs — Check function runs regularly via the API or Console to catch failures early. See Monitoring & Debugging.
What to Read Next¶
- Need the async inbound webhook path specifically? Read Webhook Ingestion.
- Need the function runtime, globals, and built-in APIs? Read Functions and Writing Functions.
- Need exact
executionParamsshapes for every trigger type? Read Trigger Parameters & Payload Shapes. - Need multi-step workflows with branching and delays after a trigger fires? Read Automations.
- Need exact trigger API fields and endpoints? Read the Triggers API.
- Need the full event payload reference? Read Event Payloads Reference.
- Need to retry or inspect prior executions? Read Function Re-run.