Updati API docs

Documentation

Ingestion API

Push events from your product into Updati. A project API key routes each request — your payload only needs a category and a title.

https://staging.updatiapp.com/api/v1

Overview

Updati is an event inbox for teams. External systems post events over HTTPS; the iOS app shows them by project, category, and highlight cards.

  • Feed events land in a category’s activity log.
  • Cards are project-level highlights (weekly stats, etc.).
  • Metric categories aggregate numeric value over time.

Authentication

Create an API key in the app under the project → API keys. The raw key is shown once. Send it on every request:

Authorization: Bearer gl_live_...

The key identifies the project. Do not put a project id in the URL or body.

Send an event

POST https://staging.updatiapp.com/api/v1/events

curl https://staging.updatiapp.com/api/v1/events \
  -H "Authorization: Bearer gl_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "category": "revenue",
    "title": "New annual subscription",
    "message": "VIP Annual",
    "level": "normal",
    "value": 9900,
    "currency": "USD",
    "external_id": "stripe_evt_123",
    "url": "https://example.com/customers/42",
    "metadata": {
      "provider": "Stripe",
      "country": "US"
    },
    "occurred_at": "2026-07-28T10:42:00Z"
  }'

Success returns 201 for a new event, or 200 when external_id already exists (idempotent replay).

Cards

Cards are still events, with placement: "card". They appear in the project’s cards carousel instead of the activity feed. Category is optional.

curl https://staging.updatiapp.com/api/v1/events \
  -H "Authorization: Bearer gl_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "placement": "card",
    "title": "This week’s revenue",
    "message": "Up on last week across Starter and VIP renewals.",
    "value": 42845,
    "currency": "USD"
  }'

Currency values are in the smallest unit (cents for USD): send 42845 for $428.45, or 0 for $0.

Batch

POST https://staging.updatiapp.com/api/v1/events/batch

Send up to 200 events in one request. Validation errors are reported per item.

curl https://staging.updatiapp.com/api/v1/events/batch \
  -H "Authorization: Bearer gl_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [
      { "category": "signups", "title": "Ada joined", "value": 1, "external_id": "a1" },
      { "category": "signups", "title": "Lin joined", "value": 1, "external_id": "a2" }
    ]
  }'

Fields

Field Required Notes
title Yes Short headline shown in the app. Max 255 characters.
category Feed: yes Category slug from the project. Required for feed events; optional for cards.
placement No feed (default) or card.
message No Supporting detail. Max 5000 characters.
level No normal (default) or critical.
value Sometimes Number for metric categories. Feed categories reject values. Cards may include one. 0 is kept and shown — only omit the field when there is no figure. Currency amounts are in minor units (cents): send 9900 for $99.00. Converted on ingest for storage and display. Non-currency metrics (counts, %, ms) use the unit as-is.
currency No 3-letter ISO code (USD, GBP, …). Marks the value as money (cents → dollars) and formats it in the app. Useful on cards that are not tied to a currency metric category. Metric categories already set to currency also treat value as cents. currency_code is accepted as an alias.
external_id No Your idempotency key per project. Replays return the existing event.
url No http(s) link opened from the event detail screen.
metadata No JSON object (not an array). Max 16 KB encoded.
occurred_at No ISO 8601 timestamp. Defaults to receive time. Not in the future.

Idempotency

When you send an external_id that already exists on that project, Updati returns the original event instead of creating another. Use this for Stripe event ids, job ids, or any retry-safe sender so metrics never double-count.

Errors & limits

  • 401 — missing or invalid API key
  • 422 — validation failed (unknown category, bad value, etc.)
  • 429 — rate limited (default 300 requests / minute / key)

Error bodies are JSON. Batch responses list per-index failures under the usual Laravel validation shape.

Examples

JavaScript

await fetch('https://staging.updatiapp.com/api/v1/events', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.UPDATI_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    category: 'revenue',
    title: 'New annual subscription',
    value: 9900,
    currency: 'USD',
    external_id: 'stripe_evt_123',
    occurred_at: new Date().toISOString(),
  }),
});

PHP (Laravel)

Http::withToken(config('services.updati.key'))
    ->post('https://staging.updatiapp.com/api/v1/events', [
        'category' => 'revenue',
        'title' => 'New annual subscription',
        'value' => 9900,
        'currency' => 'USD',
        'external_id' => 'stripe_evt_123',
        'occurred_at' => now()->toIso8601ZuluString(),
    ]);