HTTP API

Send product events from your backend. Base URL: https://usetamari.com/api/v1. Every endpoint answers 202 once the data is queued, and it reaches the dashboard within seconds.

Authentication

Send your write key as a bearer token:

Header
Authorization: Bearer <write_key>

Create keys in Settings → Write keys. A key is shown once. Revoked keys answer 401.

POST /api/v1/track

Record a conversion for one of your users, when it happens.

FieldRequiredNotes
typeyessigned_up, first_api_call, went_live, or custom:<name> (a–z, 0–9, _ . -)
user_idat least one identifierYour own user ID
emailHashed and encrypted on arrival
github_loginLinks the signup to GitHub activity
anonymous_idThe snippet's visitor ID, if your frontend passes it to your backend
occurred_atnoISO 8601 with time zone. Defaults to when Tamari receives it. Up to 5 minutes in the future
propertiesnoAny JSON object
heard_aboutnoThe answer to “How did you hear about us?”, up to 255 characters
message_idnoYour unique ID for this event. A retry with the same ID is ignored

signed_up, first_api_call and went_live are recorded once per developer. If one arrives twice, the earliest occurred_at is kept. Custom types are recorded every time.

Send signed_up (curl)
curl https://usetamari.com/api/v1/track \
  -H "Authorization: Bearer $TAMARI_WRITE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "signed_up",
    "user_id": "usr_123",
    "email": "ada@example.com",
    "heard_about": "Conference talk",
    "message_id": "signup-usr_123"
  }'

POST /api/v1/identify

Link identifiers that belong to the same person, for example when a visitor signs in, so their earlier anonymous page views are credited to them. Each item needs at least two of user_id, email, github_login and anonymous_id. occurred_at is optional.

Identify
curl https://usetamari.com/api/v1/identify \
  -H "Authorization: Bearer $TAMARI_WRITE_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "anonymous_id": "a1b2c3", "user_id": "usr_123", "email": "ada@example.com" }'

The snippet's window.tamari.identify() calls this for you from the browser.

POST /api/v1/collect

Page views from the browser snippet. You do not need to call it. It accepts text/plain bodies (for navigator.sendBeacon) and takes the key in the body as write_key. Query strings and fragments are removed from the page and referrer addresses before storage.

Batches

/track and /identify accept a JSON array of up to 100 items instead of one object. /collect accepts { "write_key": "…", "batch": [ … ] }. A batch is checked as a whole: one invalid item rejects the request, and nothing is recorded.

Responses

StatusMeaning
202Accepted: { "accepted": <count> }. Processing is asynchronous
400Invalid body: { "error", "issues": [{ "path", "message" }] }. In a batch, path starts with the item's index
401Missing, unknown or revoked write key
413Body larger than 512 KB
429Rate limit exceeded. Retry after the Retry-After header, in seconds
500Tamari error. Safe to retry with the same message_id

Rate limits

Each write key can send bursts of up to 1,000 items, and 100 items per second sustained. Each item in a batch counts as one.

Privacy

Identifiers are hashed on arrival with a key unique to your workspace, before they are queued. Email addresses and GitHub usernames are also stored encrypted so the dashboard can show them; it masks email addresses. Raw values never reach Tamari's queue or logs. Do not send sensitive data such as health or financial information in properties.

HTTP API · Tamari docs