Drive everything from a script.
Every action you can take in the Outworx dashboard — create projects, push spec versions, promote a default, soft-deactivate an old version — is a single curl away. One account-scoped token, JSON in, JSON out.
$ curl -X POST -H "Authorization: Bearer otwx_pat_..." \
-H "Content-Type: application/json" \
--data-binary '@openapi.json' \
'https://docs.outworx.io/api/v1/projects/payments-api/versions?label=v2&makeDefault=true'{
"id": "0a3b…",
"project_id": "f1b7…",
"version_label": "v2",
"spec_format": "openapi3",
"is_default": true,
"is_active": true,
"spec_size": 142884,
"created_at": "2026-05-09T13:04:11Z"
}Quick start
Sixty seconds to your first call.
Three calls. No SDK install, no codegen. The body of a spec upload is the spec itself — same shape your CI already has on disk.
Generate a personal access token
Generate a personal access token
Open Settings, find the API tokens card, click New token, name it (ci-pipeline, local-dev, etc), optionally set an expiry. The plaintext is shown once — copy it now and stash it in a CI secret. We hash and forget.
Create a project
Create a project
Slug auto-generates from name when omitted. Plan-limit checks kick in here so a runaway script can't blow past your project cap.
curl -X POST -H "Authorization: Bearer $OUTWORX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Payments API"}' \
https://docs.outworx.io/api/v1/projectsPush a spec version
Push a spec version
Body IS the spec — OpenAPI 3.x JSON/YAML, Swagger 2.0, or GraphQL SDL. 50 MB cap. The first active version becomes the default automatically.
curl -X POST -H "Authorization: Bearer $OUTWORX_TOKEN" \
-H "Content-Type: application/json" \
--data-binary '@openapi.json' \
'https://docs.outworx.io/api/v1/projects/payments-api/versions?label=v1'Promote on release
Promote on release
Single-default invariant is enforced server-side. Setting is_default: true on a non-default version demotes the previous default in the same write — no two-step dance.
curl -X PATCH -H "Authorization: Bearer $OUTWORX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"is_default":true}' \
https://docs.outworx.io/api/v1/projects/payments-api/versions/v2Endpoints
Ten routes. One token.
The full request and response shapes — including every error code, every example, every schema — live in the OpenAPI spec. Drop the URL into Postman, openapi-typescript, or your client of choice.
Projects
5 routes/api/v1/projectsList your projects (paginated, ?q= filter)/api/v1/projectsCreate a project — slug auto-generates from name/api/v1/projects/{slug}Get one project/api/v1/projects/{slug}Sparse update: name, is_public, base_url, custom domain/api/v1/projects/{slug}Delete a project (cascades to versions)Versions
5 routes/api/v1/projects/{slug}/versionsList versions (?include_previews= to surface PR-scoped rows)/api/v1/projects/{slug}/versionsPush a spec version (?label=, ?makeDefault=, ?preview= for PR previews)/api/v1/projects/{slug}/versions/{label}Get one version/api/v1/projects/{slug}/versions/{label}Promote default, soft-deactivate, override base_url/api/v1/projects/{slug}/versions/{label}Delete a version (default version is protected)400 VALIDATION_ERROR — the project would have no default and the docs would 404. DELETE on the default returns 409 CONFLICT for the same reason. Promote a replacement first; we handle the demotion in the same write.Conventions
Predictable shapes, stable codes.
Three things to know up front. Pattern-match on error.code — that's the stable identifier; messages are display-only and may change between versions.
Auth
Authorization: Bearer otwx_pat_… Tokens are sha-256 hashed at rest, optionally expire, and act as the user who created them. Revoke from the dashboard and the token returns 401 INVALID_TOKEN on the very next request.
List shape
Lists wrap data: { data: […], pagination: { total, limit, offset } }. limit clamps to 1–100 (default 50), offset defaults to 0. Single-resource endpoints return the resource directly — no envelope.
Errors
Stable code + human message + optional details. We don't differentiate “doesn't exist” from “not yours” — both are 404 NOT_FOUND so a token can't probe slug existence.
{
"error": {
"code": "VALIDATION_ERROR",
"message": "`name` is required."
}
}Ready to wire it up?
Generate a token, drop the OpenAPI spec into your client of choice, and the dashboard becomes optional. The full guide walks through CI bootstrapping, the single-default invariant, plan limits, and what we kept out of v1.