Skip to main content

API Endpoints

RESTapi.techwriter.io/v12026-04-01STABLE

API 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.

EnvironmentURLNotes
Productionhttps://api.techwriter.io/v1Primary multi-region endpoint. Latency-routed to the nearest data center.
EU residencyhttps://eu.api.techwriter.io/v1Data stays in Frankfurt. Required for GDPR-scoped workspaces.
Sandboxhttps://sandbox.api.techwriter.io/v1Test workspace. Resets nightly. Usage is excluded from billing and does not count against rate limits.
Self-hostedhttps://{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.

MethodPathDescription
GET/projectsList projects with pagination, filtering, and sorting.
GET/projects/{projectId}Fetch a single project. Use ?expand=template,owner to inline relations.
POST/projectsCreate a new project from scratch or from a template.
PATCH/projects/{projectId}Update any subset of fields. Returns the new version number.
DELETE/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.

MethodPathDescription
GET/projects/{projectId}/documentsList documents in a project. Filter by status, tag, or author.
POST/projects/{projectId}/documentsCreate a new document. Markdown or structured blocks accepted.
GET/documents/{documentId}Fetch a document at the latest or a specific version.
PATCH/documents/{documentId}Patch fields. Pass lastSeenVersion for optimistic concurrency.
POST/documents/{documentId}/publishPromote the current draft to a release on a portal channel.
POST/documents/{documentId}/revertRevert to any prior version. The revert itself becomes a new version.
DELETE/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.

MethodPathGroupDescription
GET/searchSearchFull-text search across articles you have read access to.
POST/assetsAssetsUpload an asset (image, PDF, archive). Multipart and resumable.
GET/assets/{assetId}AssetsFetch metadata for an asset, including its CDN URL and checksum.
POST/webhooksWebhooksSubscribe an HTTPS endpoint to one or more workspace events.
GET/webhooks/{hookId}/deliveriesWebhooksInspect recent delivery attempts and their HTTP responses.
GET/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.

HeaderExample valueDescription
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.

ParameterExampleDescription
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.

CodeMeaningWhen you'll see it
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.

PlanBurstSustainedNotes
Sandbox60 / minute1 000 / daySufficient for local development and CI smoke tests.
Starter600 / minute50 000 / dayPer workspace, shared across all keys and OAuth tokens.
Growth3 000 / minute500 000 / dayBulk endpoints get a separate, higher pool.
EnterpriseCustomCustomNegotiated SLA. Burst cap configurable per region.

Where to go next

Was this section helpful?
GET/projects

Request — 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.