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 |
| v2 | Stable | Deployment settings (runtime limit) |
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. Console funds it automatically from your account credits, so the request takes no deposit.
| 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. May include a params.tee field (cpu/cpu-gpu) to request Confidential Compute |
data.runtimeLimitHours body number | Close the deployment after this many hours of runtime, counted from lease start. Omit for always-on funding |
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 |
Request Confidential Compute (TEE) by adding aparams.teefield (cpuorcpu-gpu) to a service in the SDL —cpu-gpurequires GPU resources. See Confidential Compute (TEE).
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..." } }'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..." } }' 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
Deprecated. Console funds every deployment automatically for as long as your account has credits, so there is nothing for this endpoint to do. It still accepts requests so existing integrations keep working, and will be removed in a future release.
| 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. Accepted but no longer needed |
Response 200 OK
Replaced by automatic funding. Add credits to your account instead. See How Funding Works.
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. Whatever it has not yet spent returns to your Console account balance once the chain settles.
| 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 the funding settings for a specific deployment, including its runtime limit. 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 | Always true. Automatic funding cannot be turned off |
data.estimatedTopUpAmount number | Estimated top-up amount per cycle (USD) |
data.topUpFrequencyMs number | Top-up cadence in milliseconds |
data.runtimeLimitHours number \| null | Runtime limit in hours chosen at creation, or null for always-on funding |
data.runtimeEndsAt string \| null | When the runtime limit is reached, anchored at lease start; null until the lease starts or when no limit is set |
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 the settings row for a deployment when it does not yet exist. Automatic funding is on by default and cannot be turned off.
| Parameter | Description |
|---|---|
x-api-key header string
required
| Your API key |
data.dseq body string
required
| Deployment sequence number |
data.autoTopUpEnabled body boolean | Defaults to true when omitted. An explicit false is rejected |
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. Use this to set, extend, or remove a deployment's runtime limit.
| 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 | An explicit false is rejected |
data.runtimeLimitHours body number \| null | New total runtime limit in hours, counted from lease start. Send null to remove the limit and return the deployment to always-on funding. Lowering a limit is not supported |
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": { "runtimeLimitHours": 48 } }'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": { "runtimeLimitHours": 48 } }'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