REST API
The app runs on this API, and a scoped token can call most of it. Account, billing and token management need a signed-in session.
Conventions
- REST over JSON, versioned at /v1, served from https://provisore.com.
- Authenticate with the session cookie the web app uses, or with a personal access token sent as Authorization: Bearer pvs_….
- Every request is scoped to your active team. An id from another team returns 404.
- Endpoints that change something require an Idempotency-Key header of 8 to 255 characters, and refuse a request without one. A repeated request returns the original result with Idempotent-Replayed: true.
- Destructive endpoints require you to name the target in a header: X-Confirm-Domain, X-Confirm-Name, or X-Confirm-Target.
Errors
{
"code": "php_version_in_use",
"message": "PHP 8.2 still serves api.example.com.",
"fieldErrors": { "version": "Not removable while a site uses it." },
"requestId": "01J8Z…"
}Branch on code, display message, and include requestId when you contact support. The common statuses are 400 for an invalid request, 402 when the team is read-only or its plan does not allow the change, 403 for a missing scope or role, 404 for not found, 409 for conflicting state or a confirmation mismatch, 410 for a one-time secret already revealed, and 429 when rate limited.
Long-running operations
Anything that touches a server returns a job straight away and continues in the background. That covers provisioning, deploys, certificates, backups, restores, databases, daemons, cron, and firewall rules.
POST /v1/sites/{siteId}/deployments
Authorization: Bearer pvs_…
Idempotency-Key: 2026-08-18-build-4171
{ "trigger": "manual" }
202 Accepted
{ "deployment": { "id": "…", "status": "queued" },
"job": { "id": "…", "status": "queued" } }
GET /v1/jobs/{jobId} # poll
GET /v1/jobs/{jobId}/events # or stream it
POST /v1/jobs/{jobId}/cancel # applied between stepsThe event stream is Server-Sent Events. It sends a snapshot first, then log, progress, status, and periodic heartbeat events. Reconnect with Last-Event-ID and you receive the events you missed before the stream resumes.
Lists and pagination
Collections return { "data": [ … ] }. Jobs and audit entries page with a cursor. Pass the previous page's nextCursor as cursor. Entries added while you page neither shift nor duplicate results.
Example: deploy from CI
SITE=$(curl -fsS https://provisore.com/v1/sites \
-H "Authorization: Bearer $PROVISORE_TOKEN" \
| jq -r '.data[] | select(.domain=="example.com") | .id')
JOB=$(curl -fsS -X POST "https://provisore.com/v1/sites/$SITE/deployments" \
-H "Authorization: Bearer $PROVISORE_TOKEN" \
-H "Idempotency-Key: $GITHUB_RUN_ID" \
-H "Content-Type: application/json" \
-d '{"trigger":"manual"}' | jq -r .job.id)
while :; do
STATUS=$(curl -fsS "https://provisore.com/v1/jobs/$JOB" \
-H "Authorization: Bearer $PROVISORE_TOKEN" | jq -r .status)
case "$STATUS" in
succeeded) break ;;
failed|cancelled) echo "deploy $STATUS" >&2; exit 1 ;;
esac
sleep 2
doneThe token needs sites:read to find the site, deployments:write to start the deploy, and observability:read to follow its job.
To deploy when a build goes green and do nothing else, a deploy hook is simpler. It is one URL, with no token to manage.
Build requests from the examples above. There is no published OpenAPI document to generate a client from, and no client library.