Agent quickstart

An agent can go from nothing to a running sandbox on its own: one call for an account and its key, one payment, one call for the sandbox. This page walks that path over REST.

POSThttps://api.rent.dev/v1/accounts

View as Markdown

Create an account

POST /v1/accounts takes no credentials and no body. It answers 201 with the account and its first API key. The key's secret is shown this once, so store it.

Shell
curl -sX POST https://api.rent.dev/v1/accounts
JSON
{
  "account_id": "acc_01JCZ9Q4M8V7K2T3N5P6R8S0W1",
  "account": {
    "id": "acc_01JCZ9Q4M8V7K2T3N5P6R8S0W1",
    "created_at": "2026-11-10T22:00:00.000Z",
    "frozen": false,
    "recovery": {
      "agent_key": false,
      "wallet": false,
      "passkey": false
    }
  },
  "api_key": {
    "id": "key_01JCZ9Q4M8V7K2T3N5P6R8S0W1",
    "prefix": "rent_sk_0f0f",
    "name": null,
    "created_at": "2026-11-10T22:00:00.000Z",
    "last_used_at": null,
    "revoked_at": null,
    "spend_limit_micros": null,
    "spent_micros": 0,
    "secret": "rent_sk_0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"
  }
}

The examples below read the secret from RENT_KEY.

To be able to repeat the signup, send an Idempotency-Key header with a random value you keep. Repeating the call with it within 24 hours answers the same account with a new key that replaces the first, while the account is untouched: its key never used, nothing bound to it and no money in. After that the repeat answers 422 idempotency_key_reused and no key. Treat the value as a secret for those 24 hours.

Shell
curl -sX POST https://api.rent.dev/v1/accounts -H "Idempotency-Key: $SIGNUP_KEY"

Authenticate

Send the key as Authorization: Bearer rent_sk_… or as X-API-Key. The same key written e2b_ plus the same 64 hex characters is accepted everywhere, which is how the E2B SDK uses it.

An API key carries the scopes sandbox, gpu, billing and keys. An OAuth access token from https://auth.rent.dev carries the scopes it was granted.

A key can carry a spend limit, spend_limit_micros. The limit counts what rent.dev bills from the prepaid balance: its own sandboxes, with what their holds reserve. GPU pods and sandboxes that run on your own vendor key are billed by that vendor and do not count against it. A credential without the gpu scope cannot start GPU pods, but every API key carries gpu, so a key with a limit can still start a pod on a RunPod key the account has stored.

Such a key is a budget someone handed out, not authority over the account. These calls answer 403 forbidden for it:

  • Keys: it cannot create, update, rotate or revoke other keys. It can list them, and rotate or revoke itself.
  • Agent keys, wallets and people: it cannot bind or remove agent keys, or remove wallets or people. It can list them.
  • How the account pays: it cannot set auto-recharge or start saving a payment method.
  • Vendor keys: it cannot store or delete them.
  • Sandboxes another key started: it cannot extend or resume them.

It still reads the balance, the ledger and the auto-recharge settings, creates a top-up or a pay link, tops up over x402, and extends or resumes the sandboxes it started. The key that replaces it keeps only what is left of the limit after what the old key spent and still holds.

Bind a key the agent holds

A lost API key cannot be read again. To be able to get a new one, bind an Ed25519 key the agent holds. Get a nonce (single use, valid 5 min), sign the text rent.dev signup v1\n{nonce}\n{public_key}, and send the public key, the nonce and the signature.

CallWhat it does
GET /v1/accounts/nonceGet a single-use nonce for signing up, binding or recovering with an agent key
POST /v1/accountsWith public_key, nonce and signature in the body: signs up and binds the key in one call.
POST /v1/accounts/agent-keyBind an Ed25519 agent key to this account
POST /v1/accounts/recoverA new API key for a signature over rent.dev recover v1\n{nonce}\n{public_key}.

Keys, nonces and signatures are base64url without padding: 43 characters for a public key or a nonce, 86 for a signature.

Fund the balance

A new account has a zero balance. A call that needs credit answers 402 with a Problem that says how much is missing and carries two ways to pay it. This one is what creating the default sandbox answers on an empty balance:

JSON
{
  "type": "https://rent.dev/problems/insufficient_balance",
  "title": "Insufficient balance",
  "status": 402,
  "code": "insufficient_balance",
  "docs_url": "https://rent.dev/docs/errors#insufficient_balance",
  "request_id": "req_01JCZ9Q4M8V7K2T3N5P6R8S0W1",
  "balance_required_micros": 9075,
  "topup": {
    "checkout_url": "https://api.rent.dev/pay/<token>",
    "x402_url": "https://api.rent.dev/v1/credits/x402?amount_usd=0.009075"
  }
}
  • topup.x402_url: pay it yourself in USDC over x402.
  • topup.checkout_url: a pay link for a person. It opens Stripe Checkout, needs no key and works for 24 hours.

To add credit ahead of time, call the x402 route with the amount you want or create a Checkout Session. Payments covers both.

Shell
# USDC over x402: answers 402 with the payment requirement, then 200 once paid
curl -sX POST "https://api.rent.dev/v1/credits/x402?amount_usd=5" -H "Authorization: Bearer $RENT_KEY"

# or a Checkout page for a person: the answer carries checkout_url
curl -sX POST https://api.rent.dev/v1/credits/checkout -H "Authorization: Bearer $RENT_KEY" -H "Content-Type: application/json" -d '{"amount_usd":"5.00"}'

Create a sandbox

POSThttps://api.rent.dev/v1/sandboxes

With an empty body you get the base template and a 300 second timeout. rent.dev first holds the most the sandbox could cost for that timeout ($0.009075 for base), then places it.

Shell
curl -sX POST https://api.rent.dev/v1/sandboxes -H "Authorization: Bearer $RENT_KEY" -H "Prefer: wait=30" -H "Content-Type: application/json" -d '{}'

With Prefer: wait=30 the answer is 201 and the sandbox when it is ready within 30 seconds. Otherwise it is 202 and an Operation: poll GET /v1/operations/{id}, waiting Retry-After seconds between polls, until done is true. Then response holds the sandbox, or error holds a Problem.

Body fieldTypeDescription
templatestringTemplate alias: the caller's own or the public set. Default base.
timeout_sinteger, 1 to 86400Seconds until the sandbox stops (or pauses with auto_pause). Default 300.
metadataMetadataCaller metadata. Keys starting rentdev_ are reserved and dropped.
env_varsmap of string to string
auto_pausebooleanPause instead of stopping at the timeout. Default false.
auto_resumebooleanResume a paused sandbox when traffic arrives. Default false.
allow_internet_accessboolean
networkSandboxNetwork
profileagent-loop | interactive | workspaceExpected duty, used for placement when no history exists.
routingSandboxRouting

The current node has two public templates: base (2 vCPU, 512 MiB) and code-interpreter-v1 (2 vCPU, 2 GiB), which adds a Jupyter kernel for run-code.

Whether own sandboxes can be placed right now is on the status page and at GET /v1/status. When no endpoint can take a request the answer is 503 no_capacity.

Run commands and move files

Shell
curl -sX POST https://api.rent.dev/v1/sandboxes/$SANDBOX_ID/exec -H "Authorization: Bearer $RENT_KEY" -H "Content-Type: application/json" -d '{"cmd":"echo hello"}'

exec waits for the command and answers its stdout, stderr and exit code. The command is killed after timeout_s seconds (default 50). For anything longer, start a process and read its output with a cursor.

CallWhat it does
POST /v1/sandboxes/{id}/execRun a command and wait for it
POST /v1/sandboxes/{id}/processesStart a background process
GET /v1/sandboxes/{id}/processes/{process_id}/outputRead a process's output after a cursor
POST /v1/sandboxes/{id}/processes/{process_id}/stdinWrite to a process's stdin
DELETE /v1/sandboxes/{id}/processes/{process_id}Kill a process
GET /v1/sandboxes/{id}/filesList a directory
GET /v1/sandboxes/{id}/files/contentRead a file, streamed
PUT /v1/sandboxes/{id}/files/contentWrite a file, streamed
POST /v1/sandboxes/{id}/run-codeRun code on the code-interpreter-v1 kernel
GET /v1/sandboxes/{id}/ports/{port}/urlPublic URL of a port inside the sandbox
GET /v1/sandboxes/{id}/logsSandbox logs as server-sent events

Stop and settle

DELETE /v1/sandboxes/{id} stops the sandbox. The last usage is charged and the rest of the hold returns to the available balance. A sandbox also stops at its timeout; POST /v1/sandboxes/{id}:extend moves the timeout, up to 24 h from now.

:pause keeps memory and disk and stops the meter. :resume takes a new hold and continues where the sandbox left off.

Retry safely

Send an Idempotency-Key header on any POST under /v1. The first 2xx answer is replayed for 24 hours with Idempotent-Replayed: true. The same key with a different request answers 422, and while the first request is still running it answers 409. A refusal is not stored, so after a 402 you can top up and send the same key again.

Keys are tracked per account. Signup has no account yet and keeps a rule of its own: see Create an account.

Tokens without an API key

An agent that has bound an Ed25519 key can trade a signed assertion for a one-hour access token, with no API key in the request:

Shell
curl -sX POST https://auth.rent.dev/oauth/token \
  -d grant_type=client_credentials \
  -d client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer \
  -d client_assertion=$ASSERTION \
  -d resource=https://api.rent.dev
ClaimValue
alg (header)EdDSA
typ (header)client-authentication+jwt. An assertion without it is still accepted.
iss and subThe account id, acc_….
audhttps://auth.rent.dev, the issuer, as the only value. The token endpoint's URL is refused.
expAt most 5 minutes ahead.
jtiUnique. Each value is accepted once.

resource names the one API the token is for: https://api.rent.dev or https://mcp.rent.dev/mcp. Without scope the token carries what an API key carries. No refresh token is issued; sign a new assertion instead.

The device flow (RFC 8628) serves one client, rentdev-cli: it posts to https://auth.rent.dev/device_authorization, and a person enters the code at https://auth.rent.dev/device. A code lasts 10 minutes and the client polls every 5 seconds.

People sign in with a passkey at https://auth.rent.dev. Other OAuth clients use the authorization code flow with PKCE; the server's metadata is at https://auth.rent.dev/.well-known/oauth-authorization-server.

Other ways in

  • MCP: the same account, sandboxes and payments as tools.
  • E2B SDK: the unmodified SDK, pointed at rent.dev.
  • REST API: every route, parameter and status.
  • Pricing and limits.