API Endpoints
api.techwriter.io/v12026-04-01STABLEAPI endpoints
The TechWriter REST API exposes every resource the SDK wraps. Endpoints are resource-oriented, take and return JSON, and use ordinary HTTP verbs and status codes. Every write supports idempotency keys, every list supports cursor pagination, and every response includes a request ID for log correlation.
The reference below covers the twelve endpoints you'll touch most often — projects, documents, search, assets, and webhooks — plus the conventions that apply to every route: base URLs, headers, query parameters, status codes, and the shape of an error.
Pick an endpoint in the panel on the right to see a runnable request and a representative response side by side. The signature, the SDK call, and the JSON body all stay in sync.
Base URLs
Pick the host that matches your workspace's data residency and environment. The SDK takes one via the baseURL option.
https://api.techwriter.io/v1Primary multi-region endpoint. Latency-routed to the nearest data center.https://eu.api.techwriter.io/v1Data stays in Frankfurt. Required for GDPR-scoped workspaces.https://sandbox.api.techwriter.io/v1Test workspace. Resets nightly. Usage is excluded from billing and does not count against rate limits.https://{your-domain}/api/v1Pass via the baseURL client option. Mirrors the public surface 1:1.Projects
A project is a top-level container for documents, templates, and contributors. Most workspaces have between one and a few dozen.
/projectsList projects with pagination, filtering, and sorting./projects/{projectId}Fetch a single project. Use ?expand=template,owner to inline relations./projectsCreate a new project from scratch or from a template./projects/{projectId}Update any subset of fields. Returns the new version number./projects/{projectId}Soft-delete. Recoverable for 30 days via /projects/{id}/restore.Documents
Documents are versioned content units. Each move from Draft → In Review → Published bumps the version and emits a webhook event.
/projects/{projectId}/documentsList documents in a project. Filter by status, tag, or author./projects/{projectId}/documentsCreate a new document. Markdown or structured blocks accepted./documents/{documentId}Fetch a document at the latest or a specific version./documents/{documentId}Patch fields. Pass lastSeenVersion for optimistic concurrency./documents/{documentId}/publishPromote the current draft to a release on a portal channel./documents/{documentId}/revertRevert to any prior version. The revert itself becomes a new version./documents/{documentId}Archive a document. Removes it from search but preserves history.Search, assets, webhooks, and workspace
The remaining surface area: discovery, binary uploads, push notifications, and your live usage telemetry.
/searchSearchFull-text search across articles you have read access to./assetsAssetsUpload an asset (image, PDF, archive). Multipart and resumable./assets/{assetId}AssetsFetch metadata for an asset, including its CDN URL and checksum./webhooksWebhooksSubscribe an HTTPS endpoint to one or more workspace events./webhooks/{hookId}/deliveriesWebhooksInspect recent delivery attempts and their HTTP responses./usageWorkspaceCurrent period rate-limit and storage usage for your workspace.Request headers
The SDK sets these for you. If you call the API directly, send them on every request.
AuthorizationBearer {api_key}Required on every request. The SDK sets this automatically.Idempotency-Key{uuid v4}Optional on writes. Repeats with the same key return the original response.TechWriter-Version2026-04-01Pin to a dated API version. Omit to use your workspace default.X-Request-Id{uuid v4}Echoed in the response for log correlation. Auto-set if omitted.If-Match{etag}Concurrency guard for PATCH and DELETE. Server rejects on mismatch.Acceptapplication/jsonDefault. Use application/vnd.techwriter+ndjson for streamed lists.Common query parameters
Every list endpoint accepts the same pagination, filtering, and sparse-fieldset controls.
toptop=25Number of items per page. Default 25, max 200 across every list endpoint.cursorcursor=eyJ0IjoxNzE0...Opaque continuation token returned as nextCursor. Pass back verbatim.filterfilter=status eq 'draft'OData-style filter. Supports eq, ne, gt, lt, in, contains, and, or.orderByorderBy=updatedAt descField plus direction. Sortable fields are listed on each resource page.fieldsfields=id,title,statusSparse fieldsets — return only the columns you need to cut payload size.expandexpand=template,ownerInline a related resource so you don't need a follow-up GET.asOfasOf=2026-04-01Time-travel reads. Returns the resource as it was at the given instant.Response status codes
Standard HTTP semantics. The SDK maps each non-2xx into a typed exception with the same code on err.code.
200OKSuccessful read or update. Body contains the requested resource.201CreatedSuccessful create. The Location header points at the new resource.204No ContentSuccessful action with no response body — typically deletes.400Bad RequestMalformed payload, invalid filter syntax, or unknown field.401UnauthorizedAPI key missing, expired, or rotated. Refresh the credential.403ForbiddenAuthenticated, but the key lacks the scope for this resource.404Not FoundNo resource matches the supplied identifier in this workspace.409ConflictConcurrency conflict — your lastSeenVersion is stale. Re-read and retry.422Unprocessable EntityValidation failed. The error body lists which fields broke which rule.429Too Many RequestsRate limit exceeded. Honor the Retry-After header before retrying.500Server ErrorAn unexpected server-side error. Safe to retry — the SDK does this automatically by default.Error response shape
Every non-2xx response uses the same envelope. The requestId is the single most useful thing to share with support.
{
"error": {
"code": "validation_failed",
"message": "Field 'slug' must be lowercase kebab-case.",
"requestId": "req_01HW8N4Z3R8C9X",
"details": [
{
"field": "slug",
"rule": "pattern",
"expected": "^[a-z0-9-]+$"
}
]
}
}Rate limits
Limits are enforced per workspace. The SDK reads the X-RateLimit-Remaining and Retry-After headers automatically.
60 / minute1 000 / daySufficient for local development and CI smoke tests.600 / minute50 000 / dayPer workspace, shared across all keys and OAuth tokens.3 000 / minute500 000 / dayBulk endpoints get a separate, higher pool.CustomCustomNegotiated SLA. Burst cap configurable per region.Where to go next
/projectsRequest — Node.js SDK
"color: ">#8b949e">// Paginated, filterable list of projects in your workspace
const page = await client.projects.list({
top: 25,
filter: "status eq 'active'",
orderBy:
});
console.log(`Returning ${page.items.length} of ${page.totalCount}`);
for (const p of page.items) {
console.log(`${p.id} — ${p.name}`);
}Sample response
{
"items": [
{
"id": "proj_8fK29s",
"name": "Public API v3",
"slug": "public-api-v3",
"status": "active",
"documentCount": 42,
"updatedAt": "2026-04-28T14:11:09Z"
}
],
"totalCount": 87,
"nextCursor": "eyJ0IjoxNzE0MzAwMDAwfQ"
}Pro tip
Send an Idempotency-Key on every write. The server caches the response for 24 hours, so retries from a flaky network never create duplicate projects, documents, or webhook subscriptions.