Confidential Agents API

The Confidential Agents API provisions and manages per-tenant confidential VM (CVM) instances that run packaged agents such as OpenClaw. Each instance is a hardware-isolated workload running inside a Trusted Execution Environment, addressable over SSH and uniquely named under your tenant subdomain.

Quickstart

This walkthrough creates an instance, waits until it is ready, reads its instance record, and deletes it. The examples use curl and jq.

1. Obtain credentials

Tenants and API keys are provisioned by Confidential. To request access, contact us.

When your tenant is created, you receive:

Export both values before running the examples:

export API_BASE="https://api.confidential.ai"
export TENANT_SLUG="acme"
export CA_API_KEY="confai_live_replace_with_your_key"

2. Test your API key

Call the usage endpoint. A successful response returns a data.pricing object and a data.usage object for the current billing cycle.

curl -sS "$API_BASE/v1/usage" \
  -H "Authorization: Bearer $CA_API_KEY" \
  | jq .

3. Create an instance

Use an ed25519 or ecdsa SSH public key. ssh-rsa keys are rejected.

export SSH_PUBLIC_KEY="$(cat ~/.ssh/id_ed25519.pub)"

CREATE_RESPONSE="$(
  curl -sS -X POST "$API_BASE/v1/instances" \
    -H "Authorization: Bearer $CA_API_KEY" \
    -H "Content-Type: application/json" \
    --data "$(jq -n --arg public_key "$SSH_PUBLIC_KEY" '{
      public_key: $public_key,
      agent: "openclaw",
      inference_mode: "default_gateway"
    }')"
)"

echo "$CREATE_RESPONSE" | jq .

export INSTANCE_NAME="$(echo "$CREATE_RESPONSE" | jq -r '.data.name')"
export INSTANCE_HOSTNAME="$(echo "$CREATE_RESPONSE" | jq -r '.data.hostname')"

The create response is 202 Accepted. The instance starts in provisioning.

4. Get instance info

curl -sS "$API_BASE/v1/instances/$INSTANCE_NAME" \
  -H "Authorization: Bearer $CA_API_KEY" \
  | jq .

The response includes fields such as name, status, agent, hostname, inference_mode, created_at, and ready_at.

5. Poll until the instance is ready

There are no webhooks for instance state changes. Poll GET /v1/instances/{name} until status becomes ready.

while true; do
  INSTANCE_RESPONSE="$(
    curl -sS "$API_BASE/v1/instances/$INSTANCE_NAME" \
      -H "Authorization: Bearer $CA_API_KEY"
  )"

  STATUS="$(echo "$INSTANCE_RESPONSE" | jq -r '.data.status')"
  echo "status=$STATUS"

  if [ "$STATUS" = "ready" ]; then
    export INSTANCE_HOSTNAME="$(echo "$INSTANCE_RESPONSE" | jq -r '.data.hostname')"
    break
  fi

  if [ "$STATUS" = "failed" ]; then
    echo "$INSTANCE_RESPONSE" | jq .
    exit 1
  fi

  sleep 15
done

echo "Instance is ready: $INSTANCE_HOSTNAME"

Once the instance is ready, connect over SSH with the private key that matches the public key from the create request:

ssh -i ~/.ssh/id_ed25519 "$INSTANCE_HOSTNAME"

6. Delete the instance

When you are done, delete the instance. The response is 202 Accepted; teardown may complete immediately with status: "terminated" or briefly report terminating.

curl -sS -X DELETE "$API_BASE/v1/instances/$INSTANCE_NAME" \
  -H "Authorization: Bearer $CA_API_KEY" \
  | jq .

Endpoints

All endpoints are served from https://api.confidential.ai and versioned under /v1.

Create Instance

POST /v1/instances

Provisions a new confidential VM under your tenant. Returns 202 Accepted with the generated instance name. Provisioning typically completes within a few minutes; poll the Retrieve Instance endpoint until status flips from provisioning to ready.

Headers

HeaderRequiredDescription
AuthorizationyesBearer <api-key>.
Idempotency-KeyrecommendedPrevents duplicate provisioning on retry. See below.
X-Correlation-IDoptionalClient-supplied tag echoed back on the response.

Idempotency-Key

Idempotency-Key prevents duplicate side effects when retrying POST /v1/instances. It is not a request ID and not a correlation ID — each retry still receives a fresh request_id.

Request body

{
  "public_key": "ssh-ed25519 AAAAC3... user@example",
  "agent": "openclaw",
  "inference_mode": "default_gateway",
  "inference_model": "<model-id>"
}
FieldTypeRequiredDescription
public_keystringyesOpenSSH-formatted public key. Must be ssh-ed25519 or ecdsa-sha2-*. ssh-rsa is rejected.
agentstringnoPackaged agent to install. Defaults to openclaw. Currently openclaw is the only supported value.
inference_modestringnoInference routing mode. If omitted, the platform selects one for you. Currently default_gateway is the only supported value.
inference_modelstringnoModel identifier to use through the default inference gateway. If omitted, the platform selects a model for you and returns the chosen identifier in every subsequent response for this instance. Specify this field only if you need a particular model.

Response — 202 Accepted

{
  "request_id": "4f1f6b6ab27d49bdb1a6a7a21c9f3b42",
  "correlation_id": null,
  "data": {
    "name": "4k9p2xq7",
    "status": "provisioning",
    "agent": "openclaw",
    "hostname": "4k9p2xq7.acme.confidential.ai",
    "public_key": "ssh-ed25519 AAAAC3... user@example",
    "inference_mode": "default_gateway",
    "inference_model": "<model-id>",
    "egress_limit_bytes": 5368709120,
    "created_at": "2026-05-01T20:14:22Z"
  }
}

Once status reaches ready, connect over SSH:

ssh <hostname>

If your SSH client requires an explicit user or key path:

ssh -i <private-key-path> azureuser@<hostname>

Errors

CodeHTTPWhen
invalid_request400public_key is missing or not an ed25519/ecdsa OpenSSH key, or an unsupported agent or inference_mode was supplied.
unauthenticated401Missing or invalid Bearer token.
conflict409The same Idempotency-Key was reused with a different request body.

List Instances

GET /v1/instances

Returns all instances belonging to your tenant, including terminated instances retained for the 30-day name-reservation window.

Response — 200 OK

{
  "request_id": "4f1f6b6ab27d49bdb1a6a7a21c9f3b42",
  "correlation_id": null,
  "data": {
    "instances": [
      {
        "name": "4k9p2xq7",
        "status": "ready",
        "agent": "openclaw",
        "hostname": "4k9p2xq7.acme.confidential.ai",
        "public_key": "ssh-ed25519 AAAAC3... user@example",
        "inference_mode": "default_gateway",
        "inference_model": "<model-id>",
        "egress_limit_bytes": 5368709120,
        "failure_code": null,
        "failure_message": null,
        "terminated_reason": null,
        "created_at": "2026-05-01T20:14:22Z",
        "ready_at": "2026-05-01T20:18:33Z",
        "terminated_at": null
      }
    ],
    "next_cursor": null
  }
}

See the Status enum under Retrieve Instance below for the full list of status values and field semantics.

Pagination

The response includes next_cursor, currently always null. Cursor pagination will be activated in a future release; the response shape is stable.

Retrieve Instance

GET /v1/instances/{name}

Returns the full record for one instance. Use this endpoint to poll provisioning status and to read current state.

There are no webhooks for instance state changes — poll this endpoint instead.

Path parameters

ParameterDescription
nameThe 8-character generated instance name, e.g. 4k9p2xq7.

Response — 200 OK

{
  "request_id": "4f1f6b6ab27d49bdb1a6a7a21c9f3b42",
  "correlation_id": "venice-job-2026-05-03-001",
  "data": {
    "name": "4k9p2xq7",
    "status": "ready",
    "agent": "openclaw",
    "hostname": "4k9p2xq7.acme.confidential.ai",
    "public_key": "ssh-ed25519 AAAAC3... user@example",
    "inference_mode": "default_gateway",
    "inference_model": "<model-id>",
    "egress_limit_bytes": 5368709120,
    "failure_code": null,
    "failure_message": null,
    "terminated_reason": null,
    "created_at": "2026-05-01T20:14:22Z",
    "ready_at": "2026-05-01T20:18:33Z",
    "terminated_at": null
  }
}

Status enum

ValueMeaning
provisioningInstance is being claimed or cold-started.
readySSH is reachable and claim-time setup has completed. Run in-CVM verification to confirm attestation before trusting the workload.
failedProvisioning failed terminally. failure_code and failure_message may be populated.
terminatingDelete is in progress.
terminatedResources have been released. The record and name reservation are retained for at least 30 days.

Egress

Each instance has a hard 5 GB egress limit per its lifetime. The platform monitors per-VM network egress and enforces the limit at the instance firewall layer once the threshold is observed. egress_limit_bytes is reported on every instance record.

Errors

CodeHTTPWhen
not_found404Instance does not exist or belongs to another tenant.
unauthenticated401Missing or invalid Bearer token.

Delete Instance

DELETE /v1/instances/{name}

Tears down the CVM and removes its DNS record. The database row is retained with a terminal status, and the name remains reserved for at least 30 days.

Returns 202 Accepted. Teardown may complete synchronously, in which case the response shows status: "terminated". In other cases the instance briefly reports terminating and reaches terminated on subsequent retrieval.

Path parameters

ParameterDescription
nameThe 8-character generated instance name, e.g. 4k9p2xq7.

Response — 202 Accepted

{
  "request_id": "4f1f6b6ab27d49bdb1a6a7a21c9f3b42",
  "correlation_id": null,
  "data": {
    "name": "4k9p2xq7",
    "status": "terminated",
    "agent": "openclaw",
    "hostname": "4k9p2xq7.acme.confidential.ai",
    "public_key": "ssh-ed25519 AAAAC3... user@example",
    "inference_mode": "default_gateway",
    "inference_model": "<model-id>",
    "egress_limit_bytes": 5368709120,
    "failure_code": null,
    "failure_message": null,
    "terminated_reason": "deleted_via_api",
    "created_at": "2026-05-01T20:14:22Z",
    "ready_at": "2026-05-01T20:18:33Z",
    "terminated_at": "2026-05-01T20:24:22Z"
  }
}

Errors

CodeHTTPWhen
not_found404Instance does not exist or belongs to another tenant.
unauthenticated401Missing or invalid Bearer token.

Get Usage

GET /v1/usage

Returns the current billing-cycle consumption summary for your tenant — the same numbers that drive your invoice.

Response — 200 OK

{
  "request_id": "4f1f6b6ab27d49bdb1a6a7a21c9f3b42",
  "correlation_id": null,
  "data": {
    "pricing": {
      "period_start": "2026-04-15T14:32:11Z",
      "period_end": "2026-05-15T14:32:11Z",
      "subscription_cost_usd": "200.00",
      "instance_hours_included": 200,
      "overage_per_hour_usd": "0.45"
    },
    "usage": {
      "instance_hours_used": 142.7,
      "inference_cost_usd": "28.23",
      "egress_bytes": 1048576,
      "egress_limit_bytes": 5368709120,
      "egress_observed_at": "2026-05-01T20:14:22Z",
      "egress_blocked": false,
      "egress_block_applied_at": null
    }
  }
}

pricing fields

FieldDescription
period_start, period_endStart and end of the current billing window (ISO 8601). The window is anchored to your tenant's signup time, not to calendar months.
subscription_cost_usdBase subscription cost for the period.
instance_hours_includedInstance-hours included in the base subscription.
overage_per_hour_usdCost per instance-hour beyond the included amount.

usage fields

FieldDescription
instance_hours_usedTotal instance-hours consumed this period across all instances.
inference_cost_usdInference spend this period through the default gateway.
egress_bytesMost recent observed egress total for the active instance(s).
egress_limit_bytesPer-instance hard egress limit.
egress_observed_atWhen the egress total was last sampled.
egress_blockedtrue if egress is currently blocked at the firewall.
egress_block_applied_atWhen the block was applied, or null.

Per-day and per-instance usage breakdowns are not currently exposed through the API.

Authentication

All endpoints require a Bearer token in the Authorization header:

Authorization: Bearer ca_<8 char lowercase alphanumeric>_<18 char lowercase alphanumeric>

Obtaining credentials

Tenants and API keys are provisioned by Confidential. To request access, contact us. Self-service signup through the public API is not currently available.

When your tenant is created, you receive:

Authentication errors

A missing, malformed, or unknown token returns:

{
  "request_id": "4f1f6b6ab27d49bdb1a6a7a21c9f3b42",
  "correlation_id": null,
  "error": {
    "code": "unauthenticated",
    "message": "missing or invalid Bearer token"
  }
}

A valid token used against a tenant or resource it is not authorized for returns forbidden (403). See the error code table under Conventions below.

Conventions

Response envelope

Every JSON response is wrapped in a consistent envelope. On success:

{
  "request_id": "4f1f6b6ab27d49bdb1a6a7a21c9f3b42",
  "correlation_id": "venice-job-2026-05-03-001",
  "data": { }
}

On error:

{
  "request_id": "4f1f6b6ab27d49bdb1a6a7a21c9f3b42",
  "correlation_id": null,
  "error": {
    "code": "invalid_request",
    "message": "public_key must be an ed25519 or ecdsa OpenSSH public key",
    "param": "public_key"
  }
}

The same request_id is mirrored in the X-Request-ID response header. If you supplied an X-Correlation-ID, it is echoed back in X-Correlation-ID on the response.

Request IDs and Correlation IDs

The API generates the canonical request_id for every request. Clients cannot choose it. If you send X-Request-ID, it is ignored and a fresh server-side ID is generated.

To tag requests for your own tracking, send X-Correlation-ID:

X-Correlation-ID: venice-job-2026-05-03-001

Correlation IDs are not required to be unique and the server does not enforce a format.

Error codes

CodeHTTPMeaning
invalid_request400Malformed or invalid input.
unauthenticated401Missing or invalid Bearer token.
forbidden403Token is valid but not allowed for this tenant or resource.
not_found404Resource does not exist, or belongs to another tenant.
conflict409State or idempotency conflict.
internal_error500Unexpected server error. Quote request_id when reporting.

Per-tenant subdomain

Each tenant is assigned a slug at onboarding and gets a dedicated subdomain:

{customer-slug}.confidential.ai

All instances are addressable as:

{instance-name}.{customer-slug}.confidential.ai

Example:

4k9p2xq7.acme.confidential.ai

Instance names

Instances are identified by an auto-generated name scoped to your tenant.

Need help?

Contact us for access, support, or feature requests.