Repository navigation
REST API
The REST API of a Caligrafy application is its capabilities, answered in JSON. One line maps a route to a
capability (Route::capability), or one line serves them all by name (Route::capabilities). Programs send a personal
access token; pages of the application send the session and its CSRF token. Since 2.1.0.
/caligrafy-quill:new-capability create_book books:write: add a book and return it; serve it as POST /api/books
Claude adds the Route::capability line to web.php and checks it with curl: without a token, with a token that lacks
the scope, and with a good one.
Check before you say OK: the calls answer 401, 403 and 201 as below.
-
The routes. In
application/config/routing/web.php, beforeRoute::run();:The method is// BOOKSHELF: REST Route::capability('GET', '/api/books', 'list_books'); Route::capability('GET', '/api/books/{id}', 'get_book'); Route::capability('POST', '/api/books', 'create_book'); Route::capability('DELETE', '/api/books/{id}', 'delete_book');
GET,POST,PUTorDELETE. The route variables ({id}) become inputs. -
A token for the program:
php caligrafer.php token create "Bookshelf agent" --scopes books:read,books:write(Personal access tokens). -
Call it.
A
curl -s http://localhost:8080/api/books/3 # {"data":{"id":3,"title":"Kindred","published_year":1979,...}} 200: get_book is public curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -d '{"title": "Dune"}' http://localhost:8080/api/books # {"error":{"code":"unauthenticated","message":"Sign in or send a personal access token"}} 401 curl -s -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" \ -d '{"title": "The Lathe of Heaven", "published_year": 1971, "author_id": 1}' http://localhost:8080/api/books # {"data":{"id":7,"title":"The Lathe of Heaven",...}} 201
POSTwith neither a token norAccept: application/jsonis taken for a form and answers403[ERROR 3000].
Route::capabilities('/capabilities'); // GET lists, POST /capabilities/{name} calls-
GET /capabilitiesanswers what the caller may call, with the input schemas, and the session's CSRF token:{"data": [{"name", "description", "scope", "level", "readOnly", "destructive", "public", "input"}, ...], "csrf": "..."}. Web-only capabilities are left out. -
POST /capabilities/get_bookwith{"id": 3}callsget_book. The JS client uses these two routes.
Who calls (Context::fromRequest): Authorization: Bearer cq_... (a token), else the legacy API key, else the
session of a signed-in person, else a visitor.
Inputs: the JSON body, or the form fields, or the query string for GET; then the route variables, which win
(/api/books/3?id=9 is the book 3). _token, _method and uri are never inputs. Of the inputs, the capability keeps
only those of its $rules.
Files: a capability with a file input takes a multipart upload; the file is saved once the caller is allowed, and
deleted again when the call fails.
CSRF: a call made with the session cookie that is not read-only needs the session's CSRF token in an X-CSRF-Token
header (or _token), else 403 "Missing or invalid CSRF token: send the X-CSRF-Token header". A token or the legacy key
is not a cookie, so it needs none.
Responses:
| Status | Body | When |
|---|---|---|
200 |
{"data": ...} |
a success |
201 |
{"data": ...} |
a POST to a capability that is not read-only |
401 |
{"error": {"code": "unauthenticated", "message": ...}}, and WWW-Authenticate: Bearer
|
nobody, or an unknown, revoked or expired token |
403 |
{"error": {"code": "forbidden", ...}} |
the scope or the level is missing, or the CSRF token |
404 |
{"error": {"code": "not_found", ...}} |
no such capability, or the thing does not exist |
409 |
{"error": {"code": "conflict", ...}} |
it clashes with the current state |
422 |
{"error": {"code": "invalid", "message": ..., "fields": {...}}} |
an input is wrong, a message per field |
The data is plain: models become arrays of their columns. All the refusals: Refusing a call.
Reference: Route::capability(),
Route::capabilities(),
Rest.
-
A write over the session without
X-CSRF-Token(G54): 403. Use the JS client, or send thecsrfofGET /capabilities. -
A JSON call from a page without
Accept: application/json(G1): it is taken for a form and fails CSRF,[ERROR 3000]. -
register_userover REST (G81): refused; sign-up is the sign-up page. -
A closure route for JSON (G51):
Route::post('/x', function () {...})is not protected for JSON. Use a capability route.
Caligrafy: Describe it. Approve it. Ship it. Your idea turned into a complete app: every feature built once, for web pages, Vue apps, a REST API and AI agents, and online.
Why Caligrafy: the benefits, the measurements, and what it is not yet
-
Quickstart with Claude: describe it, approve it, ship it
or the Quickstart: the same three steps by hand, in 5 minutes
- How Caligrafy works: the ideas in 5 minutes
- Your first capability: build a feature, page included
-
Capabilities in depth
-
#[Capability]: every option - Scheduled jobs: capabilities on a timer
- Notifications: web push and email
- Background jobs: capabilities run later
- Migrations: database changes, in order
- The cache
-
Testing your app
- Dependency injection
- Validation rules
-
Web pages
- Views and page components
- Design styles: another look, one setting
- Declarative routes: every request, in one line
-
Route::page·Route::form·Route::auth·Route::webhook·Route::download·Route::redirect·Route::action·Route::snap· capability routes - How to export a list as CSV
- Controllers: moving one to a declared route · how to, step by step
- Forms and helpers
-
Vue apps
-
Mobile and tablet
-
REST API
-
AI agents
-
Snap Cards and the assistant
- Payments: Stripe and crypto
- Face detection and face login
- OpenAI: chat and images · Assistants
- Open models: Llama, Mistral...
- Chatbots with Watson
- Machine learning in the browser
- Metadata and social cards · search and analytics
-
Putting it online: the production image, the database at every start
-
API reference: every class and method
-
Routing ·
Route·AuthRoutes· pages ·Controller· capabilities -
Data ·
Model·Query· sign-in · jobs ·Notify· payments · AI -
Interfaces · Snap ·
Snap\Cards·Snap\Conversation· testing · tooling · helper functions
-
Routing ·
-
Caligrafer: every command
- Project · build · data · agents · jobs and tests · maintenance · deploy
- Installation, configuration and Caligrafer
- Routing · Request · Controllers · Views
- Vue: flow of information · routes · validations
- Security and upgrading
- Release notes: 2.26 · 2.25 · 2.24 · 2.23 · 2.22 · 2.21 · 2.20 · 2.19 · 2.18 · 2.17 · 2.16 · 2.15 · 2.14 · 2.13 · 2.12 · 2.11 · 2.10 · 2.9 · 2.8 · 2.7 · 2.6 · 2.5 · 2.4 · 2.3 · 2.1 · October 2026 · changelog
- Testing Caligrafy (contributors)