Connect any agent to an Agonara instance over its HTTP API — health check, password login and the agonara_session cookie, whoami, the X-Agonara-CSRF token and idempotency keys for safe mutations, and reading the accepted/rejected/fault envelope. Use when an agent needs to authenticate to Agonara, hold a session, send a state-changing request, or interpret an Agonara API response. The shared foundation every Agonara role skill builds on.
Install
npx skillscat add igor1309/ai-labs/agonara-api-access Install via the SkillsCat registry.
Agonara API access
Teaches an agent to talk to an Agonara instance at all: connect, sign in, hold a
session, send mutations correctly, and read the result. Every role skill leans on
this one.
What you need (the only assumptions)
- An instance URL (the API base, e.g.
https://your-agonara.example). - Credentials — a login and password for an identity on that instance.
- The instance's Agonara OpenAPI contract, which is the source of truth for
routes, request bodies, status codes, and rejection reasons.
Nothing else: no repository, no source checkout, no build or test tooling, no
particular agent runtime.
Connect
GET /operations/health— no auth. Returns a bare health document{ "status": "ok", "checkedAt", "requestId", "version" }. This is the one
endpoint that does not use the standard envelope below;status: "ok"
here means "service up", not theacceptedresult status.GET /identity/bootstrap/status— no auth. Use only to learn whether an
instance still needs its first Admin; it returns the standard envelope.
Authenticate and hold a session
Agonara uses a server-managed, opaque session cookie named agonara_session.
- Log in —
POST /identity/sessionswith body{ "login", "password" }.
No session or CSRF token is needed (you have none yet). On success the
response sets theagonara_sessioncookie; send that cookie on every
subsequent request. - Whoami / get a CSRF token —
GET /identity/sessionresolves the current
session and returns a CSRF token in the accepted envelope'svalue. Call it
after login (and whenever you need a fresh CSRF token). - Log out —
DELETE /identity/session. This is itself a mutation, so it
requires the session cookie and theX-Agonara-CSRFheader. It clears
the cookie.
Mutations: CSRF + idempotency
CSRF. Every state-changing, session-backed request requires theX-Agonara-CSRF header carrying the token from GET /identity/session.
Omitting or mismatching it is rejected with reason csrf_required /csrf_invalid. Mutations that run before you have a session — login
(POST /identity/sessions) and first-Admin bootstrap — carry no CSRF token and
are not CSRF-protected. Treat CSRF as required for any session-backed mutation
unless the contract's security block for that operation says otherwise.
Idempotency. The X-Agonara-Idempotency-Key header makes a submission
safe to retry: the server records the key and will not apply the same operation
twice. The contract marks this key required for Student attempt submissions;
the API never echoes it back. Reusing a key for a conflicting request is rejected
with idempotency_conflict. (Generating and sending the key for a specific
submission is the relevant role skill's job; this skill only defines the
mechanism.)
Read the envelope
Every JSON operation response (health excepted) is exactly one of three shapes:
status |
Shape | Meaning |
|---|---|---|
accepted |
{ "status": "accepted", "value": … } |
Success. Read value. |
rejected |
{ "status": "rejected", "reason": … } |
The request will not succeed unchanged. |
fault |
{ "status": "fault", "reason": "internal_error" } |
Unexpected server fault (details redacted). |
Status families track this: 200 → accepted; 400/401/403/404/409/429 →
rejected; 500 → fault.
Retry vs. stop, driven by the result:
accepted— done; usevalue.fault— transient server-side; safe to retry, reusing the same
idempotency key where one applies.rejected— do not blind-retry; branch onreason:session_expired/session_not_found/session_revoked→ re-authenticate, then resend.csrf_required/csrf_invalid→ fetch a fresh token viaGET /identity/session, then resend.rate_limited→ back off, then retry.idempotency_conflict→ you reused a key for a different request; use a new key.- anything else (e.g.
validation_failed,not_found,*_requiredauthorization reasons, domain conflicts) → fix the request or stop; resending unchanged will fail again.
reason is always a value from the contract's ApiRejectionReason enum — treat
that enum as the authoritative list and honor the reason the API returns rather
than inferring hidden state.
Stay inside what the API tells you
Read state through the API's own projections and act on the envelope it returns.
Do not infer authority, membership, or visibility the response did not grant —
the instance enforces those boundaries and a rejected reason is the contract
telling you where they are.
You are the interface, not a route directory
Agonara is bring-your-own-agent: you hold the session and call the API, so
the operator does not. When asked to view, list, check, or confirm anything,
call the relevant read endpoint yourself and present the result inline — titles,
bodies, statuses, ids, whatever they asked about. Never answer a "how do I see
this?" question by handing the operator a raw endpoint path (a GET on some/programs/... route)
to call themselves: reciting the route is the agent offloading its own job. The
operator runs an agent precisely so they don't touch HTTP. If they want a
browsable view of their own, point them at the instance's /learn (curator/
student) or /admin UI — but still show them what they asked for first.