Ingest

POST /v1/sdk/ingest is the durable mobile-event endpoint. The React Native, iOS, Android, and Flutter SDKs handle sessions, subject credentials, stable event IDs, batching, and retries for you. Do not treat the public write key as proof of user identity.

Endpoint

POST /v1/sdk/ingest HTTP/1.1
Host: api.usergist.com
Authorization: Bearer rk_live_abcdef
X-UserGist-Subject-Token: st_anonymous_or_identified_subject
Content-Type: application/json

Body

{
  "context": {
    "anonymousId": "anon_1a2b3c",
    "externalId": "user_42",
    "sdkVersion": "0.1.4",
    "platform": "ios",
    "appVersion": "1.4.0",
    "locale": "en-DE"
  },
  "events": [
    {
      "eventId": "00000000-0000-4000-8000-000000000001",
      "name": "checkout_completed",
      "timestamp": "2026-05-12T10:30:00Z",
      "anonymousId": "anon_1a2b3c",
      "externalId": "user_42",
      "properties": {
        "orderId": "ord_991",
        "amountUsd": 49
      }
    }
  ]
}

Top-level fields

FieldTypeRequiredNotes
contextobjectSubject identity plus SDK/platform metadata.
eventsarray1–500 events, all owned by the context subject.
deliveryobjectRequest immediate engagement evaluation for event IDs in this batch; see below.

Event shape

FieldTypeRequiredNotes
eventIdUUIDRecommendedStable across retries; every production SDK supplies it.
namestringSnake-cased, past tense recommended.
timestampISO 8601Must fall within the configured past/future ingestion windows.
anonymousIdstringMust match the authenticated subject and batch context.
externalIdstringMust match the identified subject token when supplied.
propertiesobjectJSON-compatible values.

Response

{
  "data": {
    "accepted": 1,
    "rejected": 0,
    "errors": []
  }
}

Events are committed to a durable Postgres outbox before acknowledgment. Analytics and trigger projection retry asynchronously. A request with delivery can also evaluate engagement after the commit and return an optional instructions array inside data. Event-level preprocessing failures appear in errors; a contract-invalid request is rejected as a whole.

Immediate engagement

The React Native and web SDKs in the immediate-delivery release manage this optional protocol extension:

{
  "delivery": {
    "eventIds": ["00000000-0000-4000-8000-000000000001"],
    "screenName": "movies"
  }
}

Include this object alongside context and events. eventIds accepts 1–20 UUIDs that belong to this batch. screenName is optional and at most 120 characters. Web additionally supplies clientId, the UUID of its active browser session, bound to the current subject token; a different user's or ended session is rejected.

The server evaluates canonical committed events through the existing trigger engine and may return authorized feedback, in-app or survey instructions. Coordinated surveys can include an authorized attempt to avoid an additional start request. Consent, audience, platforms, configured delays and frequency limits still apply. This does not make push provider delivery synchronous.

An empty or absent instructions array is not a promise that a campaign will never appear: worker processing and normal polling remain recovery paths. Immediate responses do not advance the polling cursor past older instructions. Clients must deduplicate presentations and acknowledge through their existing instruction protocol. Preserve stable event IDs and event order across retries; resending an ID with different properties does not replace its committed data.

Anonymous-only

Before identify, the SDK uses a server-bound anonymous subject token. After your backend supplies an identified subject token, aliases are linked to one canonical dashboard user without rewriting historical events.

Batching

React Native ordinarily flushes every 15 seconds or 100 events, subject to host configuration, and flushes known server-dependent engagement triggers immediately. Earlier queued activity keeps its order, so a watch event can precede its reminder. Web queues tracked events for prompt transmission and consumes immediate instructions from the response. Other SDK versions can retain their previous batching and polling behavior. Use normal batching for bulk analytics.

Protocol example

Normally you should use the SDK for your app's platform. A custom low-level client must first call POST /v1/sdk/session with its write key and anonymous ID, retain the returned subject token, and send that token with every later request:

curl https://api.usergist.com/v1/sdk/ingest \
  -H "Authorization: Bearer $USERGIST_WRITE_KEY" \
  -H "X-UserGist-Subject-Token: $USERGIST_SUBJECT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "context": {
      "anonymousId": "anon_1a2b3c",
      "sdkVersion": "0.1.4",
      "platform": "react-native"
    },
    "events": [
      { "eventId": "00000000-0000-4000-8000-000000000001",
        "anonymousId": "anon_1a2b3c",
        "name": "subscription_renewed", "timestamp": "2026-08-17T10:00:00Z",
        "properties": { "plan": "pro", "monthlyUsd": 19 } }
    ]
  }'

What's next