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

# REST API Overview

> Explore the NervesHub HTTP REST API: base URL, OpenAPI spec location, JSON response format, URL structure, and full HTTP status code reference.

NervesHub provides a REST API covering devices, firmware, deployment groups, products, organizations, signing keys, support scripts and more. Whether you are automating firmware releases in CI or building a custom dashboard, the API lets you integrate NervesHub into any workflow.

The **Endpoints** section in the sidebar is generated directly from the server's OpenAPI specification, so it always matches what the API actually accepts. This page covers what the specification does not: base URLs, how responses are shaped, and what each status code means.

## Base URL

All API requests are made over HTTPS. The base URL depends on how you host NervesHub:

| Environment           | Base URL                                              |
| --------------------- | ----------------------------------------------------- |
| NervesCloud (managed) | `https://manage.nervescloud.com`                      |
| Self-hosted           | Your server URL, e.g. `https://nerveshub.example.com` |

Every endpoint path is appended directly to the base URL:

```
https://manage.nervescloud.com/api/orgs/{org_name}/products/{product_name}/devices
```

## OpenAPI Specification

The server generates its own OpenAPI document and serves it, so it is never out of step with the running code:

* **Spec document:** `https://manage.nervescloud.com/api/openapi`
* **Interactive docs:** `https://manage.nervescloud.com/api/docs`

Both paths are relative to your base URL, so a self-hosted instance serves its own spec at `https://nerveshub.example.com/api/openapi`. Import it into Postman, Insomnia, a code generator, or anything else that reads OpenAPI.

<Note>
  The endpoint reference in this section is built from NervesCloud's specification. A self-hosted deployment exposes the same API, but if you are running an older version, fetch the spec from your own instance to see exactly what it offers.
</Note>

## URL Structure

All resource paths follow a consistent hierarchy anchored to an organization and, for most resources, a product:

```
/api/orgs/{org_name}/products/{product_name}/{resource}
```

For example:

```
GET /api/orgs/acme/products/smart-lock/devices
GET /api/orgs/acme/products/smart-lock/firmwares/{uuid}
GET /api/orgs/acme/products/smart-lock/deployments/{name}
```

Org-level resources (CA certificates, signing keys, products) omit the product segment:

```
GET /api/orgs/acme/products
GET /api/orgs/acme/ca_certificates
GET /api/orgs/acme/keys
```

<Note>
  The `deployments` path manages what the web console calls **deployment groups**. The path name is kept for backwards compatibility.
</Note>

## Response Format

All responses are JSON. Successful responses wrap the payload in a `data` key:

```json theme={null}
{
  "data": {
    "identifier": "device-001",
    "status": "online"
  }
}
```

List responses return an array under `data`:

```json theme={null}
{
  "data": [
    { "identifier": "device-001", "status": "online" },
    { "identifier": "device-002", "status": "offline" }
  ]
}
```

Error responses return an `error` or `errors` key:

```json theme={null}
{ "error": "unauthorized" }
```

```json theme={null}
{
  "errors": {
    "identifier": ["can't be blank"]
  }
}
```

## HTTP Status Codes

| Code                        | Meaning                    | When it occurs                                    |
| --------------------------- | -------------------------- | ------------------------------------------------- |
| `200 OK`                    | Success                    | Request succeeded; body contains result           |
| `201 Created`               | Resource created           | POST succeeded; body contains new resource        |
| `204 No Content`            | Deleted successfully       | DELETE succeeded; no response body                |
| `400 Bad Request`           | Invalid request parameters | Malformed JSON, missing required fields           |
| `401 Unauthorized`          | Invalid or missing token   | No `Authorization` header or expired token        |
| `403 Forbidden`             | Insufficient permissions   | Token is valid but lacks the required role        |
| `404 Not Found`             | Resource not found         | Org, product, device, or firmware does not exist  |
| `422 Unprocessable Entity`  | Validation errors          | Fields present but fail business-logic validation |
| `500 Internal Server Error` | Server error               | Unexpected server-side failure                    |

<Note>
  Before making API calls, you need a Bearer token. See [Authentication](/api/authentication) for instructions on generating and using API tokens.
</Note>
