Home Product Solutions Customers Pricing Changelog About Docs Contact
Aurora Labs Ltd · Shoreditch, London
hello@aurora.io
Home/Docs

Developer docs

Install an SDK, send one event, and watch it land in your workspace before the kettle has boiled. Everything below runs on the same API our own dashboards are built on — no private endpoints, no hidden rate tiers.

Quickstart

Three steps to your first event.

Get a sandbox workspace
01

Install the SDK

Pick the runtime closest to where your events already live. Server-side is preferable for anything that touches money — it survives ad blockers and it cannot be tampered with in the browser.

JavaScriptterminal
npm i @aurora/analytics
# or: pnpm add @aurora/analytics
Pythonterminal
pip install aurora-analytics
Goterminal
go get github.com/aurorahq/aurora-go
02

Initialise and track

Your write key lives in the workspace settings under Data sources. Keep it in an environment variable — a write key in a public repository gets rotated by our secret scanner within minutes, and you will get an email about it.

JavaScriptsrc/analytics.ts
import { Aurora } from '@aurora/analytics';

const aurora = new Aurora({
  writeKey: process.env.AURORA_WRITE_KEY,
  region: 'eu-west-2',        // London. Use 'eu-central-1' for Frankfurt.
});

await aurora.track({
  userId: 'usr_8f21c4',
  event: 'checkout_started',
  properties: {
    basket_value: 148.50,
    currency: 'GBP',
    storefront: 'uk',
    items: 3,
  },
});
Pythonanalytics.py
import os
from aurora import Aurora

aurora = Aurora(
    write_key=os.environ["AURORA_WRITE_KEY"],
    region="eu-west-2",
)

aurora.track(
    user_id="usr_8f21c4",
    event="checkout_started",
    properties={
        "basket_value": 148.50,
        "currency": "GBP",
        "storefront": "uk",
        "items": 3,
    },
)
Goanalytics.go
client := aurora.New(aurora.Config{
    WriteKey: os.Getenv("AURORA_WRITE_KEY"),
    Region:   "eu-west-2",
})
defer client.Close()

err := client.Track(ctx, aurora.Event{
    UserID: "usr_8f21c4",
    Name:   "checkout_started",
    Properties: map[string]any{
        "basket_value": 148.50,
        "currency":     "GBP",
        "storefront":   "uk",
    },
})
Shellraw HTTP
curl https://api.aurora.io/v1/events \
  -H "Authorization: Bearer $AURORA_WRITE_KEY" \
  -H "Content-Type: application/json" \
  -H "Aurora-Version: 2026-05-01" \
  -d '{"event":"checkout_started","user_id":"usr_8f21c4",
       "properties":{"basket_value":148.50,"currency":"GBP"}}'

Region defaults to eu-west-2 (London). Enterprise workspaces pinned to Frankfurt should pass eu-central-1 — see data residency.

03

Watch it land

Open the live debugger in your workspace and the event appears within a couple of seconds, with any rejected properties listed alongside the reason. Nothing is silently dropped — a property that fails schema validation is routed to the debug queue where you can inspect and replay it.

ingest — checkout_started accepted — usr_8f21c4 (eu-west-2)
schema — All 5 properties validated against catalogue v41
schema — plan_upgraded — unknown property "tier_name", routed to debug queue
sync — Snowflake destination up to date — lag 38s
webhook — hook_4471 returned 503 — retry 1 of 5 in 30s
JSONPOST /v1/events
{
  "event": "checkout_started",
  "user_id": "usr_8f21c4",
  "anonymous_id": "anon_5b1e77a9",
  "timestamp": "2026-07-14T09:32:11Z",
  "context": {
    "region": "eu-west-2",
    "locale": "en-GB",
    "app_version": "4.8.1",
    "source": "web"
  },
  "properties": {
    "basket_value": 148.50,
    "currency": "GBP",
    "items": 3,
    "storefront": "uk",
    "delivery_option": "royal_mail_tracked_24"
  }
}
The payload

One shape, everywhere.

Every SDK serialises to the same JSON body, so an event sent from a Go service and one sent from the browser are indistinguishable downstream. Only event and one of user_id or anonymous_id are required — everything else is optional and typed.

  • Timestamps are ISO 8601 in UTC; we store the workspace timezone separately so reports never drift
  • Monetary properties are stored to the penny — pass 148.50, not a rounded integer
  • Send an Idempotency-Key and a retried request will never double-count
FieldTypeRequiredNotes
eventstringYesLowercase object_verb, past tense. Max 64 characters.
user_idstringConditionalRequired unless anonymous_id is present. Never send an email address.
anonymous_idstringConditionalStitched onto the profile when you later call /v1/alias.
timestampISO 8601NoDefaults to receipt time. Backdating beyond 30 days needs a backfill token.
contextobjectNoReserved keys: region, locale, app_version, source, ip.
propertiesobjectNoUp to 120 keys, 32kB serialised. Types enforced by the catalogue.
Guides

Seven things worth reading
before you instrument.

Client SDKs

First-party libraries for JavaScript/TypeScript, Python, Go, Ruby, Swift and Kotlin. The browser bundle is 8.2kB gzipped, batches events every two seconds, retries with exponential backoff and queues offline so a flaky connection on the Northern line never loses a conversion.

HTTP API

Everything the SDKs do, over plain REST against https://api.aurora.io/v1. Bearer authentication, JSON bodies, ISO 8601 timestamps in UTC, and a dated Aurora-Version header so an API change never lands on you unannounced.

Event schema design

Our house convention is object_verb in the past tense — checkout_started, plan_upgraded, invoice_paid. Register events in the typed catalogue, run aurora schema lint in CI, and a mistyped property gets caught in code review instead of three months into a broken dashboard.

Identity & aliasing

Track anonymously, then call alias when someone signs in and Aurora stitches the pre-signup session onto the known profile. Merge rules are deterministic and replayable, and a UK GDPR erasure request removes every event for that subject across the warehouse in one call.

Warehouse sync

Bidirectional sync to Snowflake, BigQuery and Redshift using incremental change-data-capture rather than nightly full-table scans. Schedule it, trigger it from the API, backfill a date range, and watch row counts and lag per destination in real time.

Webhooks

Subscribe to alert.triggered, forecast.updated, sync.failed and eleven more. Every payload is signed with HMAC SHA-256 in the X-Aurora-Signature header, retried five times over 24 hours with jitter, and replayable from the dashboard for the last 30 days.

Self-serve SQL

Read-only SQL over the modelled tables, with the semantic layer available as functions so metric("active_users") means the same thing in a query as it does on a dashboard. Every result carries its lineage, and revenue columns are stored in pence to keep GBP arithmetic exact.


API reference

A sample of the surface.

See what changed

Every endpoint sits under https://api.aurora.io/v1 and speaks JSON in both directions. Authentication is a bearer token scoped to a single workspace; server keys start sk_live_, browser keys start pk_live_ and can only write events.

Pin the Aurora-Version header to a date and that contract is frozen for you for at least eighteen months. Unpinned requests follow the latest version, which is the wrong choice for production.

HTTPrequest headers
Authorization: Bearer sk_live_9f3c1ad2b7e4
Content-Type: application/json
Aurora-Version: 2026-05-01
Idempotency-Key: 7c2f0e51-3d9a-4a2b-9d18-6b0f2e441c30
MethodEndpointDescription
POST/v1/eventsIngest a single event. Returns 202 with the assigned event id.
POST/v1/events/batchIngest up to 500 events in one request. Partial success reported per item.
POST/v1/identifyAttach or update traits on a known user profile.
POST/v1/aliasMerge an anonymous id into a known user id after sign-in.
GET/v1/events/:idFetch one ingested event, including rejected properties, for debugging.
GET/v1/metrics/:keyRead a semantic-layer metric over a date range and optional breakdown.
POST/v1/queriesSubmit a read-only SQL query. Returns a query id immediately.
GET/v1/queries/:id/resultsPage through query results, 10,000 rows per page maximum.
GET/v1/destinationsList warehouse destinations with sync status and lag.
POST/v1/destinations/:id/syncTrigger an out-of-schedule sync or a dated backfill.
GET/v1/webhooksList webhook endpoints and their delivery health.
DELETE/v1/webhooks/:idRemove a webhook endpoint. In-flight retries are cancelled.

Full reference, including every query parameter and response shape, is generated from the live schema and versioned alongside it. Ask your solutions engineer for access to the sandbox workspace and the OpenAPI document.

Rate limits & errors

What happens when you push.

Limits by plan
PlanIngestionQuery APIBatch size
Starter60 req/min20 req/min100 events
Growth600 req/min120 req/min500 events
EnterpriseCustomCustom500 events
StatusMeaning
400Malformed JSON or a missing required field.
401Missing, revoked or wrong-type API key.
403Key lacks scope for this endpoint or workspace.
413Body above 1MB, or a property above 32kB.
422Schema validation failed. Rejected keys are named in the body.
429Rate limited. Honour Retry-After before trying again.
503Region failover in progress. Safe to retry with the same idempotency key.

Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. The SDKs read those headers and back off for you; if you are calling the API directly, please do the same rather than retrying in a tight loop. Sustained 429s never cost you data — buffer locally and replay.


Cannot find it?

Ask a person instead.

Want a sandbox
and a sample dataset?

We will spin up a workspace with your own events flowing through it, usually within a day of the call.