Reference
API reference
A small REST API over HTTPS. JSON in, JSON out, conventional status codes. The base URL is https://api.docsmithhq.com/v1.
Authentication
Create a key in Settings → API keys. Keys are shown once. Send it as a bearer token on every request.
curl https://api.docsmithhq.com/v1/repositories \
-H "Authorization: Bearer dsk_live_7Kx2mQ..."Keys are scoped to an organisation and carry the plan limits of that organisation. A key can be restricted to specific repositories when it is created. Rotate by creating a second key, deploying, then revoking the first — revocation takes effect within five seconds.
POST /v1/sync
The endpoint the whole product is built on. Analyse one change against one documentation page.
Request
| Field | Type | Notes |
|---|---|---|
diff | string, required | A unified diff, or plain before/after text. Maximum 24,000 characters. |
doc | string, required | The documentation page as Markdown or MDX. Maximum 40,000 characters. |
docPath | string, optional | Used for attribution in the response and the audit log. |
repository | string, optional | Free text, e.g. acme/checkout-sdk. |
confidenceFloor | number, optional | 0–1. Findings below this are not returned. Defaults to the organisation setting. |
Response
{
"id": "syn_9f31c0a7b4",
"verdict": "stale",
"changeSummary": "Lowered the default attempt count from 5 to 3.",
"headline": "Two passages on this page state the old defaults.",
"findings": [
{
"section": "Defaults",
"quote": "By default the client makes up to 5 attempts",
"why": "DEFAULT_ATTEMPTS moved from 5 to 3 in this change.",
"severity": "breaking",
"confidence": 0.94,
"verified": true
}
],
"reasoning": [
"Read the change: 9 lines removed, 12 added.",
"Dropped 1 finding whose quoted passage could not be located verbatim."
],
"draft": "# Retries\n\nThe checkout client retries...",
"diff": [
{ "type": "del", "oldNum": 12, "newNum": null, "text": "...up to 5 attempts" },
{ "type": "add", "oldNum": null, "newNum": 12, "text": "...up to 3 attempts" }
],
"stats": { "added": 3, "removed": 3 },
"model": "gpt-4.1",
"provider": "openai",
"ms": 6120
}verdict is current when nothing needed changing. In that case findings and diff are empty arrays and draft is the page exactly as you sent it.
severity is one of breaking, misleading or incomplete. verified is always true in a response — unverifiable findings are dropped server-side and counted in reasoning.
GET /v1/repositories
{
"data": [
{
"id": "rep_4c1e88",
"name": "acme/checkout-sdk",
"host": "github",
"paths": ["docs/**/*.md", "README.md"],
"pagesWatched": 41,
"indexedAt": "2026-07-28T09:14:22Z",
"deliveryMode": "review_queue"
}
],
"hasMore": false
}GET /v1/drafts
List drafts awaiting review. Filter with ?status=pending, ?repository=rep_4c1e88 or ?since=2026-07-01T00:00:00Z. Paginate with ?cursor= and read hasMore.
POST /v1/drafts/{id}/approve
POST /v1/drafts/{id}/reject { "reason": "Page is correct; the change was internal." }Approving a draft on a repository in pull-request mode opens the pull request synchronously and returns its URL.
Idempotency
Send an Idempotency-Key header on any POST. A repeated key within 24 hours returns the original response without running a second sync or consuming a second unit of quota. Use it whenever you call the API from a job that might retry.
curl https://api.docsmithhq.com/v1/sync \
-H "Authorization: Bearer $DOCSMITH_API_KEY" \
-H "Idempotency-Key: 2026-07-30-a4f19c2-retries-md" \
-H "Content-Type: application/json" \
-d @payload.jsonErrors
Errors carry a machine-readable code and a message written for a human reading a log at 2am.
{
"error": {
"code": "quota_exhausted",
"message": "You have used all 30 syncs on the Free plan this month. The counter resets on 1 August.",
"resetsOn": "2026-08-01T00:00:00Z"
}
}| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | A field is missing, malformed, or over its size limit. |
| 401 | unauthenticated | Missing, malformed or revoked API key. |
| 403 | forbidden | The key is not scoped to this repository. |
| 402 | quota_exhausted | Monthly sync limit reached. Never billed as overage. |
| 429 | rate_limited | Too many requests. Honour the Retry-After header. |
| 502 | provider_unavailable | Every routed model failed. The sync is not counted against your quota. Retry. |
| 503 | maintenance | Planned maintenance. Announced 72 hours ahead on the status page. |
Rate limits
60 requests an hour on Free, 600 on Team, 3,000 on Business, per organisation rather than per key. Every response carries the current state:
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 583
X-RateLimit-Reset: 1785412800A 429 always carries Retry-After in seconds. Back off exponentially from it rather than polling — we would rather you succeeded on the second attempt than hammered the first.
Versioning
The version is in the path. Additive changes — a new field, a new optional parameter, a new enum value — ship without a version bump, so parse defensively. Breaking changes get a new path and 90 days’ notice by email to every account with an active key. Nothing is ever removed from v1.