REST API

All endpoints are under /api/v1. Requests and responses use JSON.

Authentication

There are two authentication modes depending on the operation:

Bearer tokenCLI / server operations — upload, release, rollback, list. Use your organization secret key (otakit_sk_...) or a user access token from otakit login. The app ID is part of the URL path.
X-Public-KeyPlugin / read-only operations — manifest fetch, event reporting. Uses the app public key (no secret needed).
# CLI / server operations
Authorization: Bearer otakit_sk_...   # or OTAKIT_ACCESS_TOKEN

# Plugin operations
X-Public-Key: otakit_pk_...

Apps

POST/api/v1/apps

Create a new app. The slug should match your Capacitor app identifier. Returns an appId and publicKey for plugin config.

Auth: Bearer

Request body

{ "slug": "com.example.myapp" }

Response

{
  "id": "uuid",
  "slug": "com.example.myapp",
  "publicKey": "otakit_pk_...",
  "createdAt": "ISO timestamp"
}

Bundles

POST/api/v1/apps/:appId/bundles/initiate

Start a bundle upload. Returns a presigned PUT URL. Upload your zip file to this URL with Content-Type: application/zip.

Auth: Bearer

Request body

{
  "version": "1.0.1",       // semver string
  "size": 1048576,           // bundle size in bytes
  "minNativeBuild": 100     // optional
}

Response

{
  "uploadId": "uuid",
  "presignedUrl": "https://...",
  "storageKey": "...",
  "expiresAt": "ISO timestamp"
}
POST/api/v1/apps/:appId/bundles/finalize

Finalize a bundle upload. The server verifies the SHA-256 hash matches. Creates the bundle record.

Auth: Bearer

Request body

{
  "uploadId": "uuid",
  "sha256": "64-char hex hash"
}

Response

{
  "id": "uuid",
  "version": "1.0.1",
  "sha256": "...",
  "size": 1048576,
  "minNativeBuild": null,
  "createdAt": "ISO timestamp"
}
GET/api/v1/apps/:appId/bundles

List bundles sorted by creation date (newest first).

Auth: Bearer · Query: ?limit=20&offset=0

Response

{
  "bundles": [{ id, version, sha256, size, createdAt }],
  "total": 42
}
DELETE/api/v1/apps/:appId/bundles/:bundleId

Delete a bundle. Bundles that are part of a release history cannot be deleted.

Auth: Bearer

Response

{ "deleted": true, "id": "uuid" }

Channels

POST/api/v1/apps/:appId/channels/:name/release

Release a bundle to a channel. One bundle = one release.

Auth: Bearer

Request body

{
  "bundleId": "uuid"
}

Response

{
  "channel": "default",
  "release": {
    "id": "uuid",
    "bundleId": "uuid",
    "bundleVersion": "1.0.1",
    "position": 5,
    "promotedAt": "ISO timestamp"
  },
  "previousRelease": { ... } | null
}
POST/api/v1/apps/:appId/channels/:name/rollback

Roll back a channel by N releases. Creates a new release pointing to the older bundle.

Auth: Bearer

Request body

{
  "steps": 1            // optional, default 1, max 100
}

Response

{
  "channel": "default",
  "release": { ... },
  "rolledBackFrom": { ... }
}
GET/api/v1/apps/:appId/channels/:name/releases

List release history for a channel, sorted by position (newest first).

Auth: Bearer · Query: ?limit=20&offset=0

Response

{
  "releases": [{
    id, bundleId, bundleVersion, position, promotedAt
  }],
  "total": 12
}

Manifest (Plugin)

GET/api/v1/apps/:appId/channels/:channel/latest

Check for the latest bundle on a channel. Used by the Capacitor plugin. Returns a signed download URL with 10-minute TTL. X-Platform is used for manifest signature only.

Auth: X-Public-Key

Headers

X-Public-Key: otakit_pk_...
X-Platform: ios
X-Current-Version: 1.0.0        // optional
X-Native-Build: 100             // optional

Response

// 204 No Content — already up to date

// 200 OK — update available
{
  "version": "1.0.1",
  "channel": "default",
  "releaseId": "release_uuid",
  "url": "https://... (signed, 10min TTL)",
  "sha256": "...",
  "size": 1048576,
  "minNativeBuild": null,
  "signature": { "kid": "...", "sig": "...", "iat": ..., "exp": ... }
}

// 406 Not Acceptable — native build too old
{ "error": "native_build_too_old", "minNativeBuild": 200 }

Device Events

POST/api/v1/stats

Record a device event for analytics. Used by the plugin to report update lifecycle events.

Auth: X-Public-Key

Request body

{
  "platform": "ios",
  "action": "downloaded",      // downloaded, success, error
  "bundleVersion": "1.0.1",   // optional
  "channel": "default",       // optional
  "releaseId": "release_uuid",// optional
  "errorMessage": "..."       // optional, max 500 chars
}

Response

{ "success": true }

CLI Options

The otakit CLI accepts a global --app-id option that overrides OTAKIT_APP_ID and capacitor.config.ts. Resolution order:

--app-id <id>          # highest priority
OTAKIT_APP_ID          # env var
capacitor.config.ts    # Updater.appId