# API v1 (Beta)

!!! warning "Beta"
    The v1 API is in **beta**. Endpoints and behaviour may change without
    notice while it is under active development. Full endpoint documentation
    is coming later — for now this page covers the basics and how to extend
    it. The stable, fully documented API remains [v0](index.md).

    The v1 API is disabled by default. Enable it with
    `lnms config:set api.v1.enabled true`, or in the web UI under
    **Settings → API → API v1 (Beta)**. When disabled, all `/api/v1`
    endpoints return 404 and v1 token management is hidden from the web UI.

## Overview

The v1 API is served under the `/api/v1` prefix and is built on
[Laravel Restify](https://restify.binarcode.com/) with
[Sanctum](https://laravel.com/docs/sanctum) bearer-token authentication.

## Authentication

Most v1 endpoints require a personal access token sent as a bearer token:

```curl
curl -H 'Authorization: Bearer YOURAPITOKENHERE' https://librenms.org/api/v1/system
```

Create a v1 token from the web interface at `/api-access/` under the
**API v1 tokens** section.

## Available endpoints

| Method | Path             | Auth          | Description                                      |
|--------|------------------|---------------|--------------------------------------------------|
| `GET`  | `/api/v1/health` | none (public) | Rate-limited readiness probe.                    |
| `GET`  | `/api/v1/system` | bearer token  | Application version and database row statistics. |

More endpoints will be added as the v1 API matures.

### Health

`GET /api/v1/health` is public so it can report a failure even when
database-backed token authentication is unavailable. Its response is
deliberately generic: it does not identify dependencies or include exception
messages, application details, or user information. Requests are limited to
60 per minute per client.

A healthy response returns HTTP 200:

```json
{
  "meta": {
    "status": "healthy"
  }
}
```

An unavailable dependency returns a generic HTTP 503:

```json
{
  "errors": [
    {
      "status": "503",
      "title": "Service Unavailable",
      "detail": "A required service is unavailable."
    }
  ]
}
```

### System

`GET /api/v1/system` requires a bearer token. It returns application
identity and selected table row counts:

```json
{
  "meta": {
    "application": {
      "name": "LibreNMS",
      "version": "26.7.0"
    },
    "statistics": {
      "devices": 42,
      "ports": 512,
      "users": 3
    }
  }
}
```

If the database is unavailable during authentication or while gathering
statistics, the endpoint returns the same generic JSON:API 503 error used by
the health endpoint. Database connection details are never included.

## Adding custom endpoints

There are two places to add v1 functionality, depending on whether you
need a plain route or a full CRUD resource.

### 1. Custom (non-repository) endpoints

Routes that are not standard CRUD resources—health checks, actions,
reports, etc.—are declared by overriding `routes()` on a class in
`app/Restify/`. Restify discovers these classes and applies the configured
v1 prefix and middleware. Point the route at an invokable controller in
`app/Http/Controllers/Api/V1/`.

```php
// app/Restify/ExampleRepository.php
namespace App\Restify;

use App\Http\Controllers\Api\V1\ExampleController;
use Illuminate\Routing\Router;

class ExampleRepository extends Repository
{
    public static string $uriKey = '';

    public static function routes(Router $router, array $attributes, $wrap = true): void
    {
        // Routes are authenticated by default.
        $router->get('example', ExampleController::class)
            ->name('api.v1.example');

        // Authentication must be explicitly removed for a public route.
        $router->get('public-example', ExampleController::class)
            ->withoutMiddleware('auth:sanctum')
            ->name('api.v1.public-example');
    }
}
```

A minimal controller:

```php
// app/Http/Controllers/Api/V1/ExampleController.php
namespace App\Http\Controllers\Api\V1;

use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;

class ExampleController extends Controller
{
    public function __invoke(): JsonResponse
    {
        return response()->json(['meta' => ['message' => 'example']]);
    }
}
```

Guidelines:

- Place controllers in `app/Http/Controllers/Api/V1/`.
- Routes inherit `auth:sanctum`. Remove it with
  `->withoutMiddleware('auth:sanctum')` only for intentionally public
  endpoints such as the health probe.
- Give custom routes a unique `api.v1.*` name.

### 2. Restify repository (CRUD) resources

For standard create/read/update/delete access to a model, add a Restify
repository in `app/Restify/`:

```php
// app/Restify/DeviceRepository.php
namespace App\Restify;

use App\Models\Device;

class DeviceRepository extends Repository
{
    public static string $model = Device::class;
}
```

Restify discovers the repository and generates the standard resource routes
(index, show, store, update, destroy) under `/api/v1` automatically. See the
[Laravel Restify documentation](https://restify.binarcode.com/) for how to
define repositories, fields, and authorization.

### After changing routes

Route and config are cached on container start, so after adding or changing
endpoints clear the caches:

```bash
php artisan route:clear
php artisan config:clear
```

Verify the result with `php artisan route:list --path=api/v1`.
