Skip to content

Authentication ​

The API has no API key. The caller signs in as a person (email and password) and receives two tokens:

TokenLifetimePurpose
accessTokenshort (expiresIn seconds; currently 15 minutes)Goes on every call: Authorization: Bearer <accessToken>.
refreshTokenlong (up to 30 days; the institution can shorten it)Only to get a new pair at POST /auth/refresh.

Always use the expiresIn value from the response instead of hard-coding 15 minutes.

Account for the integration ​

Ask the institution's curator to create an account just for the integration (for example integracao@exemplo.org), with the minimum role needed (see Permissions). The person receives an invitation by email and sets the password in the app. Avoid using a staff member's account: if they leave, the integration stops.

Sign in ​

POST /auth/login with email and senha (password). The response is the session:

bash
curl -X POST "$API/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"email":"integracao@exemplo.org","senha":"********"}'
js
const r = await fetch(`${API}/auth/login`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ email: "integracao@exemplo.org", senha: process.env.SENHA }),
})
const session = await r.json()
python
import os, requests

r = requests.post(f"{API}/auth/login", json={"email": "integracao@exemplo.org", "senha": os.environ["SENHA"]})
session = r.json()
json
{
  "accessToken": "eyJhbGciOi...",
  "refreshToken": "q3v8...",
  "expiresIn": 900,
  "usuario": {
    "id": "0198a000-0000-7000-8000-000000000001",
    "email": "integracao@exemplo.org",
    "nome": "Example Integration",
    "locale": "en-US",
    "nationality": null,
    "vinculos": [
      { "papel": "CURADOR", "instituicaoId": "0198a000-0000-7000-8000-0000000000aa", "disciplinaId": null, "turmaId": null, "podeAvaliar": false, "podeCadastrar": false }
    ]
  }
}

usuario.vinculos shows the account's roles and where they apply (institution, discipline or class). GET /auth/me returns the same information at any time.

Calling the API ​

bash
curl "$API/auth/me" -H "Authorization: Bearer $TOKEN"

Renewing ​

Before the accessToken expires (or on 401 UNAUTHENTICATED), exchange the refreshToken:

bash
curl -X POST "$API/auth/refresh" -H "Content-Type: application/json" \
  -d '{"refreshToken":"q3v8..."}'

The response is a new session, with a new refreshToken. Rules that matter when you implement this:

  • Each refreshToken works only once. Always keep the latest pair.
  • Presenting a refreshToken that was already used is treated as possible theft: all of the account's sessions are ended and you must sign in again with the password. So, in an integration with several processes, renew in one place only and share the resulting token; two simultaneous renewals with the same refreshToken make one of them fail with 401.
  • If renewal fails with 401, sign in again with POST /auth/login.

Signing out ​

POST /auth/logout with the refreshToken ends that session. POST /auth/logout-todas (authenticated) ends all of the account's sessions.

Lockout after failed attempts ​

Repeated wrong passwords for the same email block new attempts for a while (default: 5 failures, 15-minute block). In that case, and when too many attempts come from the same origin, the response is 429 RATE_LIMITED with retryAfterSeg and the Retry-After header. Do not retry in a loop: see Pagination and limits. A wrong password, an unknown email and a locked account get the same 401 response, on purpose, so that it does not reveal which accounts exist.

Keep the secret safe ​

The password and the refreshToken give the same access the person would have. Keep them in a secrets vault or in a server environment variable, never in code, in the repository or in logs.

contato@presente.app.br