Skip to content

API for machine clients

Other systems read and change records through a JSON API under /api/v1/. The API follows the rules of the user interface: permissions and policies, tenant isolation, validation with allow lists, UUIDs only, audit. Modules declare endpoints; authentication, rate limits, logging and the OpenAPI description come from Anvil.

  • Clients are service accounts of one tenant with an explicit list of permissions, not users: they cannot log in to a panel.
  • Resource endpoints list, show, create, change and delete the records of a resource, and run its actions.
  • Custom endpoints are declared by applications and modules.
Page Content
Clients and tokens service accounts, tokens, administration in the panel and on the command line
Calling the API requests, answers and errors, idempotent writes, signed requests
Resource endpoints list, show, create, change, delete, actions and transitions of resources
Custom endpoints endpoints of applications and modules
OpenAPI generated description of the endpoints
Logging and limits audit, access log, security events, what the API does not do
  1. Switch the API on in app/Config/Anvil.php:

    public array $apiPanels = ['admin']; // resources with $api in these panels
    public array $apiEndpoints = [\App\Api\ReportApi::class]; // custom endpoint groups

    With one of them set, the route api/v1/* exists and the resource “API clients” appears next to users and roles (Access::resources()).

  2. Run the migrations: php spark migrate --all.

  3. Exclude the API from the CSRF filter and from session based filters in app/Config/Filters.php. API requests carry no cookies and no CSRF token; they authenticate with their token.

    public array $globals = [
    'before' => [
    'csrf' => ['except' => ['api/*']],
    ],
    ];
  4. Schedule php spark anvil:api:review and php spark anvil:prune daily (e.g. with codeigniter4/tasks).

  5. Operation:

    • HTTPS: in production plain HTTP is refused (403). Behind a reverse proxy, set Config\App::$proxyIPs so that the client IP and the protocol are taken from the proxy.
    • Several servers: rate limits and the replay protection of signed requests use the cache. With more than one server, use a shared cache (e.g. Redis, Config\Cache::$handler = 'redis').