> ## Documentation Index
> Fetch the complete documentation index at: https://docs.jojapi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Management API

> Publish and edit your listings from scripts, CI and agents with scoped management tokens.

> This article is for API providers. It covers the tokens that let a script or an agent act on your listings — publishing an OpenAPI document, editing endpoints, reading analytics — without your login.

The Management API is the same REST the API Studio uses. A **management token** stands in for your session on those routes, and nothing else changes: the same validation, the same ownership checks, the same audit trail.

## Management tokens are not API keys

| | API key (`jk_…`) | Management token (`jm_…`) |
| - | - | - |
| Who uses it | A consumer calling APIs they subscribe to | You, or an agent working for you, on your own listings |
| Where it is checked | The gateway | `app.jojapi.com` only |
| What it can reach | The APIs the key is allowed to call | Your listings, plans, analytics and subscribers — by scope |
| What it can never reach | — | Payments, wallet, payouts, billing, your profile and e-mail, API keys, other tokens |

Give each script or agent its own token with only the scopes it needs, set an expiry, and revoke it when the job is done. Tokens are created in **Studio → Management API**; the token itself is shown once.

## Scopes

| Scope | Allows |
| - | - |
| `listings:read` | Read your APIs, endpoints, plans, objects and FAQs |
| `listings:write` | Create and edit APIs, endpoints, parameters, responses, groups, targets, FAQs and objects; run imports |
| `plans:write` | Create and change plans, quotas, per-endpoint pricing and agent payments — including an import that creates plans |
| `analytics:read` | Traffic, transactions, request logs and the Studio dashboard |
| `subscriptions:read` | Your subscribers and their subscriptions |
| `code:read` | The edge runtime of your APIs: template, files, variable names, resources, runtime errors and console (never a secret value) |
| `code:write` | Deploy code: template, files, mode, variables, resources and Worker settings |

Deleting an API is never available to a token; do that in the Studio.

## Calling it

The [`jojapi` CLI](/studio/cli) wraps these routes in commands with previews and confirmations. To call the routes directly, send the token as a bearer token to the Studio's REST routes under `https://app.jojapi.com/rest/`:

```bash theme={null}
curl https://app.jojapi.com/rest/v2/ProviderApis \
  -H "Authorization: Bearer jm_…"
```

If a proxy in front of your script strips `Authorization`, the header `X-Management-Token: jm_…` is accepted too.

Answers are JSON with a `status` field; the HTTP status is 200 for every answer the application produced, so read `status`, not the code:

| `status` | Meaning |
| - | - |
| `success` | Done; the rest of the body is the route's answer |
| `unauthorized` | Bad, expired or revoked token — or a route that is not part of the Management API (`route` says which) |
| `insufficient_scope` | The token lacks a scope the route needs; `required_scopes` and `token_scopes` say what is missing |
| anything else | The route's own validation, exactly as the Studio would show it |

## Routes

Every route takes and returns JSON. `GET` routes take their parameters in the query string; `POST` routes take a JSON body. `slug` is the listing's slug — the last part of its Studio URL.

### Listings

| Route | Scope | What it does |
| - | - | - |
| `GET v2/ProviderApis` | `listings:read` | Your listings |
| `GET v2/provider-api?slug=` | `listings:read` | One listing with its endpoints |
| `GET v2/provider/api-endpoint?slug=&method=&url_path=` | `listings:read` | One endpoint in full: parameters, body, responses |
| `GET v2/provider-api-targets?slug=` | `listings:read` | The listing's upstream targets |
| `GET v2/studio/api-plans?slug=` · `api-objects` · `api-faqs` | `listings:read` | Plans, billable objects, FAQs |
| `POST v2/provider/add-api` | `listings:write` | Create a listing |
| `POST v2/update-api-details` · `update-api-about` | `listings:write` | Name, description, about text |
| `POST v2/provider/add-endpoint` · `provider/update-endpoint-details` · `provider/delete-endpoint` · `provider/duplicate-endpoint` | `listings:write` | Endpoints |
| `POST v2/update-url-parameters` · `update-header-parameters` · `update-path-parameters` · `update-endpoint-body` | `listings:write` | An endpoint's parameters and body |
| `POST v2/update-endpoint-responses` · `upsert-endpoint-response` | `listings:write` | Documented responses and examples |
| `POST v2/create-api-endpoint-group` · `update-endpoint-group` · `delete-endpoint-group` · `move-endpoint-to-group` · `set-group-order` · `update-endpoint-orders` | `listings:write` | Grouping and order |
| `POST v2/update-api-target-v2` · `delete-api-target` · `set-custom-gateway-prefix` | `listings:write` | Upstream targets |
| `POST v2/studio/create-api-faq` · `update-api-faq` · `delete-api-faq` · `update-api-faq-orders` | `listings:write` | FAQs |
| `POST v2/studio/create-object` · `update-object` · `delete-object` · `set-api-features` | `listings:write` | Billable objects and plan features |
| `POST v2/provider/listing-resubmit` | `listings:write` | Resubmit a listing for review |

### Imports — publishing an OpenAPI document

The fastest way to keep a listing in step with your code is to publish its OpenAPI document. An import never touches an existing endpoint's blocked/hidden flags, its group or its plans unless you ask it to.

| Route | Scope | What it does |
| - | - | - |
| `POST v2/import/preview` | `listings:write` | `{slug, bundle}` → what would be new, changed, unchanged and missing |
| `POST v2/import/apply` | `listings:write` (+ `plans:write` with `import_plans` or `publish_plans`) | `{slug, bundle, new_hidden, overwrite_changed, hide_missing, update_meta, use_groups, import_plans, publish_plans}` → writes the diff in one transaction |
| `POST v2/import/fetch` · `import/upload` · `import/fetch-rapidapi` · `import/fetch-apify` | `listings:write` | Fetch or upload a source document into a snapshot |

`bundle` is a JSON **string** in the import engine's source-agnostic shape:

```json theme={null}
{
  "meta": { "name": "…", "description": "…" },
  "endpoints": [{
    "method": "GET", "url_path": "/v1/serp", "name": "Full SERP", "description": "…", "group": "",
    "url_parameters": [{ "key": "query", "name": "", "description": "…", "example": "best running shoes",
                         "value_type": "string", "enum_values": null, "default_value": "", "required": true }],
    "header_parameters": [], "path_parameters": [], "body": null,
    "responses": [{ "status": "200", "description": "…", "schema": null,
                    "examples": [{ "name": "success", "summary": "", "description": "", "example": "{…}" }] }]
  }]
}
```

`value_type` is one of `string`, `enum`, `number`, `integer`, `boolean`, `date`, `time`, `object`, `array`, `geopoint`. Preview first, read the diff, then apply.

### Pricing

| Route | Scope |
| - | - |
| `POST v2/studio/create-api-plan` · `update-api-plan` · `delete-api-plan` · `update-plan-display` · `add-api-plan-object` | `plans:write` |
| `POST v2/provider/bulk-update-endpoint-billing` · `provider/bulk-update-endpoint-agent-price` · `provider/set-api-agent-payments` | `plans:write` |
| `POST v2/set-user-custom-api-plan` · `studio/grant-quota` | `plans:write` |

`create-api-plan` answers the new plan as `plan: {slug, type, blocked}`. A public plan must fit the pricing page: at most 10 public plans per API, one of them pay-as-you-go. This applies both when you create a plan as public and when `update-api-plan` makes a private plan public; over the limit the status is `public_plans_limit_reached`. `studio/grant-quota` takes the `subscription.id` that `ProviderSubscriptions` returns and an `object_id` from that subscription's `current_period.objects`.

### Analytics and subscribers

| Route | Scope |
| - | - |
| `GET v2/studio/dashboard` · `studio-analytics-api?slug=` · `studio-low-uptime-endpoints` · `request-logs-studio` · `ProviderTransactions` | `analytics:read` |
| `GET v2/ProviderSubscriptions` · `pending-transfers` | `subscriptions:read` |

Subscriptions and plan transfers are identified by opaque string ids (`subscription.id`, `pending_transfer.id`). Send them back exactly as you received them; they are not row numbers.

### Worker code (edge gateway)

| Route | Scope | What it does |
| - | - | - |
| `GET v2/provider-api-edge?slug=` | `code:read` | Mode, template, file list, handlers, variables, resources, settings |
| `GET v2/provider-api-edge-file?slug=&path=` | `code:read` | One stored file |
| `GET v2/provider-api-edge-source?slug=` | `code:read` | The files the Worker runs right now (generated or stored) |
| `GET v2/provider-api-edge-errors?slug=[&fingerprint=]` | `code:read` | Runtime issues, or one issue with its recent occurrences |
| `GET v2/provider-api-edge-console?slug=[&request_id=]` | `code:read` | Console output captured while logs are on |
| `GET v2/provider-api-edge-compute?slug=[&days=30]` | `code:read` | Cloudflare usage per metric, included allowance, list-price estimate |
| `GET v2/provider-api-edge-deployments?slug=` | `code:read` | Deployments, newest first: status, production / previous / latest, access, Keep active, URL; `pending` (see below) |
| `POST v2/update-api-edge-template` `{slug, template, production?, message?}` | `code:write` | Save the template (one route: origins, headers, query, switches, timeout, proxy) as a preview deployment; `production: true` deploys it at once |
| `POST v2/update-api-edge-files` `{slug, files: [{path, content}], delete: [path], production?, message?, git?, note?, redeploy?}` | `code:write` | Write and delete files (code mode) as a preview deployment; `production: true` promotes it, with `note` as its public release note; `git` records `{commit, branch, repository, pr}`; `redeploy: true` deploys unchanged files again |
| `POST v2/update-api-edge-mode` `{slug, mode: "code" \| "template", production?}` | `code:write` | Take the code over, or go back to the template, as a preview |
| `POST v2/update-api-variable` · `delete-api-variable` | `code:write` | Variables (`{slug, name, kind, value, production?}` / `{slug, name, production?}`) as a preview; `production: true` puts only this change into production at once |
| `POST v2/update-api-edge-resource` · `delete-api-edge-resource` | `code:write` | Resources (`{slug, kind, binding, class_name?}` / `{slug, binding}`) as a preview; `{slug, kind: "shared", share_id, binding}` binds a resource shared with the API; `production: true` as for variables. A removed resource is deleted once no active deployment binds it |
| `POST v2/deploy-api-edge-changes` `{slug, deployment?, note?}` | `code:write` | Deploy the pending changes: promotes the latest preview (`deployment`: the id you checked; refused when newer changes were saved since) with an optional release note |
| `POST v2/discard-api-edge-changes` `{slug}` | `code:write` | Return the template, files, variables and bindings to what production runs; archives the previews made since |
| `GET v2/provider-api-edge-shares?slug=` | `code:read` | [Shared resources](/studio/shared-resources): grants per resource, invitations and grants to the API |
| `POST v2/share-api-edge-resource` `{slug, binding, target_slug, target_binding?}` | `code:write` | Share a resource with another API (yours: bound at once with `target_binding`; another account's: an invitation) |
| `POST v2/accept-api-edge-share` · `decline-api-edge-share` · `revoke-api-edge-share` `{share_id}` | `code:write` | Answer an invitation; revoke a grant |
| `POST v2/update-api-edge-settings` `{slug, logs}` | `code:write` | Console logging switch |
| `POST v2/promote-api-edge-deployment` `{slug, deployment, note?}` | `code:write` | Serve a deployment in production; the note is the public release note |
| `POST v2/rollback-api-edge` `{slug, note?}` | `code:write` | Back to the deployment production served before |
| `POST v2/update-api-edge-deployment` `{slug, deployment, access?, keep?, active?}` | `code:write` | `access` owner / everyone, Keep active, archive (`active: false`) or activate |

Every save is a preview unless it sends `production: true`, and returns `deploy`: `deployed` with the new `deployment` (`id`, `number`, `url`) and whether it was `promoted`, or `error` with the build message. `deploy.pending` (also in `provider-api-edge` and `provider-api-edge-deployments`) lists what production does not run yet: `changes` (`[{type: mode | template | file | variable | resource, name, change: added | changed | removed}]`), `production` (`{id, number}`) and `deployment`, the latest preview (`{id, number, status, error, url, current}`; `current: false` means no deployment holds the saved changes yet). `preview: true` is still accepted and changes nothing. See [Deployments](/studio/deployments) and [Worker code](/studio/custom-code).

## Audit and limits

Every request made with a token is logged with its outcome — allowed, refused route, missing scope — and kept for 90 days; the token's **last used** stamp is on the Management API page. A token can be valid for at most a year, an account can hold 20 live tokens, and the import routes share the Studio's limit of 200 imports an hour.

Keep tokens where your scripts read secrets from, never in a repository. If one leaks, revoke it on the Management API page; the revocation is immediate.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.