API overview
The Openship HTTP API — base URL, authentication, permissions, rate limits, and the standard JSON error shape.
Everything the dashboard and the openship CLI can do is a call to the same HTTP API: create
projects, trigger deployments, attach domains, read analytics, manage backups. It's a plain JSON API built on
Hono, so you can drive it from a script, a CI job, or an AI agent exactly the way the
first-party clients do.
Base URL & auth
Every route lives under /api on your own instance — e.g. https://your-host/api/projects. There is no
hosted /v1 gateway and no separate key format: the base URL is wherever you run Openship. Authenticate with a
personal access token as a bearer header (Authorization: Bearer <token>), created with
openship token create; the dashboard uses a session cookie instead. Full details in
Authentication.
Authentication
A request proves who it is in exactly one of four ways. Openship checks for a Bearer token first, then a
session cookie, then the loopback fallback (only if that mode is enabled).
| Method | What you send | Who uses it |
|---|---|---|
| Personal access token | Authorization: Bearer opsh_pat_… | CLI, scripts, server-to-server |
| Session cookie | httpOnly cookie set at login | The dashboard, in a browser |
| MCP OAuth | An OAuth 2.1 access token bound at consent | AI agents connecting to /api/mcp |
| Zero-auth loopback | Nothing (request from 127.0.0.1) | Desktop app / opt-in single-user instance |
curl https://your-host/api/projects \
-H "Authorization: Bearer $OPENSHIP_TOKEN"Bearer is for non-browser clients only
A Bearer token presented from a browser-trusted origin is rejected (BEARER_NOT_ALLOWED_FROM_BROWSER), so an
exfiltrated session token can't be replayed past the httpOnly cookie. A token can be org-scoped (rejects a
mismatched X-Organization-Id with TOKEN_ORG_SCOPE) and read-only (rejects mutations with
TOKEN_READ_ONLY). MCP OAuth discovery is served at the origin root:
/.well-known/oauth-authorization-server and /.well-known/oauth-protected-resource.
Permissions
Once you're authenticated, every route runs one permission check. Each route declares a tag of the form
resource:action — project:read, domain:write, deployment:admin — and sub-resources add a middle segment
(project:service:write). The action lines up with the HTTP method: read (GET one), write
(POST/PUT/PATCH), admin (DELETE and destructive), list (GET a collection).
Access is decided per organization. Openship resolves which org you mean from the X-Organization-Id
header (falling back to your session default), then checks your role there — owner, admin, member, or
restricted (grant-only). Openship refuses to boot if any route is missing a tag, so there is no accidentally
unprotected endpoint. The per-endpoint tables on the module pages list the tag for every route; see
Permissions & roles for the full model.
Rate limits
The whole /api surface is rate-limited. Unauthenticated requests fall under a per-IP default; authenticated
requests get a more generous per-user budget. Some route groups carry a tighter or looser named policy. Limits
are per rolling minute.
| Policy | Limit | Keyed by | Applies to |
|---|---|---|---|
default-anon | 100 / min | IP | Any unauthenticated route |
default-authed | 600 / min | user | Any authenticated route |
auth-tight | 10 / min | IP | POST /api/auth/* (login, signup, reset) |
mcp | 300 / min | IP | /api/mcp (tool-call bursts) |
webhook-ingress | 120 / min | source IP | Inbound webhook deliveries |
billing-portal | 20 / min | org | Stripe portal / checkout creation |
Every response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. /api/health is
never limited (load balancers and SSR poll it).
429 — Too many requests
When a bucket is exhausted the request gets a 429 with body {"error":"Too many requests"} plus a
Retry-After header (seconds). Back off until the window resets rather than retrying immediately.
Error shape
Errors come back as JSON with a stable shape: a human error message and, for typed failures, a machine
code. Validation failures add a field-level details map.
{ "error": "This access token is read-only", "code": "TOKEN_READ_ONLY" }| Status | Typical code | Meaning |
|---|---|---|
400 | VALIDATION_ERROR | Request body failed the schema — see details for the offending fields. |
401 | INVALID_TOKEN, BEARER_NOT_ALLOWED_FROM_BROWSER | Not authenticated, or a bad/expired token (plain Unauthorized when no session at all). |
403 | TOKEN_ORG_SCOPE, TOKEN_READ_ONLY | Authenticated but not allowed — wrong org, read-only token, or your role/grants deny the action. |
404 | — | Resource doesn't exist, or isn't in your organization (IDOR-safe). |
409 | — | Conflict with current state (e.g. a delete already in progress). |
429 | — | Rate-limited (see above). |
503 | AUTH_UNAVAILABLE | The auth backend is unreachable — retry; never treated as "no session". |
500 | — | Unhandled server error ({"error":"Internal server error"}, no code). |
Route groups & availability
Routes are mounted per module. Most exist in every deploy mode; a handful are self-hosted-only (they touch the host filesystem, SSH, or local setup and are never loaded in cloud mode), and the cloud gateway swaps its route set by mode.
| Base path | Covers | Availability |
|---|---|---|
/api/health | Liveness + public deploy metadata | Both (unauthenticated) |
/api/auth | Sessions, OAuth, org management (Better Auth) | Both |
/api/projects | Projects, env, deploy config | Both |
/api/projects/:id/services | Services within a project | Both |
/api/deployments | Builds, deploys, rollbacks | Both |
/api/domains | Custom domains, DNS, SSL | Both |
/api/github | GitHub connection, repos, webhooks | Both |
/api/webhooks | Inbound provider webhooks (/api/webhooks/backup too) | Both |
/api/analytics | Traffic, usage, deployment stats | Both |
/api (backups) | Backup runs and restores | Both |
/api/backup-destinations | Backup targets, policies, schedules | Both |
/api/notifications | Channels, subscriptions, deliveries | Both |
/api/audit | Organization audit log | Both |
/api/settings | Workspace / deploy defaults | Both |
/api/tokens | Personal access tokens | Both |
/api/permissions | Roles and resource grants | Both |
/api/billing | Plans, usage, checkout | Both (mode-specific extensions) |
/api/mcp | Model Context Protocol JSON-RPC endpoint | Both |
/api/images | Container image helpers | Both |
/api/services/terminal | Interactive service terminal (WebSocket) | Both |
/api/cloud | Cloud account link / gateway | Both (SaaS vs local route set) |
/api/system | Filesystem browse, instance setup, provisioning | Self-hosted only |
/api/mail | Self-hosted mail server wizard | Self-hosted only |
/api/migration | Adopt an existing Docker host | Self-hosted only |
/api/terminal | Interactive server (SSH) terminal | Self-hosted only |
Module reference
Health
Public liveness and deployment-info probes.
Auth
Sessions, OAuth, and organization management.
Projects
Create and configure projects, env vars, and deploy settings.
Services
Manage the services that make up a project.
Deployments
Trigger builds, deploy, cancel, restart, and roll back.
Domains
Attach domains, preview and verify DNS, manage SSL.
GitHub
Connect accounts, browse repos, and wire push auto-deploy.
Webhooks
Inbound provider webhooks that turn a push into a deploy.
Analytics
Traffic, usage, container, and deployment metrics.
Backups
Backup runs, restores, and their status.
Backup destinations
Backup targets, policies, and schedules.
Notifications
Channels, subscriptions, and the alert feed.
Audit
Read the organization's audit log.
Settings
Workspace build and deploy defaults.
Tokens
Create and revoke personal access tokens.
Permissions
Manage roles and per-resource grants.
Billing
Plans, usage, and checkout.
MCP
The Model Context Protocol endpoint for AI agents.
Images
Browse the build-image catalog for services.
Service terminal
Open a shell into a running service container.
Cloud
Link an instance to Openship Cloud and its control-plane endpoints.
System
Self-hosted instance administration (self-hosted only).
Install and run a self-hosted mail stack (self-hosted only).
Migration
Adopt an existing Docker host as a project (self-hosted only).
Terminal
Interactive SSH shell to a managed server (self-hosted only).
Access & the raw API
Log in, switch between instances with contexts, manage personal access tokens, and call any API route directly with openship api.
Projects API
Create and configure projects, link a git repo, manage env vars and resources, read logs, deploy an uploaded folder, and transfer projects to and from Openship Cloud.