# REST API

The native API: 68 routes for accounts, keys, billing, sandboxes, GPU pods, vendor credentials and partner supply. Everything below the conventions is generated from the same source as the OpenAPI document, so it matches what the API serves.

`GET https://api.rent.dev/openapi.json`

## Conventions

Every route is under `https://api.rent.dev`. Bodies are JSON with snake_case members. Times are RFC 3339 in UTC. Money is integer micro-USD: 1 USD is 1,000,000.

The request line above answers the OpenAPI 3.1 document. It needs no key and lists these routes with their full schemas.

## Authentication

Send an API key as `Authorization: Bearer rent_sk_…` or as `X-API-Key`, or an OAuth access token issued by `https://auth.rent.dev` for the resource `https://api.rent.dev`. Routes marked "No credentials" need neither.

An OAuth token needs the scope of the area it calls: `sandbox`, `gpu`, `billing`, `keys` or `supply`. An API key carries all of them but `supply`.

[The supply routes](#supply) that manage nodes and earnings take an access token only. Ask for the scope by name, as `scope=supply`, when you [trade a signed assertion for a token](/docs/agents#oauth).

## Errors

A refusal is an RFC 9457 Problem, served as `application/problem+json`, with a stable `code`. The status tables below name the codes each route answers; [the errors page](/docs/errors) says what to do about each.

## Long-running operations

Work that can outlast a request answers 202 with an Operation and a `Location` header. Poll `GET /v1/operations/{id}`, waiting `Retry-After` seconds between polls. When `done` is true the Operation holds exactly one of `response`, the finished resource, or `error`, a Problem.

## Lists

List routes take `limit` (1 to 200, default 50) and `cursor`. Each page answers `data` and `next_cursor`, which is null on the last page.

## Actions on a resource

An action that is not a plain create, read or delete is a colon suffix on the resource:

- `POST /v1/keys/{id}:rotate`
- `POST /v1/sandboxes/{id}:extend`
- `POST /v1/sandboxes/{id}:pause`
- `POST /v1/sandboxes/{id}:resume`
- `POST /v1/supply/nodes/{id}:probe`
- `POST /v1/supply/nodes/{id}:revoke`

A `POST` under `/v1` also takes an `Idempotency-Key` header; see [retrying safely](/docs/agents#idempotency).

## Accounts

Keyless signup, agent keys, wallet login and recovery.

### Get a single-use nonce for signing up, binding or recovering with an agent key

`GET https://api.rent.dev/v1/accounts/nonce`

No credentials.

| Status | Meaning |
| --- | --- |
| `200` | A nonce valid for 5 minutes. |

### Create an account and its first API key

`POST https://api.rent.dev/v1/accounts`

No email and no proof: an empty body creates an account. Send a signed nonce to bind an Ed25519 agent key for recovery at the same time. A keyless signup sent with `Idempotency-Key` can be retried safely. No credentials.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `idempotency-key` | header | `string` | Keyless signup only. A repeat within 24 hours answers the same account with a new API key that replaces the first (revoked at once), while the account is untouched: its key never used, nothing bound, no balance, no top-up pending. After that a repeat answers 422 `idempotency_key_reused` and no key. Only a hash of the value is stored; treat it like a secret for those 24 hours. |

| Body field | Type | Description |
| --- | --- | --- |
| `public_key` | `string` | Ed25519 public key: 32 bytes, base64url without padding. |
| `nonce` | `string` |  |
| `signature` | `string` | Ed25519 signature: 64 bytes, base64url without padding. |

The body is optional.

| Status | Meaning |
| --- | --- |
| `201` | The account and its API key, shown once. |
| `400` | `invalid_request` or `unsupported_parameter`. |
| `401` | `unauthenticated`: the signature or nonce did not verify. |
| `409` | `conflict`: the agent key is already bound to an account; or `idempotency_in_flight`: the first signup with this Idempotency-Key is still running after a 5-second wait (a repeat that arrives while it runs waits for it and answers its account). |
| `422` | `idempotency_key_reused`: the Idempotency-Key was used with a different request, or its account has been used since. |

### Bind an Ed25519 agent key to this account

`POST https://api.rent.dev/v1/accounts/agent-key`

The key can then recover the account with a new API key without a spend limit, so an API key with a spend limit cannot bind one (403). Needs an API key or an OAuth access token.

| Body field | Type | Description |
| --- | --- | --- |
| `public_key` | `string` | Required. Ed25519 public key: 32 bytes, base64url without padding. |
| `nonce` | `string` | Required. |
| `signature` | `string` | Required. Ed25519 signature: 64 bytes, base64url without padding. |

| Status | Meaning |
| --- | --- |
| `201` | The bound key. |
| `400` | `invalid_request` or `unsupported_parameter`. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope, or the caller is an API key with a spend limit (use an uncapped key or a signed-in person). |
| `409` | `conflict`: the key is already bound to an account. |

### Get a new API key with a signed nonce from the bound agent key

`POST https://api.rent.dev/v1/accounts/recover`

No credentials.

| Body field | Type | Description |
| --- | --- | --- |
| `public_key` | `string` | Required. Ed25519 public key: 32 bytes, base64url without padding. |
| `nonce` | `string` | Required. |
| `signature` | `string` | Required. Ed25519 signature: 64 bytes, base64url without padding. |

| Status | Meaning |
| --- | --- |
| `200` | The account and a new API key, shown once. |
| `400` | `invalid_request` or `unsupported_parameter`. |
| `401` | `unauthenticated`: the signature or nonce did not verify. |
| `404` | `not_found`: no account has this key bound. |

### Get a new API key with a Sign-In with Ethereum message from the bound wallet

`POST https://api.rent.dev/v1/accounts/wallet-login`

No credentials.

| Body field | Type | Description |
| --- | --- | --- |
| `message` | `string` | Required. EIP-4361 (Sign-In with Ethereum) message whose `Nonce` is a nonce from GET /v1/accounts/nonce. EIP-4361 nonces are alphanumeric, so write the nonce's 32 bytes as 64 hex characters; a nonce that is already alphanumeric (no `-` or `_`) is also accepted as is. Single use, spent once the signature verifies. |
| `signature` | `string` | Required. |

| Status | Meaning |
| --- | --- |
| `200` | The account and a new API key, shown once. |
| `400` | `invalid_request` or `unsupported_parameter`. |
| `401` | `unauthenticated`: the message or signature did not verify. |
| `404` | `not_found`: no account is bound to this wallet. |

### List the agent keys bound to this account

`GET https://api.rent.dev/v1/accounts/agent-keys`

Each one recovers the account (`POST /v1/accounts/recover`) and signs agent grants. Needs an API key or an OAuth access token.

| Status | Meaning |
| --- | --- |
| `200` | The bound keys. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope. |

### Unbind an agent key

`DELETE https://api.rent.dev/v1/accounts/agent-keys/{public_key}`

From now on the key recovers nothing and its signed assertions mint no token for this account. Never refused for being the last binding: the account keeps its API keys. Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `public_key` | path | `string` | Required. Ed25519 public key: 32 bytes, base64url without padding. |

| Status | Meaning |
| --- | --- |
| `200` | The key unbound. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope, or the caller is an API key with a spend limit (use an uncapped key or a signed-in person). |
| `404` | `not_found`, also for another account's resource. |

### List the wallets bound to this account

`GET https://api.rent.dev/v1/accounts/wallets`

Needs an API key or an OAuth access token.

| Status | Meaning |
| --- | --- |
| `200` | The bound wallets. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope. |

### Unbind a wallet

`DELETE https://api.rent.dev/v1/accounts/wallets/{chain}/{address}`

From now on the wallet no longer logs in to this account (`POST /v1/accounts/wallet-login`). A later payment from it without an API key starts a new account. Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `chain` | path | `string` | Required. |
| `address` | path | `string` | Required. |

| Status | Meaning |
| --- | --- |
| `200` | The wallet unbound. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope, or the caller is an API key with a spend limit (use an uncapped key or a signed-in person). |
| `404` | `not_found`, also for another account's resource. |

### List the people linked to this account

`GET https://api.rent.dev/v1/accounts/people`

People who sign in with a passkey and act for this account through OAuth (the console, the CLI, agents they authorize). Needs an API key or an OAuth access token.

| Status | Meaning |
| --- | --- |
| `200` | The linked people. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope. |

### Remove a person from this account

`DELETE https://api.rent.dev/v1/accounts/people/{user_id}`

At once: the person no longer sees or can choose the account, and every OAuth grant and token they hold for it stops working. What they hold for their other accounts is untouched. Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `user_id` | path | `string` | Required. usr_ followed by a 26-character ULID. |

| Status | Meaning |
| --- | --- |
| `200` | The person removed. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope, or the caller is an API key with a spend limit (use an uncapped key or a signed-in person). |
| `404` | `not_found`, also for another account's resource. |

## Keys

API keys and per-key spend limits.

### List API keys

`GET https://api.rent.dev/v1/keys`

Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `cursor` | query | `string` |  |
| `limit` | query | `integer, 1 to 200` | Page size, 1-200; default 50. |

| Status | Meaning |
| --- | --- |
| `200` | Keys, newest first. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope. |

### Create an API key

`POST https://api.rent.dev/v1/keys`

An API key with a spend limit is a delegated budget: it cannot create keys (403). Use an uncapped key or a signed-in person. Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `idempotency-key` | header | `string` | Replays the first response for 24 hours. Reuse with a different payload answers 422; while the first is in flight, 409. A show-once secret (an API key's `secret`) is never stored: a replay leaves it out and names it in the `Show-Once-Omitted` header. |

| Body field | Type | Description |
| --- | --- | --- |
| `name` | `string` |  |
| `spend_limit_micros` | `integer or null` | Integer micro-USD, zero or more. |

The body is optional.

| Status | Meaning |
| --- | --- |
| `201` | The key with its secret, shown once. |
| `400` | `invalid_request` or `unsupported_parameter`. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope, or the caller is an API key with a spend limit (use an uncapped key or a signed-in person). |
| `409` | `idempotency_in_flight` or `conflict`. |
| `422` | `idempotency_key_reused`: the key was used with a different payload. |

### Rename a key or change its spend limit

`PATCH https://api.rent.dev/v1/keys/{id}`

Not open to an API key with a spend limit, for any key including itself (403). Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. key_ followed by a 26-character ULID. |

| Body field | Type | Description |
| --- | --- | --- |
| `name` | `string or null` |  |
| `spend_limit_micros` | `integer or null` | New lifetime limit; null removes it. |

| Status | Meaning |
| --- | --- |
| `200` | The updated key. |
| `400` | `invalid_request` or `unsupported_parameter`. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope, or the caller is an API key with a spend limit (use an uncapped key or a signed-in person). |
| `404` | `not_found`, also for another account's resource. |

### Replace a key with a new secret, revoking the old one

`POST https://api.rent.dev/v1/keys/{id}:rotate`

An API key with a spend limit may rotate only itself; its successor's limit is what it has left of its limit (never below zero). Rotating another key needs an uncapped key or a signed-in person (403). Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. key_ followed by a 26-character ULID. |
| `idempotency-key` | header | `string` | Replays the first response for 24 hours. Reuse with a different payload answers 422; while the first is in flight, 409. A show-once secret (an API key's `secret`) is never stored: a replay leaves it out and names it in the `Show-Once-Omitted` header. |

| Status | Meaning |
| --- | --- |
| `201` | The new key with its secret, shown once. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope, or the caller is an API key with a spend limit (use an uncapped key or a signed-in person). |
| `404` | `not_found`, also for another account's resource. |
| `409` | `idempotency_in_flight` or `conflict`. |
| `422` | `idempotency_key_reused`: the key was used with a different payload. |

### Revoke a key

`DELETE https://api.rent.dev/v1/keys/{id}`

An API key with a spend limit may revoke only itself (403 for another key). Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. key_ followed by a 26-character ULID. |

| Status | Meaning |
| --- | --- |
| `200` | The revoked key. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope, or the caller is an API key with a spend limit (use an uncapped key or a signed-in person). |
| `404` | `not_found`, also for another account's resource. |

## Operations

AIP-151 long-running operations.

### Poll a long-running operation

`GET https://api.rent.dev/v1/operations/{id}`

Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. op_ followed by a 26-character ULID. |

| Status | Meaning |
| --- | --- |
| `200` | The operation. While not done, Retry-After says when to poll again. Headers: `Retry-After`. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `404` | `not_found`, also for another account's resource. |

## Quotes

Price per task, own and vendor.

### Price per completed task for own sandboxes and published-rate vendors, and GPU rows

`GET https://api.rent.dev/v1/quotes`

No credentials.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `profile` | query | `agent-loop \| interactive \| workspace` | One profile; default all three. |
| `cpu_count` | query | `integer, 1 to 64` | Default 2. |
| `memory_mb` | query | `integer, at least 128` | Default 4096. |

| Status | Meaning |
| --- | --- |
| `200` | Quotes; cached 60 seconds. |
| `400` | `invalid_request` or `unsupported_parameter`. |

## Providers

The provider catalog: who sells compute, what they sell and what their terms permit.

### List compute providers and their published prices

`GET https://api.rent.dev/v1/providers`

The committed provider catalog: who sells compute, what they sell, and what their terms permit. Offerings carry the GPU axis (`gpu`, `usd_per_gpu_hour`) and the CPU axis (`cpu`, `usd_per_vcpu_hour`) side by side. A provider whose terms deny `display` is omitted; one whose terms deny `compare` is listed with its clause and no prices. A third-party figure keeps its `provenance`, and `attribution` names the obligations the response carries. No credentials.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `provider` | query | `string` | One provider id. |
| `gpu` | query | `string` | Canonical GPU model id (`h100-sxm-80gb`) or the vendor's own label. |
| `cpu` | query | `string` | The CPU capacity class (`shared`, `dedicated`) or the vendor's product label for a CPU offering (e.g. `macos`), matched without regard to case. |
| `architecture` | query | `x86_64 \| arm64` | Keep CPU offerings whose architecture the vendor states as this value. |
| `max_usd_per_vcpu_hour` | query | `number, at least 0` | Keep CPU offerings whose `usd_per_vcpu_hour` is at most this many USD. |
| `pricing_type` | query | `on_demand \| spot \| reserved \| serverless` |  |
| `covers` | query | `gpu \| node \| instance \| gpu_line_item` | Keep offerings whose figure covers this unit (`gpu`, `node`, `instance`, `gpu_line_item`). |
| `figure` | query | `listed \| effective_dated \| weighted_average \| median \| floor \| advertised_from` | Keep offerings whose figure was produced this way (`listed`, `floor`, …). |
| `gpu_count` | query | `integer, at least 1` | Keep GPU offerings whose `gpu.count` (GPUs per priced unit) is exactly this many. `gpu_count=1` asks for single-GPU offers only. |
| `comparison_group` | query | `string` | Keep offerings in exactly this comparison group; the id an offering carries groups it with the other offers of the same purchasable unit. |
| `region` | query | `string` | A region label or a canonical region id (`us-east-1`), matched without regard to case; either spelling of a registered place finds every offering in it. |
| `sort` | query | `price \| price_desc` | Offerings order: `price` sorts by the comparable hourly figure (per GPU, per vCPU, else per unit) ascending, `price_desc` descending; offerings without a figure sort last. Default keeps catalog order (provider, product, sku). |
| `max_usd_per_hour` | query | `number, at least 0` | Keep offerings whose hourly figure (`usd_per_gpu_hour` on a GPU row, else `usd_per_hour`) is at most this many USD. |
| `provenance` | query | `first_party \| third_party` |  |
| `use` | query | `fetch_automated \| store \| display \| compare \| redistribute \| derive` | The use the response asserts: only providers permitting it are listed, and only offerings whose `publish_mask` allows it are carried. Default `display`. |
| `agentic` | query | `true \| false` | `true` keeps providers with an agentic product; `false` keeps the rest. |
| `include` | query | `providers \| offerings \| all` | Which arrays to carry: `providers` (default), `offerings`, or `all`. |
| `limit` | query | `integer, 1 to 200` | Most members per requested array, 1-200; default 200. |

| Status | Meaning |
| --- | --- |
| `200` | Providers and the offerings matching the filters; cached 60 seconds. |
| `400` | `invalid_request` or `unsupported_parameter`. |

### Get a provider, its terms and its published prices

`GET https://api.rent.dev/v1/providers/{id}`

One provider with its full terms matrix and the offerings its terms permit publishing. A compare-denied provider answers with its clause and no offerings; a provider whose terms deny `display` answers 404. No credentials.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. Provider id (lowercase slug), e.g. `runpod`. |

| Status | Meaning |
| --- | --- |
| `200` | The provider and its offerings; cached 60 seconds. |
| `404` | `not_found`: no provider with this id is published. |

## Status

Service health.

### Service and endpoint health

`GET https://api.rent.dev/v1/status`

No credentials.

| Status | Meaning |
| --- | --- |
| `200` | Current status. |

## Billing

Balance, ledger, Stripe and x402 top-ups, auto-recharge.

### Prepaid balance

`GET https://api.rent.dev/v1/balance`

Needs an API key or an OAuth access token.

| Status | Meaning |
| --- | --- |
| `200` | Total, held and available micro-USD. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope. |

### Ledger entries, newest first

`GET https://api.rent.dev/v1/ledger`

Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `cursor` | query | `string` |  |
| `limit` | query | `integer, 1 to 200` | Page size, 1-200; default 50. |

| Status | Meaning |
| --- | --- |
| `200` | One page of entries. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope. |

### Create a Stripe Checkout Session to buy credits

`POST https://api.rent.dev/v1/credits/checkout`

Card, Link, US bank account or stablecoin. The fee, max(5.5%, $0.80), is a separate line. Credits land when the payment succeeds. Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `idempotency-key` | header | `string` | Replays the first response for 24 hours. Reuse with a different payload answers 422; while the first is in flight, 409. A show-once secret (an API key's `secret`) is never stored: a replay leaves it out and names it in the `Show-Once-Omitted` header. |

| Body field | Type | Description |
| --- | --- | --- |
| `amount_usd` | `string or number` | Required. Credits to buy, in USD, at most two decimal places. The fee is added on top. |

| Status | Meaning |
| --- | --- |
| `201` | Open checkout_url in a browser. |
| `400` | `invalid_request` or `unsupported_parameter`. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope. |
| `409` | `idempotency_in_flight` or `conflict`. |
| `422` | `idempotency_key_reused`: the key was used with a different payload. |
| `502` | `vendor_error`: the upstream vendor failed. |

### Create a Checkout Session that saves a card or Link method for auto-recharge

`POST https://api.rent.dev/v1/credits/payment-method`

Not open to an API key with a spend limit (403): auto-recharge charges the saved method off-session. Use an uncapped key or a signed-in person. Needs an API key or an OAuth access token.

| Status | Meaning |
| --- | --- |
| `201` | Open checkout_url in a browser. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope, or the caller is an API key with a spend limit (use an uncapped key or a signed-in person). |
| `502` | `vendor_error`: the upstream vendor failed. |

### Saved card and Link methods

`GET https://api.rent.dev/v1/billing/payment-methods`

Needs an API key or an OAuth access token.

| Status | Meaning |
| --- | --- |
| `200` | Saved methods. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope. |

### Auto-recharge settings

`GET https://api.rent.dev/v1/billing/auto-recharge`

Needs an API key or an OAuth access token.

| Status | Meaning |
| --- | --- |
| `200` | Current settings. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope. |

### Turn auto-recharge on or off

`PUT https://api.rent.dev/v1/billing/auto-recharge`

When available balance falls below the threshold, the saved method is charged off-session for the amount plus the fee. A decline or an SCA request turns it off. Not open to an API key with a spend limit (403): turning it on can charge the saved method at once. Use an uncapped key or a signed-in person. Needs an API key or an OAuth access token.

| Body field | Type | Description |
| --- | --- | --- |
| `enabled` | `boolean` | Required. |
| `threshold_usd` | `string or number` | Recharge when available balance falls below this. Required to enable. |
| `amount_usd` | `string or number` | Credits bought per recharge; the fee is added. Required to enable. |
| `payment_method_id` | `string` | A saved card or Link method. Required to enable. |

| Status | Meaning |
| --- | --- |
| `200` | The stored settings. |
| `400` | `invalid_request` or `unsupported_parameter`. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope, or the caller is an API key with a spend limit (use an uncapped key or a signed-in person). |
| `404` | `not_found`: no saved method with that id. |

### Top up with USDC over x402

`POST https://api.rent.dev/v1/credits/x402`

Without a `PAYMENT-SIGNATURE` header this answers 402 with `PAYMENT-REQUIRED`: x402 v2 `exact` on Base, amount = credits + 5.5% rounded up to a base unit. Retry with the signed payment. Credits land after settlement and an on-chain receipt. Without an API key, the paying wallet's account is credited, created if new, and its key returned once. Credentials are optional.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `amount_usd` | query | `string` | Required. Credits in USD, at least 0.000001. The 5.5% fee is added to the payment. |
| `payment-signature` | header | `string` | x402 v2 payment payload. |

| Status | Meaning |
| --- | --- |
| `200` | Settled and credited (also a repeat of a settled payment). Headers: `PAYMENT-RESPONSE`. |
| `201` | Settled; the wallet's new account was created and its API key is returned once. Headers: `PAYMENT-RESPONSE`. |
| `202` | Accepted: broadcast, not yet confirmed on-chain; poll the Operation at Location. When the payment was sent without an API key and created the paying wallet's account, the body also carries `account_id` (that account) and `api_key` (an `ApiKeyCreated` with its secret, shown once and never again) beside the Operation's members. Headers: `Location`, `Retry-After`. |
| `400` | `invalid_request` or `unsupported_parameter`. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `402` | Payment required, or the facilitator refused the payment. Headers: `PAYMENT-REQUIRED`. |

### Open a pay link: redirects to a fresh Checkout Session

`GET https://api.rent.dev/pay/{token}`

The link a 402 Problem carries as `topup.checkout_url`. Valid 24 hours; no key needed. No credentials.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `token` | path | `string` | Required. |

| Status | Meaning |
| --- | --- |
| `303` | Redirect to Stripe Checkout. Headers: `Location`. |
| `404` | `not_found`: the token is malformed or its signature does not verify. |
| `410` | `gone`: the link has expired. |

## Sandboxes

Sandboxes on the native API.

### Create a sandbox

`POST https://api.rent.dev/v1/sandboxes`

Takes a hold of the ceiling rate times the timeout, places the sandbox and answers 202 with an Operation. With `Prefer: wait=<s>`, answers 201 with the sandbox when it is ready within the wait. Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `idempotency-key` | header | `string` | Replays the first response for 24 hours. Reuse with a different payload answers 422; while the first is in flight, 409. A show-once secret (an API key's `secret`) is never stored: a replay leaves it out and names it in the `Show-Once-Omitted` header. |
| `prefer` | header | `string` | RFC 7240 `wait=<seconds>`: answer 201 with the sandbox if it is ready within the wait, else 202. |

| Body field | Type | Description |
| --- | --- | --- |
| `template` | `string` | Template alias: the caller's own or the public set. Default `base`. |
| `timeout_s` | `integer, 1 to 86400` | Seconds until the sandbox stops (or pauses with auto_pause). Default 300. |
| `metadata` | `Metadata` | Caller metadata. Keys starting `rentdev_` are reserved and dropped. |
| `env_vars` | `map of string to string` |  |
| `auto_pause` | `boolean` | Pause instead of stopping at the timeout. Default false. |
| `auto_resume` | `boolean` | Resume a paused sandbox when traffic arrives. Default false. |
| `allow_internet_access` | `boolean` |  |
| `network` | `SandboxNetwork` |  |
| `profile` | `agent-loop \| interactive \| workspace` | Expected duty, used for placement when no history exists. |
| `routing` | `SandboxRouting` |  |

The body is optional.

| Status | Meaning |
| --- | --- |
| `201` | Ready within the Prefer wait. |
| `202` | Accepted: poll the Operation at Location. Headers: `Location`, `Retry-After`. |
| `400` | `invalid_request` or `unsupported_parameter`. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `402` | `insufficient_balance`, `account_frozen` or `key_limit_reached`, with `balance_required_micros` and `topup` links. |
| `403` | `forbidden`: the token lacks the scope. |
| `404` | `not_found`: unknown template. |
| `409` | `idempotency_in_flight` or `conflict`. |
| `422` | `idempotency_key_reused`: the key was used with a different payload. |
| `429` | `rate_limited`: the vendor's 429, with its Retry-After. |
| `502` | `vendor_error`: the upstream vendor failed. |
| `503` | `no_capacity`: no endpoint can place the request now. |

### List sandboxes

`GET https://api.rent.dev/v1/sandboxes`

Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `cursor` | query | `string` |  |
| `limit` | query | `integer, 1 to 200` | Page size, 1-200; default 50. |
| `state` | query | `requested \| placing \| running \| paused \| settling \| terminated \| failed` | Only leases in this state. Default: every state but terminated and failed. |

| Status | Meaning |
| --- | --- |
| `200` | One page, newest first. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope. |

### Get a sandbox

`GET https://api.rent.dev/v1/sandboxes/{id}`

Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. lse_ followed by a 26-character ULID. |

| Status | Meaning |
| --- | --- |
| `200` | The sandbox. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `404` | `not_found`, also for another account's resource. |

### Terminate a sandbox

`DELETE https://api.rent.dev/v1/sandboxes/{id}`

Stops it and settles: the last usage window is captured and the rest of the hold released. Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. lse_ followed by a 26-character ULID. |

| Status | Meaning |
| --- | --- |
| `200` | The sandbox, settling or terminated. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `404` | `not_found`, also for another account's resource. |
| `502` | `vendor_error`: the upstream vendor failed. |

### Set a new timeout

`POST https://api.rent.dev/v1/sandboxes/{id}:extend`

The added time is held on the key that started the sandbox, so an API key with a spend limit cannot extend a sandbox another key started (403). Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. lse_ followed by a 26-character ULID. |

| Body field | Type | Description |
| --- | --- | --- |
| `timeout_s` | `integer, 1 to 86400` | Required. New timeout, in seconds from now. Takes the added hold first. |

| Status | Meaning |
| --- | --- |
| `200` | The sandbox with its new timeout_at. |
| `400` | `invalid_request` or `unsupported_parameter`. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `402` | `insufficient_balance`, `account_frozen` or `key_limit_reached`, with `balance_required_micros` and `topup` links. |
| `403` | `forbidden`: the token lacks the scope, or the caller is an API key with a spend limit and another key started the sandbox (its holds are charged to that key; use the key that started it, an uncapped key or a signed-in person). |
| `404` | `not_found`, also for another account's resource. |
| `409` | `conflict`: the sandbox is not running. |
| `502` | `vendor_error`: the upstream vendor failed. |

### Pause a sandbox, keeping memory and disk

`POST https://api.rent.dev/v1/sandboxes/{id}:pause`

Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. lse_ followed by a 26-character ULID. |

| Status | Meaning |
| --- | --- |
| `200` | The sandbox, settling then paused. |
| `400` | `unsupported_parameter`: the endpoint cannot pause. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `404` | `not_found`, also for another account's resource. |
| `409` | `conflict`: the sandbox is not running. |
| `502` | `vendor_error`: the upstream vendor failed. |

### Resume a paused sandbox

`POST https://api.rent.dev/v1/sandboxes/{id}:resume`

The resumed time is held on the key that started the sandbox, so an API key with a spend limit cannot resume a sandbox another key started (403). Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. lse_ followed by a 26-character ULID. |

| Body field | Type | Description |
| --- | --- | --- |
| `timeout_s` | `integer, 1 to 86400` | Timeout after resuming, in seconds. Default 300. |

The body is optional.

| Status | Meaning |
| --- | --- |
| `200` | The running sandbox. |
| `202` | Accepted: poll the Operation at Location. Headers: `Location`, `Retry-After`. |
| `400` | `invalid_request` or `unsupported_parameter`. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `402` | `insufficient_balance`, `account_frozen` or `key_limit_reached`, with `balance_required_micros` and `topup` links. |
| `403` | `forbidden`: the token lacks the scope, or the caller is an API key with a spend limit and another key started the sandbox (its holds are charged to that key; use the key that started it, an uncapped key or a signed-in person). |
| `404` | `not_found`, also for another account's resource. |
| `409` | `conflict`: the sandbox is not paused. |
| `502` | `vendor_error`: the upstream vendor failed. |
| `503` | `no_capacity`: no endpoint can place the request now. |

### Run a command and wait for it

`POST https://api.rent.dev/v1/sandboxes/{id}/exec`

Synchronous, up to timeout_s (default 50). For longer work start a process. Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. lse_ followed by a 26-character ULID. |

| Body field | Type | Description |
| --- | --- | --- |
| `cmd` | `string` | Required. Shell command, run with `/bin/bash -l -c`. |
| `cwd` | `string` |  |
| `env` | `map of string to string` |  |
| `user` | `string` | Default `user`. |
| `stdin` | `string` | Written to stdin, then closed. |
| `timeout_s` | `integer, 1 to 3600` | Seconds before the command is killed. Default 50. |

| Status | Meaning |
| --- | --- |
| `200` | Output and exit code. |
| `400` | `invalid_request` or `unsupported_parameter`. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `404` | `not_found`, also for another account's resource. |
| `409` | `conflict`: the sandbox is not running. |
| `502` | `vendor_error`: the upstream vendor failed. |

### Start a background process

`POST https://api.rent.dev/v1/sandboxes/{id}/processes`

Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. lse_ followed by a 26-character ULID. |

| Body field | Type | Description |
| --- | --- | --- |
| `cmd` | `string` | Required. |
| `cwd` | `string` |  |
| `env` | `map of string to string` |  |
| `user` | `string` |  |
| `stdin` | `boolean` | Keep stdin open for writes. |

| Status | Meaning |
| --- | --- |
| `201` | The started process. |
| `400` | `invalid_request` or `unsupported_parameter`. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `404` | `not_found`, also for another account's resource. |
| `409` | `conflict`: the sandbox is not running. |
| `502` | `vendor_error`: the upstream vendor failed. |

### Read a process's output after a cursor

`GET https://api.rent.dev/v1/sandboxes/{id}/processes/{process_id}/output`

Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. lse_ followed by a 26-character ULID. |
| `process_id` | path | `string` | Required. |
| `cursor` | query | `string` | Cursor from the previous read; omit to read from the start. |
| `wait_ms` | query | `integer, 0 to 30000` | Wait up to this long for new output before answering. Default 0. |

| Status | Meaning |
| --- | --- |
| `200` | New output and the next cursor. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `404` | `not_found`, also for another account's resource. |
| `502` | `vendor_error`: the upstream vendor failed. |

### Write to a process's stdin

`POST https://api.rent.dev/v1/sandboxes/{id}/processes/{process_id}/stdin`

Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. lse_ followed by a 26-character ULID. |
| `process_id` | path | `string` | Required. |

| Body field | Type | Description |
| --- | --- | --- |
| `data` | `string` | Required. |
| `eof` | `boolean` | Close stdin after writing. |

| Status | Meaning |
| --- | --- |
| `204` | Written. |
| `400` | `invalid_request` or `unsupported_parameter`. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `404` | `not_found`, also for another account's resource. |
| `502` | `vendor_error`: the upstream vendor failed. |

### Kill a process

`DELETE https://api.rent.dev/v1/sandboxes/{id}/processes/{process_id}`

Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. lse_ followed by a 26-character ULID. |
| `process_id` | path | `string` | Required. |

| Status | Meaning |
| --- | --- |
| `204` | Killed. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `404` | `not_found`, also for another account's resource. |
| `502` | `vendor_error`: the upstream vendor failed. |

### List a directory

`GET https://api.rent.dev/v1/sandboxes/{id}/files`

Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. lse_ followed by a 26-character ULID. |
| `path` | query | `string` | Required. |
| `depth` | query | `integer, 1 to 10` | List depth. Default 1. |

| Status | Meaning |
| --- | --- |
| `200` | Entries. |
| `400` | `invalid_request` or `unsupported_parameter`. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `404` | `not_found`, also for another account's resource. |
| `502` | `vendor_error`: the upstream vendor failed. |

### Read a file, streamed

`GET https://api.rent.dev/v1/sandboxes/{id}/files/content`

Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. lse_ followed by a 26-character ULID. |
| `path` | query | `string` | Required. |
| `depth` | query | `integer, 1 to 10` | List depth. Default 1. |

| Status | Meaning |
| --- | --- |
| `200` | File bytes. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `404` | `not_found`, also for another account's resource. |
| `502` | `vendor_error`: the upstream vendor failed. |

### Write a file, streamed

`PUT https://api.rent.dev/v1/sandboxes/{id}/files/content`

Creates parent directories. The edge refuses request bodies over 100 MB. Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. lse_ followed by a 26-character ULID. |
| `path` | query | `string` | Required. |
| `depth` | query | `integer, 1 to 10` | List depth. Default 1. |

The body is the file's bytes, sent as `application/octet-stream`.

| Status | Meaning |
| --- | --- |
| `200` | The written file. |
| `400` | `invalid_request` or `unsupported_parameter`. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `404` | `not_found`, also for another account's resource. |
| `502` | `vendor_error`: the upstream vendor failed. |

### Public URL of a port inside the sandbox

`GET https://api.rent.dev/v1/sandboxes/{id}/ports/{port}/url`

Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. lse_ followed by a 26-character ULID. |
| `port` | path | `integer, 1 to 65535` | Required. |

| Status | Meaning |
| --- | --- |
| `200` | The URL. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `404` | `not_found`, also for another account's resource. |

### Run code on the code-interpreter-v1 kernel

`POST https://api.rent.dev/v1/sandboxes/{id}/run-code`

Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. lse_ followed by a 26-character ULID. |

| Body field | Type | Description |
| --- | --- | --- |
| `code` | `string` | Required. |
| `language` | `python \| javascript \| typescript \| r \| java \| bash` | Default python. |
| `context_id` | `string` | Kernel context to run in; state persists across calls in one context. |
| `env` | `map of string to string` |  |
| `timeout_s` | `integer, 1 to 3600` | Default 50. |

| Status | Meaning |
| --- | --- |
| `200` | Results, output and any error. |
| `400` | `unsupported_parameter`: the sandbox is not on code-interpreter-v1. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `404` | `not_found`, also for another account's resource. |
| `502` | `vendor_error`: the upstream vendor failed. |

### Sandbox logs as server-sent events

`GET https://api.rent.dev/v1/sandboxes/{id}/logs`

Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. lse_ followed by a 26-character ULID. |
| `last-event-id` | header | `string` | Resume after this event. |

| Status | Meaning |
| --- | --- |
| `200` | Server-sent events. Each event has an `id` and a JSON `LogEntry` as `data`; reconnect with Last-Event-ID. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `404` | `not_found`, also for another account's resource. |
| `502` | `vendor_error`: the upstream vendor failed. |

## GPU pods

RunPod GPU pods on your own key.

### Create a GPU pod on your own RunPod key

`POST https://api.rent.dev/v1/gpu-pods`

BYOK only: RunPod bills your key; no rent.dev balance is used. Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `idempotency-key` | header | `string` | Replays the first response for 24 hours. Reuse with a different payload answers 422; while the first is in flight, 409. A show-once secret (an API key's `secret`) is never stored: a replay leaves it out and names it in the `Show-Once-Omitted` header. |

| Body field | Type | Description |
| --- | --- | --- |
| `gpu_type` | `string` | Required. RunPod GPU type id, as listed by its catalog. |
| `gpu_count` | `integer, at least 1` | Default 1. |
| `image` | `string` | Required. |
| `cloud_type` | `SECURE \| COMMUNITY` | Default SECURE. |
| `container_disk_gb` | `integer, at least 1` | Default 20. |
| `volume_gb` | `integer, at least 0` | Default 0. |
| `ports` | `array of string` |  |
| `env` | `map of string to string` |  |
| `credential_id` | `string` | RunPod credential to use; default the account's only RunPod credential. |

| Status | Meaning |
| --- | --- |
| `201` | The pod. |
| `202` | Accepted: poll the Operation at Location. Headers: `Location`, `Retry-After`. |
| `400` | `invalid_request`, or `unsupported_parameter` when no RunPod credential is stored. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope. |
| `409` | `idempotency_in_flight` or `conflict`. |
| `422` | `idempotency_key_reused`: the key was used with a different payload. |
| `429` | `rate_limited`: the vendor's 429, with its Retry-After. |
| `502` | `vendor_error`: the upstream vendor failed. |
| `503` | `no_capacity`: RunPod has none of the asked GPU now (a stock-out), with `retry_after`; retry later, or ask for another `gpu_type` or `cloud_type`. |

### List GPU pods

`GET https://api.rent.dev/v1/gpu-pods`

Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `cursor` | query | `string` |  |
| `limit` | query | `integer, 1 to 200` | Page size, 1-200; default 50. |

| Status | Meaning |
| --- | --- |
| `200` | One page, newest first. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope. |

### Get a GPU pod

`GET https://api.rent.dev/v1/gpu-pods/{id}`

Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. lse_ followed by a 26-character ULID. |

| Status | Meaning |
| --- | --- |
| `200` | The pod. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `404` | `not_found`, also for another account's resource. |

### Terminate a GPU pod

`DELETE https://api.rent.dev/v1/gpu-pods/{id}`

Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. lse_ followed by a 26-character ULID. |

| Status | Meaning |
| --- | --- |
| `200` | The terminated pod. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `404` | `not_found`, also for another account's resource. |
| `502` | `vendor_error`: the upstream vendor failed. |

### Pod logs as server-sent events

`GET https://api.rent.dev/v1/gpu-pods/{id}/logs`

Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. lse_ followed by a 26-character ULID. |
| `last-event-id` | header | `string` | Resume after this event. |

| Status | Meaning |
| --- | --- |
| `200` | Server-sent events. Each event has an `id` and a JSON `LogEntry` as `data`; reconnect with Last-Event-ID. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `404` | `not_found`, also for another account's resource. |
| `502` | `vendor_error`: the upstream vendor failed. |

### Run a command in a pod

`POST https://api.rent.dev/v1/gpu-pods/{id}/exec`

RunPod's limits pass through: timeoutSeconds 1-25, output up to 1 MiB. Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. lse_ followed by a 26-character ULID. |

| Body field | Type | Description |
| --- | --- | --- |
| `command` | `string` | Required. |
| `timeout_s` | `integer, at least 1` | Passed to RunPod as `timeoutSeconds`; RunPod accepts 1-25 and caps output at 1 MiB, and its errors pass through. |

| Status | Meaning |
| --- | --- |
| `200` | Output and exit code. |
| `400` | `invalid_request` or `unsupported_parameter`. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `404` | `not_found`, also for another account's resource. |
| `502` | `vendor_error`: the upstream vendor failed. |

## BYOK

Vendor credentials.

### Store a vendor credential

`POST https://api.rent.dev/v1/byok/credentials`

Checked with one read-only vendor call, then stored encrypted. Never returned. Not open to an API key with a spend limit (403). Needs an API key or an OAuth access token.

| Body field | Type | Description |
| --- | --- | --- |
| `vendor` | `e2b` | Required. |
| `label` | `string` |  |
| `fields` | `object` | Required. |

| Body field | Type | Description |
| --- | --- | --- |
| `vendor` | `vercel` | Required. |
| `label` | `string` |  |
| `fields` | `object` | Required. |

| Body field | Type | Description |
| --- | --- | --- |
| `vendor` | `runpod` | Required. |
| `label` | `string` |  |
| `fields` | `object` | Required. |

| Status | Meaning |
| --- | --- |
| `201` | The stored credential, without its secret. |
| `400` | `invalid_request`: malformed, or the vendor refused the credential. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope, or the caller is an API key with a spend limit (use an uncapped key or a signed-in person). |
| `502` | `vendor_error`: the upstream vendor failed. |

### List stored vendor credentials

`GET https://api.rent.dev/v1/byok/credentials`

Needs an API key or an OAuth access token.

| Status | Meaning |
| --- | --- |
| `200` | Ids and vendors only. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope. |

### Delete a vendor credential

`DELETE https://api.rent.dev/v1/byok/credentials/{id}`

Never refused for what it would leave running: the answer lists every sandbox and GPU pod created with the credential that is still running (`orphaned`). Those keep running on your own vendor bill, and rent.dev can no longer stop them, run commands in them or read their logs: terminate each one at the vendor (`vendor_resource_id` is the vendor's id for it). Not open to an API key with a spend limit (403). Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. cred_ followed by a 26-character ULID. |

| Status | Meaning |
| --- | --- |
| `200` | Deleted, with what is still running on the credential. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope, or the caller is an API key with a spend limit (use an uncapped key or a signed-in person). |
| `404` | `not_found`, also for another account's resource. |

## Usage

Usage and showback.

### Own usage billed by rent.dev beside BYOK showback billed by vendors

`GET https://api.rent.dev/v1/usage`

Needs an API key or an OAuth access token.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `from` | query | `string` | Default 30 days ago. |
| `to` | query | `string` | Default now. |

| Status | Meaning |
| --- | --- |
| `200` | Usage for the period. |
| `400` | `invalid_request` or `unsupported_parameter`. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the scope. |

## Supply

Partner supply: enrol nodes, manage them and read their earnings.

### Enrol nodes: get one single-use claim token per node and the install lines

`POST https://api.rent.dev/v1/supply/enrollments`

Needs a token with the `supply` scope. Each claim token is shown once and expires after 600 seconds; pass it to the node kit on stdin, never in a process's arguments. An API key is not enough: send an OAuth access token with the `supply` scope.

| Body field | Type | Description |
| --- | --- | --- |
| `wire` | `e2b \| rent` | Required. |
| `region` | `string` | Required. |
| `ask` | `SupplyAsk` | Required. What the provider asks per hour, in USD. |
| `capacity` | `SupplyCapacity` | Required. |
| `payout` | `SupplyPayout` | Payout details, stored as given; nothing pays out yet. |
| `count` | `integer, 1 to 1000` | Claim tokens to make, one per node; default 1. |

| Status | Meaning |
| --- | --- |
| `201` | The claim tokens, shown once, and the install lines. |
| `400` | `invalid_request` or `unsupported_parameter`. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the `supply` scope. API keys never carry it; ask the agent grant for `scope=supply`. |
| `503` | `supply_link_unavailable`: partner supply is not configured in this environment (`owner_action_required`, not retryable); or this environment has no node image to install (`owner_action_required`). |

### Claim a node with its claim token and a new node key

`POST https://api.rent.dev/v1/supply/claims`

Called by the node kit, with no key: the claim token is the credential, and `signature` proves the node holds the private half of `public_key`. Answers the node's link and the platform keys its gate verifies requests with. No credentials.

| Body field | Type | Description |
| --- | --- | --- |
| `claim_token` | `string` | Required. |
| `public_key` | `string` | Required. Ed25519 public key: 32 bytes, base64url without padding. |
| `signature` | `string` | Required. The node key over `rent.dev claim v1\n{claim_token}\n{public_key}`. |
| `agent_version` | `string` | Required. |
| `wire` | `e2b \| rent` | Required. |
| `facts` | `SupplyFacts` | Required. |
| `checks` | `array of SupplyCheckResult` | Required. |

| Status | Meaning |
| --- | --- |
| `201` | The node, its link, and the next step: start the link. |
| `400` | `invalid_request`: the body is malformed, or its `wire` is not the enrolment's. |
| `401` | `claim_proof_invalid`: the proof of possession did not verify. |
| `409` | `node_exists`: a live node already holds this host fingerprint. |
| `410` | `claim_invalid`: the token is unknown, used or expired (`next.action` `re_enroll`). |
| `422` | `checks_failed`: a blocking check failed; the `checks` with their fixes, `next.action` `fix_checks`. The token stays unused until it expires. |
| `503` | `supply_link_unavailable`: the node's link could not be made; retry after `retry_after`, or wait for rent.dev when `owner_action_required`. |

### List this account's nodes

`GET https://api.rent.dev/v1/supply/nodes`

An API key is not enough: send an OAuth access token with the `supply` scope.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `cursor` | query | `string` |  |
| `limit` | query | `integer, 1 to 200` | Page size, 1-200; default 50. |

| Status | Meaning |
| --- | --- |
| `200` | One page, newest first. |
| `400` | `invalid_request` or `unsupported_parameter`. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the `supply` scope. API keys never carry it; ask the agent grant for `scope=supply`. |
| `503` | `supply_link_unavailable`: partner supply is not configured in this environment (`owner_action_required`, not retryable). |

### Get a node: its state, checks, next step and placement

`GET https://api.rent.dev/v1/supply/nodes/{id}`

An API key is not enough: send an OAuth access token with the `supply` scope.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. |

| Status | Meaning |
| --- | --- |
| `200` | The node. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the `supply` scope. API keys never carry it; ask the agent grant for `scope=supply`. |
| `404` | `not_found`, also for another account's resource. |
| `503` | `supply_link_unavailable`: partner supply is not configured in this environment (`owner_action_required`, not retryable). |

### Change a node's ask, capacity, region or payout

`PATCH https://api.rent.dev/v1/supply/nodes/{id}`

A new ask applies to leases placed from now on; running leases keep the rates they were placed at. More slots or a new region sends the node back to `probing`. Changing `payout` needs `signed_nonce`: a nonce from `GET /v1/accounts/nonce` signed over `rent.dev payout v1\n{node_id}\n{nonce}\n{public_key}` by the account's agent key. An API key is not enough: send an OAuth access token with the `supply` scope.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. |

| Body field | Type | Description |
| --- | --- | --- |
| `ask` | `SupplyAsk` | What the provider asks per hour, in USD. |
| `capacity` | `SupplyCapacity` |  |
| `region` | `string` |  |
| `payout` | `SupplyPayout` | Payout details, stored as given; nothing pays out yet. |
| `signed_nonce` | `SignedNonce` | An Ed25519 signature over `rent.dev signup v1\n{nonce}\n{public_key}` (signup, binding) or `rent.dev recover v1\n{nonce}\n{public_key}` (recovery). |

| Status | Meaning |
| --- | --- |
| `200` | The updated node. |
| `400` | `invalid_request` or `unsupported_parameter`. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the `supply` scope; or `payout_proof_required`: `signed_nonce` is not a fresh nonce signed over `rent.dev payout v1\n{node_id}\n{nonce}\n{public_key}` by an agent key of this account. |
| `404` | `not_found`, also for another account's resource. |
| `409` | `conflict`: the node changed state while the request ran; send it again. |
| `410` | `node_revoked`: the node is revoked. |
| `503` | `supply_link_unavailable`: partner supply is not configured in this environment (`owner_action_required`, not retryable). |

### Probe a node now

`POST https://api.rent.dev/v1/supply/nodes/{id}:probe`

Schedules the node's adapter probe at once; a suspended node goes back to `probing`. An API key is not enough: send an OAuth access token with the `supply` scope.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. |

| Status | Meaning |
| --- | --- |
| `200` | The node, its probe due now. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the `supply` scope. API keys never carry it; ask the agent grant for `scope=supply`. |
| `404` | `not_found`, also for another account's resource. |
| `409` | `probe_in_progress`: a probe of the node is running; or `conflict`: the node changed state while the request ran. |
| `410` | `node_revoked`: the node is revoked. |
| `503` | `supply_link_unavailable`: partner supply is not configured in this environment (`owner_action_required`, not retryable). |

### Revoke a node and remove its link

`POST https://api.rent.dev/v1/supply/nodes/{id}:revoke`

No new lease is placed on it. Earnings already accrued stay. An API key is not enough: send an OAuth access token with the `supply` scope.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `id` | path | `string` | Required. |

| Status | Meaning |
| --- | --- |
| `200` | The revoked node. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the `supply` scope. API keys never carry it; ask the agent grant for `scope=supply`. |
| `404` | `not_found`, also for another account's resource. |
| `409` | `conflict`: the node changed state while the request ran; send it again. |
| `503` | `supply_link_unavailable`: partner supply is not configured in this environment (`owner_action_required`, not retryable); or the node is revoked but its link could not be removed: send the revoke again. |

### Accrued earnings of this account's nodes

`GET https://api.rent.dev/v1/supply/earnings`

A lease accrues when it settles; nothing shows while it runs. An accrual becomes releasable after the holdback, longer when the paying account topped up by card. An API key is not enough: send an OAuth access token with the `supply` scope.

| Parameter | In | Type | Description |
| --- | --- | --- | --- |
| `cursor` | query | `string` |  |
| `limit` | query | `integer, 1 to 200` | Page size, 1-200; default 50. |

| Status | Meaning |
| --- | --- |
| `200` | The totals and one page of entries, newest first. |
| `401` | `unauthenticated`: no key, or one that is unknown or revoked. |
| `403` | `forbidden`: the token lacks the `supply` scope. API keys never carry it; ask the agent grant for `scope=supply`. |

### The platform keys a node's gate verifies platform requests with

`GET https://api.rent.dev/v1/supply/keys`

No credentials.

| Status | Meaning |
| --- | --- |
| `200` | Current and retiring keys. |

### The provider guide, as Markdown

`GET https://api.rent.dev/v1/supply/guide`

No credentials.

| Status | Meaning |
| --- | --- |
| `200` | The guide. |
