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.
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
valueover 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 key422— 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(),
]);