GuidesEvent Forwarding Setup

Setting Up Event Forwarding

This guide walks you through collecting real-time events from your applications and forwarding them to analytics and marketing tools. By the end, you’ll have instrumented your app, defined event contracts for data quality, configured forwarding rules, set up consent management, and monitored everything in the event debugger.

When to Use Event Forwarding

Event forwarding is the right approach when you need to:

  • Send real-time behavioral data (page views, clicks, purchases) to analytics tools like Amplitude or Mixpanel
  • Feed marketing automation platforms with event triggers (e.g., “user completed checkout” triggers a thank-you email)
  • Collect events from multiple sources (web, mobile, server) into a single pipeline
  • Enforce data quality on incoming events with schema validation
  • Manage user consent for event data processing
  • Write events to your warehouse for historical analysis alongside your batch data

Prerequisites

  • A SignalSmith workspace with at least one connected source
  • An application (web, mobile, or server-side) that you want to instrument
  • A destination account (e.g., Amplitude, Mixpanel, Google Analytics) for receiving forwarded events
  • Admin or Editor role in your SignalSmith workspace

Step 1: Create Write Keys

Write keys authenticate event sources when sending data to the SignalSmith events API. Create a separate write key for each event source so you can track where events originate and revoke access independently.

  1. Navigate to Events > Write Keys in the left sidebar
  2. Click Create Write Key
  3. Name it descriptively to identify the source:
    • Production Web App — For your main website
    • iOS App — For your iOS application
    • Backend Server — For server-side events
    • Staging Environment — For development and testing
  4. Click Create
  5. Copy the generated key immediately — It will only be shown once. Store it securely in your application’s environment variables.

Repeat for each event source. Having separate keys per source lets you:

  • Track origin — See which source generated each event in the debugger
  • Revoke independently — Disable one source without affecting others
  • Monitor volume — Track event volume per source for capacity planning

Step 2: Instrument Your Application

SignalSmith’s event API is Segment-compatible, supporting track, identify, page, and group event types. Integrate with your application using direct HTTP calls.

JavaScript (Browser)

// signalsmith-events.js — Lightweight event tracking client
 
const SIGNALSMITH_ENDPOINT = 'https://your-instance.signalsmith.io/api/v1/events';
const WRITE_KEY = process.env.SIGNALSMITH_WRITE_KEY;
 
async function track(eventName, properties = {}) {
  await fetch(`${SIGNALSMITH_ENDPOINT}/track`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${WRITE_KEY}`
    },
    body: JSON.stringify({
      type: 'track',
      event: eventName,
      userId: getCurrentUserId(),
      anonymousId: getAnonymousId(),
      properties,
      timestamp: new Date().toISOString(),
      context: {
        page: { url: window.location.href, title: document.title },
        userAgent: navigator.userAgent
      }
    })
  });
}
 
async function identify(userId, traits = {}) {
  await fetch(`${SIGNALSMITH_ENDPOINT}/identify`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${WRITE_KEY}`
    },
    body: JSON.stringify({
      type: 'identify',
      userId,
      traits,
      timestamp: new Date().toISOString()
    })
  });
}
 
async function page(name, properties = {}) {
  await fetch(`${SIGNALSMITH_ENDPOINT}/page`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${WRITE_KEY}`
    },
    body: JSON.stringify({
      type: 'page',
      name,
      userId: getCurrentUserId(),
      properties,
      timestamp: new Date().toISOString()
    })
  });
}
 
// Usage examples
track('Product Viewed', {
  product_id: 'prod_123',
  product_name: 'Premium Widget',
  price: 49.99,
  category: 'Widgets'
});
 
identify('user_456', {
  email: 'jane@example.com',
  plan: 'enterprise',
  company: 'Acme Corp'
});
 
page('Pricing Page', {
  referrer: document.referrer
});

Server-Side (curl)

# Track event
curl -X POST https://your-instance.signalsmith.io/api/v1/events/track \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_WRITE_KEY" \
  -d '{
    "type": "track",
    "event": "Order Completed",
    "userId": "user_456",
    "properties": {
      "order_id": "ord_789",
      "total": 149.99,
      "currency": "USD",
      "items": 3,
      "coupon_code": "SUMMER20"
    },
    "timestamp": "2024-12-15T14:30:00Z"
  }'
 
# Identify user
curl -X POST https://your-instance.signalsmith.io/api/v1/events/identify \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_WRITE_KEY" \
  -d '{
    "type": "identify",
    "userId": "user_456",
    "traits": {
      "email": "jane@example.com",
      "name": "Jane Doe",
      "plan": "enterprise",
      "created_at": "2024-01-15T10:00:00Z"
    }
  }'

Best Practices for Instrumentation

  • Always include userId for authenticated users — This enables identity stitching and profile enrichment
  • Use anonymousId for unauthenticated users — Generate a stable anonymous ID (e.g., UUID stored in a cookie) so pre-login events can be linked post-login
  • Include timestamps — Always pass an ISO 8601 timestamp. If omitted, the server uses receipt time, which may not reflect when the event actually occurred.
  • Keep properties flat — Avoid deeply nested objects. Flat properties are easier to query in your warehouse and map to destination schemas.
  • Use consistent naming — Adopt a naming convention (e.g., “Object Action” like “Product Viewed”, “Order Completed”) and apply it across all sources.

Step 3: Define Event Contracts

Event contracts enforce data quality by validating incoming events against a schema. Contracts define which properties are required, their expected data types, and what happens when an event violates the contract.

  1. Navigate to Events > Contracts
  2. Click Create Contract
  3. Select the event name (e.g., “Order Completed”)
  4. Define the schema:
PropertyTypeRequiredDescription
order_idstringYesUnique order identifier
totalnumberYesOrder total in base currency
currencystringYesISO 4217 currency code
itemsintegerNoNumber of items in the order
coupon_codestringNoApplied coupon code
  1. Set the violation policy:

    • Allow — Accept the event even if it violates the contract (log the violation for review)
    • Drop Property — Accept the event but strip non-conforming properties
    • Reject — Reject the entire event and return a 422 error to the sender
  2. Click Save

Contract Strategy

Start with Allow mode for new events to collect data on what’s being sent without blocking anything. Once you understand the event shape, tighten to Drop Property or Reject to enforce quality.

Create contracts for your most critical events first:

PriorityEventsWhy
HighOrder Completed, Signup Completed, Subscription StartedRevenue-impacting — bad data here means wrong analytics
MediumProduct Viewed, Cart Updated, Search PerformedBehavioral analytics — important but less critical
LowerPage Viewed, Button ClickedHigh volume, lower individual impact

Step 4: Configure Forwarding Rules

Forwarding rules route events from SignalSmith to downstream analytics and marketing tools in real time.

  1. Navigate to Events > Forwarding

  2. Click Create Forwarding Rule

  3. Select a destination (e.g., Amplitude)

  4. Configure which events to forward:

    • All events — Forward everything (simple but may be costly for high-volume sources)
    • Specific events — Select event names to forward (e.g., only “Order Completed” and “Signup Completed”)
    • Filter by properties — Forward events matching specific property conditions (e.g., only events where total > 100)
  5. Configure property mapping if the destination uses different field names:

SignalSmith PropertyDestination PropertyNotes
userIduser_idAmplitude uses snake_case
properties.totalrevenueMap to Amplitude’s revenue field
properties.product_iditem_idRename for destination schema
  1. Click Save

Forwarding to Multiple Destinations

You can create multiple forwarding rules for the same events. For example:

  • Forward all events to your data warehouse for analysis
  • Forward revenue events to Amplitude for product analytics
  • Forward marketing events to Braze for campaign triggering
  • Forward page views to Google Analytics for web analytics

Each forwarding rule operates independently, so events can flow to multiple destinations simultaneously.

Consent management ensures that events are only forwarded to destinations when the user has granted the appropriate consent.

  1. Navigate to Events > Consent
  2. Define consent categories that match your privacy policy:
CategoryDescriptionDestinations
analyticsProduct analytics and usage trackingAmplitude, Mixpanel
marketingMarketing automation and email campaignsBraze, HubSpot
advertisingAd targeting and retargetingGoogle Ads, Facebook Ads
functionalEssential product functionalityInternal systems
  1. Map each forwarding rule to a consent category
  2. Set the default consent state for new users (granted or denied depending on your jurisdiction)

When a user grants or revokes consent, send a consent event:

// User grants marketing consent
track('Consent Updated', {
  category: 'marketing',
  status: 'granted',
  source: 'cookie_banner'
});
 
// User revokes advertising consent
track('Consent Updated', {
  category: 'advertising',
  status: 'denied',
  source: 'privacy_settings'
});

SignalSmith automatically updates the user’s consent state and begins or stops forwarding events to the affected destinations.

Step 6: Monitor in the Event Debugger

The event debugger provides a real-time view of events flowing through your pipeline.

  1. Navigate to Events > Live
  2. See events arriving in real-time as a scrolling feed
  3. Use filters to focus on specific events:
    • Event name — Show only “Order Completed” events
    • User ID — Show all events for a specific user (useful for debugging)
    • Write key — Show events from a specific source
    • Contract status — Show only events that violated a contract
  4. Click any event to expand its full payload — properties, context, contract validation result, and forwarding status
  5. Check forwarding delivery status:
    • Delivered — Event was successfully forwarded to the destination
    • Pending — Event is queued for forwarding
    • Failed — Forwarding failed (click for error details)
    • Blocked — Event was blocked by consent or forwarding rules

Debugging Common Issues

IssueHow to Debug
Events not appearingCheck the write key is correct. Filter by write key to isolate.
Contract violationsFilter by contract status “violated.” Review the violation details.
Forwarding failuresClick the failed delivery to see the destination’s error response.
Missing propertiesExpand the event to see what was actually sent vs. what the contract expects.
Duplicate eventsCheck timestamps — your app may be sending the same event twice.

Step 7: Write Events to Your Warehouse

Persist events in your data warehouse for historical analysis, joining with your batch data.

  1. Navigate to Events > Warehouse
  2. Click Configure Warehouse Destination
  3. Select your warehouse source (the same warehouse connected as a source)
  4. Configure the target schema and table name (e.g., SIGNALSMITH_EVENTS.RAW_EVENTS)
  5. Set the write frequency:
    • Real-time — Events are written as they arrive (highest cost, lowest latency)
    • Micro-batch — Events are buffered and written every 1-5 minutes (good balance)
    • Batch — Events are written on a schedule (e.g., every hour, lowest cost)
  6. Click Save

Once configured, events are available in your warehouse for SQL analysis:

SELECT
  event,
  COUNT(*) AS event_count,
  COUNT(DISTINCT user_id) AS unique_users
FROM SIGNALSMITH_EVENTS.RAW_EVENTS
WHERE timestamp >= DATEADD(day, -7, CURRENT_TIMESTAMP)
GROUP BY event
ORDER BY event_count DESC

Verification

After completing all steps, verify your setup:

  1. Send a test event from your application
  2. Check the debugger — The event should appear in the live feed within seconds
  3. Verify contract validation — If you defined a contract, check that it passes
  4. Check forwarding — Confirm the event arrived at your destination (check Amplitude, Mixpanel, etc.)
  5. Check consent — Send an event for a user who hasn’t granted consent and verify it’s blocked from forwarding
  6. Query the warehouse — After the configured write delay, verify the event is in your warehouse table

Next Steps