# Polyglot language API

Instructions for adding a read-only HTTP API in another language that the Elixir site can rotate onto.

The **source of truth** for routes and payloads is [`openapi.yaml`](openapi.yaml) in this directory. If you change the public contract, update that file (and this page will follow). Do **not** implement Ash JSON:API (`application/vnd.api+json`); siblings speak ordinary JSON over the v1 REST + SQL-view contract.

## Purpose

The Phoenix app (`Carolina.Polyglot`) keeps **at most one** language API warm and reads speakers/sponsors from it. With no APIs registered, it falls back to Ash. Your process must:

1. Query PostgreSQL **v1 views**, never Ash resource tables.
2. Expose the routes in `openapi.yaml`.
3. **Register once on boot** with the Elixir site (no heartbeat).

## Environment

| Variable | Example | Role |
|---|---|---|
| `DATABASE_URL` | `postgres://postgres:postgres@127.0.0.1:5432/carolina_dev` | SQL views |
| `CAROLINA_URL` | `http://127.0.0.1:4000` | Elixir site |
| `POLYGLOT_REGISTER_TOKEN` | `dev` | Bearer token for register |
| `PUBLIC_BASE_URL` | `http://127.0.0.1:4001` | URL Elixir will call |
| `PORT` | `4001` | Listen port |

## SQL views (query these)

`v1_speakers`, `v1_sponsors`, `v1_years`, `v1_talks`, `v1_sponsorships`, `v1_year_speakers`, `v1_year_sponsors`.

Year-scoped listing filters need `languages` and `topics` on `v1_talks` (and year speaker rows).

## Required HTTP routes

Wrap list payloads as `{ "data": [ ... ] }` unless noted. See `openapi.yaml` for examples.

- `GET /health` — liveness (`{ "status": "ok" }`)
- `GET /` — identity (language, framework, api_version, endpoints)
- `GET /v1/years`
- `GET /v1/speakers` and `GET /v1/speakers?year=2026`
- `GET /v1/speakers/{slug}` and `GET /v1/speakers/{year}/{slug}`
- `GET /v1/sponsors` and `GET /v1/sponsors?year=2026`
- `GET /v1/sponsors/{slug}` and `GET /v1/sponsors/{year}/{slug}`

Year-scoped detail examples: `/v1/speakers/2026/diana-pham`, `/v1/sponsors/2026/flywheel`.

## Register on boot (once)

`POST {CAROLINA_URL}/internal/api-endpoints/register`

```
Authorization: Bearer {POLYGLOT_REGISTER_TOKEN}
Content-Type: application/json
```

Body fields: `language`, `language_version`, `api_version`, `framework`, `created_year`, `base_url` (`PUBLIC_BASE_URL`), `schema_version` (1), `endpoints` (list of `"GET /path"` strings).

Do not heartbeat. Elixir keep-alives the currently warm API.

## Checklist

- [ ] All OpenAPI paths return 200 with example-shaped JSON (404 on unknown slug)
- [ ] `?year=` listing rows include `languages` / `topics` (speakers) and `tier` (sponsors)
- [ ] Register runs once at process start against a **live** Elixir site
- [ ] No writes; no Ash table names
