Reference

REST API

The four public endpoints the SDK + ad networks talk to. All hosted on Cloudflare Workers — fast, cheap, edge-routed.

Authentication

Every authenticated endpoint expects three headers:

HeaderValue
X-Reflect-Company-Keyco_live_… — your tenant identifier
X-Reflect-App-Keyapp_live_… — per-app identifier
X-Reflect-SignatureLowercase hex HMAC-SHA256 of the body bytes, using your app’s SigningSecret

If Content-Encoding: gzip is sent, the signature is computed over the compressed wire bytes. Server verifies, then decompresses.

POST /event

Submit a batch of events.

POST /event HTTP/1.1
Host: reflect.bablu147147.workers.dev
Content-Type: application/json
Content-Encoding: gzip                       ← optional, ≥10 events recommended
X-Reflect-Sdk: 2.1.0
X-Reflect-Company-Key: co_live_…
X-Reflect-App-Key: app_live_…
X-Reflect-Signature: <64-char hex>

{
  "events": [ { "event_id": "...", "event_name": "...", "install_uuid": "...", ... } ],
  "sent_at_ms": 1735999999000
}

Response (always 200 once auth passes; per-event errors in body):

{
  "accepted": 47,
  "rejected": 3,
  "results": [
    { "event_id": "abc", "status": "accepted" },
    { "event_id": "def", "status": "rejected", "reason": "bad_event_id" }
  ],
  "audit_key": "audit/2026/04/26/co1/app42/abc-receivedat.jsonl.gz"
}

Error responses:

StatusReason
400missing_auth_headers, empty_body, bad_json, bad_batch_shape, bad_gzip
401unknown_app_key, unknown_company_key, company_suspended, app_company_mismatch, bad_signature
413body_too_large, decompressed_too_large
429cap_exceeded (Free tier exhausted)
503audit_write_failed

GET /l/:link_id

Tracking link redirect. Public, no auth. Pipeline:

  1. Edge filter (UA / ASN / country)
  2. Honeypot check (auto-block source IP if hit)
  3. Rate limiter (per IP /24 sliding window)
  4. Click row written to D1
  5. 302 to the platform-specific store URL with attribution params encoded

Query parameters honored:

ParamEffect
click_idPartner-supplied ID stored as ext_click_id
sub1sub5Free-form fields, threaded to attribution + postbacks
mobile_only=1Drop desktop UA

POST /privacy/delete

GDPR / CCPA right-to-be-forgotten. Same auth headers as /event. Body:

{ "install_uuid": "8f2a1c0e94d7423b8b53af7c9e21d630" }

Returns 202 { "ok": true, "queued": true }. Server queues the request; nightly cron drains in batches of ≤25 requests × ≤1000 rows/table to keep D1 row writes bounded.

POST /skan-postback

Apple SKAdNetwork postbacks. Public, no auth (Apple signs the payload). Body is Apple’s standard JSON. Reflect persists to R2 audit and returns 200.

To register Reflect as your SKAN postback endpoint, add to your iOS app’s Info.plist:

<key>NSAdvertisingAttributionReportEndpoint</key>
<string>https://reflect.bablu147147.workers.dev/skan-postback</string>

GET /health

Liveness probe. Returns 200 with binding status (D1, R2, KV, Queues). Used by the login footer’s status link.

Rate limits

None enforced server-side beyond per-tenant caps (see Plans & billing). Cloudflare’s native DDoS protection applies; if you’re hitting it accidentally, contact us.