Management API
Read and edit flow content programmatically with a user API key. Built for coding agents and scripts.
Overview#
The Management API lets a script or coding agent read your flows and edit their content without opening the dashboard. It is the API behind workflows like “update the paywall copy and let me preview it on my phone.”
It is a separate lane from the endpoints your app calls. The SDK endpoints authenticate a single app with X-App-Key and serve published content to devices. The Management API authenticates you with a personal key and can reach every app you have access to.
A flow holds configuration — its slug, name, and success event. The HTML, CSS, and JavaScript belong to a version of that flow. Almost all editing is therefore a request against a version.
Authentication#
Create a key in the dashboard under Account → API Keys. The secret is shown once at creation and stored only as a hash, so save it somewhere safe. Pass it as a bearer token:
curl https://mobana.ai/api/manage/apps \
-H "Authorization: Bearer mb_your_key_here"Keys are scoped to your user account and carry either read or read + write permission. Permissions combine with your role on each app: a read-write key still cannot modify an app you were invited to as a viewer.
These endpoints send no CORS headers and will not work from browser JavaScript. A management key grants access to every app you can reach — treat it like a password and keep it out of client code and version control.
Base URL#
https://mobana.ai/api/manageUnlike the SDK endpoints, this API is served only from the main domain. Your app subdomain will not answer management requests.
Endpoints#
Flows are addressed by app ID and flow slug — the same slug you pass to startFlow(). Versions are addressed by their ID.
| Method | Path | Notes |
|---|---|---|
GET | /me | Verify a key and see its permissions |
GET | /apps | Apps you own or have been given access to |
GET | /apps/{appId} | App detail with platform identifiers |
GET | /apps/{appId}/flows | Flows with version summaries. ?archived=true for archived flows |
POST | /apps/{appId}/flows | Create a flow. Starts with a draft v1 |
GET | /apps/{appId}/flows/{slug} | Flow detail with version summaries |
PATCH | /apps/{appId}/flows/{slug} | Update name, successEvent, archived |
GET | …/flows/{slug}/versions | Version summaries. ?status= filters, ?include=content adds code |
POST | …/flows/{slug}/versions | Create a draft, optionally cloned from another version |
GET | …/versions/{versionId} | Full content of any version |
PATCH | …/versions/{versionId} | Edit content. Drafts only |
DELETE | …/versions/{versionId} | Discard a draft. Drafts only |
GET | …/flows/{slug}/assets | Uploaded images and video with their CDN URLs |
There is no publish endpoint. An agent can prepare and refine a draft, but a person decides when it goes live to real users.
Listing apps#
Start here to turn a key into an app ID:
{
"apps": [
{
"id": "a1b2c3d4e5f6a7b8",
"name": "My App",
"role": "OWNER",
"createdAt": "2026-01-13T18:13:09.497Z"
}
]
}role is OWNER, MANAGER, or VIEWER. A viewer's key is read-only on that app no matter what the key itself allows. App keys used by the SDK are never returned here.
Working with versions#
{
"versions": [
{
"id": "clx8f2k1a0001pb7d9xk2m3n4",
"previewId": "clx8f2k1a0001pb7d9xk2m3n4",
"version": 4,
"status": "DRAFT",
"isActive": false,
"weight": 1,
"name": "with confetti",
"changelog": null,
"createdAt": "2026-02-01T10:00:00.000Z",
"updatedAt": "2026-02-01T10:42:00.000Z",
"publishedAt": null
}
]
}| Parameter | Type | Description |
|---|---|---|
status | "DRAFT" | "PUBLISHED" | "ARCHIVED" | Drafts are editable. Published versions are immutable — to change one, clone it into a new draft. |
previewId | string | null | Set on drafts only. Pass it to startFlow() in a development build to see the draft on a device before it is published. |
isActive | boolean | Whether this published version is currently being served. Multiple versions can be active at once for A/B tests. |
version | number | Sequential number within the flow, starting at 1. Assigned automatically. |
Editing a draft
Send only the fields you want to change. Any subset of html, css, js, name, and changelog is accepted:
curl -X PATCH \
"https://mobana.ai/api/manage/apps/APP_ID/flows/paywall/versions/VERSION_ID" \
-H "Authorization: Bearer mb_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "css": ".cta { background: #6d28d9; }" }'One draft at a time
Creating a draft when one is already open returns 409 along with the existing draft, so you can continue that work instead of piling up abandoned versions:
{
"error": "An open draft already exists for this flow.",
"code": "DRAFT_EXISTS",
"draft": {
"id": "clx8f2k1a0001pb7d9xk2m3n4",
"previewId": "clx8f2k1a0001pb7d9xk2m3n4",
"version": 4,
"status": "DRAFT",
"updatedAt": "2026-02-01T10:42:00.000Z"
}
}Add ?force=true if you genuinely want a second draft.
The usual loop#
Most automated editing follows the same shape:
# 1. Find the app
GET /api/manage/apps
# 2. Find the flow
GET /api/manage/apps/{appId}/flows
# 3. Look for an open draft
GET /api/manage/apps/{appId}/flows/{slug}/versions?status=DRAFT
# 4a. Draft exists — keep editing it
PATCH /api/manage/apps/{appId}/flows/{slug}/versions/{versionId}
# 4b. No draft — branch one off the live version
POST /api/manage/apps/{appId}/flows/{slug}/versions
{ "cloneFromVersionId": "{publishedVersionId}" }
# 5. A human reviews and publishes in the dashboardBetween steps 4 and 5 you can preview the draft on a real device using its previewId, without publishing anything. See startFlow().
Errors#
Errors return a message and, where useful, a stable code you can branch on:
{
"error": "Version 3 is published and cannot be edited. Create a new draft instead.",
"code": "NOT_A_DRAFT"
}| Status | Meaning |
|---|---|
400 | Malformed body or missing required field |
401 | Missing, invalid, or revoked API key |
403 | Key is read-only, your role on the app is read-only, or your plan does not include flows |
404 | App, flow, or version does not exist or is not accessible to you |
409 | Slug already taken, or an open draft already exists |
422 | Valid JSON but not an allowed change — editing a published version, or an invalid slug |
Inaccessible apps return 404 rather than 403, so IDs cannot be probed with a valid key.