# Every page and every form

The complete route list, what the header and footer link to, and every form on the site with the fields it collects and where they go.

**What this covers:** Navigation — the header and the footer; Every route on the site; Every form, and where it posts; The account surfaces.

**Assumed knowledge:** Nothing.

**Canonical HTML:** https://jobopportunitiesapi.org/docs/website/pages  
**Machine-readable index:** https://jobopportunitiesapi.org/docs/ai/index.md  
**Last verified:** 2026-08-22  
**Superseded by:** the live API at https://api.jobopportunitiesapi.org and its spec at https://jobopportunitiesapi.org/openapi.json — where this file and the API disagree, the API is right.

---

<a id="site-nav"></a>

## 1. Navigation — the header and the footer

Five links in the header plus an account slot; four columns in the footer covering everything the header cannot.

### 1.1 In depth

| Header link | Goes to | What it is |
| --- | --- | --- |
| **Job Openings** | `/` | The ledger browser. |
| **Companies** | `/companies` | The employer directory. |
| **Docs** | `/docs` | This documentation. |
| **Facts** | `/facts` | Checkable claims about the data. |
| **API Access** | `/api` | The API sales page: what it is, what it costs. |
| The account slot | `/login` or `/dashboard` | Rendered on the client from the live session, so an edge-cached page never bakes in a signed-in state. |

The account slot is the one piece of the header that is not a fixed link. It used to be, and looked identical signed in or out — which is what “the site does not remember me” actually was, on a session that lasts thirty days. When signed in it shows a green dot and the address; the address is in the link text and the title attribute, so the state does not depend on colour alone.

### 1.2 Exact contract

| Footer column | Links |
| --- | --- |
| **The ledger** | Browse openings · Companies · Add a listing · How this works |
| **API** | Guides & examples · Documentation & plans · Pricing · Data coverage · Provenance · Status · OpenAPI spec · Get a key · My account · Sign in |
| **Check our work** | Facts · Newsletter · Contact · `support@jobopportunitiesapi.org` |
| **Legal** | Privacy · Cookies · AI disclosure · Cookie preferences · Terms · Corrections & takedowns · Remove my website |

Two footer entries are not ordinary links. **Cookie preferences** opens the consent manager rather than navigating. The support address is a `mailto:` — a findable address, not only a form, because a contact form alone does not satisfy someone who wants somewhere to write.

The footer base carries three fixed items: the line *Every listing here comes from the employer or a government feed*, the payment provider's mark (who handles the money), and a link to the parent product.

**See also**

- [Every route on the site](./website-pages.md#site-marketing-pages)
- [The legal pages](./website-legal.md#site-legal-pages)
- [Telling us a page is wrong](./conventions.md#feedback-loop)

<a id="site-marketing-pages"></a>

## 2. Every route on the site

Twenty-odd public pages, four account pages, an admin console, and the machine-readable files. This is all of them.

### 2.1 In depth

| Route | What it is |
| --- | --- |
| `/` | The ledger browser. [Detail](./website-ledger.md#site-home). |
| `/companies` | Employer directory. |
| `/company/<slug>` | One employer and their open roles. |
| `/job/<slug>` | One listing. [Detail](./website-ledger.md#site-job-page). |
| `/docs` | This documentation. `/guides` redirects here. |
| `/docs/ai/*.md` | The machine-readable mirror. |
| `/api` | The API sales page: pitch, quick reference, plans. |
| `/pricing` | The price list, rendered from the live plans table. |
| `/checkout` | Buy a plan. `/checkout/done` and `/checkout/failed` are its outcomes. |
| `/register` | How to get a key; routes to `/login` for the free one, and to a grant application. |
| `/login` | Passwordless sign-in. |
| `/auth/verify` | Consumes the magic-link token. Not a page you visit directly. |
| `/dashboard` | Your keys. [Detail](./account-sign-in.md#dashboard-tour). |
| `/coverage` | The coverage report, rendered. |
| `/coverage/countries` | Per-country coverage. |
| `/provenance` | Where rows come from, and the sources we refuse. |
| `/facts` | Checkable claims, each with the endpoint that proves it. |
| `/compare` | How this compares with the alternatives. |
| `/about` | How the ledger works. |
| `/status` | Is the data current. [Detail](./account-billing.md#status-page). |
| `/post` | Employer listing submission form. |
| `/optout` | Site-owner removal. [Detail](./ledger-optout.md#optout-how). |
| `/contact` | Contact form, topic-aware. |
| `/newsletter` | Newsletter subscription. |
| `/confirm` | Confirms a newsletter subscription from an emailed link. |
| `/unsubscribe` | Unsubscribes, from an emailed link. |
| `/invoice/<token>` | A customer's invoice, reached from an email. No session needed. |
| `/privacy`, `/cookies`, `/terms`, `/ai` | Legal. [Detail](./website-legal.md). |
| `/admin/*` | Operator console. Not for customers; separately authenticated. |
| `/openapi.json`, `/openapi.yaml` | The machine contract. |
| `/llms.txt` | Short doorway for machine readers. |
| `/robots.txt`, `/sitemap.xml`, `/sitemaps/<kind>/<page>` | Crawler files. |

### 2.2 Exact contract

`/api` and `/docs` are deliberately different pages for different moments. `/api` is the pitch and the price list — “can this do the thing, and what does it cost”. `/docs` is the reference — “how exactly does it work”. They cross-link and do not duplicate; the full parameter reference lives here, not there.

> **`/api` the page is not `api.` the host** — `https://jobopportunitiesapi.org/api` is a marketing page. `https://api.jobopportunitiesapi.org` is the API. It is an unfortunate collision and it is documented rather than renamed, because the page's URL has been linked to from outside. [Hosts](./api-overview.md#hosts).

`/admin/*` is the operator console — ads, donations, keys, messages, providers, submissions — behind its own authentication and deliberately plain. It is listed here for completeness, not as something to use. Its nav entry is coloured differently from every other nav item precisely so it cannot be reached by accident or mistaken for a customer surface.

**See also**

- [Every form, and where it posts](./website-pages.md#site-forms)
- [The account surfaces](./website-pages.md#site-account-pages)
- [Two hosts, and which one to send requests to](./api-overview.md#hosts)

<a id="site-forms"></a>

## 3. Every form, and where it posts

Six public forms. Each posts to one of the site's own JSON routes, which forwards to the API; most are protected by a bot check.

### 3.1 In depth

| Form | Fields | Posts to | Protection |
| --- | --- | --- | --- |
| **Search** (home) | `q`, plus every active filter as hidden fields | `GET /` — it is a link, not a POST | none needed |
| **Sign in** (`/login`) | email | `/api/auth/request` → `POST /public/auth/request` | rate limited |
| **Add a listing** (`/post`) | title, company, category, city, country, remote, employment type, salary, apply URL, contact email, description, plus a hidden honeypot | `/api/submit` → `POST /public/submissions` | Turnstile + honeypot |
| **Contact** (`/contact`) | topic, email, name, message, hidden honeypot | `/api/contact` → `POST /public/contact` | Turnstile + honeypot |
| **Newsletter** (`/newsletter`) | email, topic checkboxes | `/api/subscribe` → `POST /public/subscribe` | Turnstile |
| **Remove my website** (`/optout`) | domain, email at that domain; then a six-digit code | `/api/optout` → `POST /public/optout/request` then `/verify` | domain-verified code |
| **Checkout** (`/checkout`) | plan, name, email, country, VAT number | `/api/checkout` → `POST /public/checkout` | Turnstile |
| **Documentation feedback** (every `/docs` page) | up or down; a comment box on down | `/api/docs-feedback` | one vote per page per browser |

The honeypot is an off-screen field named `website` with `tabIndex={-1}` and autocomplete off. A human never fills it; a naive bot does, and the submission is discarded.

### 3.2 Exact contract

The `/api/*` routes are the **website's own** JSON endpoints, not part of the public API, and they exist so that the browser never talks to the API host directly — the API base URL stays internal and the forms inherit the site's own edge protections. They are excluded from crawling in `robots.txt` and they are explicitly allowlisted at the edge, because a bot challenge in front of an XHR is a challenge no XHR can solve. That was once true of `/api/checkout`, and the consequence was that the buy button could not work.

- **Turnstile** — A privacy-preserving bot check. It runs in the browser; the token is verified server-side before the submission is accepted.
- **The opt-out code** — Six digits, cryptographically random, stored only as a domain-salted hash. Five attempts, thirty-minute expiry, two-minute resend window.
- **Failure behaviour** — Every form reports the API's own message rather than a generic one, and none of them clears what you typed on failure.

**See also**

- [Removing your site](./ledger-optout.md#optout-how)
- [Checkout](./account-billing.md#checkout-flow)
- [Telling us a page is wrong](./conventions.md#feedback-loop)

<a id="site-account-pages"></a>

## 4. The account surfaces

Four routes: sign in, verify, dashboard, and the register page that explains the options before you commit to one.

### 4.1 In depth

- **`/register`** — Explains the free key and the grant key and routes to each. **Start — get a free key →** goes to `/login`; **Apply for a grant key** goes to `/contact?topic=grant`.
- **`/login`** — One email field and one button. Passwordless.
- **`/auth/verify`** — Consumes the one-time token from the email and sets the session cookie. Not visited directly.
- **`/dashboard`** — Your keys, their limits, their usage, and the rotate / cancel controls. Signed out, it offers a sign-in link and a **Check a key** box that reports one key's status from its secret.

All four are explicitly exempt from the site's bot challenge. A challenge on `/login` is friction on the only funnel there is; a challenge on `/auth/verify` silently breaks every emailed link, because mail clients and link scanners do not solve interstitials.

### 4.2 Exact contract

The dashboard is rendered per request rather than cached, because it is per customer. Everything on it is described control by control in [the dashboard tour](./account-sign-in.md#dashboard-tour).

> **There is no password anywhere on this site** — Not on the customer side. There is no password field, no reset flow and no password store — so there is nothing of that kind to be leaked in a breach.

**See also**

- [Signing in](./account-sign-in.md#signin-magic-link)
- [Every control on the dashboard](./account-sign-in.md#dashboard-tour)
- [Creating, naming, rotating and revoking](./api-authentication.md#auth-keys-lifecycle)

---

## Where to go next

This file is part of **The website**. Others in the same group:

- [The ledger browser](./website-ledger.md) — Every control on the home page, the job page and the company pages — what each one sends to the API, and what each label on a row actually asserts.
- [Legal, cookies and crawlers](./website-legal.md) — The legal pages and what each covers, exactly what this site stores in your browser, the AI disclosure, and the crawler policy — including where it differs by bot.

Always useful:

- [index.md](./index.md) — the map of every file here
- [BUILD-A-SITE.md](./BUILD-A-SITE.md) — the paste-whole brief for building against this API
- [quickstart.md](./quickstart.md) — zero to a first authenticated response
- [api-errors.md](./api-errors.md) — every status code and whether to retry it
