Authentication
Every request must include the x-api-key header. Create and manage keys in Console under Settings → API Keys.
Authentication errors return 401 Unauthorized with { "error": "Unauthorized", "message": "Invalid API key" }.
GET /v1/deployments HTTP/1.1
Host: console-api.akash.network
x-api-key: YOUR_API_KEY
# 401 response when the key is missing or invalid:
{ "error": "Unauthorized", "message": "Invalid API key" }GET /v1/deployments HTTP/1.1
Host: console-api.akash.network
x-api-key: YOUR_API_KEY
# 401 response when the key is missing or invalid:
{ "error": "Unauthorized", "message": "Invalid API key" }Response envelope
Every JSON response is wrapped in a top-level data field. For list endpoints, data carries both the array and a pagination object.
# Single resource:
{ "data": { "...": "..." } }
# List resource:
{ "data": { "deployments": [ ... ], "pagination": { ... } } }# Single resource:
{ "data": { "...": "..." } }
# List resource:
{ "data": { "deployments": [ ... ], "pagination": { ... } } }Errors
The API uses standard HTTP status codes. All error responses share the same JSON shape.
| HTTP | Code string | Description |
|---|---|---|
| 400 | BadRequest | The request body or query parameters are invalid. Check message for details. |
| 401 | Unauthorized | x-api-key is missing, expired, or invalid. |
| 404 | NotFound | The resource (dseq) does not exist or is not owned by the authenticated user. |
| 429 | TooManyRequests | Request rate exceeded. Wait before retrying. |
| 500 | InternalServerError | Unexpected server error. Retry with exponential backoff. |
# Error response schema:
{ "error": "string", "message": "string" }# Error response schema:
{ "error": "string", "message": "string" }API Versioning
Endpoints are prefixed with a version number (/v1/ or /v2/). Versions are independent — upgrading one endpoint version does not affect others.
| Version | Status | Endpoints |
|---|---|---|
| v1 | Stable | Deployments, bids, leases, deposit |
| v2 | Stable | Deployment settings (auto top-up) |
Future breaking changes are introduced as a new version prefix; prior versions remain available for a deprecation period announced in the changelog.
Pagination
GET /v1/deployments uses offset-based pagination via skip and limit. The response includes a pagination object with total, skip, limit, and hasMore so you can drive a paging loop without guessing when to stop.
| Parameter | Type | Default | Minimum | Description |
|---|---|---|---|---|
| skip | integer | 0 | 0 | Number of records to skip (offset) |
| limit | integer | 1000 | 1 | Maximum records to return per page |
# Stream every page sequentially:
SKIP=0
LIMIT=100
while :; do
curl -s "https://console-api.akash.network/v1/deployments?skip=$SKIP&limit=$LIMIT" \
-H "x-api-key: $AKASH_API_KEY" > page.json
jq -e '.data.pagination.hasMore' page.json > /dev/null || break
SKIP=$((SKIP + LIMIT))
done# Stream every page sequentially:
SKIP=0
LIMIT=100
while :; do
curl -s "https://console-api.akash.network/v1/deployments?skip=$SKIP&limit=$LIMIT" \
-H "x-api-key: $AKASH_API_KEY" > page.json
jq -e '.data.pagination.hasMore' page.json > /dev/null || break
SKIP=$((SKIP + LIMIT))
done POST /v1/deployments
Create a new deployment from an SDL manifest and fund its escrow.
| Parameter | Description |
|---|---|
x-api-key header string
required
| Your API key |
data.sdl body string
required
| Deployment manifest in SDL (YAML) format, as a JSON string |
data.deposit body number
required
| Initial escrow deposit in USD. Minimum 0.5 |
Response 201 Created
| Field | Description |
|---|---|
data.dseq string | Deployment sequence ID |
data.manifest string | Rendered manifest blob to send with POST /v1/leases |
data.signTx.code number | Cosmos tx code (0 = success) |
data.signTx.transactionHash string | Broadcast transaction hash |
data.signTx.rawLog string | Raw chain log |
curl -X POST https://console-api.akash.network/v1/deployments \
-H "x-api-key: $AKASH_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "data": { "sdl": "version: \"2.0\"\n...", "deposit": 0.5 } }'curl -X POST https://console-api.akash.network/v1/deployments \
-H "x-api-key: $AKASH_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "data": { "sdl": "version: \"2.0\"\n...", "deposit": 0.5 } }' GET /v1/bids
List provider bids for a deployment. Poll until bids arrive.
| Parameter | Description |
|---|---|
x-api-key header string
required
| Your API key |
dseq query string
required
| Deployment sequence ID returned by create deployment |
Response 200 OK
| Field | Description |
|---|---|
data[].bid.id.owner string | Owner address |
data[].bid.id.dseq string | Deployment sequence ID |
data[].bid.id.gseq number | Group sequence |
data[].bid.id.oseq number | Order sequence |
data[].bid.id.provider string | Provider address |
data[].bid.id.bseq number | Bid sequence (chain height) |
data[].bid.state string | Bid state (e.g. open) |
data[].bid.price.denom string | Chain denom (e.g. uact) |
data[].bid.price.amount string | Chain amount in micro-units, per block |
data[].bid.created_at string | Chain height at creation |
data[].bid.resources_offer array | Offered CPU / memory / storage / GPU / endpoints |
data[].escrow_account object | Provider's bid escrow account |
There is no flatbid.idstring — the bid is identified by the composite{owner, dseq, gseq, oseq, provider, bseq}. When creating a lease, copydseq,gseq,oseq, andproviderfrom this object.
While bids are pending, the endpoint returns { "data": [] }. Poll every few seconds. curl "https://console-api.akash.network/v1/bids?dseq=1234567" \
-H "x-api-key: $AKASH_API_KEY"curl "https://console-api.akash.network/v1/bids?dseq=1234567" \
-H "x-api-key: $AKASH_API_KEY" POST /v1/leases
Accept one or more provider bids and ship the manifest in a single call.
| Parameter | Description |
|---|---|
x-api-key header string
required
| Your API key |
manifest body string
required
| Manifest blob returned by POST /v1/deployments |
leases body array
required
| One entry per bid to accept |
leases[].dseq body string
required
| Deployment sequence ID |
leases[].gseq body number
required
| Group sequence (from bid.id.gseq) |
leases[].oseq body number
required
| Order sequence (from bid.id.oseq) |
leases[].provider body string
required
| Provider address (from bid.id.provider) |
Response 200 OK
Response is the full deployment object — same shape as GET /v1/deployments/{dseq}. curl -X POST https://console-api.akash.network/v1/leases \
-H "x-api-key: $AKASH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"manifest": "<MANIFEST>",
"leases": [
{ "dseq": "1234567", "gseq": 1, "oseq": 1, "provider": "akash1provider..." }
]
}'curl -X POST https://console-api.akash.network/v1/leases \
-H "x-api-key: $AKASH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"manifest": "<MANIFEST>",
"leases": [
{ "dseq": "1234567", "gseq": 1, "oseq": 1, "provider": "akash1provider..." }
]
}' POST /v1/deposit-deployment
Add USD funds to a deployment's escrow to extend its runtime.
| Parameter | Description |
|---|---|
x-api-key header string
required
| Your API key |
data.dseq body string
required
| Deployment sequence ID |
data.deposit body number
required
| Deposit amount in USD. Minimum 0.5 |
Response 200 OK
curl -X POST https://console-api.akash.network/v1/deposit-deployment \
-H "x-api-key: $AKASH_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "data": { "dseq": "1234567", "deposit": 0.5 } }'curl -X POST https://console-api.akash.network/v1/deposit-deployment \
-H "x-api-key: $AKASH_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "data": { "dseq": "1234567", "deposit": 0.5 } }' GET /v1/deployments
List all deployments for the authenticated user, with pagination.
| Parameter | Description |
|---|---|
x-api-key header string
required
| Your API key |
skip query integer | Offset. Default 0 |
limit query integer | Max records. Default 1000 |
Response 200 OK
| Field | Description |
|---|---|
data.deployments[] array | One entry per deployment, same shape as GET /v1/deployments/{dseq} minus per-lease status |
data.pagination.total number | Total deployments for the user |
data.pagination.skip number | Skip value used |
data.pagination.limit number | Limit value used |
data.pagination.hasMore boolean | True when more pages are available |
curl "https://console-api.akash.network/v1/deployments?skip=0&limit=10" \
-H "x-api-key: $AKASH_API_KEY"curl "https://console-api.akash.network/v1/deployments?skip=0&limit=10" \
-H "x-api-key: $AKASH_API_KEY" GET /v1/deployments/{dseq}
Retrieve full details for a single deployment by its sequence ID.
| Parameter | Description |
|---|---|
x-api-key header string
required
| Your API key |
dseq path string
required
| Deployment sequence ID |
Response 200 OK
| Field | Description |
|---|---|
data.deployment.id.owner string | Owner address |
data.deployment.id.dseq string | Deployment sequence ID |
data.deployment.state string | Deployment state (active, closed) |
data.deployment.hash string | Manifest hash |
data.deployment.created_at string | Chain height at creation |
data.leases[] array | Active leases (composite id + state, price, created_at, closed_on, status) |
data.escrow_account object | Deployment escrow account with funds[], deposits[], transferred[] (raw chain denoms) |
curl "https://console-api.akash.network/v1/deployments/1234567" \
-H "x-api-key: $AKASH_API_KEY"curl "https://console-api.akash.network/v1/deployments/1234567" \
-H "x-api-key: $AKASH_API_KEY" PUT /v1/deployments/{dseq}
Update an active deployment with a revised SDL.
| Parameter | Description |
|---|---|
x-api-key header string
required
| Your API key |
dseq path string
required
| Deployment sequence ID |
data.sdl body string
required
| Updated SDL in YAML format, passed as a JSON string |
Response 200 OK
curl -X PUT "https://console-api.akash.network/v1/deployments/1234567" \
-H "x-api-key: $AKASH_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "data": { "sdl": "version: \"2.0\"\n..." } }'curl -X PUT "https://console-api.akash.network/v1/deployments/1234567" \
-H "x-api-key: $AKASH_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "data": { "sdl": "version: \"2.0\"\n..." } }' DELETE /v1/deployments/{dseq}
Close a deployment. Remaining escrow is returned asynchronously by the chain to your Console balance.
| Parameter | Description |
|---|---|
x-api-key header string
required
| Your API key |
dseq path string
required
| Deployment sequence ID to close |
Response 200 OK
curl -X DELETE "https://console-api.akash.network/v1/deployments/1234567" \
-H "x-api-key: $AKASH_API_KEY"curl -X DELETE "https://console-api.akash.network/v1/deployments/1234567" \
-H "x-api-key: $AKASH_API_KEY" GET /v2/deployment-settings/{dseq}
Get auto top-up settings for a specific deployment. Settings are auto-created on first read.
| Parameter | Description |
|---|---|
x-api-key header string
required
| Your API key |
dseq path string
required
| Deployment sequence number |
userId query string | Defaults to authenticated user |
Response 200 OK
| Field | Description |
|---|---|
data.id string (uuid) | Setting record id |
data.userId string | Owning user id |
data.dseq string | Deployment sequence number |
data.autoTopUpEnabled boolean | Whether auto top-up is enabled |
data.estimatedTopUpAmount number | Estimated top-up amount per cycle (USD) |
data.topUpFrequencyMs number | Top-up cadence in milliseconds |
data.createdAt string | ISO-8601 creation time |
data.updatedAt string | ISO-8601 last update time |
curl "https://console-api.akash.network/v2/deployment-settings/1234567" \
-H "x-api-key: $AKASH_API_KEY"curl "https://console-api.akash.network/v2/deployment-settings/1234567" \
-H "x-api-key: $AKASH_API_KEY" POST /v2/deployment-settings
Create deployment settings (typically used to enable auto top-up when the settings row does not yet exist).
| Parameter | Description |
|---|---|
x-api-key header string
required
| Your API key |
data.dseq body string
required
| Deployment sequence number |
data.autoTopUpEnabled body boolean
required
| Enable or disable automatic top-up |
data.userId body string (uuid) | Defaults to authenticated user |
Response 201 Created
curl -X POST https://console-api.akash.network/v2/deployment-settings \
-H "x-api-key: $AKASH_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "data": { "dseq": "1234567", "autoTopUpEnabled": true } }'curl -X POST https://console-api.akash.network/v2/deployment-settings \
-H "x-api-key: $AKASH_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "data": { "dseq": "1234567", "autoTopUpEnabled": true } }' PATCH /v2/deployment-settings/{dseq}
Update an existing settings row.
| Parameter | Description |
|---|---|
x-api-key header string
required
| Your API key |
dseq path string
required
| Deployment sequence number |
userId query string (uuid) | Defaults to authenticated user |
data.autoTopUpEnabled body boolean
required
| Enable or disable automatic top-up |
Response 200 OK
curl -X PATCH https://console-api.akash.network/v2/deployment-settings/1234567 \
-H "x-api-key: $AKASH_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "data": { "autoTopUpEnabled": false } }'curl -X PATCH https://console-api.akash.network/v2/deployment-settings/1234567 \
-H "x-api-key: $AKASH_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "data": { "autoTopUpEnabled": false } }'JWT for provider access
JWT for provider access
The Console API and the provider share a JWT-based access model for lease-scoped operations (logs, events, status, shell). Mint a short-lived token from the Console API, then call the provider directly with it.
TTL: tokens are short-lived (default 1800 s in the Console UI). There is no refresh endpoint — re-call POST /v1/create-jwt-token to extend lifetime.
Scope: grant the narrowest set you need. Missing scope → 401 from the provider. Valid values: send-manifest, get-manifest, logs, shell, events, status, restart, hostname-migrate, ip-migrate.
Spec: JWT payload follows AEP-64. The provider validates it the same way regardless of who minted it.
Stability:POST /v1/create-jwt-tokenis Swagger-only / Tier 2 — observed in production but not in the official API reference. It may change without notice. Self-custody users sign locally with@akashnetwork/chain-sdkand bypass this endpoint entirely.
POST /v1/create-jwt-token
Mint a short-lived, lease-scoped JWT for accessing provider endpoints.
| Parameter | Description |
|---|---|
x-api-key header string
required
| Your API key |
data.ttl body number
required
| Token TTL in seconds (Console UI default: 1800) |
data.leases.access body enum
required
| One of full, scoped, or granular |
data.leases.scope body array<string> | Required when access is scoped. Any of send-manifest, get-manifest, logs, shell, events, status, restart, hostname-migrate, ip-migrate |
data.leases.permissions body array<object> | Required when access is granular. Per-provider, per-deployment rules |
Response 201 Created
| Field | Description |
|---|---|
data.token string (JWT) | Bearer token to send to provider endpoints as Authorization: Bearer |
Swagger-only / Tier 2 endpoint — may change without notice.
No refresh endpoint exists. To extend a token's lifetime, re-call this endpoint and discard the old token.
JWT=$(curl -sX POST https://console-api.akash.network/v1/create-jwt-token \
-H "x-api-key: $AKASH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": {
"ttl": 1800,
"leases": {
"access": "scoped",
"scope": ["status", "logs", "events", "shell"]
}
}
}' | jq -r .data.token)JWT=$(curl -sX POST https://console-api.akash.network/v1/create-jwt-token \
-H "x-api-key: $AKASH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"data": {
"ttl": 1800,
"leases": {
"access": "scoped",
"scope": ["status", "logs", "events", "shell"]
}
}
}' | jq -r .data.token)Lease operations (provider)
Provider-side lease endpoints
These endpoints are served by the provider, not by console-api.akash.network. Resolve the provider's hostUri via GET /v1/providers/{address} (network-data API), then call the provider directly with the JWT in Authorization: Bearer <token>.
TLS / cert pinning: provider certificates are self-signed against the provider's on-chain wallet address — browsers will reject them. Server-side, you have to look the cert up on-chain: the CN must be the provider's bech32 wallet address, and the chain's MsgCreateCertificate record for that (address, serial number) must match the leaf cert's fingerprint. That lookup is a chain query, so it has to happen after the TLS handshake — Node's checkServerIdentity is synchronous and can't await. The canonical pattern (used by Console) is to disable Node's default verification, then validate the peer cert asynchronously against the chain. See the provider-proxy CertificateValidator for a reference implementation. In a browser, route through a provider-proxy service.
events vs kubeevents gotcha: Console UI accepts events as a path component and rewrites it to kubeevents client-side. The wire path on the provider is always kubeevents — use that directly to avoid surprises.
Common errors:
401— JWT scope doesn't include the requested operation (e.g. calling/logswithoutlogsin scope).- Empty log stream — the deployment hasn't started yet, or the named service isn't running.
- TLS handshake failure — you're missing the cert-pinning agent, or the provider has rotated keys.
GET https://{hostUri}/lease/{dseq}/{gseq}/{oseq}/status
Provider
Polled status of each service in a lease: ready/total replicas, forwarded ports, IPs, restart counts.
| Parameter | Description |
|---|---|
Authorization header string
required
| Bearer |
dseq path string
required
| Deployment sequence ID |
gseq path number
required
| Group sequence |
oseq path number
required
| Order sequence |
Response 200 OK
| Field | Description |
|---|---|
services.<name>.ready_replicas number | Replicas currently passing readiness checks |
services.<name>.available_replicas number | Replicas marked available by the Kubernetes controller |
services.<name>.replicas number | Current replicas |
services.<name>.total number | Desired replicas |
services.<name>.uris array<string> | Accessible URIs for the service (when leasing endpoints) |
forwarded_ports.<name> array<object> | Provider-side port forwards, keyed by service name (top-level — not nested under services) |
ips.<name> array<object> | Public IPs assigned to the service, keyed by service name (top-level, when leasing IP endpoints) |
Same shape as deployment.leases[].status in the Console API, but freshly polled from the provider.JWT scope must include status. # -k skips TLS verification (provider certs are self-signed against the
# on-chain wallet). For production, validate the cert against the chain
# out-of-band — see the provider-endpoints preface.
curl -k "https://${HOSTURI#https://}/lease/${DSEQ}/${GSEQ}/${OSEQ}/status" \
-H "Authorization: Bearer $JWT"# -k skips TLS verification (provider certs are self-signed against the
# on-chain wallet). For production, validate the cert against the chain
# out-of-band — see the provider-endpoints preface.
curl -k "https://${HOSTURI#https://}/lease/${DSEQ}/${GSEQ}/${OSEQ}/status" \
-H "Authorization: Bearer $JWT" WSS wss://{hostUri}/lease/{dseq}/{gseq}/{oseq}/logs
Provider
Stream container logs for a lease. Send follow=true to tail continuously.
| Parameter | Description |
|---|---|
Authorization header string
required
| Bearer (scope must include logs) |
dseq path string
required
| Deployment sequence ID |
gseq path number
required
| Group sequence |
oseq path number
required
| Order sequence |
tail query number | Number of lines to tail from the end of the log |
follow query boolean | Stream new logs continuously when true |
service query string | Filter to a specific service name |
| Field | Description |
|---|---|
(stream) binary | Stream of text chunks; one chunk ≈ one log line |
JWT scope must include logs.In a browser, the self-signed provider cert will be rejected — route through a provider-proxy.
# --insecure skips TLS verification (provider certs are self-signed).
# Requires websocat 1.13+. See provider-endpoints preface for production
# cert validation.
websocat --insecure "wss://${HOSTURI#https://}/lease/${DSEQ}/${GSEQ}/${OSEQ}/logs?follow=true&tail=200" \
-H "Authorization: Bearer $JWT"# --insecure skips TLS verification (provider certs are self-signed).
# Requires websocat 1.13+. See provider-endpoints preface for production
# cert validation.
websocat --insecure "wss://${HOSTURI#https://}/lease/${DSEQ}/${GSEQ}/${OSEQ}/logs?follow=true&tail=200" \
-H "Authorization: Bearer $JWT" WSS wss://{hostUri}/lease/{dseq}/{gseq}/{oseq}/kubeevents
Provider
Stream Kubernetes events for the lease (pod scheduling, container restarts, etc.).
| Parameter | Description |
|---|---|
Authorization header string
required
| Bearer (scope must include events) |
dseq path string
required
| Deployment sequence ID |
gseq path number
required
| Group sequence |
oseq path number
required
| Order sequence |
| Field | Description |
|---|---|
(stream) JSON objects | One Kubernetes Event per message frame |
The wire path iskubeevents, notevents. Some client tools accepteventsand rewrite it client-side, but the provider only responds onkubeevents.
JWT scope must include events. # --insecure skips TLS verification (provider certs are self-signed).
websocat --insecure "wss://${HOSTURI#https://}/lease/${DSEQ}/${GSEQ}/${OSEQ}/kubeevents" \
-H "Authorization: Bearer $JWT"# --insecure skips TLS verification (provider certs are self-signed).
websocat --insecure "wss://${HOSTURI#https://}/lease/${DSEQ}/${GSEQ}/${OSEQ}/kubeevents" \
-H "Authorization: Bearer $JWT" WSS wss://{hostUri}/lease/{dseq}/{gseq}/{oseq}/shell
Provider
Interactive kubectl exec-style shell. Binary multiplexed stream of stdin/stdout/stderr/resize. Advanced — prefer the SDK helpers for most use cases.
| Parameter | Description |
|---|---|
Authorization header string
required
| Bearer (scope must include shell) |
dseq path string
required
| Deployment sequence ID |
gseq path number
required
| Group sequence |
oseq path number
required
| Order sequence |
service query string
required
| Service name to exec into |
podIndex query number
required
| Pod index (zero-based) |
tty query number
required
| 1 to allocate a TTY, 0 otherwise |
stdin query number
required
| 1 to attach stdin, 0 otherwise |
cmd0, cmd1, … query string
required
| Command and each argument as separate numbered params, e.g. cmd0=/bin/sh&cmd1=-c&cmd2=ls. There is no single (or base64) cmd param. |
| Field | Description |
|---|---|
(stream) binary | Multiplexed binary frames; the first byte is the stream code — 100=stdout, 101=stderr, 102=result (JSON, e.g. {\"exit_code\":0}), 103=failure. Client→server uses 104=stdin, 105=terminal-resize. |
JWT scope must include shell.Command and arguments are passed as separate numbered query params (cmd0,cmd1, …) — there is no single base64-encodedcmdparam.ttyandstdinare required.
The example above is a one-shot command (tty=0&stdin=0). For an interactive session, settty=1&stdin=1, stream keystrokes as104-prefixed binary frames, and send terminal resizes as105frames. Most users should use the Akash CLI (provider-services lease-shell --tty) or SDK helpers instead of driving this directly — see Shell Access.
Console UI uses xterm.js on the client side to render the stream.For programmatic shells, use the provider's kubectl exec-style protocol via the SDK helpers rather than constructing this WebSocket by hand. See also
- Getting Started — full workflow with examples
- Quickstart — end-to-end deployment in five API calls
- SDL Reference — SDL syntax and options
- Console — visual interface for managing deployments
- GitHub: akash-network/console — source and issues