Skip to content

Authentication

A route takes one of 3 credentials: none, a Supabase Auth token, or the bank’s read token. The tables on this page are generated from the API’s own route table, so they never drift.

A child plays without signing in. These routes take no token:

RouteWhat it does
GET /api/healthAnswers when the API is up.
GET /api/v1/featured-lessonsLists the featured lessons anyone may play.
GET /api/v1/playground-examples/{language}Lists the playground's authored examples in one language, with their keys.
POST /api/v1/assignments/{id}/attemptsStarts an attempt at an assignment.
GET /api/v1/attempts/{id}Reads an attempt: its plan and everything asked so far.
POST /api/v1/attempts/{id}/answersSends one try at the current question, or a skip.
POST /api/v1/attempts/{id}/questions/{seq}/helpReleases the next rung of help for one question.
PATCH /api/v1/attempts/{id}Pauses or resumes an attempt.
GET /api/v1/attempts/{id}/resultsReads an attempt's results in question order, mid-lesson too.
GET /api/v1/attempts/{id}/reviewReads a finished attempt's questions again, for the child.

An attempt route is bound by its attempt id. Whoever holds the id may read and answer that attempt. Only the reply that starts an attempt carries its id, so keep it safe and never log it. An unknown id, or one from another pod, is 404.

An adult signs in to the app, and Supabase Auth runs the sign-in. However the adult signs in, the result is a Supabase session, and its access token is what these routes take:

RouteWhat it does
GET /api/v1/meReads the signed-in adult: account, pod and learners.
POST /api/v1/accountsMakes the account and pod on the first sign-in.
POST /api/v1/learnersAdds a child to the caller's pod.
POST /api/v1/attempts/{id}/claimsAttaches an anonymous attempt to a learner in the caller's pod.
GET /api/v1/learners/{id}/attemptsLists a learner's attempts, newest first.
GET /api/v1/assignments/{id}/attemptsLists one assignment's attempts the caller's pod may read: the lesson overview.
GET /api/v1/learners/{id}/attempts/{attempt}/reviewReads a finished attempt of the caller's learner, with every try.

Send the token as a bearer token:

Terminal window
curl https://app.jadwigo.pl/api/v1/me \
-H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN"
  • The token is a JWT signed with RS256, with the audience authenticated.
  • No token, or one that fails verification, is 401 NotSignedIn.
  • A token whose exp has passed fails verification. Refresh the session and try again.
  • GET /api/v1/me reads the signed-in adult.
  • On the first sign-in there is no account yet, and the parent routes answer 409 AccountMissing. Call POST /api/v1/accounts once. It makes the account and its pod. A repeat call returns the same records, so a double tap or a reload is safe.

The bank section of the API reference has both routes.

The question bank is searched and read with a shared read token, not a Supabase token:

RouteWhat it does
POST /api/v1/question-searchesSearches the question bank: a need in, ranked questions out.
GET /api/v1/questions/{id}Reads one bank question's body, with its answer key.
Terminal window
curl -X POST https://app.jadwigo.pl/api/v1/question-searches \
-H "Authorization: Bearer $JADWIGO_BANK_READ_TOKEN" \
-H "Content-Type: application/json" \
-d @need.json

A question’s body carries its answer key, so it is behind the same token:

Terminal window
curl https://app.jadwigo.pl/api/v1/questions/$QUESTION_ID \
-H "Authorization: Bearer $JADWIGO_BANK_READ_TOKEN"
  • A missing, malformed or wrong token is 401 Unauthorized, whatever the id.
  • With the token, an unknown question id is 404.
  • When the server cannot reach the bank, the reply is 502 BankUnavailable. Try again later.
  • The read token is shared. It is a stopgap until each caller gets a token of its own.

No error message ever names a token.