|
# retoor <retoor@molodetz.nl>
|
|
|
|
GROUP = {
|
|
"slug": "conventions",
|
|
"title": "Conventions & Errors",
|
|
"intro": """
|
|
# Conventions & Errors
|
|
|
|
Shared rules that apply to every endpoint in this reference.
|
|
|
|
## Base URL
|
|
|
|
Every example uses your current host:
|
|
|
|
```
|
|
{{ base }}
|
|
```
|
|
|
|
## Authentication
|
|
|
|
Each endpoint is tagged **public**, **user**, or **admin**. Authenticate user and admin
|
|
endpoints with any of the four methods in [Authentication](/docs/authentication.html): the
|
|
`session` cookie, an `X-API-KEY` header, a `Bearer` token, or HTTP Basic credentials. The
|
|
interactive panels on this site pre-fill your own API key, so you can run user-level calls
|
|
immediately.
|
|
|
|
## Request bodies
|
|
|
|
POST endpoints accept `application/x-www-form-urlencoded` form fields (the same fields the
|
|
website submits). File uploads use `multipart/form-data`. A small number of endpoints accept
|
|
a JSON body; those are noted explicitly.
|
|
|
|
## HTML or JSON (content negotiation)
|
|
|
|
**Every** endpoint that renders a page or returns a redirect can also answer in JSON - the
|
|
website keeps working exactly as before, and automation gets structured data from the same
|
|
URLs. A request is served JSON when it sends either of:
|
|
|
|
- `Accept: application/json`
|
|
- `Content-Type: application/json`
|
|
|
|
A normal browser navigation (`Accept: text/html`) always receives HTML, so nothing existing
|
|
changes. Responses are defined by Pydantic models, so each page returns the same data the
|
|
template renders.
|
|
|
|
**Page reads** (GET) return the page payload as a JSON object (lists include a `next_cursor`
|
|
for pagination; detail pages embed author, comments, reactions, poll, and attachments).
|
|
|
|
**Actions** (the form POSTs: create / edit / delete / follow / send / mark-read …) return a
|
|
uniform envelope instead of a `302` redirect:
|
|
|
|
```json
|
|
{ "ok": true, "redirect": "/posts/abc-my-post", "data": { "uid": "…", "slug": "…", "url": "…" } }
|
|
```
|
|
|
|
`data` carries the created/affected resource where applicable, or `null`. Cookies (e.g. the
|
|
session set on login/signup) are still set on JSON responses.
|
|
|
|
**Errors** are JSON too when JSON is requested:
|
|
|
|
```json
|
|
{ "error": { "status": 404, "message": "Not found" } }
|
|
```
|
|
|
|
Validation failures return `422` with `{ "error": "validation", "fields": { "field": ["msg"] } }`.
|
|
Unauthenticated JSON requests to a protected endpoint return `401` (browsers are redirected to
|
|
the login page instead); non-admins calling an admin endpoint get `403`.
|
|
|
|
## Trying it here
|
|
|
|
Every endpoint below has a live panel. Pick the response format (**JSON** by default, or
|
|
**HTML** where the endpoint negotiates) and the panel sets the matching `Accept` header on the
|
|
request and the generated cURL/JavaScript/Python snippets. The **Expected** tab always shows the
|
|
modeled response shape; the **Live response** tab shows the real result after you press
|
|
**Send request**.
|
|
|
|
## AJAX responses (legacy shape)
|
|
|
|
The [Votes, Reactions, Bookmarks & Polls](/docs/social-actions.html) endpoints predate the
|
|
envelope and keep their original flat JSON shapes (e.g. `{ "saved": true }`). They return JSON
|
|
when the request carries an `X-Requested-With: fetch` header (a subset of the rule above);
|
|
without it they `302` redirect, mirroring the browser flow. The interactive panels send the
|
|
header for you.
|
|
|
|
## Pagination
|
|
|
|
Most list endpoints page with an opaque cursor. Pass the `before` query parameter set to the
|
|
`created_at` (or `synced_at`) value of the last item you received to fetch the next page; the
|
|
JSON payload returns a `next_cursor` to use as the next `before`. This covers the feed, the
|
|
post/project/gist/news lists, notifications, and saved bookmarks.
|
|
|
|
The follower and following lists are the exception: `GET /profile/{username}/followers` and
|
|
`GET /profile/{username}/following` use classic page-based pagination via the `page` query
|
|
parameter (25 per page), not a cursor.
|
|
|
|
## Identifiers
|
|
|
|
Posts, projects, gists, and news articles accept either their slug or their bare UUID in the
|
|
path. Slugs embed the first eight characters of the UUID.
|
|
|
|
## Dates
|
|
|
|
All dates rendered to users are `DD/MM/YYYY`. Timestamps in stored records are ISO-8601 UTC.
|
|
|
|
## Status codes
|
|
|
|
| Code | Meaning |
|
|
|------|---------|
|
|
| `200` | Success (JSON or HTML) |
|
|
| `201` | Resource created (uploads) |
|
|
| `302` | Redirect (browser-style success for form posts) |
|
|
| `400` | Invalid request body or parameters |
|
|
| `401` | Credentials supplied but invalid |
|
|
| `403` | Authenticated but not allowed |
|
|
| `404` | Resource not found |
|
|
| `413` | Upload exceeds the configured size limit (see [Uploads](/docs/uploads.html)) |
|
|
| `415` | Upload file type not allowed (see [Uploads](/docs/uploads.html)) |
|
|
| `422` | Form/body validation failed (JSON clients) |
|
|
| `429` | Rate limit exceeded (see Rate limiting) |
|
|
| `503` | Maintenance mode |
|
|
|
|
## Rate limiting
|
|
|
|
Mutating requests (`POST`/`PUT`/`DELETE`/`PATCH`) are rate limited per client IP over a
|
|
rolling window; reads are not limited. The limit and window are configurable by an
|
|
administrator (defaults: 60 requests per 60 seconds). When you exceed the limit you receive a
|
|
`429` whose `Retry-After` header gives the number of seconds to wait before retrying. The
|
|
OpenAI gateway (`/openai/...`) is exempt.
|
|
|
|
## Troubleshooting
|
|
|
|
**I get HTML back instead of JSON.** Send `Accept: application/json` (or
|
|
`Content-Type: application/json` on a body). A request is only served JSON when it asks for it
|
|
and does not also accept `text/html`; a normal browser navigation always gets HTML.
|
|
`X-Requested-With: fetch` is **not** a general JSON switch - it only applies to the legacy
|
|
engagement actions (votes, reactions, bookmarks, polls).
|
|
|
|
**An action returns `302` instead of the JSON envelope.** Same cause: the request did not ask
|
|
for JSON. Add the `Accept: application/json` header and the action returns
|
|
`{ "ok": true, "redirect": "...", "data": {...} }` instead of redirecting.
|
|
|
|
**A protected endpoint redirects me to the login page.** Browser-style (HTML) requests to a
|
|
`user`/`admin` endpoint without valid credentials are redirected to login; the same request
|
|
with `Accept: application/json` returns `401` instead. Non-admins calling an `admin` endpoint
|
|
get `403` (JSON) or a redirect to the feed (HTML).
|
|
|
|
**An upload is rejected with `413` or `415`.** `413` means the file exceeds the configured
|
|
size limit; `415` means the file type is not in the allowed list. Both limits are set by an
|
|
administrator.
|
|
""",
|
|
"endpoints": [],
|
|
}
|