# Arraya Public API

Arraya is a gamified programming-learning platform: players solve puzzles on an 8x8 grid that
teach conditionals, loops, lists, functions, recursion and object orientation in Python, C++,
Java and JavaScript.

This page documents the part of the API that answers **without any credential**. The machine
readable version of the same thing is at
[`/openapi.json`](https://arraya.com.br/openapi.json) (OpenAPI 3.1), and it is advertised as
`service-desc` in [`/.well-known/api-catalog`](https://arraya.com.br/.well-known/api-catalog).

- Authentication, and why most agents do not need it: [`/auth.md`](https://arraya.com.br/auth.md)
- What the site is, in one page: [`/llms.txt`](https://arraya.com.br/llms.txt)
- Reuse preferences for this content: [`/robots.txt`](https://arraya.com.br/robots.txt)

## Base URL

```
https://arraya.com.br/api
```

Always call the API through `arraya.com.br/api`. That path is a same-origin rewrite to the
backend; requesting the backend deployment host directly is unsupported and drops the session
cookie on Safari.

## Ground rules

- Everything below is `GET` and needs no credential. Send no `Authorization` header and no cookie.
- Responses are JSON. Free text is Brazilian Portuguese.
- Dates in `date` fields are calendar dates in `America/Sao_Paulo`; `created_at` and friends are
  ISO 8601 timestamps.
- Writing endpoints (creating phases, submitting solutions, everything under `/organizations`)
  are out of scope here. They require a logged-in human account.
- There is a rate limit per IP on the authentication and code-validation routes. The read
  endpoints below are not aggressively limited, but batch politely: check `/api/health` once
  rather than probing a data endpoint in a loop.

## Availability

### `GET /health`

`200` while the API can reach its database, `503` otherwise.

```json
{ "status": "ok", "version": "2.0", "database": "up", "checked_at": "2026-07-29T20:11:04-03:00" }
```

## Phases

A *phase* is one puzzle: a grid, a start position, a target path, and a reference solution in
each language it supports. Phases belong to a *world* (a concept) and have a difficulty.

| Value | Worlds |
|---|---|
| `world` | `condicionais`, `lacos`, `listas`, `funcoes`, `recursao`, `tuplas-e-dicionarios`, `orientacao-objetos`, `community` |
| `difficulty` | `easy` (5 pts), `normal` (10), `hard` (15), `challenge` (20) |
| `mode` | `steps` (trace the code, shown as "Seguir"), `write` ("Escrever"), `code` ("Desafios") |
| `language` | `python`, `cpp`, `java`, `javascript` |

### `GET /phases/`

Paginated list of published phases, most liked first.

Query: `page`, `limit` (max 500), `difficulty`, `world`, `mode`, `language`, `search`, `creator_id`.

```
GET /api/phases/?world=recursao&difficulty=hard&limit=5
```

```json
{
  "items": [
    {
      "id": 42,
      "slug": "recursao-3",
      "title": "Descendo a pilha",
      "difficulty": "hard",
      "world": "recursao",
      "creator_id": 1,
      "creator_username": "arraya",
      "creator_name": "Equipe Arraya",
      "user_completed": false,
      "like_count": 12,
      "created_at": "2026-02-11T09:02:00"
    }
  ],
  "total": 7,
  "page": 1,
  "limit": 5,
  "pages": 2
}
```

`user_completed` is always `false` for an anonymous caller.

### `GET /phases/search?q=`

Typo-tolerant search on the title, accent and case insensitive, up to 20 results. Use this
instead of the `search` filter above when the query may be misspelled. Returns a bare array.

### `GET /phases/daily`

The phase featured today, stable for the whole day in `America/Sao_Paulo`. Optional `mode` and
`language`.

### `GET /phases/{id-or-slug}`

Full detail, including `grid`, `targetPath` and the reference solutions (`code` for Python,
`codeCpp`, `codeJava`, `codeJavascript`). `404` if there is no such phase.

### `GET /phases/user/{user_id}`

Every published phase authored by that user.

## Challenges

A *challenge* is a classic input/output exercise, run in the browser with Pyodide, Python only.

### `GET /challenges/`

Paginated, same shape as `/phases/`. Query: `page`, `limit`, `difficulty`, `search`.

### `GET /challenges/daily`

The challenge of the day, plus the `date` it is for.

### `GET /challenges/{challenge_id}`

Statement and public test cases. Hidden test cases are never returned.

### `GET /challenges/user/{user_id}`

Every published challenge authored by that user.

## Users

Only what the user chose to publish is exposed. E-mail addresses are never returned.

### `GET /users/ranking`

Global ranking by score, ties broken by who got there first. Query: `page`, `limit` (max 100).

```json
{
  "items": [
    { "position": 1, "id": 1, "name": "Joab Henrique", "username": "joab",
      "avatar_url": null, "score": 320, "is_verified": true }
  ],
  "total": 1840, "page": 1, "limit": 10, "pages": 184
}
```

### `GET /users/contributors`

Same shape, ranked by how many phases the user created.

### `GET /users/popular`

Same shape, ranked by likes received on the profile.

### `GET /users/search?q=`

Typo-tolerant search over name and username.

### `GET /users/{username}`

Public profile: score, badges, counts of phases created and completed, bio and social links.

### `GET /users/{username}/activity?days=365`

Daily activity heatmap over a window ending today.

## Errors

Errors are FastAPI's shape:

```json
{ "detail": "Phase not found" }
```

`404` for a missing resource, `422` for a malformed query parameter, `429` when a rate limit is
hit, `503` from `/health` when the database is unreachable.

## Stability

This document and `/openapi.json` are maintained by hand and describe the endpoints Arraya
intends to keep stable. Endpoints not listed here exist but may change without notice.
