Loading the quickstart…
HANK · Developers
API quickstart
Publish and integrate over the platform's REST API and MCP server. Looking for every endpoint and schema? Browse the full API reference.
HANK · Developers
Publish and integrate over the platform's REST API and MCP server. Looking for every endpoint and schema? Browse the full API reference.
Loading the quickstart…
The platform exposes two machine surfaces: a bearer-authenticated REST API at https://docs.hank.ai/api/v1 and a Model Context Protocol (MCP) server at https://docs.hank.ai/mcp. Both run the same publish pipeline, so anything you publish is secret- and PHI-scanned, link-checked, and routed through review exactly like a human edit. This page takes you from zero to your first call; the full API reference documents every endpoint, and Connect a publisher covers the product context.
Every call carries a Hank-issued API key with the hank-docs scope. Issue keys from the Hank Codes console and send the key as a bearer credential on every request:
Authorization: Bearer <your-hank-docs-token>A missing or malformed token returns 401 (unauthorized); a valid token without the hank-docs scope returns 403 (forbidden). A few operator-only routes additionally require operator privilege — most integrations never touch them.
GET https://docs.hank.ai/api/v1/topics returns a paginated list of topics. Page through it with ?limit and ?offset, and optionally filter by ?status= or ?sourceKey=.
curl https://docs.hank.ai/api/v1/topics?limit=20 \
-H "Authorization: Bearer $HANK_DOCS_TOKEN"import os
import requests
token = os.environ["HANK_DOCS_TOKEN"]
resp = requests.get(
"https://docs.hank.ai/api/v1/topics",
headers={"Authorization": f"Bearer {token}"},
params={"limit": 20, "offset": 0},
)
resp.raise_for_status()
page = resp.json()const token = process.env.HANK_DOCS_TOKEN;
const resp = await fetch("https://docs.hank.ai/api/v1/topics?limit=20", {
headers: { Authorization: "Bearer " + token },
});
const page = await resp.json();The response is a page envelope — read hasMore (and advance offset by limit) to walk every page:
{
"items": [],
"limit": 20,
"offset": 0,
"total": 128,
"hasMore": true
}POST https://docs.hank.ai/api/v1/topics/upsert is the idempotent publish path. Key it on a stable sourceKey: re-running the same upstream document updates the same topic instead of forking a duplicate. A brand-new sourceKey returns 201 Created; an update to an existing one returns 200 OK.
curl -X POST https://docs.hank.ai/api/v1/topics/upsert \
-H "Authorization: Bearer $HANK_DOCS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"manualId": "00000000-0000-0000-0000-000000000000",
"slug": "rate-limits",
"title": "Rate limits",
"excerpt": "How request quotas work.",
"bodyMdx": "# Rate limits — how request quotas work.",
"sourceKey": "kb/rate-limits",
"visibility": "public"
}'import os
import requests
token = os.environ["HANK_DOCS_TOKEN"]
topic = {
"manualId": "00000000-0000-0000-0000-000000000000",
"slug": "rate-limits",
"title": "Rate limits",
"excerpt": "How request quotas work.",
"bodyMdx": "# Rate limits — how request quotas work.",
"sourceKey": "kb/rate-limits",
"visibility": "public",
}
resp = requests.post(
"https://docs.hank.ai/api/v1/topics/upsert",
headers={"Authorization": f"Bearer {token}"},
json=topic,
)
print(resp.status_code) # 201 created, or 200 on an idempotent updateconst token = process.env.HANK_DOCS_TOKEN;
const resp = await fetch("https://docs.hank.ai/api/v1/topics/upsert", {
method: "POST",
headers: {
Authorization: "Bearer " + token,
"Content-Type": "application/json",
},
body: JSON.stringify({
manualId: "00000000-0000-0000-0000-000000000000",
slug: "rate-limits",
title: "Rate limits",
excerpt: "How request quotas work.",
bodyMdx: "# Rate limits — how request quotas work.",
sourceKey: "kb/rate-limits",
visibility: "public",
}),
});Every error response carries the same envelope: a human-readable error message and a stable machine code. Branch on the code — never on the message text, which can change.
{ "error": "manualId references a manual that does not exist", "code": "unknown_parent" }The full code catalog:
code | HTTP | When you get it |
|---|---|---|
invalid_request | 400 | The body, query, or path param failed validation. |
duplicate_slug | 400 | A unique slug or sourceKey collides with an existing topic. |
unknown_parent | 400 | A referenced product, manual, or chapter does not exist. |
content_rejected | 400 | The content failed the publish pipeline — fix the content, not the request shape. |
unauthorized | 401 | Missing, malformed, or unrecognized bearer token. |
forbidden | 403 | A valid token lacking the hank-docs scope or the required privilege. |
not_found | 404 | The addressed resource does not exist. |
payload_too_large | 413 | The request body exceeds the size cap. |
rate_limited | 429 | The write rate limit was exceeded — see Retry-After. |
Write responses carry X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (epoch seconds). Exceed the quota and the API returns 429 (rate_limited) with a Retry-After header — wait that many seconds, then retry. GET reads are not write-rate-limited.
import time
import requests
def upsert_with_backoff(topic, token, attempts=5):
delay = 1.0
for _ in range(attempts):
resp = requests.post(
"https://docs.hank.ai/api/v1/topics/upsert",
headers={"Authorization": f"Bearer {token}"},
json=topic,
)
if resp.status_code != 429:
return resp
# Honor Retry-After when present; otherwise back off exponentially.
wait = float(resp.headers.get("Retry-After", delay))
time.sleep(wait)
delay *= 2
raise RuntimeError("still rate limited after retries")The MCP server at https://docs.hank.ai/mcp speaks JSON-RPC 2.0 over HTTP. The handshake is initialize → tools/list → tools/call.
Public read tools: search_docs, list_docs, get_page, get_section, list_changelog, list_api_specs, and get_openapi. The write tool is upsert_topic (the same pipeline and force-pend as the REST upsert). Org-scoped ticket tools — list_my_tickets, get_ticket, and search_tickets — read only your own organization’s tickets.
curl -X POST https://docs.hank.ai/mcp \
-H "Authorization: Bearer $HANK_DOCS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {} }'curl -X POST https://docs.hank.ai/mcp \
-H "Authorization: Bearer $HANK_DOCS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": { "name": "search_docs", "arguments": { "query": "rate limits" } }
}'