# Integration (API reference) Route: `/integration`. Component: `IntegrationPage.dc.html`, mounted by `AppFrame`. The API keys sub-page is in `screens/integration-api-keys.md`. - **Visual reference:** `source/RenewCMS Screens Back-office.dc.html`, group "Integration", drawn by `source/IntegrationPage.dc.html` (open it in a browser; each state is drawn at desktop and mobile, with a light/dark toggle). ## Navigation decision Sticky section index on the right (desktop), "Jump to" select (mobile). A reference is read in one scroll and searched with the browser, so tabs would hide content and break find-in-page and deep links; the index keeps one page with anchors and shows position. ## Page copy - Header: breadcrumb "Integration", title "API reference", description "Everything a developer needs to build on RenewCMS." - Intro: "RenewCMS is headless: it stores your content and your own code displays it. There are two ways in. Public read-only access lets a website read online articles. API keys let a script or service manage articles, media and users without the back-office." - Base URL: label "Base URL", button "Copy" / "Copied" (2 s, announced). - Authentication, card "Public": "No key needed. Two read-only routes, and only online articles are returned." `GET /v1/article/`, `GET /v1/article/{id}`. - Card "API key": "Send `Authorization: Bearer rcms_…`. Requests act as the user who created the key, with that user's role (owner or admin). A key never has more rights than its creator." CodeBlock tabs curl, fetch (GET /v1/auth/me). "Keys start with `rcms_` and are shown in full once, when you create them." Link "Manage API keys". - Note 1: "A key can't create, list or revoke API keys, log in, register, verify or reset a password, or call anything under `/v1/system`. Those routes answer 403 "This action needs a real session." An invalid, unknown, expired or revoked key answers 401, even on public routes, and so does a key whose user was deleted or unverified." - Note 2: "Each key can make 120 requests per minute. Beyond that the API answers 429 with a `Retry-After` header." - Groups: Articles (7), Media (6), Users and invites (6), Settings (1), Session only (12 routes, 3 sub-groups; every `/v1/system` route is in this group). Session-only banner: "These routes need a signed-in browser session. With an API key they answer 403 "This action needs a real session."" - Errors: "Every error is a plain-text body with a single sentence. The status code is in the HTTP status line, and the Content-Type is text/plain." Table 400, 401, 403, 404, 429, 500 with the real messages below. Sample: a status line plus the text body (`HTTP/1.1 403 Forbidden`, `Content-Type: text/plain`, blank line, `This action needs a real session.`). - Notes: image URL prefix, CORS (existing copy), and "Article routes accept `?format=markdown`, `?format=html` or `?format=json` to choose how `body` is returned. With `json`, `body` is the stored JSON structure. Articles have `body_format: markdown` by default." ## EndpointCard states - Collapsed (44 px row): method badge, mono path, one-line description, chips, chevron. Desktop is one line; mobile is three (method and path, description, chips). - Expanded: "Request body" table (Name, Type, Required, Rule; "No request body." when empty), tabs fetch / curl / Response with one CodeBlock and Copy, then "Errors": status chip plus one-line caption (also its `title` tooltip). - Session only: dashed border, muted fill, lock icon, "Session only" chip, no method colour, no expand. - Chips: access "Public", "Verified user", "Owner only"; key "API key OK" (success soft) or "Session only" (dashed, muted). - Method badges: GET `--ui-bg-inverted`, POST success soft, PATCH warning soft, DELETE error soft. Existing tokens only. ## States Desktop 1440×900, mobile 390×844, light and dark. Names are matched exactly by the visual-check script. ### 1. Default ### 2. Group scrolled, one card expanded ### 3a. Copied: base URL ### 3b. Copied: CodeBlock ### 4. Authentication ### 5. Session only ### 6. Errors ### 7. Navigation ## Error messages Plain text, one sentence. Used in the table and in the per-endpoint error captions. - 400: "The request cannot be processed due to a mismatch in the format of the body." / "The expiry must be a date in the future." - 401: "You must be logged in to access this resource." - 403: "This action needs a real session." (session-only routes called with a key) / "Only the owner can do this." / "You must verify your account to access this resource." - 404: "Article not found" / "Image not found" / "Invite not found" / "API key not found" - 429: "Too many requests. Please try again later." A per-key limit also sends `Retry-After`. - 500: "Internal server error" - 409, only as examples under their endpoints: `POST /v1/user/invite` "This address already has an account."; `POST /v1/media/merge` "Some of the images to merge changed. Refresh and try again." There is no 409 or 422 row in the table. - 413 and 415 on `POST /v1/media/` keep their earlier plain captions; no real message is known. ## Request bodies - `POST /v1/article/`: `title` (string, required, 3 to 200 characters), `body` (string, required, max 10000). - `PATCH /v1/article/{id}`: `body` (string, required, max 10000). - `PATCH /v1/media/{id}` (rename): `filename` (string, required). - `POST /v1/media/merge`: `keep_id` (number, required, > 0), `merge_ids` (number[], required, at least 1, each > 0). - `POST /v1/media/`: multipart/form-data, 1 file field (shown as "multipart/form-data, 1 file field"). - `PATCH /v1/user/me`: `display_name` (string, required, max 60 characters). - `POST /v1/user/invite`: `email` (string, required, valid email). - `POST /v1/system/update/restart`: optional body `{ "kind": "update" | "restart" }`. ## Component inventory and Nuxt UI v4 - EndpointCard (extended): `UCollapsible` (trigger is a button with aria-expanded), `UBadge` (method, chips, status), `UTable` (body fields), `UTabs` (fetch, curl, Response), CodeBlock. - SectionIndex (new): custom `