Base URLs
| Environment | Base URL |
|---|---|
| production | https://api.flowpilot.io/v1 |
| staging | https://staging-api.flowpilot.io/v1 |
| development | https://dev-api.flowpilot.io/v1 |
Authentication
Every request carries an SDK API key as a bearer token:sdk. A key of any other type is rejected with 401. Key prefixes:
fp_live_for production keys.fp_test_for staging/test keys.
Resolve
Fetch the flow for a placement and user.appIdis the app UUID (the App ID from the dashboard).placementIdis the human-readable placement key (for exampleonboarding).
Request
Stable identifier for the user. The SDK uses a persistent per-install UUID by default.
Identifier for this session.
One of
ios, android, web.Targeting attributes for audience matching (for example
{"country": "US", "app.version": "2.1.0"}). See Audience targeting.Response (200)
A match returns the flow. No match returns200 with flow_id: null (and the backend records a resolve_no_flow event server-side).
The matched flow ID, or
null when no flow matched.The specific published version served.
The version number.
The wire format version (see Schema versions).
The full flow JSON. See Flow schema reference.
Base URL for relative image and media paths.
Base URL for icon SVGs.
Font files to download and register. Each item is
{ family, weight, style, url }.Set when an experiment is active on the placement.
The assigned variant, when in an experiment.
The assigned variant’s name.
How long the SDK may treat the result as fresh. The SDK uses
300 (5 minutes) when omitted.Status codes
| Code | Meaning |
|---|---|
| 200 | Resolved (including the no-flow case, flow_id: null). |
| 400 | Missing/invalid app_id, placement_id, body, or validation failure. |
| 401 | Missing/invalid key, or wrong key type. |
| 403 | The app does not belong to the key’s workspace. |
| 404 | App not found. |
| 429 | Rate limited. |
Variations
- Offline export: add
?export=trueto get the deterministic default flow for the placement. The resolver skips the audience filter and A/B assignment and always returns the placement’s default flow version, so the export is stable. Used to snapshot a flow for offline bundling. - Legacy route:
POST /v1/placements/{placementId}/resolveexists for backward compatibility and is deprecated. Prefer the app-scoped route.
Example
Batch resolve
Resolve several placements for one identity in a single round-trip. The SDK uses this for launch prefetch (whenprefetchOnLaunch declares two or more placements) so it can warm many placements without one request per placement. If the call fails, the SDK falls back to per-placement resolves, so launch prefetch never depends on it.
Request
The identity fields (user_id, session_id, device_platform, attributes) apply to every key in placement_keys, exactly as a single resolve personalizes one placement.
The placement keys to resolve (for example
["onboarding", "paywall"]). Must be non-empty. Duplicates are removed server-side, and the de-duplicated set is capped at 50 placements.Stable identifier for the user.
Identifier for this session.
One of
ios, android, web.Targeting attributes for audience matching, applied to every placement. See Audience targeting.
Response (200)
Aresults array with one entry per de-duplicated placement key. Each entry is the same shape as a single resolve response (flow_id, flow_version_id, flow_schema, fonts, cache_ttl_seconds, and so on), tagged with its placement_key.
Resolution is best-effort per placement, so one placement never sinks the batch:
- A placement that matched no flow returns
flow_id: null(and the backend records aresolve_no_flowevent server-side, as with a single resolve). - A placement that failed to resolve returns
flow_id: nulland anerrorstring. The overall response is still200.
One result per requested placement key.
The placement key this result is for. Use it to correlate results regardless of order.
Present only when that one placement failed to resolve. Absent on success and on the no-flow case.
flow_id, flow_version_id, flow_version, schema_version, flow_schema, media_base_url, icon_base_url, fonts, experiment_id, variant_id, variant_name, cache_ttl_seconds).
Status codes
| Code | Meaning |
|---|---|
| 200 | Resolved. Per-placement no-flow and per-placement errors are reported inside results, not as a non-200 status. |
| 400 | Missing/invalid app_id, empty placement_keys, more than 50 placements, or a validation failure. |
| 401 | Missing/invalid key, or wrong key type. |
| 403 | The app does not belong to the key’s workspace. |
| 404 | App not found. |
| 429 | Rate limited. |
Example
Events
Send one analytics event or a batch.Request fields (EventInput)
Required:
Unique event ID.
Must match the
appId in the URL (mismatches are rejected).The flow the event belongs to.
Must reference a real flow version in the app, or the event is rejected.
Stable user identifier.
Session identifier.
One of
ios, android, web.One of the recognized event types.
When the event happened.
The SDK version that produced the event.
placement_id, flow_version, experiment_id, variant_id, variant_name, screen_id, screen_name, screen_index, element_id, element_type, interaction_type, revenue, currency, time_since_flow_start_ms, time_on_screen_ms, app_version, country, properties.
Limits
- Up to 100 events per request.
- Up to 256 KB payload.
Response (202)
Events buffered for storage.
Events dropped (validation failure, app_id mismatch, or out-of-scope flow version).
202 Accepted; events are written to analytics asynchronously.
Variations
- Legacy route:
POST /v1/eventsexists for backward compatibility and is deprecated. Prefer the app-scoped route.
Example
Notes
- The app-scoped routes are authoritative for
app_id: the value in the URL wins, and a conflicting bodyapp_idis rejected. - This contract may change. Build against the SDK, not raw HTTP, for production apps.