Skip to content
Sendora Cloud
Create account
Concepts

The event envelope

Every event in Sendora Cloud shares one shape — five fields that every module understands.

Required fields

  • orgId — your organization id (UUID).
  • projectId — required. Identity is per-project.
  • module — which product module the event belongs to (e.g. analytics, links, billing).
  • eventType — dotted name describing what happened (e.g. order.placed).
  • timestamp — ISO 8601, UTC.

Optional fields

  • userId — identified user id (set after identify()).
  • anonymousId — anonymous device id (auto-managed by the SDK).
  • properties — free-form JSON for the event payload.
  • context — auto-populated by the SDK: userAgent, referrer, page, locale. IP is hashed (SHA-256, truncated) at write — never stored raw.

Example

{
  "orgId":     "5b8f1f4f-7b8e-4e9d-9d9b-...",
  "projectId": "0a1b2c3d-...",
  "module":    "analytics",
  "eventType": "order.placed",
  "timestamp": "2026-05-01T12:00:00Z",
  "userId":    "u_123",
  "anonymousId": "anon_xyz",
  "properties": { "orderId": "ord_81", "amount": 4200, "currency": "USD" },
  "context": {
    "userAgent": "Mozilla/5.0 ...",
    "referrer":  "https://...",
    "page":      { "url": "...", "title": "..." }
  }
}

Identify (auto-upsert profile traits)

A special eventType: "identify" with context.traits lands the traits onto the profile row keyed by userId. Use it on signup and any time a customer-attribute changes.

{
  "orgId":     "...",
  "projectId": "...",
  "module":    "profiles",
  "eventType": "identify",
  "timestamp": "2026-05-01T12:00:00Z",
  "userId":    "u_123",
  "context":   { "traits": { "email": "sam@acme.co", "plan": "growth" } }
}

Why this shape

Because every module reads from and writes to the same envelope, analytics funnels, messaging templates, and support timelines all speak the same language. Add a new module, and it instantly has every event it needs — no pipeline to build.

Naming conventions

We recommend lowercase, dot-separated object.past_tense_verb: order.placed, session.started, link.clicked. Sendora Cloud doesn't enforce this — but it makes searching events easier.

Anonymous → identified

Use sendora.auth.signInAnonymously() to mint a stable user_id before signup. When the visitor signs up, sendora.auth.signUp() upgrades the same row in place — attached events stay bound. See Identity for the full lifecycle.