GenieOSdocs

Events

First-class custom events. The join key between GenieOS and your product.

Events are how GenieOS learns about your product. Emit one any time something interesting happens to a contact — a sign-up, a purchase, a trial expiry, a feature touched for the first time. Events:

  • Drive sequence transitions.
  • Trigger workspace-level webhooks.
  • Show up in the audit log and the contact timeline in the dashboard.
  • Are queryable from the API for the last 90 days.

There is no fixed event vocabulary. Pick names that read like sentences in your domain — subscription.upgraded, lesson.completed, document.shared. We never invent events on your behalf.

Emitting

await mg.events.emit(
  {
    type: 'order.completed',
    contact: { external_id: 'usr_123' },
    metadata: {
      order_id: 'ord_8a72c0',
      total_pence: 4299,
      first_purchase: true,
    },
  },
  { idempotencyKey: 'ord_8a72c0:completed' },
);
mg.events.emit(
    type="order.completed",
    contact={"external_id": "usr_123"},
    metadata={
        "order_id": "ord_8a72c0",
        "total_pence": 4299,
        "first_purchase": True,
    },
    idempotency_key="ord_8a72c0:completed",
)
curl https://api.genieos.pro/v1/events \
  -H "Authorization: Bearer $GENIEOS_API_KEY" \
  -H "Idempotency-Key: ord_8a72c0:completed" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "order.completed",
    "contact": { "external_id": "usr_123" },
    "metadata": {
      "order_id": "ord_8a72c0",
      "total_pence": 4299,
      "first_purchase": true
    }
  }'

The response is small; the side-effects are large.

{
  "id": "evt_01JABC...",
  "type": "order.completed",
  "contact": { "external_id": "usr_123", "email": "ada@example.com" },
  "received_at": "2026-04-20T11:34:00.123Z",
  "fan_out": {
    "sequences_woken": 1,
    "webhooks_dispatched": 2
  }
}

fan_out.sequences_woken is the number of sequence nodes that transitioned because of this event; webhooks_dispatched is the number of subscriptions that matched. Both are useful for debugging "why didn\u2019t my sequence advance?".

Identifying the contact

Events must identify the contact. Three ways, in order of preference:

  1. external_id — your stable id (user id, customer id). Best because it survives email changes.
  2. email — accepted, normalised (lower-cased, dot-stripped for Gmail).
  3. id — GenieOS\u2019s own contact id, if you stored it.

Sending all three resolves to a single contact; sending none returns 422 contact_required. New external_ids create a new contact; new emails on an existing external_id overwrite. See the dashboard for the full identity model.

Metadata

Metadata is a free-form JSON object up to 4 KB serialised. Keys must be snake_case strings; values may be string, number, boolean, or arrays of those. Nested objects are accepted but flattened with . separators in the audit log.

Use metadata to:

  • Carry IDs you\u2019ll want to read back from a webhook (order_id, subscription_id).
  • Drive sequence branches (first_purchase: true).
  • Power per-contact reporting (plan, arr_band).

Don\u2019t use metadata for:

  • PII you don\u2019t need (don\u2019t send full addresses, payment methods).
  • Massive payloads (we cap at 4 KB; if you need more, send a URL).
  • Anything you wouldn\u2019t want printed in a support response.

Querying past events

GET /v1/events?contact[external_id]=usr_123&type=order.completed&limit=50

Returns the most recent matching events for the contact. Filterable on:

  • type (exact, or order.* prefix).
  • since / until (ISO-8601).
  • contact.email / contact.external_id / contact.id.

Webhooks: the inverse direction

Once you\u2019ve emitted an event, you can also receive events from GenieOS — transactional.delivered, sequence.transitioned, schema_contract.changed. See Webhooks.

Idempotency on emit is cheap insurance

Two writers emitting "order.completed" on the same order is a real and common shape. Use the order id (or any naturally-unique key) as the idempotency key and you\u2019ll never double-fire your sequences.

On this page