BookStackApp/BookStack · error · ApiAuthException
errors.api_user_no_api_permission
Error message
errors.api_user_no_api_permission
What it means
ApiTokenGuard::validateToken throws ApiAuthException('errors.api_user_no_api_permission') with status 403 when $token->user->can(Permission::AccessApi) is false. The token itself is valid (exists, secret matches, not expired), but the owning user account lacks the 'Access the API' system permission, so all its API tokens are refused.
Source
Thrown at app/Api/ApiTokenGuard.php:136
* @throws ApiAuthException
*/
protected function validateToken(?ApiToken $token, string $secret): void
{
if ($token === null) {
throw new ApiAuthException(trans('errors.api_user_token_not_found'));
}
if (!Hash::check($secret, $token->secret)) {
throw new ApiAuthException(trans('errors.api_incorrect_token_secret'));
}
$now = Carbon::now();
if ($token->expires_at <= $now) {
throw new ApiAuthException(trans('errors.api_user_token_expired'), 403);
}
if (!$token->user->can(Permission::AccessApi)) {
throw new ApiAuthException(trans('errors.api_user_no_api_permission'), 403);
}
}
/**
* {@inheritdoc}
*/
public function validate(array $credentials = []): bool
{
if (empty($credentials['id']) || empty($credentials['secret'])) {
return false;
}
$token = ApiToken::query()
->where('token_id', '=', $credentials['id'])
->with(['user'])->first();
if ($token === null) {
return false;View on GitHub (pinned to 18f8469a1c)
Solutions
- In BookStack admin, grant the user (or its role) the 'Access the API' permission
- If the user account is intentionally restricted, issue a token under a dedicated integration user with API access and minimal role permissions
- Audit role permission changes when API 403s start appearing
- Keep a dedicated service account for integrations so human permission changes don't break them
Example fix
// before // user 'report-bot' role has no 'Access the API' permission -> all its tokens 403 // after // Admin UI: Edit role 'bots' -> check 'Access the API' -> save; retry the request
Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure the service user has API access before deploying the integration
// Check in admin: the user's role has 'Access the API' permission enabled
// Optionally verify with a cheap authenticated call at startup:
$probe = $client->get('/api/books', ['headers' => ['Authorization' => "Token {$id}:{$secret}"]]);
if ($probe->getStatusCode() !== 200) { throw new RuntimeException('API user lacks AccessApi permission'); } Type guard
function apiProbeSucceeds(array $headers): bool {
$code = (int) (probeBookstack('/api/books', $headers) ?? 500);
return $code === 200;
} Try / catch
try {
$res = $client->get($url, ['headers' => ['Authorization' => "Token {$id}:{$secret}"]]);
} catch (ClientException $e) {
if ($e->getResponse()->getStatusCode() === 403) {
// ask an admin to re-enable 'Access the API' for the token's user/role
}
throw $e;
} Prevention
- Use a dedicated service account whose role never loses API permission
- Re-check role permissions after any BookStack upgrade or role refactor
- Include the AccessApi permission in deployment checklists for integrations
- Separate human accounts from integration accounts to avoid offboard side effects
When it happens
Trigger: An admin revoked or never granted the 'Access the API' permission from the user's role/profile; the token owner's role was changed to one without API access; API access was disabled for a whole role during a security lockdown.
Common situations: Role refactors that silently drop API permission; offboarding processes that strip permissions while integrations still run under that user; new tokens created for a read-only role that lacks AccessApi.
Related errors
- User does not have permission to create a chapter within the
- User does not have permission to create a page within the ne
- Only top-level comments can be archived.
- Only top-level comments can be un-archived.
- Unauthorized
AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02).
Data as JSON: /api/errors/1dad47e94b6e92fa.
Report an issue: GitHub.