AgentOnboard Docs
Partner GuideExample app

Example app: The action — create & read notes

The two endpoints an agent actually uses — GET and POST /api/notes — with query filters, zod body validation, response shapes, and a worked end-to-end example

Example app: The action — create & read notes

This page documents the two endpoints an agent uses constantly — reading notes and creating notes — against the real handlers in apps/web/src/app/api/notes/route.ts. By the end you can replay the entire flow by hand.

Base URL and auth

All requests go to https://notes.aawej.in and carry the session token in the x-session-token header (see REST API integration for how that header is verified). The user must already have an account at notes.aawej.in — there is no auto-provisioning, and the email in the token is the join key to the local account.

GET /api/notes — read notes

Lists the authenticated user's notes, newest-updated first.

GET /api/notes?search=meeting&limit=10&offset=0
x-session-token: <session-token>

Query parameters

ParamTypeDescription
searchstringFree-text search — matches note titles and the plain-text body
tagIdUUIDOnly notes with this tag
limitintegerMax notes to return, 1500 (default 100)
offsetintegerPagination offset (default 0)

All parameters are optional. Invalid values — a non-UUID tagId, a limit of 0 or 501 — return:

{ "error": "Invalid query parameters", "details": { ... } }

with status 400.

Response — 200 OK

{
  "notes": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "title": "Meeting notes",
      "content": [],
      "contentText": "Discussed the Q3 roadmap and the partner integration.",
      "createdAt": "2026-07-12T05:34:00.889Z",
      "updatedAt": "2026-07-12T05:34:14.142Z",
      "tags": [
        {
          "id": "660e8400-e29b-41d4-a716-446655440001",
          "name": "work",
          "createdAt": "2026-07-10T12:00:00.000Z"
        }
      ]
    }
  ],
  "total": 1
}
  • notes — the page of notes. Each note has an id (UUID), title, content (BlockNote/ProseMirror JSON, possibly empty), contentText (plain text for search), ISO-8601 createdAt/updatedAt, and a tags array.
  • total — the number of notes matching the filters across all pages, so an agent can paginate with offset.

POST /api/notes — create a note

Creates a note for the authenticated user.

POST /api/notes
x-session-token: <session-token>
Content-Type: application/json

{ "title": "Grocery List", "tagIds": ["660e8400-e29b-41d4-a716-446655440001"] }

Body

The body is validated with zod against this schema:

FieldTypeRequiredRules
titlestringtrimmed, 1500 characters
contentanyBlockNote / ProseMirror JSON document (defaults to [])
contentTextstringplain-text representation, used for search
tagIdsUUID[]tag UUIDs to assign (tags not owned by the user are ignored)

Errors

StatusWhenResponse body
400Body is not valid JSON{ "error": "Request body must be valid JSON" }
400Body fails the schema (e.g. missing/empty title){ "error": "Invalid request body", "details": { ... } }

Response — 201 Created

{
  "note": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "title": "Grocery List",
    "content": [],
    "contentText": "",
    "createdAt": "2026-07-12T05:34:00.889Z",
    "updatedAt": "2026-07-12T05:34:00.889Z",
    "tags": []
  }
}

Success is 201, not 200 — a new resource was created. The note is returned in the same serialized shape as GET /api/notes (dates as ISO-8601 strings).

The whole flow, end to end

Putting it together — the exact sequence an agent runs:

Get a session token

aon token get

Prints a token valid for 5 minutes. Save it to a variable:

TOKEN=$(aon token get)
curl -H "x-session-token: $TOKEN" \
  "https://notes.aawej.in/api/notes?search=meeting&limit=5"

Create a note

curl -X POST -H "x-session-token: $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title":"Grocery List","contentText":"Milk, eggs, bread"}' \
  "https://notes.aawej.in/api/notes"

Returns 201 with the new note — note its id.

Read it back

curl -H "x-session-token: $TOKEN" \
  "https://notes.aawej.in/api/notes?search=Grocery"

The new note appears in the results, scoped to the user whose email the token verified to.

Refresh when the token expires

5 minutes later the same calls fail with 401. Run aon token get again — the CLI mints a fresh token from your saved key.

One email, one account, all their notes

Every call is scoped to the user behind the token: search, tagId, create, pagination — all of it happens inside that user's data. That is the payoff of the whole email-is-the-join-key design: the agent never names a user, the token does.

The full endpoint surface

For reference, the complete REST API exposed by the notes app:

MethodPathPurpose
GET/apiAPI index — lists every endpoint
GET/api/notesList notes (search, tagId, limit, offset)
GET/api/notes/:idFetch one note by UUID
POST/api/notesCreate a note
PATCH/api/notes/:idUpdate a note (partial body)
DELETE/api/notes/:idDelete a note
GET/api/tagsList the user's tags

Next steps

On this page