Deploy & Catch

Deploy — report lifecycle events

POST /api/deploy/report — SDK reports OTA bundle lifecycle events (download_complete, apply_success, rollback, crash_on_update). Best-effort telemetry — never blocks the SDK.

After an OTA bundle is downloaded, applied, or rolled back, the React Native SDK posts a lifecycle event here. The engine writes them to ClickHouse for adoption + crash analytics, and the dashboard's per-release stats roll up from these events.

This endpoint is telemetry only — auto-rollback is triggered separately by Catch's spike detector + the explicit /api/v1/deploy/releases/:id/rollback endpoint, not by reports here.

POST/api/deploy/report

Authentication

Required header: x-api-key: sk_live_… or x-api-key: sk_test_…. See Authentication.

Request body

JSON
{
"events": [
  {
    "event_type": "apply_success",
    "release_id": "rel_xyz789",
    "distinct_id": "device_abc",
    "app_version": "1.2.0",
    "bundle_label": "1.2.0-rc.1",
    "platform": "ios",
    "os_version": "17.4",
    "device_model": "iPhone15,2",
    "metadata": "{\"prev_label\":\"1.2.0\",\"first_render_ms\":820}"
  }
]
}
eventsarray<object>Required
Batch of lifecycle events. No documented per-batch cap; practical limit is the 500 MB body size.
events[].event_typestringRequired
One of `download_complete`, `apply_success`, `rollback`, `crash_on_update`.
events[].release_idstring (`rel_*`)Required
Release the event refers to. From the response of `/api/deploy/check`.
events[].distinct_idstringRequired
Device identifier — matches what the SDK sent on `/api/deploy/check`.
events[].app_versionstring
Native app version at event time.
events[].bundle_labelstring
OTA bundle label — same shape as `release.label`.
events[].platformstring
`ios` / `android`.
events[].os_versionstring
OS version.
events[].device_modelstring
Hardware identifier.
events[].metadatastring (JSON)
Free-form event-specific JSON metadata. Examples: `{first_render_ms: 820}` for `apply_success`, `{stack_trace: "...", retry_count: 2}` for `crash_on_update`.

Event types

event_typeWhenCommon metadata
download_completeThe SDK has finished downloading the OTA archive and verified its SHA-256.{download_ms: 8200, retried: false}
apply_successThe bundle is live and the SDK's notifyAppReady() has fired.{prev_label: "1.2.0", first_render_ms: 820}
rollbackThe SDK reverted to the previous bundle. Either device-driven (two-crash rule) or server-pushed (kill-switch).{reason: "two_crash_rule", crash_count: 2} or {reason: "kill_switch", forced: true}
crash_on_updateThe SDK detected a crash within N seconds of applying a new bundle (configurable, default 30 s).{stack_trace: "...", first_render_ms: null}

Response

202 Accepted on success — even when validation fails on individual events:

JSON
{
"accepted": 1,
"rejected": 0
}

The handler is best-effort — it doesn't return per-event error reporting. If an event has a missing field or an invalid event_type, the engine drops it silently and counts it under rejected. This matches the SDK's "fire and forget" expectation: telemetry should never block the user-facing flow.

Server-side enrichment

The engine fills these fields automatically — your client doesn't need to send them:

FieldSource
timestampServer-stamped on receipt. Ignores any client-supplied timestamp.
project_idResolved from API key
organization_idResolved from API key
environmentlive or test, resolved from API key

ClickHouse table

Events land in deploy_events. Each row is a flat record with no nesting; the metadata column is parsed as JSON for queryable columns where useful.

Aggregation

The dashboard's per-release stats (downloads, installs, rollbacks, crash rate, unique devices) roll up from these events. The aggregation runs on every GET /api/v1/deploy/releases and GET /api/v1/deploy/releases/:id query — there's no scheduled materialization step you have to wait on.

What this endpoint does not do

  • It does not trigger auto-rollback. That's the job of:
    • The SDK's two-crash detector (crash_on_update x 2 within 30 seconds → SDK self-reverts on next launch).
    • Catch's spike detector (crash_free_sessions below threshold → posts to /api/v1/deploy/releases/:id/rollback).
  • It does not validate that release_id exists. Orphaned release_id values are accepted as telemetry (useful for tracking devices on disabled releases that are about to roll back).
  • It does not dispatch outbound webhooks. Those are dispatched separately by the dashboard / management API actions.

Practical patterns

iOS / Android telemetry

The React Native SDK auto-batches lifecycle events and flushes:

  • After every successful applyPending().
  • On app suspend (so events from the just-installed bundle aren't lost).
  • Periodically (every 30 seconds) while the app is foregrounded.

Direct HTTP integrations should follow the same pattern — batch events, flush on lifecycle transitions, keep retries idempotent (orphaned re-deliveries don't double-count).

Crash reporting

For the crash_on_update event type, include the stack trace + the time-to-crash in metadata. The dashboard correlates these with Catch issues that fired during the rollout window.

What's next

Edit this page on GitHub