Events

Events are the behavioral signals that Odyssey collects to understand how users navigate through journeys. The ingest API receives batches of events from the embed script or your own tracking implementation.

Event models

Odyssey tracks several types of events, all sharing a common base structure with event-specific properties.

Base event properties

All events include these properties:

  • Name
    _tag
    Type
    string
    Description

    The event type (e.g., session_start, step_view, field_focus).

  • Name
    schemaVersion
    Type
    integer
    Description

    Event schema version (currently 1).

  • Name
    id
    Type
    string
    Description

    Unique identifier for this event.

  • Name
    ts
    Type
    timestamp
    Description

    ISO 8601 timestamp when the event occurred.

  • Name
    orgKey
    Type
    string
    Description

    The organization slug.

  • Name
    journeyKey
    Type
    string
    Description

    The journey slug.

  • Name
    sessionId
    Type
    string
    Description

    Unique session identifier (UUID v4).

  • Name
    clientId
    Type
    string
    Description

    Persistent anonymous client identifier.

  • Name
    url
    Type
    string
    Description

    The page URL where the event occurred.

  • Name
    referrer
    Type
    string
    Description

    The referrer URL (optional).

  • Name
    embedVersion
    Type
    string
    Description

    Version of the embed script (optional).

  • Name
    source
    Type
    string
    Description

    Event source (e.g., "simpligov", "custom") (optional).

Event types

Session events

  • Name
    session_start
    Description

    Fired when a user begins a new journey session.

  • Name
    session_end
    Description

    Fired when a session completes or times out.

Navigation events

  • Name
    page_view
    Description

    Fired on page load. Includes optional title property.

  • Name
    step_view
    Description

    Fired when entering a journey step. Includes stepKey property.

Interaction events

  • Name
    field_focus
    Description

    Fired when focusing on a field. Includes optional fieldKey property.

  • Name
    field_blur
    Description

    Fired when leaving a field. Includes optional fieldKey property.

  • Name
    field_change
    Description

    Fired when a field value changes. Includes optional fieldKey property.

Validation and submission events

  • Name
    validation_error
    Description

    Fired on form validation failure. Includes optional fieldKey and errorType properties.

  • Name
    submit_attempt
    Description

    Fired when user attempts form submission.

  • Name
    submit_success
    Description

    Fired on successful form submission.

Analytics events

  • Name
    dropoff_signal
    Description

    Fired when user behavior indicates likely abandonment.


POST/api/ingest

Send events

This endpoint receives batches of events from the embed script or your custom tracking implementation.

Request body

  • Name
    schemaVersion
    Type
    integer
    Description

    Batch schema version (currently 1).

  • Name
    context
    Type
    object
    Description

    Context object with orgKey, journeyKey, optional language, and optional deviceClass.

  • Name
    events
    Type
    array
    Description

    Array of event objects to ingest.

Request

POST
/api/ingest
curl -X POST https://odysseylabs.ai/api/ingest \
  -H "Content-Type: application/json" \
  -H "Origin: https://grants.example.com" \
  -d '{
    "schemaVersion": 1,
    "context": {
      "orgKey": "example-org",
      "journeyKey": "small-business-grant",
      "language": "en-US",
      "deviceClass": "desktop"
    },
    "events": [
      {
        "_tag": "session_start",
        "schemaVersion": 1,
        "id": "evt_abc123",
        "ts": "2024-01-15T10:30:00.000Z",
        "orgKey": "example-org",
        "journeyKey": "small-business-grant",
        "sessionId": "sess_xyz789",
        "clientId": "client_def456",
        "url": "https://grants.example.com/apply",
        "referrer": "https://google.com",
        "embedVersion": "1.0.0"
      },
      {
        "_tag": "step_view",
        "schemaVersion": 1,
        "id": "evt_abc124",
        "ts": "2024-01-15T10:30:01.000Z",
        "orgKey": "example-org",
        "journeyKey": "small-business-grant",
        "sessionId": "sess_xyz789",
        "clientId": "client_def456",
        "url": "https://grants.example.com/apply/eligibility",
        "stepKey": "eligibility",
        "embedVersion": "1.0.0"
      },
      {
        "_tag": "field_focus",
        "schemaVersion": 1,
        "id": "evt_abc125",
        "ts": "2024-01-15T10:30:05.000Z",
        "orgKey": "example-org",
        "journeyKey": "small-business-grant",
        "sessionId": "sess_xyz789",
        "clientId": "client_def456",
        "url": "https://grants.example.com/apply/eligibility",
        "fieldKey": "business-name",
        "embedVersion": "1.0.0"
      }
    ]
  }'

Response

204 No Content

Using the embed script

The easiest way to send events is using the Odyssey embed script, which automatically tracks user behavior:

Embed script installation

<script
  src="https://odysseylabs.ai/embed/v1.js"
  data-org="example-org"
  data-journey="small-business-grant"
  data-endpoint="https://odysseylabs.ai/api/ingest"
  data-env="prod"
  async
></script>

The embed script:

  • Creates anonymous session and client IDs
  • Automatically tracks navigation and interaction events
  • Buffers events and sends them in batches
  • Uses navigator.sendBeacon for reliable delivery
  • Never collects PII or field values

See the Events & Analytics guide for complete details.

Manual event tracking

For custom tracking scenarios, you can call the optional API exposed by the embed script:

Manual tracking

// Track a custom event
window.JourneyEmbed.track('step_view', {
  stepKey: 'custom-step'
})

// Force flush buffered events
window.JourneyEmbed.flush()

Or send events directly to the ingest API from your backend:

Server-side tracking

await fetch('https://odysseylabs.ai/api/ingest', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    schemaVersion: 1,
    context: {
      orgKey: 'example-org',
      journeyKey: 'small-business-grant',
      deviceClass: 'desktop'
    },
    events: [
      {
        _tag: 'submit_success',
        schemaVersion: 1,
        id: generateId(),
        ts: new Date().toISOString(),
        orgKey: 'example-org',
        journeyKey: 'small-business-grant',
        sessionId: sessionId,
        clientId: clientId,
        url: currentUrl
      }
    ]
  })
})

Rate limiting

The ingest endpoint applies rate limiting to prevent abuse:

Per-organization rate limits

  • Default: 1,000 requests per hour per organization/journey pair
  • Custom limits: Configurable via organization settings
  • Unknown origins: 20 requests per minute (more restrictive)

Rate limit headers

Rate limit information is not currently included in response headers, but you'll receive a 429 Too Many Requests status when the limit is exceeded.

To request a rate limit increase, contact support from your organization dashboard.

CORS and security

The ingest endpoint validates requests based on origin:

  1. If a journey has allowedOrigins configured, only requests from those origins are accepted
  2. If allowedOrigins is empty, all origins are allowed (not recommended for production)
  3. Unknown origins receive more aggressive rate limiting

Set allowed origins when creating or updating a journey:

Set allowed origins

curl -X PUT https://odysseylabs.ai/api/journeys/small-business-grant \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "allowedOrigins": [
      "https://grants.example.com",
      "https://staging.grants.example.com"
    ]
  }'

Event validation

The ingest API validates events against the schema:

  • Required fields must be present
  • Field types must match (strings, integers, timestamps)
  • Timestamps must be valid ISO 8601 format
  • Event tags must be recognized types
  • Session and client IDs must be valid UUIDs or strings

Invalid events return a 400 Bad Request response with details:

Validation error

{
  "error": "Invalid request: ts must be a valid ISO 8601 timestamp"
}

Common errors

Organization not found (404)

{
  "error": "Organization not found"
}

Journey not found (404)

{
  "error": "Journey not found"
}

Rate limit exceeded (429)

{
  "error": "Rate limit exceeded for organization"
}

Invalid event data (400)

{
  "error": "Invalid request: events array is required"
}

Privacy considerations

Odyssey is designed with privacy as a core principle:

What is collected:

  • Event types and timestamps
  • Step and field identifiers
  • Anonymous session/client IDs
  • Page URLs and referrers
  • Device class and language

What is NOT collected:

  • Field values or user input
  • Personally identifiable information
  • Uploaded files
  • Form content
  • Names, emails, addresses, etc.

The clientId stored in localStorage allows repeat user tracking without revealing identity.

Was this page helpful?