API coverage matches UI actions
Every teacher action is a versioned /api/v1 endpoint backed by the same pure engine the UI uses. That covers generating a comment, scoring against standards, evaluating fluency, and exporting a class.
Built for the agents your district will run
The next bottleneck in EdTech isn't whether software has AI. It's whether a district's own agent can safely use the software it already pays for. Papalotl EDU is built to be used by an agent the way a teacher uses it in the browser: same actions, same permissions, fully audited, nothing destructive.
The rubric is from Kris Hagel's Build Products Our Agents Can Use. We hold ourselves to Level 3 today and name the Level 4 gaps plainly.
Green means it ships today. Sand means it's deliberately scoped. The architecture is ready. The rest is a district's policy call.
Every teacher action is a versioned /api/v1 endpoint backed by the same pure engine the UI uses. That covers generating a comment, scoring against standards, evaluating fluency, and exporting a class.
A live OpenAPI 3.1 document sits at /api/v1/openapi. A one-call discovery root at /api/v1 lists endpoints, scopes, and how to authenticate.
A zero-dependency stdio MCP server (mcp/server.mjs) exposes five tools over the same API. Register it with Claude Code, Claude Desktop, or any district agent.
Scoped Bearer keys (standards:read, comments:generate, fluency:read, export:create). Never a god-mode service account. The acting teacher rides in X-On-Behalf-Of for attribution.
Every call emits a structured audit record (requestId, at, action, principal, on-behalf-of, sandbox, dry-run) for a district to ship to its SIEM. A queryable store is the district's sink, not ours. The API keeps no student records of its own, and records saved to a school account are a separate store the API does not query.
The engine is generative, not destructive. Comments are drafts a teacher reviews. Exports take dryRun:true to preview rows before producing a file. The MCP tool defaults to dry-run.
In development (or with CNA_ALLOW_SANDBOX=true) the API runs keyless in open sandbox mode. In production a missing key list fails closed with 503. The engine stores no student records either way.
Today it is a signed scoped-key list driven by environment config. The auth boundary is isolated in one module so a district can swap in OAuth 2.0 scopes or SCIM without touching handlers.
Not yet. The engine is request/response and stateless, so there are no server-side events to stream. This is the honest gap between Level 3 and Level 4.
We collect as little as possible: in No-Save mode nothing student-bearing is written to the browser or a server, comments are generated on-device with no AI provider in the path, and nothing is ever sold. A district DPA remains a required paperwork step before saved-history deployment.
| Method | Path | Scope | Purpose |
|---|---|---|---|
| GET | /api/v1 | public | |
| GET | /api/v1/openapi | public | |
| GET | /api/v1/standards | standards:read | |
| GET | /api/v1/skills | standards:read | |
| POST | /api/v1/comments | comments:generate | |
| POST | /api/v1/fluency | fluency:read | |
| POST | /api/v1/export | export:create |
curl -X POST https://your-deployment/api/v1/comments \
-H "Authorization: Bearer $CNA_KEY" \
-H "X-On-Behalf-Of: teacher@district.org" \
-H "Content-Type: application/json" \
-d '{
"mode": "standards",
"student": { "alias": "S-K7MPQ2XW", "pronoun": "she" },
"scores": { "rl_kid": "proficient", "w_text_types": "developing" },
"languages": ["en", "es"]
}'# Register the MCP server with any district-owned agent claude mcp add papalotl-edu -- \ node /path/to/papalotl-edu/mcp/server.mjs # Tools it exposes: # list_standards · list_skills · generate_comment # evaluate_fluency · export_class (dry-run by default)
The browser, the API, and the agent all lead to the same place. That place is standards-aligned, bilingual communication that tells a family their child is seen, written in a fraction of the time. Open by design, so it can meet a district wherever it already works.