Browse docsOverview
Reference

REST API

The public endpoints the SDK and ad networks talk to. Hosted on the Reflect Global Edge Fabric — sub-millisecond, low-latency, routed within 5ms of mobile devices worldwide.

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: api.reflect.cloud
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 record committed to high-throughput authority shard
  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

GET /i/:link_id — impression pixel & CTV

The view-through half of a tracking link. Same link_id as /l/, no auth, no redirect: it records an impression and returns a 43-byte 1×1 transparent GIF. Give this URL to the network alongside the click URL — they embed it in the display or video creative, and an install with no click can then be credited to that campaign.

<img src="https://api.reflect.cloud/i/AB12CD34?sub1={adgroup}&sub2={creative}&creative_id={creative}"
     width="1" height="1" style="display:none" alt="" />

Query parameters honored:

ParamEffect
sub1, sub2Stored on the impression row; same reporting meaning as on /l/
creative_idStored as the impression’s creative identifier

The response is always the pixel — an unknown or malformed link_id returns the same GIF rather than an error, so a mis-typed tag never breaks a creative. The database write happens after the response is sent, so serving the pixel never waits on it. Copy the URL from any link row in the console (Copy pixel, or Copy for network for the click URL and pixel together).

POST /ctv/impression

Connected-TV and other server-side inventory, where no browser loads a pixel. The ad server calls this endpoint directly with a Reflect server key; matching is by household IP, so no device identifier is involved.

POST /ctv/impression HTTP/1.1
Host: api.reflect.cloud
Authorization: Bearer <server key>
Content-Type: application/json

{
  "app_id": 42,
  "partner_id": 7,
  "household_ip": "203.0.113.10",
  "country": "IN",
  "campaign_name": "diwali-2026-ctv"
}

household_ip must be IPv4 — it is hashed at the /24 level before storage, and the install matcher only matches IPv4 households, so an IPv6 address is rejected with400 rather than stored as an unmatchable hash. app_id and partner_idmust belong to the company that owns the key (403 otherwise). Returns { "ok": true }.

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 database write throughput bounded.

POST /skan-postback

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

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

<key>NSAdvertisingAttributionReportEndpoint</key>
<string>https://api.reflect.cloud/skan-postback</string>

GET /health

Liveness probe. Returns 200 with core subsystem health (Ingestion, Storage, Cache, Pipelines). Used by the login footer’s status link.

Rate limits

None enforced server-side beyond per-tenant caps (see Plans & billing). Reflect Shield™ adaptive edge rate-limiting and DDoS filtering applies; if you are encountering throttling, contact support.