API reference
The API is documented with OpenAPI annotations (darkaonline/l5-swagger, swagger-php) on the
controllers of the app and the packages. There are five ways to read it.
| What | Where | Best for |
|---|---|---|
| API browser | API browser on this site | Searching and reading every endpoint, request and response examples, trying calls against the demo tenant |
| Swagger UI | /api/documentation on any API host |
Trying endpoints against a running tenant |
| OpenAPI JSON | storage/api-docs/api-docs.json inside the API container |
Tools, code generation |
| TypeScript path types | front/sdk/src/generated/openapi.ts |
Typed requests in @ulams/sdk |
| Endpoint index | API endpoints on this site | Finding which package owns a route |
Swagger UI
Section titled “Swagger UI”Every API host serves the UI at /api/documentation (routes.api in
api/config/l5-swagger.php):
- platform:
http://api.localhost/api/documentation - tenant:
http://coffee.localhost/api/documentation
Use Authorize with a token from POST /api/auth/login (or a scoped ulams_pat_ token, see Scoped API tokens) to call authenticated endpoints. The host
decides the tenant, so requests made from the coffee host hit coffee’s database
(Tenancy). Unknown hosts get 404 before Laravel routes the request.
API browser
Section titled “API browser”The API browser is Scalar’s API reference
(MIT) over the generated document, served from the docs site itself: nothing loads from a CDN and no
request goes through a third-party proxy. yarn workspace @ulams/docs api-browser builds its two files
into public/ (both ignored by git):
vendor/scalar/standalone.js: the pinned Scalar browser bundle, fetched from the npm registry and checked against its integrity hash (scripts/vendor-scalar.mjs). It is not a yarn dependency, so the monorepo lockfile stays untouched.openapi.json: fromphp api/scripts/openapi.php, which scans the same paths asl5-swagger:generate(theannotationslist inapi/config/l5-swagger.php) without booting Laravel or a database; it needs onlycomposer install --no-scriptsinapi/. Without PHP the script falls back to the local API container.DOCS_OPENAPI=<file>uses a ready document. In CI a missing spec fails the build.
Try-it-out calls go straight from the browser to the server picked in the page, so that server must allow the docs origin (CORS). The demo tenant does.
Generating the spec
Section titled “Generating the spec”The spec is built from annotations in app/ and in the src/ of every package listed under
annotations in api/config/l5-swagger.php. A new package with documented endpoints must be added
to that list.
make -C api swagger-generate # from the repository root (or `make swagger-generate` inside api/)# runs: docker compose exec api bash -c "XDEBUG_MODE=off php artisan l5-swagger:generate"The output is api/storage/api-docs/api-docs.json. It is listed in api/.gitignore, so it is not in
the repository: every checkout and every container builds its own. L5_SWAGGER_GENERATE_ALWAYS
(default true in the config) regenerates it when the documentation is requested.
SDK types
Section titled “SDK types”@ulams/sdk generates path types from that file:
make -C api swagger-generate # in the API containeryarn workspace @ulams/sdk generate # openapi-typescript ../../api/storage/api-docs/api-docs.jsonThe result, front/sdk/src/generated/openapi.ts, is committed. ApiPath (keyof paths) types every
request the client makes; response shapes are hand-written in front/sdk/src/types.ts until the spec
documents them. Regenerate after changing routes or annotations. The workflow is described in
OpenAPI and SDK and the client in
Reference frontend.
Endpoint index on this site
Section titled “Endpoint index on this site”API endpoints lists every route by package, generated when the site is built from the packages’ route files. It shows methods, paths and the owning package, not request and response schemas; follow it to Swagger UI for those.
Why the site does not embed the OpenAPI spec
Section titled “Why the site does not embed the OpenAPI spec”Starlight has OpenAPI plugins that render a spec as pages. We do not use one, because:
- the spec is generated at runtime inside the API container (PHP, swagger-php, the full Laravel app) and is not committed, so the docs build would need a running API, or a copy that drifts;
- the docs site builds on its own (Node only, in CI and on GitHub Pages) and must not depend on the API image;
- the spec is incomplete for responses, so rendered pages would look authoritative while missing most of the useful content.
The endpoint index is therefore generated statically from the route files in api/packages/*, which
are in the repository, and Swagger UI on a running API stays the full reference. When the spec is
complete and published as a build artefact (roadmap 7.3), embedding it can be reconsidered.
Related
Section titled “Related”- Authentication, Scoped API tokens and the error codes the API returns.
- Rate limits and Webhooks.
- SDK usage: Node and browser examples with pagination and error handling.