Layers
Partner APIConcepts

SDK health & verification

Four signals partners can read to know whether an SDK install is healthy — `lastEventAt`, `events.list`, `capi-status`, and the active `verify-tracking` probe.

View as Markdown

When a partner installs the Layers SDK in an end-customer's app, four signals tell you whether tracking is working. Each surfaces a different layer of the pipeline. Pick the right one for the question you're asking.

The four signals

SignalEndpointWhat it tells youLatency
lastEventAtGET /v1/projects/:id/sdk-apps/:appIdWall-clock of the most recent event Layers received from this SDK app. Passive.Real-time.
Event timelineGET /v1/projects/:id/eventsThe actual stream of events with timestamps, properties, user identity. Passive.Real-time.
CAPI statusGET /v1/projects/:id/ads/capi-statusWhether the CAPI relay is forwarding events to Meta / TikTok, including the per-platform events_received count from the platform side. Passive, CAPI-only.5–15 min lag (platform-side aggregation).
Verify-trackingPOST /v1/projects/:id/sdk-apps/:appId/verify-trackingActive probe — sends a synthetic test event tagged test_event_code; reports whether it was received and which platforms would forward it.< 30s.

When to use which

"Is the SDK installed at all?" → check lastEventAt. If null, the SDK has never sent a single event from this app. If recent, the SDK is at least booting and emitting app_open.

"Is the SDK emitting the events we expect?" → list events filtered by name. Confirms specific events (Purchase, Subscribe, custom events) are firing in the right shape.

"Is CAPI forwarding to Meta / TikTok?"capi-status. Returns per-platform configuration health (pixel_id, access_token_vault_id, enabled) plus events_received from the platform's perspective. A drift between Layers' send count and the platform's received count indicates a forwarding problem.

"Is tracking healthy end-to-end right now — including for a quiet app?"verify-tracking. The active probe sends a synthetic event tagged test_event_code that Meta and TikTok both render in their event-debugger UIs. The partner doesn't need real user traffic; the probe round-trips in seconds and returns a structured status field.

Verify-tracking response shape

{
  "status": "healthy",
  "lastEventAt": "2026-05-08T17:02:11.123Z",
  "expectedEvents": ["app_open", "Purchase", "Subscribe"],
  "observedEvents": ["app_open", "Purchase", "Subscribe", "session_start"],
  "missing": [],
  "samples": [
    {
      "name": "Purchase",
      "receivedAt": "2026-05-08T17:01:55.000Z",
      "platforms": [
        { "platform": "meta", "forwarded": true, "fbtrace_id": "AbCDeFg..." },
        { "platform": "tiktok", "forwarded": true, "request_id": "abc123" }
      ]
    }
  ],
  "checks": [
    { "name": "ingest_endpoint_reachable", "passed": true },
    { "name": "app_id_recognized", "passed": true },
    { "name": "capi_meta_configured", "passed": true },
    { "name": "capi_tiktok_configured", "passed": true },
    { "name": "test_event_round_trip", "passed": true }
  ]
}

status enum: "healthy", "missing_events", "schema_drift", "no_install". Use it as a single boolean-ish gate; render the per-check breakdown as the diagnostic detail when not "healthy".

Pattern: partner dashboard widget

async function renderSdkHealthBadge(projectId: string, appId: string) {
  const app = await fetch(`/v1/projects/${projectId}/sdk-apps/${appId}`).then(r => r.json());

  if (!app.lastEventAt) return { color: 'red', label: 'No events received' };

  const ageMs = Date.now() - new Date(app.lastEventAt).getTime();
  if (ageMs > 24 * 60 * 60 * 1000) {
    return { color: 'amber', label: `Last event ${Math.round(ageMs / 3600_000)}h ago` };
  }

  // For ads-running customers, also check CAPI:
  const capi = await fetch(`/v1/projects/${projectId}/ads/capi-status`).then(r => r.json());
  if (capi.meta?.enabled && capi.meta.events_received_24h === 0) {
    return { color: 'amber', label: 'Meta CAPI configured but receiving 0 events' };
  }

  return { color: 'green', label: 'SDK healthy' };
}

For the active probe path (e.g. a "Test connection" button in your dashboard):

const verify = await fetch(
  `/v1/projects/${projectId}/sdk-apps/${appId}/verify-tracking`,
  { method: 'POST' }
).then(r => r.json());

if (verify.status !== 'healthy') {
  showInstallTroubleshooting(verify.checks.filter(c => !c.passed));
}

See also

On this page