Axuz Developers

Your first API call

From zero to a first authenticated call to the Axuz API with an axp_ personal token, using curl, Node or Python.

This guide takes you from an Axuz account to your first 200 response from the API using a personal token (axp_...). It takes about five minutes and needs nothing beyond curl, Node 20+ or Python 3.

The examples use the staging environment, https://api.axuz.net. In production the host is https://api.axuz.club; the rest of the contract is the same.

1. Create the token

  1. Sign in to the workspace and open Personal settings → API tokens (path /me/settings/api-tokens).
  2. Give it a name that says where the token will live (for example backup-script), then pick the scopes and the expiry.
  3. Copy the axp_... value right away: the server stores only the hash and never shows the secret again.

The recognised scopes are me:read (reads on /me/*), me:write (create, change and delete personal notes, tasks, files and events) and me:profile. For this guide me:read is enough. The per-minute request ceiling ranges from 1 to 1000 (default 60) and expiry from 1 day to 5 years (or no expiry).

Keep the token in an environment variable, never in code or in a repository:

export AXUZ_TOKEN='paste-the-axp_-token-here'
export AXUZ_API='https://api.axuz.net'

2. Make the first call: who am I and which teams do I have

The main service has no GET /me for tokens. The equivalent is GET/me/summary, which returns the active teams of your account together with task and finance counters for each one. It is how you discover the team.id values you will use later.

curl

curl -sS "$AXUZ_API/me/summary" \
  -H "Authorization: Bearer $AXUZ_TOKEN" \
  -H "Accept: application/json" \
  -i

Node

// node 20+, ESM (.mjs file)
const res = await fetch(`${process.env.AXUZ_API}/me/summary`, {
  headers: { Authorization: `Bearer ${process.env.AXUZ_TOKEN}` },
});
if (!res.ok) {
  throw new Error(`HTTP ${res.status}: ${JSON.stringify(await res.json().catch(() => null))}`);
}
const summary = await res.json();
for (const entry of summary.teams ?? []) {
  console.log(entry.team.id, entry.team.name, `open tasks: ${entry.tasks.open}`);
}

Python

import json
import os
import urllib.error
import urllib.request

req = urllib.request.Request(
    os.environ["AXUZ_API"] + "/me/summary",
    headers={"Authorization": "Bearer " + os.environ["AXUZ_TOKEN"]},
)
try:
    with urllib.request.urlopen(req, timeout=15) as res:
        summary = json.load(res)
except urllib.error.HTTPError as e:
    raise SystemExit(f"HTTP {e.code}: {e.read().decode()}")

for entry in summary.get("teams") or []:
    print(entry["team"]["id"], entry["team"]["name"], "open tasks:", entry["tasks"]["open"])

What to expect

A 200 response with Content-Type: application/json. The shape (illustrative values):

{
  "generated_at": "2026-10-22T12:00:00Z",
  "teams": [
    {
      "team": { "id": "2f1c9a52-6a1e-4a3b-9d55-0c7e1d2b7a10", "name": "Acme", "slug": "acme" },
      "tasks": { "open": 3, "overdue": 1, "due_week": 2, "assigned": 3 },
      "financer": { "month_spent_cents": 0, "to_pay_cents": 0, "overdue_cents": 0, "overdue_count": 0 }
    }
  ],
  "unavailable": [
    { "bucket": "calendar", "owner_service": "axuz_calendar_api", "reason": "events live in the local DB of axuz_calendar_api; ..." }
  ]
}

Three things to notice. Money values are integers in cents, never decimals. The unavailable field declares what the endpoint cannot aggregate (today, the calendar) instead of returning zero. And, with Redis enabled on the server, the response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset; see Rate limits. The response also carries X-Trace-Id, handy when you open a support request.

3. A second call: your notes

GET/me/notes lists personal notes (also with me:read):

curl -sS "$AXUZ_API/me/notes?limit=5" -H "Authorization: Bearer $AXUZ_TOKEN"

The response is {"items": [...]}. To read past the first page see Pagination.

Common errors at this point

SymptomLikely cause
401 with {"error":"unauthorized"}Missing, mistyped, expired or revoked token. The header must be exactly Authorization: Bearer axp_...; the token does not go in the URL.
403 with scope required: me:readThe token was created without the scope for the operation (me:read for GET, me:write for the rest). Create another one: scopes cannot be changed afterwards.
401 on /teams or /teams/{id}/...Those routes do not accept axp_, only a session. Use /me/summary to list teams.
429 with rate_limitedYou went over the token’s per-minute ceiling. Wait for Retry-After.

The full list of statuses and the body format is in Errors.

Next steps

  • Errors: the {"error": "..."} format, what each status means and which errors are safe to retry.
  • Pagination: how to walk large lists without missing or repeating items.
  • Rate limits: headers, Retry-After and backoff with jitter.
  • API reference: every operation, generated from the contract.
  • Changelog: what changed and when.

Security: treat the token like a password. If it leaks, revoke it under API tokens (or with DELETE/me/api-tokens/{tokenID} using a session): revocation takes effect on the very next request (the server invalidates the token’s 30-second cache).

Updated