Skip to content

Event Payloads Reference

This document provides comprehensive documentation for all event payloads that trigger compute 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 structure, your compute 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; // Structure 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,
    structure: recordSlug
  });

  return { success: true };
}

Event Types

Centrali supports the following record events:

Event Description Trigger Condition
record_created New record created After successful record creation
records_bulk_created Multiple records created in bulk After successful bulk 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_reverted Record reverted to previous version After version rollback
record_created_failed Record creation failed When create operation fails
records_bulk_created_failed Bulk record creation failed When bulk create operation fails
record_updated_failed Record update failed When update operation fails
record_deleted_failed Record deletion failed When delete operation fails

Payload Structures

record_created

Triggered when a new record is successfully created.

Payload Structure:

{
  event: "record_created",
  workspaceSlug: string,     // Your workspace identifier
  recordSlug: string,        // Structure slug (e.g., "orders", "customers")
  recordId: string,          // UUID of the created record
  data: {
    id: string,              // Record UUID
    workspaceSlug: string,   // Workspace identifier
    recordSlug: string,      // Structure 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,        // Structure slug (e.g., "orders", "customers")
  structureId: string,       // UUID of the structure
  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",
    structure: 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",
    structure: 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


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,
    structure: recordSlug,
    error,
    timestamp,
    severity: "warning"
  });

  api.logError({
    message: `${event} failed`,
    error,
    structure: 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
}