API reference
Declare an endpoint once — method, path, fields, one request — and the kit renders the badge, the tables, and a code tab per client. Base URL, auth, and the client set are config.
The API-reference kit#
Six components compose a full endpoint reference — no hand-rolled badges or per-language heredocs.
An API-reference page is the same page you'd write for anything else — a Phlex class of DocsUI::Sections (see Authoring pages). What's different is the vocabulary you reach for inside #content:
DocsUI::Endpoint— a verb badge + monospace path, inline;DocsUI::FieldTable/DocsUI::ErrorTable— schema-driven tables;DocsUI::RequestExample— one request declaration → one code tab per client;DocsUI::JsonResponse— a Ruby Hash rendered as pretty JSON.
Three config knobs — api_base_url, api_auth_header, api_clients — feed every RequestExample, so a snippet is authored once and points at the right host with the right auth. The Components reference lists every arg; this page walks the workflow.
Endpoint — the method + path line#
A colour-coded verb badge and a monospace path, rendered inline.
DocsUI::Endpoint.new(method, path) renders an HTTP method badge coloured per verb — GET success, POST primary, PUT/PATCH warning, DELETE error, anything else neutral — followed by the path in a <code>. It renders inline (no wrapper), on purpose: drop it straight into a Section's description:.
DocsUI::Section("List customers",
description: DocsUI::Endpoint.new(:get, "/v1/customers")) do
# fields, request example, response…
endThe badge live, for each verb:
GET /v1/customersPOST /v1/customersPATCH /v1/customers/:idDELETE /v1/customers/:id:trace) degrades to a neutral badge rather than raising.FieldTable & ErrorTable — the schemas#
Keyword-schema presets over DocsUI::Table for request fields and endpoint errors.
DocsUI::FieldTable takes an Array of field Hashes and renders Name / Type / Required / Description. name is auto code-styled, required: defaults to false (a ✓ when true, the canonical — when not), and the description cell follows DocsUI::Table's convention — a plain String is escaped text, [:code, "x"] is inline code, [:md, "…"] is inline Markdown.
render DocsUI::FieldTable.new([
{ name: "email", type: "string", required: true,
description: "The customer's email address." },
{ name: "metadata", type: "object",
description: [:md, "Up to 50 keys, e.g. `plan: pro`."] }
])DocsUI::ErrorTable renders Scenario / Status / Type — plus a Param column, but only when at least one error names a param:. An endpoint whose errors are all param-free renders a clean three-column table; when the column IS shown, a param-free row gets the em-dash. type and param are auto code-styled.
render DocsUI::ErrorTable.new([
{ scenario: "Missing or invalid API key", status: "401",
type: "authentication_error" },
{ scenario: "Email already taken", status: "422",
type: "validation_error", param: "email" }
])— placeholder is the kit's ONE canonical "no value" glyph (an em-dash, never an ASCII hyphen) — both tables share it, so a page never types a stray -.RequestExample — one declaration, every client#
Declare method/path/body once; get a syntax-highlighted tab per configured client.
DocsUI::RequestExample is the payoff. Declare the request once and it renders one code tab per configured client — curl, javascript, ruby, python by default — wrapped in a DocsUI::Example, so the reader's sticky global language choice persists across every endpoint on the site.
render DocsUI::RequestExample.new(
method: :post,
path: "/v1/customers",
body: { email: "[email protected]", name: "Ada Lovelace" }
)Under the hood each tab is fed a DocsKit::ApiRequest — an immutable value object carrying method/path/url/query/headers/body with display helpers (#http_method, #body?, #pretty_body_json, #url_with_query). Every shipped template guards its payload lines on #body?, so a body-less GET emits no dangling -d / json= / request.body = line.
| Option | Type | Default | Description |
|---|---|---|---|
method: / path: | Symbol/String, String | — | The verb and path; path is appended to c.api_base_url. |
body: | Hash, String, nil | nil | Payload — deep-stringified into each snippet. Omit for a GET. |
query: | Hash | {} | Query params, URL-encoded onto every snippet's URL. |
headers: | Hash | {} | Extra headers, merged over the config auth header (explicit wins). |
clients: | Array<Symbol>, nil | all configured | Filter AND order the tabs, e.g. %i[curl ruby]. |
DocsUI::Example needs at least two. When demoing one custom client, pair it (e.g. clients: %i[cli curl]). An unknown token in clients: is silently skipped — a typo yields fewer tabs, never a raise.Configure the base URL & auth#
Two knobs point every snippet at your real host with a real auth line.
RequestExample reads three config knobs. Set them once in the initializer (see Configuration) and every endpoint inherits them.
c.api_base_url— prefixed onto everypath:. Defaults to the neutralhttps://api.example.com.c.api_auth_header— an exampleAuthorizationline merged into every snippet. Defaults tonil→ no auth line (clean snippets for a no-auth API). It's split on the first colon into a{ name => value }header, merged under any explicitheaders:you pass (so your explicit header wins).c.api_clients— override or extend the tab set (next section).
DocsKit.configure do |c|
c.api_base_url = "https://api.acme.com"
c.api_auth_header = "Authorization: Bearer sk_live_..."
end| Option | Type | Default | Description |
|---|---|---|---|
c.api_base_url | String | "https://api.example.com" | Host prefixed onto every RequestExample path. |
c.api_auth_header | String, nil | nil | Example Authorization line merged into every snippet. |
c.api_clients | Hash | {} | Overrides/extensions merged over the four defaults (writer only). |
Custom clients — SDK-flavored tabs#
Replace a default tab with an SDK snippet, or append your own (a cli tab).
The gem ships generic HTTP snippets because it can't know your SDK. A DocsKit::ApiClient describes one tab — a label, a Rouge lexer, an optional filename (a String or a (request) -> String proc), and a template: a (DocsKit::ApiRequest) -> String callable that renders the snippet.
Set c.api_clients to a { token => ApiClient } Hash. It merges over the four defaults (curl, javascript, ruby, python): reusing a default token replaces that tab with an SDK-flavored one; a new token appends a tab. Hash merge preserves order — reused tokens keep their slot, new ones append in declaration order.
DocsKit.configure do |c|
c.api_clients = {
# Replace the generic Ruby tab with the SDK flavour:
ruby: DocsKit::ApiClient.new(
label: "Ruby", lexer: :ruby, filename: "acme.rb",
template: ->(req) { %(Acme.#{req.http_method.downcase}("#{req.path}")) }
),
# Append a brand-new CLI tab:
cli: DocsKit::ApiClient.new(
label: "Acme CLI", lexer: :shell, filename: "cli.sh",
template: ->(req) { "acme #{req.http_method.downcase} #{req.path}" }
)
}
endDocsKit.configuration.api_clients (which merges over the defaults), never the raw ivar. Because it merges, setting one cli: client yields FIVE tabs, not one. Config can't remove a default tab — use RequestExample's clients: to select a subset.A full endpoint, live#
POST /v1/customers
Everything above, composed — an Endpoint description, a FieldTable, an ErrorTable, a RequestExample (four real client tabs, on THIS site's config), and a JsonResponse. This is the real kit rendering, not a screenshot:
| Name | Type | Required | Description |
|---|---|---|---|
email | string | ✓ | The customer's email address. |
name | string | — | The customer's full name. |
metadata | object | — | Up to 50 key/value pairs, e.g. plan: pro. |
| Scenario | Status | Type | Param |
|---|---|---|---|
| Missing or invalid API key | 401 | authentication_error | — |
| Email already registered | 422 | validation_error | email |
Try it — one declaration renders every client tab:
curl -X POST 'https://api.example.com/v1/customers' \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"name": "Ada Lovelace",
"metadata": {
"plan": "pro"
}
}'const response = await fetch("https://api.example.com/v1/customers", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({"email":"[email protected]","name":"Ada Lovelace","metadata":{"plan":"pro"}}),
});
const data = await response.json();require "net/http"
require "json"
uri = URI("https://api.example.com/v1/customers")
request = Net::HTTP::Post.new(uri)
request["Content-Type"] = "application/json"
request.body = {
"email": "[email protected]",
"name": "Ada Lovelace",
"metadata": {
"plan": "pro"
}
}.to_json
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
http.request(request)
endimport requests
response = requests.post(
"https://api.example.com/v1/customers",
headers={"Content-Type": "application/json"}, json={
"email": "[email protected]",
"name": "Ada Lovelace",
"metadata": {
"plan": "pro"
}
},
)
data = response.json()A successful response:
{
"id": "cus_1a2b3c",
"object": "customer",
"email": "[email protected]",
"name": "Ada Lovelace",
"metadata": {
"plan": "pro"
},
"created": 1720000000
}The calls that produced the block above:
DocsUI::Section("Create a customer",
description: DocsUI::Endpoint.new(:post, "/v1/customers")) do
render DocsUI::FieldTable.new([
{ name: "email", type: "string", required: true,
description: "The customer's email address." }
])
render DocsUI::ErrorTable.new([
{ scenario: "Email already registered", status: "422",
type: "validation_error", param: "email" }
])
render DocsUI::RequestExample.new(
method: :post, path: "/v1/customers",
body: { email: "[email protected]", name: "Ada Lovelace" }
)
render DocsUI::JsonResponse.new(
{ id: "cus_1a2b3c", object: "customer", email: "[email protected]" }
)
endmd prose used throughout this page.