Voyti
REST API
The JSON REST API is optional and pluggable: the base API package carries no resource endpoints of its own, only Bearer-token authentication, RBAC-admin gating, and the shared route groups resource packages plug into. Install a resource package to expose actual endpoints.
There are three route groups, contributed under yiirocks/voyti → api → routes /
authenticatedRoutes / publicRoutes respectively:
| Params key | Middleware | Use for |
|---|---|---|
routes |
Bearer auth + extensions + admin-access check | Admin-only endpoints (e.g. user CRUD, RBAC) |
authenticatedRoutes |
Bearer auth + extensions | Any authenticated caller acting on their own behalf (e.g. “my profile”, “my sessions”) |
publicRoutes |
Extensions only, no auth | Endpoints reachable before a token exists (e.g. login, registration) |
Every group still passes through installed extension middleware. The optional rate limiter enforces
limits only after Bearer authentication resolves a user, so public routes pass through it unchanged.
routes additionally has AccessRuleMiddleware enforce administratorPermissionName. Each resource package also
contributes its own OpenAPI paths and schemas, merged into one openapi.json document.
SCIM 2.0 provisioning for identity providers: Voyti users and RBAC roles exposed through Users and Groups resources. See the full SCIM provisioning page for endpoint and behavior details.
A REST API for any stateless client: credential login/logout, self-registration, password reset, own-profile and own-sessions management, plus admin RBAC and audit-log endpoints. See the full Stateless Client page for the complete endpoint list.
REST CRUD endpoints for users, built on this package's token authentication and admin-access middleware. Contributes its routes into the shared authenticated group below and its own OpenApiSpecContributorInterface implementation into the merged openapi.json spec - no route or OpenAPI wiring of its own.
| Route name | Method | Path | Purpose |
|---|---|---|---|
voyti/api-v1-users-index |
GET |
v1/users |
List users |
voyti/api-v1-users-view |
GET |
v1/users/{id} |
View a user |
voyti/api-v1-users-create |
POST |
v1/users |
Create a user |
voyti/api-v1-users-update |
PATCH |
v1/users/{id} |
Update a user |
voyti/api-v1-users-delete |
DELETE |
v1/users/{id} |
Delete a user |
Pulled in automatically as a dependency by resource packages, the base package supplies token authentication, admin-access gating, OpenAPI spec assembly, and the shared route group.
| Route name | Method | Path | Purpose |
|---|---|---|---|
voyti/api-openapi |
GET |
openapi.json |
OpenAPI 3.1 spec (JSON), assembled from every installed resource package. Public, so tooling (Swagger UI, codegen) can fetch it without a Bearer token. |
Two console commands are registered under yiisoft/yii-console:
| Command | Description |
|---|---|
voyti:api-token:generate |
Generate a REST API access token for a user (printed once) |
voyti:api-token:revoke |
Revoke all REST API access tokens for a user |
Configuration
// config/params.php
return [
'yiirocks/voyti' => [
'api' => [
'apiTokenLifespan' => 31536000,
],
],
];00 disables expiry entirely (tokens never expire). Enforced when resolving a Bearer token.Routes are not auto-registered. Pull the voyti-routes-api config
group into your router and mount it at whatever prefix you like:
use Yiisoft\Config\Config;
use Yiisoft\Definitions\DynamicReference;
use Yiisoft\Router\Group;
use Yiisoft\Router\RouteCollection;
use Yiisoft\Router\RouteCollectionInterface;
use Yiisoft\Router\RouteCollector;
/** @var Config $config */
return [
RouteCollectionInterface::class => [
'class' => RouteCollection::class,
'__construct()' => [
'collector' => DynamicReference::to(
static fn() => (new RouteCollector())
->addRoute(
Group::create('/api/')
->routes(...$config->get('voyti-routes-api')),
)
),
],
],
];Authentication
Requests authenticate with an Authorization: Bearer <token> header.
ApiTokenAuthenticationMiddleware resolves the token to a user for that request only and returns
401 when the header is missing or the token is invalid or expired. AccessRuleMiddleware then
enforces administratorPermissionName as usual, so API tokens only grant what that permission grants.
Rate limiting
Per-user rate limiting for these routes ships as a separate package,
voyti-api-rate-limiter, built on
yiisoft/rate-limiter. It scopes
limits per authenticated user. It applies to every installed package’s authenticated
endpoints, while public routes pass through without rate-limit headers because no user
identity exists to key the limit. Limited responses carry X-Rate-Limit-Limit,
X-Rate-Limit-Remaining, and X-Rate-Limit-Reset headers; requests over the limit
get a 429 Too Many Requests response.
No wiring is required: this package’s own middleware chain runs every installed
extension package automatically, so installing voyti-api-rate-limiter
turns rate limiting on immediately, and removing it turns rate limiting back off.
By default, your application must have a PSR-16 Psr\SimpleCache\CacheInterface
implementation configured and bound in your DI container. Any PSR-16 compliant
cache works; see the yiisoft/cache
documentation for one option and its available backends. Setting useApcu to
true drops this requirement entirely: counters are stored in APCu instead,
for real atomic compare-and-swap.
// config/params.php
return [
'yiirocks/voyti' => [
'api' => [
'rateLimiter' => [
'useApcu' => true,
],
],
],
];6060falseWriting a resource plugin
-
Contribute routes
Append routes to
yiirocks/voyti→api→routesinconfig/params.php. They’re spliced into the base package’s shared authenticated group, inheriting Bearer-token auth, extension middleware (e.g. rate limiting), and the admin-access check:// config/params.php use MyNamespace\MyResourceController; use Yiisoft\Router\Route; return [ 'yiirocks/voyti' => [ 'api' => [ 'routes' => [ Route::get('v1/my-resource') ->name('voyti/api-v1-my-resource-index') ->action([MyResourceController::class, 'index']), ], ], ], ];Route lists merge and append, so multiple resource packages coexist without collision. The group itself isn’t version-scoped: carry your own version segment in each route’s path/name (
v1/...,v2/...). -
Implement the OpenAPI contract
OpenApiSpecContributorInterface(namespaceYiiRocks\Voyti\Api\OpenApi, provided by the base package) is the contract you implement and tag withvoyti-api.openapi-contributorinconfig/di.php:Method Purpose getMethodSpec($routeName, $method)The OpenAPI operation object for a route name + HTTP method your package owns, or nullif it isn’t yours.schemas()Component schemas to merge into components.schemas, keyed by schema name.// config/di.php use MyNamespace\MyResourceOpenApiSpecContributor; return [ MyResourceOpenApiSpecContributor::class => [ 'class' => MyResourceOpenApiSpecContributor::class, 'tags' => ['voyti-api.openapi-contributor'], ], ];No wiring beyond the tag is needed:
openapi.jsonmerges every installed contributor’s paths and schemas automatically, alongside the base package’s genericinfo/servers/securityshell andErrorResponse/MessageResponseschemas.