Files
RenewCMS/docs/design/screens/integration.md
T

14 KiB
Raw Blame History

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: RenewCMS Screens Back-office.dc.html, group "Integration".

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", a CodeSnippet with one tab ("Base URL") and the 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." CodeSnippet 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. The 403 row has a note line under it: "Site SEO defaults are owner only. Article SEO fields need only the right to edit the article." (12/18, --ui-text-muted, in the cause column, 2 px under the cause text). It is the only row with a note; rows below it move down by the wrapped note height (two 18 px lines on desktop, three on mobile, plus 2 px). 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 in one CodeSnippet with 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. 1 Default · 2 Media group, POST /v1/media/ expanded with curl tab · 3a Base URL copied · 3b CodeSnippet copied · 4 Authentication · 5 Session only · 6 Errors · 7 Navigation (index active item Media; mobile select open).

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." Image requests (/media/photo.png?v=) answer 400 with a JSON body (status, error, message, detail) in two cases: { "status": 400, "error": "unknown_variant", "message": "Unknown variant", "detail": "unknown" } for an unknown or disabled variant, and { "status": 400, "error": "unsupported_parameter", "message": "Unsupported parameter", "detail": "width" } for any other query parameter (width, height, format, quality). Both are rows of the "Responses" table in "Images and variants".
  • 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." Note under the 403 row: "Site SEO defaults are owner only. Article SEO fields need only the right to edit the article." The inline parenthetical that said the same thing in the row's cause text was removed to avoid saying it twice.
  • 404: "Article not found" / "Image not found" / "Invite not found" / "API key not found" / site_not_found (GET /v1/site/{key}/sitemap only; body is the code, not a sentence)
  • 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, optional, no length limit).
  • PATCH /v1/article/{id}: body (string, optional, no length limit).
  • 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), CodeSnippet (fetch, curl, Response). CodeSnippet is the only code block in the product: Integration, Integration images, API keys (one-time key) all use it. Selected tab: 2 px --ui-primary underline. Syntax colouring was dropped so there is one rendering.
  • SectionIndex (new): custom <nav> of anchor buttons with aria-current; sticky, scroll-spy. Mobile: USelectMenu labelled "Jump to".
  • SessionOnlyGroup (new): UAlert-style info note plus list rows; no new colour.
  • ErrorTable: UTable. Info notes: existing note style (or UAlert variant naked).

Assumptions to confirm

  • Paths not in the brief: /v1/settings, /v1/system/update/*, POST /v1/auth/register|verify|logout.
  • [to confirm] The multipart field name on POST /v1/media/ is written as file as a placeholder.
  • Response field names in the samples (for example articlesUpdated, display_name in the profile response) are illustrative.
  • Code samples use a simple keyword and string colouring.

Tokens

No new tokens. Foundations check: colours, radius (0) and spacing come from existing --ui-* and --rc-* values; every control has a 2 px primary focus ring; index, select and cards are keyboard operable. Open question: the board hard-codes a few pixel sizes, as the other screens do.

Copy per state (generated from the board source, pass 2)

Every state below is drawn in light and dark (theme switch on the board), at 1440×900 and 390×844 (both frames sit side by side). Board: RenewCMS Screens Back-office.dc.html. Copy is exact; where a cell is empty the state reuses the fixed copy.

Integration: API reference (group "Integration", 8 states)

Route: /integration. API: none (static). Replaces "Connect your website"..

Navigation: a sticky section index on the right on desktop, and a "Jump to" select on mobile. A long reference is read in one scroll and searched with the browser, so tabs would hide content and break find-in-page; the index keeps every group on one page, links to anchors and shows where you are. Written for a developer. The base URL is window.location.origin. The API keys screen has its own board.

# State Trigger Copy
1 1. Default Opened from the sidebar or the Home card. Every endpoint card is collapsed. Desktop shows the sticky index on the right; mobile shows the "Jump to" select above the content. "Integration"
2 2. Group scrolled, one card expanded POST /v1/media/ opened in the Media group: multipart body with 1 file field, curl tab shown. Only one example block is visible per card. Other cards stay collapsed. "Integration"
3 3a. Copied: base URL The base URL was copied. The button reads "Copied" for 2 s and this is announced. "Integration"
4 3b. Copied: CodeBlock The Copy button in the API key example after a click. Back to "Copy" after 2 s. "Integration"
5 4. Authentication The API key card with its example, the "Manage API keys" link (goes to the API keys board) and the two notes. "Integration"
6 5. Session only Routes API keys cannot call. Dashed, muted and locked, so they read as unavailable and not as an error. "Integration"
7 6. Errors Status table and the plain-text error response (status line plus a one-sentence body). Key-specific causes are in the 401, 403 and 429 rows. "Integration"
8 7. Navigation Desktop: the sticky index with the active item (Media). Mobile: the "Jump to" select open. Keyboard: the index buttons and the select are in the tab order, Enter jumps, Escape closes the list. "Integration"

Integration: images and ?v= (group "Integration: images", 9 states)

Route: /integration#images. API: GET /v1/image-variants (existing).

New section "Images and variants" on the Integration page, after Media and before Errors. It explains the ?v= parameter, shows one CodeSnippet with curl, HTML and JavaScript tabs, a table of enabled variants, a URL builder and the response codes. When there are no variants, admins see a link to /settings/media/variants and other users see plain information. A removed variant is not replaced by the original: the request returns 400 with the body shown in the builder.

# State Trigger Copy
1 Many variants Twelve enabled variants. The table scrolls at 264 px. On mobile the table and snippet scroll horizontally.
2 One variant A single enabled variant. The snippets use its name.
3 No variants, admin Empty state with a link to /settings/media/variants.
4 No variants, not an admin Plain information, no link.
5 Variant removed The builder holds "hero", which was deleted. Warning with the exact 400 body.
6 Loading Skeleton blocks while variants load. Snippets are hidden until the list is known.
7 Error loading 500 or network failure. Snippets still render with the placeholder name.
8 Snippet copied Copy pressed on the snippet. The label reads Copied for 2 s and is announced politely.
9 URL builder copied Copy pressed on the built URL.

Pass 2 notes

  • The base URL field and the image URL builder output are CodeSnippets with a single tab.
  • Page padding 24 px (16 px on mobile), page title 20/28, section headings 18/26, card titles 15/24, sub headings 14/22 (foundations.md).

Search and sharing (SEO round)

New group "Search and sharing" after Articles, in the sidebar index and the mobile Jump to select. Two cards in the existing endpoint style (collapsed, method badge, auth badge "Public", example, 401 and 404 rows, copy buttons):

  • GET /v1/article/{id}/seo: the resolved SEO object. Fields: title, description, canonical_url, robots, og{type,title,description,url,site_name,image,image_alt,locale}, twitter{card,title,description,image,image_alt}, image{url,alt,source}, authors[{name,url,same_as}], published_at, modified_at, fallbacks, json_ld. Drafts answer 404 to anonymous callers.
  • GET /v1/site/{key}/sitemap?page=&size=: yes, shown per site by key. Items: { url, modified_at, noindex }. Total in X-Total-Count. Sizes 25, 50, 100, 500, default 100. Errors: 400 "The request cannot be processed due to a mismatch in the format of the body." (same text as the other routes; bad page or size), 401, 404 site_not_found (unknown site key; this is the body the API returns, shown as a code and not rewritten as prose). PATCH /v1/article/{id} lists the new optional fields (slug, excerpt, seo_title, seo_description, cover_image_id, cover_image_alt, social_title, social_description, social_image_id, canonical_url, noindex, seo_dismissed). The 403 row of the HTTP status table adds: site SEO defaults are owner only; article SEO fields need only the right to edit the article.