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

# Novala REST API — base URLs, errors, and pagination

> Learn how the Novala REST API is structured, what base URLs to use, how pagination and errors work, and what HTTP status codes to expect.

The Novala REST API gives you programmatic access to every module in the platform — contacts, leads, pipeline deals, inspections, invoices, and bookings. All requests and responses use JSON, and every endpoint is scoped to your tenant so data from different organizations never mixes.

## Base URL

Novala runs each tenant on a dedicated subdomain. Use the subdomain form for all v1 endpoints:

```
https://{tenant}.novala.ai/api/v1
```

Replace `{tenant}` with your tenant slug (for example, `acme` → `https://acme.novala.ai/api/v1`).

Endpoints that are not yet under `/v1` use the same subdomain base without the version prefix:

```
https://{tenant}.novala.ai/api
```

<Note>
  All examples in this reference use `app.novala.ai` as a placeholder. Replace it with your tenant subdomain when making real requests.
</Note>

## Authentication

Every request to a protected endpoint must include an `Authorization` header carrying a bearer API key:

```
Authorization: Bearer sk_live_…
```

See the [Authentication](/api-reference/authentication) page for how to issue keys, select scopes, and rotate credentials.

## Pagination

The API uses two pagination styles depending on the endpoint.

### Offset-based pagination

Most list endpoints accept `page` (1-based, default `1`) and `limit` (default `25`, max `100`) query parameters. The response envelope looks like:

```json theme={null}
{
  "data": [...],
  "total": 142,
  "page": 1,
  "pageSize": 25
}
```

### Cursor-based pagination

Newer endpoints (leads, and future additions) use keyset/cursor pagination. Pass `limit` and an opaque `cursor` string obtained from the previous response:

```json theme={null}
{
  "data": [...],
  "nextCursor": "eyJpZCI6Ii4uLi4iLCJjcmVhdGVkQXQiOiIuLi4ifQ==",
  "hasMore": true
}
```

When `hasMore` is `false` or `nextCursor` is `null`, you have reached the last page.

## Error responses

All errors return a JSON body with an `error` field:

```json theme={null}
{
  "error": "Contact not found"
}
```

Validation errors return additional detail:

```json theme={null}
{
  "error": "Validation failed",
  "fields": [
    { "field": "email", "message": "Invalid email address" }
  ]
}
```

## HTTP status codes

| Code                        | Meaning                                                    |
| --------------------------- | ---------------------------------------------------------- |
| `200 OK`                    | Request succeeded.                                         |
| `201 Created`               | Resource created successfully.                             |
| `400 Bad Request`           | Request body failed validation.                            |
| `401 Unauthorized`          | Missing or invalid API key.                                |
| `403 Forbidden`             | API key does not have the required scope.                  |
| `404 Not Found`             | Resource not found or not accessible to this tenant.       |
| `409 Conflict`              | Conflict with existing data (for example, duplicate slug). |
| `429 Too Many Requests`     | Rate limit exceeded.                                       |
| `500 Internal Server Error` | Unexpected server error.                                   |

## Rate limiting

API requests are rate-limited per tenant. When you exceed the limit, the server returns `429 Too Many Requests`. Implement exponential backoff and retry logic in your integration.

## Versioning

Endpoints under `/api/v1/` follow a stable versioning contract. Breaking changes are never made within a major version. Endpoints outside `/v1/` (inspections, invoices, bookings) are production-stable but may receive additive changes without a version increment.
