API Reference
All API endpoints are served under /v1/ota/ and require authentication via an API key (passed as a Bearer token) and a project slug (passed as the X-Project header).
curl https://api.appdispatch.com/v1/ota/updates \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "X-Project: your-project-slug"Authentication
Headers
| Header | Description |
|---|---|
Authorization | Bearer YOUR_API_KEY |
X-Project | Your project slug |
Getting an API key
Create API keys from the dashboard under Settings → API Keys.
Updates
List updates
GET /v1/ota/updatesCreate update
POST /v1/ota/updatesUpdate an update
PATCH /v1/ota/updates/{id}Delete update
DELETE /v1/ota/updates/{id}Republish update
POST /v1/ota/updates/{id}/republishRepublishes an existing update to additional channels or with updated release notes.
{
"channels": ["staging", "canary"],
"releaseMessage": "Republished with fix"
}| Field | Type | Required | Description |
|---|---|---|---|
channels | string[] | No | Channels to publish to |
releaseMessage | string | No | Updated release message |
Returns { "updates": [...], "groupId": "uuid" }.
Get update history
GET /v1/ota/updates/{id}/historyBuilds
List builds
GET /v1/ota/buildsUpload build
POST /v1/ota/buildsMultipart form upload. Fields:
| Field | Type | Required | Description |
|---|---|---|---|
runtimeVersion | string | Yes | Runtime version fingerprint |
platform | string | Yes | ios or android |
message | string | No | Release message |
expoConfig | JSON | No | Expo app config |
gitCommitHash | string | No | Git commit hash |
gitBranch | string | No | Git branch name |
assets | files | Yes | Bundle and asset files |
Publish build
Publishes a build to a channel, creating an update (release).
POST /v1/ota/builds/{id}/publish{
"channel": "production",
"rolloutPercentage": 100,
"isCritical": false,
"releaseMessage": "v2.1 release",
"groupId": "optional-uuid",
"linkedFlags": [
{ "flagId": 1, "enabled": true }
]
}| Field | Type | Required | Description |
|---|---|---|---|
channel | string | Yes | Target channel name |
rolloutPercentage | number | No | Initial rollout percentage (0–100, default 100) |
isCritical | boolean | No | Mark update as critical |
releaseMessage | string | No | Release notes |
groupId | string | No | UUID to group multi-platform publishes together |
linkedFlags | array | No | Feature flags to link to this release |
linkedFlags[].flagId | number | Yes | Flag ID |
linkedFlags[].enabled | boolean | Yes | Whether the flag is enabled for this release |
Delete build
DELETE /v1/ota/builds/{id}Branches
List branches
GET /v1/ota/branchesCreate branch
POST /v1/ota/branches{
"name": "staging"
}Delete branch
DELETE /v1/ota/branches/{name}Channels
List channels
GET /v1/ota/channelsCreate channel
POST /v1/ota/channels{
"name": "staging",
"branchName": "staging"
}Update channel
PATCH /v1/ota/channels/{name}{
"branchName": "new-branch",
"rolloutBranchName": "canary",
"rolloutPercentage": 10,
"minRuntimeVersion": "2.0.0"
}Delete channel
DELETE /v1/ota/channels/{name}Feature Flags
List flags
GET /v1/ota/flagsCreate flag
POST /v1/ota/flags{
"name": "New Checkout",
"key": "new-checkout",
"flagType": "boolean",
"defaultValue": false,
"enabled": true,
"description": "Enable the new checkout flow",
"variations": [
{ "name": "On", "value": "true" },
{ "name": "Off", "value": "false" }
]
}Get flag
GET /v1/ota/flags/{id}Update flag
PATCH /v1/ota/flags/{id}Delete flag
DELETE /v1/ota/flags/{id}Create targeting rule
POST /v1/ota/flags/{id}/rules{
"ruleType": "percentage_rollout",
"ruleConfig": {
"rollout": [
{"variantValue": "true", "weight": 50},
{"variantValue": "false", "weight": 50}
]
},
"variantValue": "true",
"priority": 0,
"channelName": "production"
}Rule types: user_list, percentage_rollout, attribute, segment, ota_update
Segment rule example:
{
"ruleType": "segment",
"ruleConfig": {
"segmentKey": "ios-pro-users"
},
"variantValue": "true",
"priority": 2,
"channelName": "production"
}Update rule
PATCH /v1/ota/flags/{id}/rules/{ruleId}Delete rule
DELETE /v1/ota/flags/{id}/rules/{ruleId}Update variation
PATCH /v1/ota/flags/{id}/variations/{variationId}Update environment settings
PATCH /v1/ota/flags/{id}/env/{channelName}{
"enabled": true,
"defaultValue": "variant-a"
}Get flag evaluations
GET /v1/ota/flags/{id}/evaluationsFlag definitions (client endpoint)
GET /v1/ota/flag-definitions/{projectSlug}?channel=productionPublic endpoint used by the OpenFeature provider. Returns enabled flags with their rules and variations for the specified channel.
Health Metrics
Report health events (client endpoint)
POST /v1/ota/health-metricsPublic endpoint used by the health reporter in @appdispatch/react-native. No authentication required.
{
"projectSlug": "my-app",
"deviceId": "device-xyz",
"channel": "production",
"platform": "ios",
"runtimeVersion": "49.0.0",
"updateUuid": "abc-123",
"events": [
{
"type": "js_error",
"message": "TypeError: undefined is not an object",
"count": 3,
"flagStates": {
"new-checkout": true,
"theme": "dark"
}
},
{
"type": "app_launch",
"count": 1
}
]
}| Field | Type | Required | Description |
|---|---|---|---|
projectSlug | string | Yes | Project slug |
deviceId | string | Yes | Stable device identifier |
channel | string | No | Channel name |
platform | string | Yes | "ios" or "android" |
runtimeVersion | string | Yes | Runtime version from expo-updates |
updateUuid | string | No | Current update UUID from expo-updates |
events | array | Yes | Array of health events |
events[].type | string | Yes | "js_error", "crash", "custom", or "app_launch" |
events[].name | string | No | Event name (for custom events) |
events[].message | string | No | Error message |
events[].count | number | Yes | Number of occurrences (deduplicated client-side) |
events[].flagStates | object | No | Flag variations active at error time |
Returns 204 No Content on success.
Segments
List segments
GET /v1/ota/segmentsCreate segment
POST /v1/ota/segments{
"name": "iOS Pro Users",
"key": "ios-pro-users",
"description": "Pro-tier users on iOS",
"matchType": "all",
"conditions": [
{"attribute": "platform", "operator": "eq", "valuesJson": ["ios"]},
{"attribute": "plan", "operator": "eq", "valuesJson": ["pro"]}
]
}| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name |
key | string | No | Unique key (auto-generated from name if omitted) |
description | string | No | Human-readable description |
matchType | string | Yes | "all" (AND) or "any" (OR) |
conditions | array | Yes | Array of condition objects |
conditions[].attribute | string | Yes | Context attribute name |
conditions[].operator | string | Yes | Comparison operator |
conditions[].valuesJson | string[] | Yes | Values to match (empty for exists/not_exists) |
Get segment
GET /v1/ota/segments/{id}Update segment
PATCH /v1/ota/segments/{id}Delete segment
DELETE /v1/ota/segments/{id}Returns 409 Conflict if the segment is referenced by any flag rules. Remove the segment rules first.
Webhooks
List webhooks
GET /v1/ota/webhooksCreate webhook
POST /v1/ota/webhooks{
"url": "https://your-app.example.com/webhook",
"events": ["build.published", "channel.updated"],
"secret": "whsec_your_signing_secret"
}Update webhook
PATCH /v1/ota/webhooks/{id}Use isActive to enable or disable a webhook:
{
"isActive": false
}Delete webhook
DELETE /v1/ota/webhooks/{id}Get delivery history
GET /v1/ota/webhooks/{id}/deliveriesAudit Log
List audit log
GET /v1/ota/audit-log?limit=100&before=500| Param | Description |
|---|---|
limit | Max entries to return (default 200, max 200) |
action | Filter by action (e.g., build.published) |
entity_type | Filter by entity type (e.g., update, flag) |
entity_id | Filter by entity ID |
before | Cursor: return entries with ID less than this (for pagination) |
Rollback
Create rollback
POST /v1/ota/rollback{
"runtimeVersion": "1.0.0",
"platform": "ios",
"channel": "production",
"rollbackToUpdateId": 123
}| Field | Type | Required | Description |
|---|---|---|---|
runtimeVersion | string | Yes | Runtime version to target |
platform | string | Yes | "ios" or "android" |
channel | string | Yes | Channel name |
rollbackToUpdateId | number | No | Specific update ID to roll back to. If omitted, rolls back to the embedded update. |
User Overrides
List overrides
GET /v1/ota/user-overridesCreate override
POST /v1/ota/user-overrides{
"userId": "user-123",
"branchName": "beta-testers",
"note": "QA tester for v2.1"
}Delete override
DELETE /v1/ota/user-overrides/{id}Contexts
List contexts
GET /v1/ota/contextsReturns all contexts for the project. Supports query parameters for filtering by kind and search.
Create context
POST /v1/ota/contexts{
"kind": "user",
"targetingKey": "user-abc-123",
"name": "Alice",
"attributes": {
"plan": "pro",
"platform": "ios"
}
}| Field | Type | Required | Description |
|---|---|---|---|
kind | string | Yes | Context kind: user, device, organization, service, or environment |
targetingKey | string | Yes | Unique key for this context |
name | string | No | Display name |
attributes | object | No | Key-value attributes |
Get context
GET /v1/ota/contexts/{id}Delete context
DELETE /v1/ota/contexts/{id}List context kinds
GET /v1/ota/contexts/kindsReturns the available context kinds: user, device, organization, service, environment.
Rollout Policies
List rollout policies
GET /v1/ota/rollout-policiesCreate rollout policy
POST /v1/ota/rollout-policies{
"name": "Staged production rollout",
"stages": [
{ "percentage": 5, "minDevices": 100, "minDurationMinutes": 60 },
{ "percentage": 25, "minDevices": 500, "minDurationMinutes": 120 },
{ "percentage": 100, "minDevices": 0, "minDurationMinutes": 0 }
],
"thresholds": {
"errorRate": 2.0,
"crashRate": 0.5,
"action": "auto-rollback"
}
}Get rollout policy
GET /v1/ota/rollout-policies/{id}Update rollout policy
PATCH /v1/ota/rollout-policies/{id}Editing is locked while an execution is running for this policy.
Delete rollout policy
DELETE /v1/ota/rollout-policies/{id}Rollout Executions
List executions
GET /v1/ota/rollout-executionsGet execution
GET /v1/ota/rollout-executions/{id}Pause execution
POST /v1/ota/rollout-executions/{id}/pauseResume execution
POST /v1/ota/rollout-executions/{id}/resumeCancel execution
POST /v1/ota/rollout-executions/{id}/cancelAdvance execution
POST /v1/ota/rollout-executions/{id}/advanceManually advances the execution to the next stage.
Flag Health
Get flag health
GET /v1/ota/flags/{id}/healthReturns health status for a flag, including error rate, crash-free percentage, affected devices, and per-variation health breakdown.
Adoption
Get adoption metrics
GET /v1/ota/insights/adoptionReturns adoption metrics including total active devices and download counts. Supports days query parameter (7, 14, 30, or 90).
Telemetry
Get timeseries data
GET /v1/ota/telemetry/timeseriesReturns time-bucketed metrics for devices, errors, and crashes over the specified period.
Get flag impacts
GET /v1/ota/telemetry/flag-impactsReturns the flag impact matrix showing error rate and crash-free percentage per flag variation, update, and channel.
Get telemetry events
GET /v1/ota/telemetry/eventsReturns raw telemetry events with filtering by type, channel, and platform.
Observe
List observe events
GET /v1/ota/observe/eventsReturns errors, crashes, and custom events from devices. Supports filtering by event type (js_error, crash, custom), channel, and platform. Results are paginated (50 per page).