Skip to content

n8n Event Data Reference

This document describes how WhiteLabelCRO Integration API events are structured and accessed in n8n workflows.

Event to API Mapping

The Integration API emits 10 event types. In n8n, you work directly with the API event structure.

API Event Type Description Common Use Case
client.created When a new client is enrolled Send welcome email, create CRM record
client.status_changed When client moves to different stage Trigger stage-specific workflows
lead.created When a new lead is captured Add to nurture campaign
invoice.created When invoice is generated Send payment reminder
invoice.status_changed When invoice payment status changes Update accounting system
payment.succeeded When payment processes successfully Send receipt, update billing
payment.failed When payment is declined Alert finance team, retry payment
affiliate.created When referral partner is added Send onboarding materials
document.uploaded When file is attached to record Process document, extract data
agreement.signed When e-signature is completed Trigger post-signature workflow

Event Envelope Structure

The Integration API returns events in this format:

{
  "id": 12345,
  "type": "client.created",
  "createdUtc": "2026-01-28T10:30:00Z",
  "companyId": 92,
  "payload": {
    "client_id": 5678,
    "first_name": "John",
    "last_name": "Doe",
    "email": "john@example.com"
  },
  "metadata": {
    "source": "web_portal",
    "user_id": 42
  }
}

Accessing Fields in n8n

Standard Fields (All Events)

Present in every event:

In n8n expressions: - Event ID: {{ $json.id }} - Event type: {{ $json.type }} - Timestamp: {{ $json.createdUtc }} - Company ID: {{ $json.companyId }}

Payload Fields

Payload structure varies by event type. Access using dot notation:

Examples: - Client ID: {{ $json.payload.client_id }} - First name: {{ $json.payload.first_name }} - Email: {{ $json.payload.email }} - Invoice amount: {{ $json.payload.amount }} - Payment status: {{ $json.payload.invoice_status }}

See the Event Catalog (/03-events/event-catalog.md) for complete field definitions per event type.

Metadata Fields

Optional context provided with some events:

  • Source: {{ $json.metadata.source }}
  • User ID: {{ $json.metadata.user_id }}

Field Naming Conventions

Common Field Patterns

Across all event types: - IDs: client_id, employee_id, affiliate_id, invoice_id, payment_id - Status fields: status, previous_status, new_status, invoice_status - Date fields: created_date, changed_date, signed_date, due_date (ISO 8601 strings) - Person fields: first_name, last_name, full_name, email, phone

Handling Nested Data

For nested objects (if present):

{
  "payload": {
    "address": {
      "street": "123 Main St",
      "city": "Springfield"
    }
  }
}

Access in n8n: - Street: {{ $json.payload.address.street }} - City: {{ $json.payload.address.city }}

Array Handling

If the payload contains arrays:

{
  "payload": {
    "tags": ["vip", "high_priority"]
  }
}

Access in n8n: - First tag: {{ $json.payload.tags[0] }} - All tags: {{ $json.payload.tags }} - Iterate: Use Split In Batches or Loop Over Items node

Working with Event Data

Routing by Event Type

Use Switch node to route based on event type:

Add case: - Mode: Expression - Value: {{ $json.type }} - Cases: client.created, payment.succeeded, etc.

Or use IF node:

{{ $json.type === 'client.created' }}

Extracting Specific Fields

Use Set node to create simplified data structure:

Example:

{
  "clientId": "{{ $json.payload.client_id }}",
  "fullName": "{{ $json.payload.first_name }} {{ $json.payload.last_name }}",
  "email": "{{ $json.payload.email }}"
}

Handling Missing Fields

Some optional fields may be null. Use default values:

{{ $json.payload.phone || 'No phone provided' }}
{{ $json.payload.affiliate_id !== null ? $json.payload.affiliate_id : 'None' }}

Or use IF node to check existence:

{{ $json.payload.phone !== null }}

Field Type Handling

Date/Time Fields

  • API returns: ISO 8601 strings (e.g., 2026-01-28T10:30:00Z)
  • In n8n: Use Date & Time nodes to parse and format
  • Access as string: {{ $json.createdUtc }}
  • Parse to date: Use Date & Time node with "Parse Date" operation

Boolean Fields

  • API returns: true or false (JSON boolean)
  • In n8n expressions: {{ $json.payload.is_approved }}
  • In conditions: {{ $json.payload.is_approved === true }}

Numeric Fields

  • IDs: Always integers (e.g., client_id: 5678)
  • Amounts: May be decimal (e.g., amount: 99.99)
  • Access: {{ $json.payload.amount }}
  • Math operations: {{ $json.payload.amount * 1.1 }}

Null/Missing Fields

  • If a field is null or absent: {{ $json.payload.field }} returns null
  • Check for null: {{ $json.payload.field !== null }}
  • Optional fields may not appear in every event

Debugging Event Data

View Full Event Structure

  1. Add Edit Fields or Code node after webhook/HTTP node
  2. Log full event: console.log(JSON.stringify($input.all(), null, 2))
  3. Check execution data in n8n UI

Common Issues

Undefined field errors: - Field doesn't exist for this event type - Check Event Catalog for event-specific fields - Use optional chaining or null checks

Type mismatch: - Ensure numeric fields aren't treated as strings - Use parseInt() or parseFloat() if needed - Check field type in API response

JSON Processing

Parsing Full Payload

If you need to process the entire payload programmatically:

// In Code node
const event = $input.first().json;
const payload = event.payload;

// Process fields
const result = {
  id: event.id,
  type: event.type,
  clientName: `${payload.first_name} ${payload.last_name}`,
  email: payload.email
};

return result;

Transforming Data

Use Code node for complex transformations:

const events = $input.all().map(item => {
  const data = item.json;
  return {
    eventId: data.id,
    clientId: data.payload.client_id,
    timestamp: new Date(data.createdUtc).toISOString(),
    source: data.metadata?.source || 'unknown'
  };
});

return events;

Performance Considerations

Minimize Expression Complexity

For repeatedly accessed fields, use Set node to extract once:

Instead of:

{{ $json.payload.client_id }}
{{ $json.payload.client_id }}
{{ $json.payload.client_id }}

Do:

{
  "clientId": "{{ $json.payload.client_id }}"
}

Then reference: {{ $json.clientId }}

Batch Processing

For high-volume workflows: 1. Use Split In Batches to process events in groups 2. Aggregate before external API calls 3. Cache frequently accessed data

Version Stability

Current Implementation

The Integration API uses v1 (/api/v1/...). Event structures are stable within v1.

Backward Compatibility

Event payload structures are stable within v1: - New fields may be added (non-breaking) - Existing fields will not change type or be removed - Always check for field existence before accessing

If a v2 API is introduced in the future, migration guidance will be provided in advance.