API reference
Every Confidential Agents API endpoint — create, list, retrieve, and delete a confidential-VM instance, and read usage — with request fields, response shapes, authentication, and the conventions the API follows.
The Confidential Agents API provisions and manages per-organization 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 organization subdomain.
New here? Get started creates an instance end to end.
Endpoints
All endpoints are served from https://api.confidential.ai and versioned under /v1.
Create Instance
POST /v1/instancesProvisions a new confidential VM under your organization. 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
| Header | Required | Description |
|---|---|---|
Authorization | yes | Bearer <api-key>. |
Idempotency-Key | recommended | Prevents duplicate provisioning on retry. See below. |
X-Correlation-ID | optional | Client-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.
- A duplicate request within 24 hours returns the original result and does not provision a second CVM.
- The response body is the original API result, but
request_idandcorrelation_idreflect the current attempt. - Reusing the same
Idempotency-Keywith a different request body returns409 conflict.
Request body
{
"public_key": "ssh-ed25519 AAAAC3... user@example",
"agent": "openclaw",
"inference_mode": "default_gateway",
"inference_model": "<model-id>"
}| Field | Type | Required | Description |
|---|---|---|---|
public_key | string | yes | OpenSSH-formatted public key (ssh-rsa, ssh-ed25519, ecdsa-sha2-*, etc.). We recommend ssh-ed25519 for new keys; ssh-rsa is accepted for backwards compatibility. |
agent | string | no | Packaged agent to install. Defaults to openclaw. Currently openclaw is the only supported value. |
inference_mode | string | no | Inference routing mode. If omitted, the platform selects one for you. Currently default_gateway is the only supported value. |
inference_model | string | no | Model 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> <hostname>Errors
| Code | HTTP | When |
|---|---|---|
invalid_request | 400 | public_key is missing or not a valid OpenSSH public key, or an unsupported agent or inference_mode was supplied. |
unauthenticated | 401 | Missing or invalid Bearer token. |
conflict | 409 | The same Idempotency-Key was reused with a different request body. |
List Instances
GET /v1/instancesReturns all instances belonging to your organization, 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
| Parameter | Description |
|---|---|
name | The 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
| Value | Meaning |
|---|---|
provisioning | Instance is being claimed or cold-started. |
ready | SSH is reachable and claim-time setup has completed. Run in-CVM verification to confirm attestation before trusting the workload. |
failed | Provisioning failed terminally. failure_code and failure_message may be populated. |
terminating | DELETE /v1/instances/{name} has been accepted and resource teardown is in progress. The instance transitions to terminated once Azure resources are fully released. |
terminated | Resources 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
| Code | HTTP | When |
|---|---|---|
not_found | 404 | Instance does not exist or belongs to another organization. |
unauthenticated | 401 | Missing 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 with status: terminating. Azure resource teardown completes asynchronously; the instance transitions to terminated once cleanup finishes. Poll GET /v1/instances/{name} until status is terminated if you need confirmation that resources have been released.
Path parameters
| Parameter | Description |
|---|---|
name | The 8-character generated instance name, e.g. 4k9p2xq7. |
Response — 202 Accepted
{
"request_id": "4f1f6b6ab27d49bdb1a6a7a21c9f3b42",
"correlation_id": null,
"data": {
"name": "4k9p2xq7",
"status": "terminating",
"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": null
}
}Errors
| Code | HTTP | When |
|---|---|---|
not_found | 404 | Instance does not exist or belongs to another organization. |
unauthenticated | 401 | Missing or invalid Bearer token. |
Get Usage
GET /v1/usageReturns the current billing-cycle consumption summary for your organization — 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
| Field | Description |
|---|---|
period_start, period_end | Start and end of the current billing window (ISO 8601). The window is anchored to your organization's signup time, not to calendar months. |
subscription_cost_usd | Base subscription cost for the period. |
instance_hours_included | Instance-hours included in the base subscription. |
overage_per_hour_usd | Cost per instance-hour beyond the included amount. |
usage fields
| Field | Description |
|---|---|
instance_hours_used | Total instance-hours consumed this period across all instances. |
inference_cost_usd | Inference spend this period through the default gateway. |
egress_bytes | Most recent observed egress total for the active instance(s). |
egress_limit_bytes | Per-instance hard egress limit. |
egress_observed_at | When the egress total was last sampled. |
egress_blocked | true if egress is currently blocked at the firewall. |
egress_block_applied_at | When 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
Organizations 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 organization is created, you receive:
- An organization slug (e.g.
acme), which is used in your per-organization subdomain ({instance-name}.acme.confidential.ai). - One or more API keys of the form
ca_<prefix>_<secret>, whereprefixandsecretare lowercase alphanumeric strings (8 and 18 characters respectively). Treat these as secrets — anyone with the key can provision and delete instances against your organization.
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 an organization 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 is not a valid OpenSSH public key: ...",
"param": "public_key"
}
}request_id— server-generated identifier for this HTTP request. Always present. Quote it when contacting support.correlation_id— the value of theX-Correlation-IDrequest header if you sent one, otherwisenull.error.param— present when the error maps cleanly to a single request field.error.message— safe to surface to end users.
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-001Correlation IDs are not required to be unique and the server does not enforce a format.
Error codes
| Code | HTTP | Meaning |
|---|---|---|
invalid_request | 400 | Malformed or invalid input. |
unauthenticated | 401 | Missing or invalid Bearer token. |
forbidden | 403 | Token is valid but not allowed for this organization or resource. |
not_found | 404 | Resource does not exist, or belongs to another organization. |
conflict | 409 | State or idempotency conflict. |
internal_error | 500 | Unexpected server error. Quote request_id when reporting. |
Per-organization subdomain
Each organization is assigned a slug at onboarding and gets a dedicated subdomain:
{customer-slug}.confidential.aiAll instances are addressable as:
{instance-name}.{customer-slug}.confidential.aiExample:
4k9p2xq7.acme.confidential.aiInstance names
Instances are identified by an auto-generated name scoped to your organization.
- Format: 8-character lowercase alphanumeric string (e.g.
4k9p2xq7). - Uniqueness: Names are unique within an organization.
- Reuse: Names are not reused for at least 30 days after termination, so a stored reference cannot silently point at a different instance.
- Custom names: Customer-supplied instance names are not currently supported.
Need help?
Contact us for access, support, or feature requests.