Skip to content

Triggers

Overview

Triggers (function-triggers) define when and how your functions execute. A function contains the code; a trigger controls when that code runs.

Every function needs at least one trigger to execute. A single function can have multiple triggers — for example, the same order-processing function might run on new records (event-driven) and be invocable manually for retries (on-demand).

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

  1. You create a trigger linked to a function, specifying the event type and collection (via recordSlug)
  2. When a matching record event occurs, Centrali automatically executes your function
  3. 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.invokeFunction('manual-export', {
  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 executes your function when called. This is ideal for receiving webhooks from third-party services.

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",
      "params": {
        "signingSecret": {
          "value": "whsec_abc123",
          "encrypt": true
        }
      }
    }
  }'

The trigger URL will be:

https://api.centrali.io/data/workspace/YOUR_WORKSPACE/api/v1/http-trigger/payments/stripe

Accessing the HTTP Payload

The incoming request body is available as executionParams:

async function run() {
  // The HTTP request body
  const event = executionParams;

  // Verify webhook signature using encrypted secret
  const signature = event.headers?.['stripe-signature'];
  const expectedSig = api.hmacSha256(triggerParams.signingSecret, JSON.stringify(event.body));

  api.log({ message: 'Webhook received', type: event.body?.type });

  return { success: true };
}

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.

How It Works

  1. You create a trigger with executionType: "endpoint" and a unique path
  2. Centrali generates an endpoint URL: https://api.centrali.io/data/workspace/YOUR_WORKSPACE/api/v1/endpoints/YOUR_PATH
  3. 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:

{
  "subtotal": 29.97,
  "tax": 2.40,
  "total": 32.37
}

Returning Custom Status Codes and Headers

Your function controls the HTTP response using context.res.json():

export default async (context) => {
  const { payload } = context.req;

  if (!payload.items || payload.items.length === 0) {
    return context.res.json({
      statusCode: 400,
      body: { error: "No items provided" }
    });
  }

  const total = calculateTotal(payload.items);

  return context.res.json({
    statusCode: 200,
    headers: { "X-Calculation-Version": "2" },
    body: { total }
  });
};

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

  1. Name triggers descriptively — Use names like notify-on-new-order or daily-sales-report, not trigger-1.

  2. Encrypt sensitive parameters — Always use { "value": "...", "encrypt": true } for API keys, secrets, and credentials.

  3. 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.

  4. Keep event-driven functions fast — They run on every matching event. Offload heavy processing to scheduled or on-demand triggers.

  5. 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.

  6. Set timezones explicitly — Cron and once triggers default to UTC. Always set timezone if your schedule is business-hours dependent.

  7. Monitor execution logs — Check function runs regularly via the API or Console to catch failures early. See Monitoring & Debugging.