Skip to content

Event Payloads Reference

This document provides comprehensive documentation for all event payloads that trigger Functions in Centrali.

Table of Contents

  1. Overview
  2. Accessing Event Data
  3. Event Types
  4. Payload Structures
  5. record_created
  6. records_bulk_created
  7. record_updated
  8. record_deleted
  9. record_restored
  10. record_reverted
  11. Failed Events
  12. Event Differences Table
  13. Common Patterns

Overview

When a record event occurs in Centrali, the system publishes an event payload containing all relevant information about the change. If you have an event-driven trigger configured for that event type and collection, your Function will receive this payload.

Event Flow

Record Operation (create/update/delete)
    Event Published to NATS
    Orchestrator Matches Triggers
    Your Function Receives Event Payload

Accessing Event Data

Event data is available in your function through the executionParams global variable. Static configuration from your trigger is available in triggerParams.

executionParams vs triggerParams

Variable Contains Set By
executionParams Dynamic event data System (from the event)
triggerParams Static configuration You (in trigger metadata)

Example: Accessing Event Data

async function run() {
  // Event data from the record operation
  const event = executionParams.event;           // "record_created"
  const workspaceSlug = executionParams.workspaceSlug;
  const recordSlug = executionParams.recordSlug; // Collection slug
  const recordId = executionParams.recordId;
  const data = executionParams.data;             // Record data
  const timestamp = executionParams.timestamp;

  // Static config from trigger creation
  const emailTemplate = triggerParams.emailTemplate;
  const notifyAdmin = triggerParams.notifyAdmin;

  api.log({
    message: `Processing ${event}`,
    recordId,
    collection: recordSlug
  });

  return { success: true };
}

Event Types

Centrali supports the following record events:

Per-Record Events

These events fire once per record. They are published by single-record operations and by batch multi-record operations.

Event Description Trigger Condition
record_created New record created After successful record creation
record_updated Record modified After successful record update
record_deleted Record deleted After successful deletion (soft or hard)
record_restored Deleted record restored After restoring a soft-deleted record
record_expired Record expired via TTL When a record's time-to-live expires
record_reverted Record reverted to previous version After version rollback

Aggregate Events (Bulk)

These events fire once per bulk operation with all affected record IDs. They are published by bulk multi-record operations.

Event Description Trigger Condition
records_bulk_created Multiple records created in bulk After successful bulk creation
records_bulk_updated Multiple records updated in bulk After successful bulk update
records_bulk_deleted Multiple records deleted in bulk After successful bulk deletion

Failure Events

Event Description Trigger Condition
record_created_failed Record creation failed When create operation fails
record_updated_failed Record update failed When update operation fails
record_deleted_failed Record deletion failed When delete operation fails
records_bulk_created_failed Bulk record creation failed When bulk create operation fails
records_bulk_updated_failed Bulk record update failed When bulk update operation fails
records_bulk_deleted_failed Bulk record deletion failed When bulk delete operation fails

Bulk vs Batch: Which Events Fire?

Centrali provides two distinct paths for multi-record operations. The path you choose determines which events fire:

HTTP API Compute Function API Events
Bulk (aggregate) POST /records/bulk api.bulkCreateRecords() 1 aggregate event (e.g., records_bulk_created)
Batch (per-record) POST /records/batch api.batchCreateRecords() N per-record events (e.g., record_created × N)
  • Use bulk when your trigger function processes all records together (indexing, batch notifications, summary reports)
  • Use batch when your trigger function processes each record individually (validation, per-record notifications, thumbnail generation)

Payload Structures

record_created

Triggered when a new record is successfully created.

Payload Structure:

{
  event: "record_created",
  workspaceSlug: string,     // Your workspace identifier
  recordSlug: string,        // Collection slug (e.g., "orders", "customers")
  recordId: string,          // UUID of the created record
  data: {
    id: string,              // Record UUID
    workspaceSlug: string,   // Workspace identifier
    recordSlug: string,      // Collection slug
    data: {                  // Your record fields
      [fieldName: string]: any
    },
    status: "active",        // Record status
    version: 1,              // Always 1 for new records
    createdAt: string,       // ISO 8601 timestamp
    updatedAt: string,       // ISO 8601 timestamp
    createdBy: string,       // User ID who created the record
    updatedBy: string        // User ID who created the record
  },
  timestamp: string,         // ISO 8601 timestamp of the event
  createdBy: string          // User ID who created the record
}

Real Example:

// executionParams for a new order
{
  event: "record_created",
  workspaceSlug: "acme-corp",
  recordSlug: "orders",
  recordId: "550e8400-e29b-41d4-a716-446655440000",
  data: {
    id: "550e8400-e29b-41d4-a716-446655440000",
    workspaceSlug: "acme-corp",
    recordSlug: "orders",
    data: {
      orderNumber: "ORD-2025-001",
      customerId: "cust_abc123",
      items: [
        { productId: "prod_123", quantity: 2, price: 29.99 },
        { productId: "prod_456", quantity: 1, price: 49.99 }
      ],
      status: "pending",
      total: 109.97,
      shippingAddress: {
        street: "123 Main St",
        city: "New York",
        zip: "10001"
      }
    },
    status: "active",
    version: 1,
    createdAt: "2025-01-15T10:30:00.000Z",
    updatedAt: "2025-01-15T10:30:00.000Z",
    createdBy: "user_xyz789",
    updatedBy: "user_xyz789"
  },
  timestamp: "2025-01-15T10:30:00.123Z",
  createdBy: "user_xyz789"
}

Function Example:

async function run() {
  const { recordId, data, recordSlug } = executionParams;

  // Access record fields
  const orderNumber = data.data.orderNumber;
  const customerId = data.data.customerId;
  const total = data.data.total;

  api.log({
    message: "New order received",
    orderNumber,
    total
  });

  // Send notification
  await api.http.post("https://api.example.com/notify", {
    type: "new_order",
    orderId: recordId,
    orderNumber,
    total
  });

  return { success: true, orderNumber };
}

records_bulk_created

Triggered when multiple records are created in a single bulk operation. Unlike record_created, this event contains only record IDs, not the full record data. This is for performance reasons - bulk operations can create thousands of records.

Payload Structure:

{
  event: "records_bulk_created",
  workspaceSlug: string,     // Your workspace identifier
  recordSlug: string,        // Collection slug (e.g., "orders", "customers")
  structureId: string,       // UUID of the collection
  recordIds: string[],       // Array of created record UUIDs
  count: number,             // Number of records created
  timestamp: string,         // ISO 8601 timestamp of the event
  createdBy: string,         // User ID who created the records
  schemaDiscoveryMode: string // "strict" | "schemaless" | "auto-evolving"
}

Real Example:

// executionParams for bulk order creation
{
  event: "records_bulk_created",
  workspaceSlug: "acme-corp",
  recordSlug: "orders",
  structureId: "123e4567-e89b-12d3-a456-426614174000",
  recordIds: [
    "550e8400-e29b-41d4-a716-446655440001",
    "550e8400-e29b-41d4-a716-446655440002",
    "550e8400-e29b-41d4-a716-446655440003"
  ],
  count: 3,
  timestamp: "2025-01-15T10:30:00.123Z",
  createdBy: "user_xyz789",
  schemaDiscoveryMode: "strict"
}

Function Example:

async function run() {
  const { recordIds, count, recordSlug, workspaceSlug } = executionParams;

  api.log({
    message: "Bulk records created",
    collection: recordSlug,
    count,
    recordIds: recordIds.slice(0, 5) // Log first 5 IDs
  });

  // Optionally fetch full records if needed
  // Note: For large batches, consider processing asynchronously
  if (count <= 100) {
    const records = await api.centrali.getRecordsByIds(recordSlug, recordIds);
    // Process records...
  }

  // Send summary notification
  await api.http.post("https://api.example.com/notify", {
    type: "bulk_import_complete",
    collection: recordSlug,
    count,
    timestamp: executionParams.timestamp
  });

  return { success: true, processedCount: count };
}

Important Notes: - Bulk events do NOT contain full record data - only IDs - Use api.centrali.getRecordsByIds() if you need full record data - Consider batch processing for large imports to avoid timeouts - Single record_created events are NOT fired for bulk operations


records_bulk_updated

Triggered when multiple records are updated in a single bulk operation. Contains only the affected record IDs, not full record data.

Payload Structure:

{
  event: "records_bulk_updated",
  workspaceSlug: string,
  recordSlug: string,
  structureId: string,
  recordIds: string[],       // Array of updated record UUIDs
  count: number,             // Number of records updated
  timestamp: string,
  updatedBy: string
}

Function Example:

async function run() {
  const { recordIds, count, recordSlug } = executionParams;

  api.log({
    message: "Bulk records updated",
    collection: recordSlug,
    count
  });

  // Fetch updated records if needed
  if (count <= 100) {
    for (const id of recordIds) {
      const record = await api.fetchRecord(id);
      // Process each updated record...
    }
  }

  return { success: true, processedCount: count };
}

records_bulk_deleted

Triggered when multiple records are deleted in a single bulk operation. Contains only the deleted record IDs.

Payload Structure:

{
  event: "records_bulk_deleted",
  workspaceSlug: string,
  recordSlug: string,
  structureId: string,
  recordIds: string[],       // Array of deleted record UUIDs
  count: number,             // Number of records deleted
  timestamp: string,
  deletedBy: string
}

Function Example:

async function run() {
  const { recordIds, count, recordSlug } = executionParams;

  api.log({
    message: "Bulk records deleted",
    collection: recordSlug,
    count
  });

  // Clean up related data in external systems
  await api.httpPost("https://api.example.com/cleanup", {
    deletedIds: recordIds,
    collection: recordSlug
  });

  return { success: true, cleanedUp: count };
}

record_expired

Triggered when a record is automatically deleted because its TTL (time-to-live) expired. Similar to record_deleted but indicates the deletion was automatic, not user-initiated.

Payload Structure:

{
  event: "record_expired",
  workspaceSlug: string,
  recordSlug: string,
  recordId: string,
  data: { /* full record data at time of expiry */ },
  timestamp: string,
  deletedBy: string
}

Function Example:

async function run() {
  const { recordId, recordSlug, data } = executionParams;

  api.log({
    message: "Record expired via TTL",
    recordId,
    collection: recordSlug
  });

  // Archive expired record data to external storage
  await api.storeAsJSON(data, `expired/${recordSlug}/${recordId}.json`, {
    folder: "archives"
  });

  return { success: true, archived: recordId };
}

record_updated

Triggered when an existing record is modified. Includes both the previous and current state.

Payload Structure:

{
  event: "record_updated",
  workspaceSlug: string,
  recordSlug: string,
  recordId: string,
  data: {
    before: {                // Record BEFORE the update
      id: string,
      workspaceSlug: string,
      recordSlug: string,
      data: { [fieldName: string]: any },
      status: string,
      version: number,       // Previous version number
      createdAt: string,
      updatedAt: string,     // Previous update timestamp
      createdBy: string,
      updatedBy: string      // Previous updater
    },
    after: {                 // Record AFTER the update
      id: string,
      workspaceSlug: string,
      recordSlug: string,
      data: { [fieldName: string]: any },
      status: string,
      version: number,       // Incremented version
      createdAt: string,
      updatedAt: string,     // New update timestamp
      createdBy: string,
      updatedBy: string      // Current updater
    }
  },
  timestamp: string,
  updatedBy: string
}

Real Example:

// executionParams for an order status change
{
  event: "record_updated",
  workspaceSlug: "acme-corp",
  recordSlug: "orders",
  recordId: "550e8400-e29b-41d4-a716-446655440000",
  data: {
    before: {
      id: "550e8400-e29b-41d4-a716-446655440000",
      workspaceSlug: "acme-corp",
      recordSlug: "orders",
      data: {
        orderNumber: "ORD-2025-001",
        status: "pending",
        total: 109.97,
        shippingAddress: { street: "123 Main St", city: "New York", zip: "10001" }
      },
      status: "active",
      version: 1,
      createdAt: "2025-01-15T10:30:00.000Z",
      updatedAt: "2025-01-15T10:30:00.000Z",
      createdBy: "user_xyz789",
      updatedBy: "user_xyz789"
    },
    after: {
      id: "550e8400-e29b-41d4-a716-446655440000",
      workspaceSlug: "acme-corp",
      recordSlug: "orders",
      data: {
        orderNumber: "ORD-2025-001",
        status: "shipped",
        total: 109.97,
        trackingNumber: "1Z999AA10123456784",
        shippingAddress: { street: "123 Main St", city: "New York", zip: "10001" }
      },
      status: "active",
      version: 2,
      createdAt: "2025-01-15T10:30:00.000Z",
      updatedAt: "2025-01-15T14:45:00.000Z",
      createdBy: "user_xyz789",
      updatedBy: "user_admin001"
    }
  },
  timestamp: "2025-01-15T14:45:00.456Z",
  updatedBy: "user_admin001"
}

Function Example - Detecting Changes:

async function run() {
  const { recordId, data } = executionParams;
  const { before, after } = data;

  // Compare before and after to find changes
  const previousStatus = before.data.status;
  const newStatus = after.data.status;

  if (previousStatus !== newStatus) {
    api.log({
      message: "Order status changed",
      orderId: recordId,
      from: previousStatus,
      to: newStatus
    });

    // Send notification only when status changes to "shipped"
    if (newStatus === "shipped" && before.data.status !== "shipped") {
      const trackingNumber = after.data.trackingNumber;

      await api.http.post("https://api.example.com/notify-customer", {
        type: "order_shipped",
        orderId: recordId,
        trackingNumber,
        email: after.data.customerEmail
      });
    }
  }

  return { success: true, statusChanged: previousStatus !== newStatus };
}

Function Example - Finding Changed Fields:

async function run() {
  const { data } = executionParams;
  const { before, after } = data;

  // Find which fields changed
  const changedFields = [];
  for (const key in after.data) {
    if (JSON.stringify(before.data[key]) !== JSON.stringify(after.data[key])) {
      changedFields.push({
        field: key,
        from: before.data[key],
        to: after.data[key]
      });
    }
  }

  api.log({
    message: "Fields changed",
    changes: changedFields
  });

  return { changedFields };
}

record_deleted

Triggered when a record is deleted (soft delete or hard delete).

Payload Structure:

{
  event: "record_deleted",
  workspaceSlug: string,
  recordSlug: string,
  recordId: string,
  data: {                    // Complete record as it existed before deletion
    id: string,
    workspaceSlug: string,
    recordSlug: string,
    data: { [fieldName: string]: any },
    status: string,          // Status before deletion
    version: number,
    createdAt: string,
    updatedAt: string,
    createdBy: string,
    updatedBy: string
  },
  timestamp: string,
  deletedBy: string          // User who deleted the record
}

Real Example:

// executionParams for a deleted order
{
  event: "record_deleted",
  workspaceSlug: "acme-corp",
  recordSlug: "orders",
  recordId: "550e8400-e29b-41d4-a716-446655440000",
  data: {
    id: "550e8400-e29b-41d4-a716-446655440000",
    workspaceSlug: "acme-corp",
    recordSlug: "orders",
    data: {
      orderNumber: "ORD-2025-001",
      status: "cancelled",
      total: 109.97,
      cancellationReason: "Customer requested"
    },
    status: "active",
    version: 3,
    createdAt: "2025-01-15T10:30:00.000Z",
    updatedAt: "2025-01-15T16:00:00.000Z",
    createdBy: "user_xyz789",
    updatedBy: "user_admin001"
  },
  timestamp: "2025-01-15T16:30:00.789Z",
  deletedBy: "user_admin001"
}

Function Example:

async function run() {
  const { recordId, data, deletedBy } = executionParams;

  // Archive deleted record to external system
  await api.http.post("https://api.example.com/archive", {
    type: "deleted_order",
    originalId: recordId,
    orderNumber: data.data.orderNumber,
    deletedBy,
    archivedData: data.data,
    deletedAt: executionParams.timestamp
  });

  api.log({
    message: "Order archived after deletion",
    orderNumber: data.data.orderNumber,
    deletedBy
  });

  return { success: true, archived: true };
}

record_restored

Triggered when a soft-deleted record is restored.

Payload Structure:

{
  event: "record_restored",
  workspaceSlug: string,
  recordSlug: string,
  recordId: string,
  data: {
    before: {                // Record in deleted/archived state
      id: string,
      workspaceSlug: string,
      recordSlug: string,
      data: { [fieldName: string]: any },
      status: "archived",    // Was archived/deleted
      version: number,
      createdAt: string,
      updatedAt: string,
      createdBy: string,
      updatedBy: string
    },
    after: {                 // Record after restoration
      id: string,
      workspaceSlug: string,
      recordSlug: string,
      data: { [fieldName: string]: any },
      status: "active",      // Now active again
      version: number,       // Version incremented
      createdAt: string,
      updatedAt: string,     // Updated to restoration time
      createdBy: string,
      updatedBy: string      // User who restored
    }
  },
  timestamp: string,
  restoredBy: string
}

Function Example:

async function run() {
  const { recordId, data, restoredBy } = executionParams;
  const { before, after } = data;

  api.log({
    message: "Record restored",
    recordId,
    restoredBy,
    previousStatus: before.status,
    newStatus: after.status
  });

  // Re-sync restored record with external system
  await api.http.post("https://api.example.com/sync", {
    action: "restore",
    recordId,
    data: after.data
  });

  return { success: true };
}

record_reverted

Triggered when a record is reverted to a previous version.

Payload Structure:

{
  event: "record_reverted",
  workspaceSlug: string,
  recordSlug: string,
  recordId: string,
  data: {
    before: {                // Record before revert
      id: string,
      data: { [fieldName: string]: any },
      version: number,       // Higher version number
      // ... other fields
    },
    after: {                 // Record after revert (restored to older version's data)
      id: string,
      data: { [fieldName: string]: any },
      version: number,       // New version (incremented, not the old version number)
      // ... other fields
    }
  },
  timestamp: string,
  revertedBy: string,
  revertedToVersion: number  // The version number that was restored
}

Function Example:

async function run() {
  const { recordId, data, revertedBy } = executionParams;
  const { before, after } = data;

  api.log({
    message: "Record reverted to previous version",
    recordId,
    revertedBy,
    fromVersion: before.version,
    toVersion: after.version
  });

  return { success: true };
}

Failed Events

Failed events are triggered when a record operation fails. They include the error information.

record_created_failed

{
  event: "record_created_failed",
  workspaceSlug: string,
  recordSlug: string,
  data: { [fieldName: string]: any },  // Data that was attempted to be created
  timestamp: string,
  createdBy: string,
  error: string                        // Error message
}

records_bulk_created_failed

{
  event: "records_bulk_created_failed",
  workspaceSlug: string,
  recordSlug: string,
  count: number,                       // Number of records that were attempted
  timestamp: string,
  createdBy: string,
  error: string                        // Error message
}

record_updated_failed

{
  event: "record_updated_failed",
  workspaceSlug: string,
  recordSlug: string,
  recordId: string,
  data: { [fieldName: string]: any },  // Data that was attempted
  timestamp: string,
  updatedBy: string,
  error: string                        // Error message
}

record_deleted_failed

{
  event: "record_deleted_failed",
  workspaceSlug: string,
  recordSlug: string,
  recordId: string,
  timestamp: string,
  deletedBy: string,
  error: string                        // Error message
}

Function Example - Handling Failed Events:

async function run() {
  const { event, recordSlug, error, timestamp } = executionParams;

  // Log failure to monitoring system
  await api.http.post("https://api.example.com/alerts", {
    type: "operation_failed",
    event,
    collection: recordSlug,
    error,
    timestamp,
    severity: "warning"
  });

  api.logError({
    message: `${event} failed`,
    error,
    collection: recordSlug
  });

  return { logged: true };
}

Event Differences Table

Field record_created record_updated record_deleted record_restored record_reverted
data structure Full record { before, after } Full record { before, after } { before, after }
Initial version 1 Varies Varies Varies Varies
version changes N/A Incremented N/A Incremented Incremented
Previous data available No Yes (in before) No (is current) Yes (in before) Yes (in before)
User field createdBy updatedBy deletedBy restoredBy revertedBy
Can detect changes N/A Yes N/A Yes Yes

Common Patterns

Pattern 1: Conditional Processing Based on Changed Fields

async function run() {
  const { event, data } = executionParams;

  // Only process updates where specific fields changed
  if (event === "record_updated") {
    const { before, after } = data;

    // Only proceed if 'status' field changed
    if (before.data.status === after.data.status) {
      api.log({ message: "Status unchanged, skipping" });
      return { skipped: true, reason: "no_status_change" };
    }
  }

  // Process the event...
  return { success: true };
}

Pattern 2: Combining Trigger Params with Event Data

async function run() {
  const { recordId, data, event } = executionParams;

  // Static config from trigger
  const webhookUrl = triggerParams.webhookUrl;
  const includeFullRecord = triggerParams.includeFullRecord;

  const payload = {
    event,
    recordId,
    timestamp: executionParams.timestamp
  };

  if (includeFullRecord) {
    payload.record = event === "record_updated" ? data.after : data;
  }

  await api.http.post(webhookUrl, payload);

  return { success: true };
}

Pattern 3: Handling Multiple Event Types in One Function

async function run() {
  const { event, recordId, data } = executionParams;

  switch (event) {
    case "record_created":
      await handleCreated(recordId, data);
      break;

    case "record_updated":
      await handleUpdated(recordId, data.before, data.after);
      break;

    case "record_deleted":
      await handleDeleted(recordId, data);
      break;

    default:
      api.log({ message: "Unknown event type", event });
  }

  return { success: true, event };
}

async function handleCreated(recordId, record) {
  api.log({ message: "New record", recordId });
  // ... creation logic
}

async function handleUpdated(recordId, before, after) {
  api.log({
    message: "Record updated",
    recordId,
    versionBefore: before.version,
    versionAfter: after.version
  });
  // ... update logic
}

async function handleDeleted(recordId, record) {
  api.log({ message: "Record deleted", recordId });
  // ... deletion logic
}