Authentication
All API requests must include a Personal Access Token in the Authorization header. Tokens can be created at /settings/tokens.
REQUEST HEADER
Authorization: Bearer dpx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
EXAMPLE (curl)
curl -H "Authorization: Bearer dpx_..." \ https://deploxa.com/api/v1/projects
EXAMPLE (Node.js)
const res = await fetch("https://deploxa.com/api/v1/projects", {
headers: { Authorization: "Bearer dpx_..." }
});
const { data } = await res.json();Errors
The API uses standard HTTP status codes. All error responses include an error field.
400Bad RequestInvalid parameters or missing required fields401UnauthorizedMissing or invalid token403ForbiddenToken lacks permission for this action404Not FoundResource doesn't exist or you lack access429Too Many RequestsRate limit or build slot exhausted500Server ErrorInternal error — try againERROR RESPONSE
{ "error": "Project not found" }Pagination
List endpoints return a pagination object. Pass cursor as a query param to fetch the next page.
{
"data": [...],
"pagination": {
"hasMore": true,
"nextCursor": "clm1abc...",
"limit": 20
}
}Projects
/api/v1/projectsList all projects the authenticated user has access to.
Query Parameters
orgqueryFilter by organization sluglimitqueryNumber of results (max 100, default 20)cursorqueryPagination cursor from previous responseResponse
{ "data": [{ "id": "...", "name": "my-app", "slug": "my-app", "framework": "NEXTJS", "current_deployment": { "status": "READY" } }], "pagination": { "hasMore": false, "nextCursor": null } }/api/v1/projectsCreate a new project.
Request Body (JSON)
namestringProject nameorgSlugstringOrganization to create the project underframeworkstringFramework: NEXTJS | REACTJS | VITE | NODEJSappTypestringApp type: STATIC | NODEJSdescriptionstringOptional project descriptionResponse
{ "data": { "id": "...", "name": "my-app", "slug": "my-app", "created_at": "2026-01-01T00:00:00Z" } }/api/v1/projects/:idGet a project by ID.
Response
{ "data": { "id": "...", "name": "my-app", "slug": "my-app", "domains": [...], "current_deployment": { ... } } }/api/v1/projects/:idUpdate a project's settings.
Request Body (JSON)
namestringNew project namedescriptionstringProject descriptionbuild_commandstringBuild commandinstall_commandstringInstall commandoutput_dirstringOutput directory/api/v1/projects/:idDelete a project. Only the project owner can delete.
Response
{ "deleted": true }Deployments
/api/v1/projects/:id/deploymentsList deployments for a project.
Query Parameters
statusqueryFilter by status: QUEUED | BUILDING | READY | ERRORlimitqueryNumber of results (max 100)cursorqueryPagination cursorResponse
{ "data": [{ "id": "...", "status": "READY", "branch": "main", "deployment_domain": "my-app.deploxa.com" }] }/api/v1/projects/:id/deploymentsTrigger a new deployment for a project.
Request Body (JSON)
branchstringBranch to deploy (defaults to last deployed branch)commitMessagestringCommit message label for this deploymentcommitShastringCommit SHA to associate with this deploymentResponse
{ "data": { "id": "...", "status": "QUEUED" } }/api/v1/deployments/:idGet a single deployment by ID.
Response
{ "data": { "id": "...", "status": "READY", "branch": "main", "buildDurationMs": 45000, "bundleSizeKb": 1024 } }Environment Variables
/api/v1/projects/:id/env-varsList all environment variable keys for a project. Values are not returned for security.
Response
{ "data": [{ "id": "...", "key": "DATABASE_URL", "targets": ["production"], "sensitive": false }] }/api/v1/projects/:id/env-varsCreate or update an environment variable.
Request Body (JSON)
keystringVariable name (must match [A-Za-z_][A-Za-z0-9_]*)valuestringVariable value (stored encrypted)targetsstring[]Environments: production | preview | developmentsensitivebooleanMark as sensitive (masked in UI)Response
{ "data": { "id": "...", "key": "DATABASE_URL", "targets": ["production"] } }/api/v1/projects/:id/env-vars/:keyDelete an environment variable by key.
Response
{ "deleted": true }Organizations
/api/v1/orgsList all organizations the authenticated user belongs to.
Response
{ "data": [{ "id": "...", "name": "Acme Corp", "slug": "acme", "role": "owner" }] }/api/v1/orgs/:slugGet a single organization by slug.
Response
{ "data": { "id": "...", "name": "Acme Corp", "slug": "acme", "_count": { "members": 5, "projects": 12 } } }/api/v1/orgs/:slug/membersList all members of an organization.
Response
{ "data": [{ "id": "...", "role": "ADMIN", "user": { "email": "alice@acme.com" } }] }Webhooks
Configure webhooks in your project's Integrations settings. Deploxa sends HTTP POST requests to your URL for the events you subscribe to.
Available Events
deployment.startedDeployment has been queued/starteddeployment.readyDeployment successfully completeddeployment.failedDeployment failed with an errordeployment.rollbackA rollback was performedPayload Example
POST https://your-server.com/webhook
Content-Type: application/json
X-Deploxa-Signature: sha256=abc123...
X-Deploxa-Event: deployment.ready
{
"event": "deployment.ready",
"deployment": {
"id": "clm1abc...",
"status": "READY",
"branch": "main",
"url": "https://my-app.deploxa.com"
},
"project": { "id": "...", "name": "my-app", "slug": "my-app" }
}Webhook Signature Verification
Every webhook request includes a X-Deploxa-Signature header. Verify it to confirm the request came from Deploxa and was not tampered with.
How it works
- Deploxa computes
HMAC-SHA256(rawBody, webhookSecret) - The hex digest is sent as
X-Deploxa-Signature: sha256=<hex> - You compute the same HMAC and compare using a timing-safe comparison
Node.js
import { createHmac, timingSafeEqual } from "crypto";
function verifySignature(rawBody: string, secret: string, signature: string) {
const expected = "sha256=" + createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
const a = Buffer.from(expected, "utf8");
const b = Buffer.from(signature, "utf8");
return a.length === b.length && timingSafeEqual(a, b);
}
// In your webhook handler:
app.post("/webhook", express.raw({ type: "*/*" }), (req, res) => {
const sig = req.headers["x-deploxa-signature"] as string;
if (!verifySignature(req.body.toString(), process.env.WEBHOOK_SECRET!, sig)) {
return res.status(401).send("Invalid signature");
}
const payload = JSON.parse(req.body.toString());
console.log("Event:", payload.event);
res.sendStatus(200);
});Python
import hmac
import hashlib
def verify_signature(raw_body: bytes, secret: str, signature: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode(), raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
# In your Flask/FastAPI handler:
@app.post("/webhook")
async def webhook(request: Request):
raw = await request.body()
sig = request.headers.get("x-deploxa-signature", "")
if not verify_signature(raw, WEBHOOK_SECRET, sig):
raise HTTPException(status_code=401, detail="Invalid signature")
payload = json.loads(raw)
print("Event:", payload["event"])
return {"ok": True}Go
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
)
func verifySignature(body []byte, secret, signature string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(body)
expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(signature))
}