API reference
The HotCodePush REST API at api.hotcodepush.com, with API tokens, pagination, relations, idempotency keys, rate limits and the error shape.
In development
The API is still in development: until we declare it stable, breaking changes can happen. Build on the CLI, the Node client or the Python client where you can, since they track the current API.
The REST API lives at https://api.hotcodepush.com/v1 and is the one API behind the console, the CLI, the MCP server and your own scripts. Every endpoint's parameters, bodies and responses are in the Swagger reference. This page holds the conventions every endpoint follows.
- Node.js client: Install
@hotcodepush/node, create a client with an API token, release a bundle and handle errors. - Python client: Install
hotcodepush, create a client with an API token, release a bundle and handle errors.
Base URL
Every endpoint sits under https://api.hotcodepush.com/v1. Three paths beside it carry no version: /openapi is the OpenAPI document, /docs the Swagger reference over it, and /health answers ok.
Request bodies are JSON with Content-Type: application/json, and every response is JSON, errors included. CORS admits the console's origin only, so call the API from a server, a script or a pipeline, not from a page in a browser.
Authentication
Send an API token as a bearer on every request:
GET /v1/organizations HTTP/1.1
Host: api.hotcodepush.com
Authorization: Bearer hcp_aB3dE6gH9jK2mN5pQ8sT1vW4yZ7bC0dF2gH4jKA token is hcp_ followed by 36 characters. API tokens has where to create one, the expiry options and what it may do.
A missing, malformed, expired or deleted token answers 401 unauthenticated. In CI, the CLI, the Node and Python clients and the MCP server take an API token the same way your own script does. On your machine the CLI signs in through the browser instead.
What a token cannot do
Endpoints that change who may act in an organization or spend its money take a signed-in session: the console, or the CLI after hotcodepush login. An API token on them answers 403 forbidden, and the Swagger reference marks them with the sessionAuth security scheme. They are:
- organizations: create, update and delete
- members: changing or removing one, and invitations
- apps: delete and transfer
- webhooks, the audit log, the limits and the SSO provider
- billing and your account's settings
Versioning
The version is the /v1 in the path. Once the API is declared stable, changes within /v1 are additive only, and a retired endpoint carries Deprecation and Sunset headers for a year before it goes. The webhook payload follows the additive rule already.
Conventions
Ids are UUIDs, sent as strings, and wherever a path takes a {userId}, me names the caller. Fields are camelCase and timestamps ISO 8601 in UTC. A write answers the resource it wrote, exactly as the GET does, and a PATCH whose body equals it writes nothing.
Every change writes its own entry in the audit log, and the audit type, release.created, is the name a webhook event carries.
Users, organizations and apps are soft-deleted: gone at once, hard-deleted after the retention window, and restorable by support inside it. Everything else deletes instantly, and a release is never deleted, only revoked. A bulk delete takes DELETE …?ids= with 1 to 100 ids.
Pagination
Lists take limit and offset, newest first, and answer a plain JSON array:
curl "https://api.hotcodepush.com/v1/apps/$APP_ID/releases?channelId=$CHANNEL_ID&limit=20&offset=20" \
-H "Authorization: Bearer $HOTCODEPUSH_TOKEN"limit defaults to 50 and allows up to 100, 1,000 on a bundle's files list, and a larger value answers 400 validation. The offset defaults to 0, and a page as long as limit implies another one. Beside every list sits GET …/count, which takes the list's filters and answers { "count": n }.
Filters are query parameters named after the fields, ?channelId= or ?status=. A searched list takes one query, matched case-insensitively against its text fields, such as an app's name or a member's email, never an id.
Relations
A GET embeds linked resources through ?relations=, a comma-separated list the endpoint's Swagger entry enumerates. A relation is named after the resource it points to, in camelCase and without the Id, such as channel or currentRelease. A nested relation is a dot path whose parent is in the list too, relations=user,user.accounts, and a child without its parent answers 400 validation.
An embed carries the whole linked resource. One the caller may not read comes as { id, name } or { id, number }, and one that is gone as null.
Idempotency keys
The creating POSTs of organizations, invitations, apps, channels, releases, rollbacks, bundles and binaries accept an Idempotency-Key header. The key is 1 to 255 characters. The same key with the same path and body answers the first response again for 24 hours and creates nothing. The same key with another path or body answers 409 idempotency_key_reused.
curl -X POST "https://api.hotcodepush.com/v1/apps/$APP_ID/channels" \
-H "Authorization: Bearer $HOTCODEPUSH_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "name": "staging" }'The Node and Python clients send a UUID per call and keep it across their own retries. The CLI sends a fresh key per run and keeps it across its retries, so a command run twice creates twice.
Rate limits
An API token may make 600 requests a minute, and the upload PUTs count against their own allowance of 3,000 a minute. A refused request answers 429 rate_limited with Retry-After: 60 and details.retryAfterSeconds: 60.
The limits of your plan, such as the bundle size or the releases per day, refuse with 422 and a code of their own. The numbers are on Limits and fair use.
Errors
Every error is one JSON shape, and the status code matches the code:
{
"code": "validation",
"message": "The query parameter limit is invalid: it must be at most 100.",
"details": { "field": "limit", "rule": "too_big", "target": "query" }
}A code is lowercase snake_case and means the same thing on every endpoint, so branch on the code and never on the message. The details field is null unless the code defines it.
The code validation covers every refused input. Its details.target names where the API looked (json, query, param, header or body) and details.field the field. Its details.rule is an identifier you can branch on, such as too_big, uuid or parent_missing, and a body that does not parse carries malformed_json.
| Code | Status | When |
|---|---|---|
account_banned | 403 | The account is suspended, and the message names support@hotcodepush.com. |
client_update_required | 400 | The CLI, console, MCP, Node or Python client version is listed as faulty, and details.minimumVersion names the fix. Your own scripts send no client version. |
forbidden | 403 | The role is below the endpoint's minimum, the caller is no member, or an API token reaches a session-only endpoint. |
idempotency_key_reused | 409 | An Idempotency-Key was sent again with another path or body. |
internal | 500 | Something failed on the API's side and is already reported. |
ip_not_allowed, country_not_allowed | 403 | The request came from outside the organization's IP or country allowlist. |
maintenance | 503 | A maintenance window is open, while devices keep receiving updates meanwhile. |
not_found | 404 | The resource is absent or soft-deleted, or the path is no endpoint. |
plan_required | 402 | The organization's plan does not include the feature, such as webhooks or the audit log. |
rate_limited | 429 | The rate limit is reached, and details.retryAfterSeconds and Retry-After carry the window. |
two_factor_required | 403 | A member without two-factor authentication acts in an organization that requires it. |
unauthenticated | 401 | The token or session is missing, invalid or expired. |
validation | 400 | The endpoint refuses a body, query, parameter or header, with details as above. |
The codes one endpoint answers, such as channel_name_taken on a channel create or release_revoked on a second revoke, stand on that operation in the Swagger reference. The CLI prints every API code with its cause and its fix.
Last updated on