# VpsAgent — Full Reference

{
  "openapi": "3.1.0",
  "info": {
    "title": "VpsAgent",
    "version": "0.1.0",
    "summary": "Disposable VPS instances for AI agents — paid via x402 (USDC on Base).",
    "description": "Two product lines: ephemeral sandboxes (1h / 6h / 24h, $0.50–$5.00) for code execution, CI runs, browser automation; persistent servers (small / medium / large, 30-day or 365-day, $14.99–$449.99) for hosting agents and services. Pay-on-success: USDC settles only when the box is reachable on TCP :22. Failed provisions never settle — the EIP-3009 authorization expires unused, agent pays nothing. No signup, no API keys, machine-to-machine.",
    "license": {
      "name": "Proprietary"
    },
    "contact": {
      "name": "VpsAgent",
      "url": "https://vps.phoneagent.xyz/feedback"
    }
  },
  "servers": [
    {
      "url": "https://vps.phoneagent.xyz"
    }
  ],
  "x-x402": {
    "scheme": "exact",
    "network": "eip155:8453",
    "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "payTo": "0x8eCbd65228930060363C70854da7D37D57ffC358"
  },
  "paths": {
    "/catalog": {
      "get": {
        "operationId": "listCatalog",
        "summary": "List rentable SKUs across both tracks.",
        "description": "Live storefront. Returns nine SKUs across two tracks (sandbox + server) with prices, spec floors, durations, and Phase-1 availability flags. Paid discovery: 0.01 USDC per call, settled inline. The dust price exists to register the resource in CDP Bazaar's discovery index.",
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "X-Payment-Response": {
                "description": "Base64-encoded `{tx, network}` of the on-chain settlement.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CatalogList"
                },
                "examples": {
                  "live": {
                    "summary": "Every SKU, priced, as served today.",
                    "value": [
                      {
                        "sku": "sandbox_1h",
                        "track": "sandbox",
                        "price_usdc": 0.5,
                        "duration_minutes": 60,
                        "spec_floor": {
                          "vcpu": 1,
                          "memory_mb": 1024
                        },
                        "available": false,
                        "description": "1 vCPU / 1 GB / 25 GB SSD ephemeral sandbox, destroyed after 1 hour. Code execution, CI runs, browser automation.",
                        "disk": {
                          "size_gb": 25,
                          "type": "SSD"
                        }
                      },
                      {
                        "sku": "sandbox_6h",
                        "track": "sandbox",
                        "price_usdc": 2,
                        "duration_minutes": 360,
                        "spec_floor": {
                          "vcpu": 1,
                          "memory_mb": 1024
                        },
                        "available": false,
                        "description": "1 vCPU / 1 GB / 25 GB SSD ephemeral sandbox, destroyed after 6 hours.",
                        "disk": {
                          "size_gb": 25,
                          "type": "SSD"
                        }
                      },
                      {
                        "sku": "sandbox_24h",
                        "track": "sandbox",
                        "price_usdc": 5,
                        "duration_minutes": 1440,
                        "spec_floor": {
                          "vcpu": 1,
                          "memory_mb": 1024
                        },
                        "available": false,
                        "description": "1 vCPU / 1 GB / 25 GB SSD ephemeral sandbox, destroyed after 24 hours.",
                        "disk": {
                          "size_gb": 25,
                          "type": "SSD"
                        }
                      },
                      {
                        "sku": "server_small_1mo",
                        "track": "server",
                        "price_usdc": 14.99,
                        "duration_minutes": 43200,
                        "spec_floor": {
                          "vcpu": 4,
                          "memory_mb": 8192
                        },
                        "available": true,
                        "description": "4 vCPU / 8 GB / 100 GB SSD / 200 Mbit/s persistent server, 30 days. Renewable.",
                        "disk": {
                          "size_gb": 100,
                          "type": "SSD"
                        },
                        "network": {
                          "port_mbits": 200,
                          "traffic": "unmetered"
                        },
                        "region_pricing": {
                          "default": "EU",
                          "surcharge_usd": {
                            "EU": 0,
                            "US-central": 3,
                            "US-east": 3,
                            "US-west": 3,
                            "SIN": 6,
                            "UK": 3,
                            "AUS": 6,
                            "JPN": 6,
                            "IND": 6
                          }
                        }
                      },
                      {
                        "sku": "server_small_12mo",
                        "track": "server",
                        "price_usdc": 149.99,
                        "duration_minutes": 525600,
                        "spec_floor": {
                          "vcpu": 4,
                          "memory_mb": 8192
                        },
                        "available": true,
                        "description": "4 vCPU / 8 GB / 100 GB SSD / 200 Mbit/s persistent server, 365 days. ~20% off the monthly rate.",
                        "disk": {
                          "size_gb": 100,
                          "type": "SSD"
                        },
                        "network": {
                          "port_mbits": 200,
                          "traffic": "unmetered"
                        },
                        "region_pricing": {
                          "default": "EU",
                          "surcharge_usd": {
                            "EU": 0,
                            "US-central": 36,
                            "US-east": 36,
                            "US-west": 36,
                            "SIN": 72,
                            "UK": 36,
                            "AUS": 72,
                            "JPN": 72,
                            "IND": 72
                          }
                        }
                      },
                      {
                        "sku": "server_medium_1mo",
                        "track": "server",
                        "price_usdc": 22.99,
                        "duration_minutes": 43200,
                        "spec_floor": {
                          "vcpu": 6,
                          "memory_mb": 12288
                        },
                        "available": true,
                        "description": "6 vCPU / 12 GB / 200 GB SSD / 300 Mbit/s persistent server, 30 days. Renewable.",
                        "disk": {
                          "size_gb": 200,
                          "type": "SSD"
                        },
                        "network": {
                          "port_mbits": 300,
                          "traffic": "unmetered"
                        },
                        "region_pricing": {
                          "default": "EU",
                          "surcharge_usd": {
                            "EU": 0,
                            "US-central": 4.5,
                            "US-east": 4.5,
                            "US-west": 4.5,
                            "SIN": 9,
                            "UK": 4.5,
                            "AUS": 9,
                            "JPN": 9,
                            "IND": 9
                          }
                        }
                      },
                      {
                        "sku": "server_medium_12mo",
                        "track": "server",
                        "price_usdc": 229.99,
                        "duration_minutes": 525600,
                        "spec_floor": {
                          "vcpu": 6,
                          "memory_mb": 12288
                        },
                        "available": true,
                        "description": "6 vCPU / 12 GB / 200 GB SSD / 300 Mbit/s persistent server, 365 days. ~25% off the monthly rate.",
                        "disk": {
                          "size_gb": 200,
                          "type": "SSD"
                        },
                        "network": {
                          "port_mbits": 300,
                          "traffic": "unmetered"
                        },
                        "region_pricing": {
                          "default": "EU",
                          "surcharge_usd": {
                            "EU": 0,
                            "US-central": 54,
                            "US-east": 54,
                            "US-west": 54,
                            "SIN": 108,
                            "UK": 54,
                            "AUS": 108,
                            "JPN": 108,
                            "IND": 108
                          }
                        }
                      },
                      {
                        "sku": "server_large_1mo",
                        "track": "server",
                        "price_usdc": 44.99,
                        "duration_minutes": 43200,
                        "spec_floor": {
                          "vcpu": 8,
                          "memory_mb": 24576
                        },
                        "available": true,
                        "description": "8 vCPU / 24 GB / 300 GB SSD / 600 Mbit/s persistent server, 30 days. Renewable.",
                        "disk": {
                          "size_gb": 300,
                          "type": "SSD"
                        },
                        "network": {
                          "port_mbits": 600,
                          "traffic": "unmetered"
                        },
                        "region_pricing": {
                          "default": "EU",
                          "surcharge_usd": {
                            "EU": 0,
                            "US-central": 9,
                            "US-east": 9,
                            "US-west": 9,
                            "SIN": 18,
                            "UK": 9,
                            "AUS": 18,
                            "JPN": 18,
                            "IND": 18
                          }
                        }
                      },
                      {
                        "sku": "server_large_12mo",
                        "track": "server",
                        "price_usdc": 449.99,
                        "duration_minutes": 525600,
                        "spec_floor": {
                          "vcpu": 8,
                          "memory_mb": 24576
                        },
                        "available": true,
                        "description": "8 vCPU / 24 GB / 300 GB SSD / 600 Mbit/s persistent server, 365 days. ~25% off the monthly rate.",
                        "disk": {
                          "size_gb": 300,
                          "type": "SSD"
                        },
                        "network": {
                          "port_mbits": 600,
                          "traffic": "unmetered"
                        },
                        "region_pricing": {
                          "default": "EU",
                          "surcharge_usd": {
                            "EU": 0,
                            "US-central": 108,
                            "US-east": 108,
                            "US-west": 108,
                            "SIN": 216,
                            "UK": 108,
                            "AUS": 216,
                            "JPN": 216,
                            "IND": 216
                          }
                        }
                      }
                    ]
                  }
                }
              }
            }
          },
          "402": {
            "description": "Payment required. The challenge is in the JSON body AND base64'd into the `PAYMENT-REQUIRED` response header. Read the HEADER: the x402 v2 reference client (`@x402/core`) only falls back to the body when `x402Version == 1`, and throws `Invalid payment required response` otherwise. Send the signed payload back in `PAYMENT-SIGNATURE` (v2 clients do this by default); `X-Payment` is also accepted.",
            "headers": {
              "PAYMENT-REQUIRED": {
                "description": "Base64 of the same challenge as the body. This is the header v2 clients read.",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentChallenge"
                }
              }
            }
          },
          "422": {
            "description": "The payment was well-formed and understood but cannot be executed: the payer wallet's on-chain USDC balance is below the quoted price. Body is `insufficient_funds` with `balance_usdc`, `required_usdc` and `shortfall_usdc`. This is TERMINAL, not a challenge — it carries no `accepts[]`, and retrying without funding the wallet fails identically. Fund it, then retry. Every other payment failure (bad signature, spent nonce, unreadable balance) stays 402 because those may succeed on a retry."
          }
        }
      }
    },
    "/rent": {
      "post": {
        "operationId": "rent",
        "summary": "Provision a VPS instance.",
        "description": "Submit a SKU; receive a 402 with priced paymentRequirements; sign an EIP-3009 transferWithAuthorization for the SKU price; resubmit with the X-Payment header. We verify (NOT settle), provision the box upstream, and persist the rental. Worker calls /settle only when the box is reachable on TCP :22 — typically 3–5 minutes for server-track SKUs. If the box never comes up, settle is never called and the EIP-3009 authorization dies via validBefore. Agent paid nothing.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RentBody"
              },
              "examples": {
                "small": {
                  "value": {
                    "sku": "server_small_1mo"
                  }
                },
                "large_with_region": {
                  "value": {
                    "sku": "server_large_1mo",
                    "region": "US-east"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Provisioned. Settle pending — the worker will charge once the box is up.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RentalCreated"
                }
              }
            }
          },
          "400": {
            "description": "Invalid body. See the response detail for which field failed."
          },
          "402": {
            "description": "Payment required (or payment invalid). The challenge is returned in the JSON body AND base64'd into the `PAYMENT-REQUIRED` response header — x402 v2 clients read the header. Send the signed payload back in `PAYMENT-SIGNATURE` (or `X-Payment`; both are accepted)."
          },
          "422": {
            "description": "The payment was well-formed and understood but cannot be executed: the payer wallet's on-chain USDC balance is below the quoted price. Body is `insufficient_funds` with `balance_usdc`, `required_usdc` and `shortfall_usdc`. This is TERMINAL, not a challenge — it carries no `accepts[]`, and retrying without funding the wallet fails identically. Fund it, then retry. Every other payment failure (bad signature, spent nonce, unreadable balance) stays 402 because those may succeed on a retry."
          },
          "503": {
            "description": "SKU advertised but not yet routable in Phase 1, or upstream provider unavailable."
          }
        }
      }
    },
    "/rentals": {
      "get": {
        "operationId": "listMyRentals",
        "summary": "Every rental your wallet has paid for — status, expiry, and what extending costs against your balance.",
        "description": "Identify by wallet instead of rental_id. Unsigned, this answers 402 with an identification challenge for one base unit (0.000001 USDC) valid 60 seconds. Sign it with any x402 client and resend with PAYMENT-SIGNATURE (or X-Payment): we recover your wallet from the signature and NEVER settle the authorization, so nothing is charged. Includes expired rentals. Credentials are never returned here.",
        "parameters": [
          {
            "name": "PAYMENT-SIGNATURE",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Signed x402 payment header for the identification challenge."
          }
        ],
        "responses": {
          "200": {
            "description": "The wallet's rentals, newest first, each with `renew` pricing and a `next_step`."
          },
          "402": {
            "description": "Identification challenge (also in the base64 PAYMENT-REQUIRED header). Never settled."
          }
        }
      }
    },
    "/rentals/{rental_id}": {
      "get": {
        "operationId": "getRental",
        "summary": "Public lifecycle snapshot, including the box's ipv4 once it boots.",
        "parameters": [
          {
            "name": "rental_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RentalStatus"
                }
              }
            }
          },
          "404": {
            "description": "Rental not found."
          }
        }
      }
    },
    "/rentals/{rental_id}/renew": {
      "post": {
        "operationId": "renew",
        "summary": "Extend a rental's expires_at.",
        "description": "Token-gated. Stack cap periods ∈ [1, 12]. Same flow as /rent: 402 → sign → resubmit → verify (no settle yet). The worker clears the scheduled destruction upstream, settles, then re-locks it at the new expires_at — normally within a minute and with no human involved. Crucially: expires_at does NOT change until the renewal fully settles — if the renewal never completes, the original expires_at remains in force and the rental is destroyed at the original cancelDate. Agent paid nothing.",
        "parameters": [
          {
            "name": "rental_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "root_token from POST /rent."
          },
          {
            "name": "periods",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 12,
              "default": 1
            },
            "description": "How many full SKU terms to add. Price is the (sku, region) rate × periods, and the 200 echoes back the `periods` it applied — check it. Accepted here as a query parameter or in the JSON body; the query wins if both are sent. Send it AT MOST ONCE: two conflicting ?periods= values are refused with 400, never guessed between (appending &periods=12 to a URL that already carries &periods=1 is the way this happens). Out-of-range values are refused too, not clamped."
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RenewBody"
              },
              "examples": {
                "default": {
                  "value": {
                    "periods": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Renewal verified, settle pending.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RenewalCreated"
                }
              }
            }
          },
          "400": {
            "description": "`periods` outside 1–12, not a whole number (`invalid_periods`), or sent more than once with conflicting values (`ambiguous_periods`)."
          },
          "401": {
            "description": "Missing or invalid token."
          },
          "402": {
            "description": "Payment required (or invalid). The challenge is returned in the JSON body AND base64'd into the `PAYMENT-REQUIRED` response header — x402 v2 clients read the header. Send the signed payload back in `PAYMENT-SIGNATURE` (or `X-Payment`; both are accepted)."
          },
          "404": {
            "description": "Rental not found."
          },
          "409": {
            "description": "`not_renewable`: rental not in 'active' status. Or `too_late_to_renew`: the rental ends within 15 minutes, too little runway for the extension to be in place before the box is destroyed — it would take your signature, extend nothing and leave you with no box, so it is refused while you still have time to rent a fresh one. Body carries `expires_at` and `rent_url`. Start renewals days early."
          },
          "422": {
            "description": "The payment was well-formed and understood but cannot be executed: the payer wallet's on-chain USDC balance is below the quoted price. Body is `insufficient_funds` with `balance_usdc`, `required_usdc` and `shortfall_usdc`. This is TERMINAL, not a challenge — it carries no `accepts[]`, and retrying without funding the wallet fails identically. Fund it, then retry. Every other payment failure (bad signature, spent nonce, unreadable balance) stays 402 because those may succeed on a retry."
          }
        }
      }
    },
    "/instance/{rental_id}": {
      "get": {
        "operationId": "getInstance",
        "summary": "Live status + full SSH connection details.",
        "description": "Token-gated. Poll after /rent until `phase == \"ready\"` (equivalently `running == true`) — a server-track box typically takes 3–5 minutes. The 200 body carries everything needed to log in: ipv4, port, user, password, and a paste-ready `connection.command`. Credentials stay retrievable here for the life of the rental, so an agent that dropped the /rent response can recover them with the rental_id + root_token.",
        "parameters": [
          {
            "name": "rental_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "rental_id from POST /rent."
          },
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "root_token from POST /rent. Not the rental_id."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InstanceStatus"
                }
              }
            }
          },
          "401": {
            "description": "Missing token, or a token that doesn't match this rental."
          },
          "404": {
            "description": "Unknown rental id, or a rental purged ~1h after expires_at."
          },
          "502": {
            "description": "Upstream provider unreachable; last-known fields and valid credentials returned."
          }
        }
      }
    },
    "/regions": {
      "get": {
        "operationId": "listRegions",
        "summary": "Where every region code actually puts a box. Free, no payment.",
        "description": "Maps each `region` code accepted by POST /rent to a place, with its per-month surcharge. Read this before buying a non-default region — the codes are not self-explanatory and guessing one wrong buys a box on the wrong continent. `location` is the most precise placement we will stand behind and `precision` says what it rests on: `city` (geolocated from inside a box we delivered), `country`, or `area` (a multi-country region such as EU). `city_expected` is upstream's label — unconfirmed, and it has disagreed with where a box actually landed, so it is reported separately and promises nothing.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RegionList"
                },
                "examples": {
                  "default": {
                    "value": {
                      "default": "EU",
                      "regions": [
                        {
                          "code": "EU",
                          "location": "European Union",
                          "precision": "area",
                          "area": "European Union",
                          "country": null,
                          "country_code": null,
                          "city": null,
                          "confirmed_on": null,
                          "city_expected": null,
                          "surcharge_usdc_per_month": 0,
                          "is_default": true,
                          "note": "Spans several member states — we guarantee the EU, not a country within it. One box sold as EU (2026-08-28) geolocated to north-eastern France. If you need a specific country for data residency, ask via POST /feedback before buying."
                        },
                        {
                          "code": "US-central",
                          "location": "United States (Central)",
                          "precision": "country",
                          "area": "United States (Central)",
                          "country": "United States",
                          "country_code": "US",
                          "city": null,
                          "confirmed_on": null,
                          "city_expected": "St. Louis",
                          "surcharge_usdc_per_month": 3,
                          "is_default": false
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/feedback": {
      "get": {
        "operationId": "feedbackUsage",
        "summary": "How to send feedback. Free.",
        "responses": {
          "200": {
            "description": "Usage instructions."
          }
        }
      },
      "post": {
        "operationId": "sendFeedback",
        "summary": "Tell a human something is wrong. Free, no auth, no payment.",
        "description": "Simplest form: POST your report as the raw body with Content-Type text/markdown or text/plain. Structured form: JSON {message, rental_id?, token?, contact?}. Include rental_id + feedback_token to be flagged as a verified customer — it is compared and never stored. Never send root_token to this endpoint; it is ignored and answered with a warning. Wanted most: anything you had to guess, anything that looked broken but wasn't, and any documentation that was wrong.",
        "requestBody": {
          "required": true,
          "content": {
            "text/markdown": {
              "schema": {
                "type": "string"
              },
              "examples": {
                "default": {
                  "value": "EXAMPLE — replace this line with your own report: what you did, what you expected, what happened instead."
                }
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FeedbackBody"
              },
              "examples": {
                "default": {
                  "value": {
                    "message": "EXAMPLE — replace this line with your own report: what you did, what you expected, what happened instead.",
                    "rental_id": "<optional: your rental_id>",
                    "feedback_token": "<optional: feedback_token from POST /rent or GET /instance>",
                    "contact": "<optional: an email, URL or handle we can reply to>"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Filed."
          },
          "400": {
            "description": "Empty or unparseable."
          },
          "413": {
            "description": "Over 8000 characters."
          },
          "429": {
            "description": "Hourly cap reached across all callers."
          }
        }
      }
    },
    "/healthz": {
      "get": {
        "operationId": "healthz",
        "summary": "Liveness probe.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "CatalogList": {
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "sku": {
              "type": "string",
              "enum": [
                "sandbox_1h",
                "sandbox_6h",
                "sandbox_24h",
                "server_small_1mo",
                "server_small_12mo",
                "server_medium_1mo",
                "server_medium_12mo",
                "server_large_1mo",
                "server_large_12mo"
              ]
            },
            "track": {
              "type": "string",
              "enum": [
                "sandbox",
                "server"
              ]
            },
            "price_usdc": {
              "type": "number"
            },
            "duration_minutes": {
              "type": "integer"
            },
            "spec_floor": {
              "type": "object",
              "properties": {
                "vcpu": {
                  "type": "integer"
                },
                "memory_mb": {
                  "type": "integer"
                }
              }
            },
            "network": {
              "type": "object",
              "description": "Uplink. `unmetered` means no traffic billing — not infinite bandwidth.",
              "properties": {
                "port_mbits": {
                  "type": "integer",
                  "description": "Port speed in Mbit/s."
                },
                "traffic": {
                  "type": "string",
                  "const": "unmetered"
                }
              }
            },
            "disk": {
              "type": "object",
              "description": "Root disk shipped with the box.",
              "properties": {
                "size_gb": {
                  "type": "integer"
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "SSD",
                    "NVMe"
                  ]
                }
              }
            },
            "available": {
              "type": "boolean"
            },
            "description": {
              "type": "string"
            },
            "region_pricing": {
              "type": "object",
              "description": "Server track only. `default` is the free region; `surcharge_usd` maps each deployable region code to the USDC added over the SKU window. Add to price_usdc; pass the code as `region` in POST /rent.",
              "properties": {
                "default": {
                  "type": "string"
                },
                "surcharge_usd": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "number"
                  }
                }
              }
            }
          }
        }
      },
      "PaymentChallenge": {
        "type": "object",
        "description": "The 402 body. Also base64'd into the PAYMENT-REQUIRED response header.",
        "properties": {
          "x402Version": {
            "type": "integer",
            "const": 2
          },
          "resource": {
            "type": "object",
            "description": "What you are buying. Echo it back unchanged in the payment payload.",
            "properties": {
              "url": {
                "type": "string",
                "format": "uri"
              },
              "description": {
                "type": "string"
              },
              "mimeType": {
                "type": "string",
                "const": "application/json"
              }
            }
          },
          "accepts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentRequirements"
            }
          },
          "extensions": {
            "type": "object",
            "description": "Discovery metadata under `bazaar`. Echo it back unchanged."
          }
        }
      },
      "PaymentRequirements": {
        "type": "object",
        "description": "One acceptable way to pay. v2 shape — note `amount`, not `maxAmountRequired`.",
        "properties": {
          "scheme": {
            "type": "string",
            "const": "exact"
          },
          "network": {
            "type": "string",
            "const": "eip155:8453",
            "description": "CAIP-2 chain id."
          },
          "amount": {
            "type": "string",
            "description": "Exact price in USDC base units (6 decimals). Sign this value verbatim."
          },
          "asset": {
            "type": "string",
            "const": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
          },
          "payTo": {
            "type": "string",
            "const": "0x8eCbd65228930060363C70854da7D37D57ffC358"
          },
          "maxTimeoutSeconds": {
            "type": "integer",
            "description": "How long the signed authorization stays claimable."
          },
          "extra": {
            "type": "object",
            "description": "EIP-712 domain for the asset: {name, version}."
          }
        }
      },
      "RegionList": {
        "type": "object",
        "properties": {
          "default": {
            "type": "string",
            "description": "Region billed with no surcharge when `region` is omitted."
          },
          "regions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string",
                  "description": "Pass verbatim as `region` in POST /rent."
                },
                "location": {
                  "type": "string",
                  "description": "The most precise placement we will stand behind."
                },
                "precision": {
                  "type": "string",
                  "enum": [
                    "city",
                    "country",
                    "area"
                  ],
                  "description": "What `location` rests on. `city` = confirmed from inside a delivered box."
                },
                "area": {
                  "type": "string",
                  "description": "The area we commit to. Always present."
                },
                "country": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Null when the region spans several countries (EU does)."
                },
                "country_code": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "ISO 3166-1 alpha-2."
                },
                "city": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Set only where a box we sold was geolocated from inside it."
                },
                "confirmed_on": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Date of that in-box confirmation. Non-null exactly when `city` is."
                },
                "city_expected": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "Upstream's listed city. UNCONFIRMED — has disagreed with reality; never a guarantee."
                },
                "surcharge_usdc_per_month": {
                  "type": "number",
                  "description": "Added per month of the SKU term, on top of the base price."
                },
                "is_default": {
                  "type": "boolean"
                },
                "note": {
                  "type": "string",
                  "description": "Caveats worth reading before buying this region."
                }
              }
            }
          },
          "note": {
            "type": "string"
          },
          "location_note": {
            "type": "string",
            "description": "How to read `location` / `precision` / `city_expected`."
          }
        }
      },
      "FeedbackBody": {
        "type": "object",
        "required": [
          "message"
        ],
        "properties": {
          "message": {
            "type": "string",
            "minLength": 1,
            "maxLength": 8000
          },
          "rental_id": {
            "type": "string",
            "description": "Optional. With `feedback_token`, flags the report as coming from a verified customer."
          },
          "feedback_token": {
            "type": "string",
            "description": "Optional. From your POST /rent or GET /instance response. Scoped to this single purpose — it badges a report and authorizes nothing else. Do NOT send root_token here: it authorizes credential retrieval and renewal payments, this endpoint never asks for it, and it is ignored if sent."
          },
          "contact": {
            "type": "string",
            "description": "Optional. Anything we can reply to. Omit to stay anonymous."
          }
        }
      },
      "RentBody": {
        "type": "object",
        "required": [
          "sku"
        ],
        "properties": {
          "sku": {
            "type": "string",
            "enum": [
              "sandbox_1h",
              "sandbox_6h",
              "sandbox_24h",
              "server_small_1mo",
              "server_small_12mo",
              "server_medium_1mo",
              "server_medium_12mo",
              "server_large_1mo",
              "server_large_12mo"
            ]
          },
          "region": {
            "type": "string",
            "enum": [
              "EU",
              "US-central",
              "US-east",
              "US-west",
              "SIN",
              "UK",
              "AUS",
              "JPN",
              "IND"
            ],
            "description": "Optional deployment region. Defaults to EU (free). Non-EU regions carry a per-month surcharge that is included in the priced 402 challenge — see `region_pricing` in GET /catalog for the exact amounts. An unknown code is rejected with 400."
          }
        }
      },
      "RentalCreated": {
        "type": "object",
        "properties": {
          "rental_id": {
            "type": "string",
            "format": "uuid"
          },
          "sku": {
            "type": "string",
            "enum": [
              "sandbox_1h",
              "sandbox_6h",
              "sandbox_24h",
              "server_small_1mo",
              "server_small_12mo",
              "server_medium_1mo",
              "server_medium_12mo",
              "server_large_1mo",
              "server_large_12mo"
            ]
          },
          "ipv4": {
            "type": [
              "string",
              "null"
            ],
            "description": "Box IPv4. May be null at first; poll GET /instance/{id}."
          },
          "expires_at": {
            "type": "integer",
            "description": "Unix ms."
          },
          "root_token": {
            "type": "string",
            "description": "Bearer for GET /instance/{id} and POST /rentals/{id}/renew. Treat as a credential."
          },
          "root_user": {
            "type": "string",
            "const": "root",
            "description": "SSH username."
          },
          "root_password": {
            "type": "string",
            "description": "SSH password — set via cloud-init at first boot. Re-fetchable via GET /instance/{id}?token=…"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending_settlement"
            ]
          },
          "note": {
            "type": "string"
          }
        }
      },
      "RentalStatus": {
        "type": "object",
        "properties": {
          "rental_id": {
            "type": "string",
            "format": "uuid"
          },
          "sku": {
            "type": "string",
            "enum": [
              "sandbox_1h",
              "sandbox_6h",
              "sandbox_24h",
              "server_small_1mo",
              "server_small_12mo",
              "server_medium_1mo",
              "server_medium_12mo",
              "server_large_1mo",
              "server_large_12mo"
            ]
          },
          "track": {
            "type": "string",
            "enum": [
              "sandbox",
              "server"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending_settlement",
              "active",
              "renewing",
              "expired"
            ]
          },
          "ipv4": {
            "type": [
              "string",
              "null"
            ],
            "description": "Public IPv4 of the box. Null until it finishes booting (3–5 min) — poll until set. SSH credentials live on GET /instance/{rental_id}?token=…, not here."
          },
          "created_at": {
            "type": "integer"
          },
          "expires_at": {
            "type": "integer"
          },
          "grace_until": {
            "type": [
              "integer",
              "null"
            ]
          },
          "settle_tx_hash": {
            "type": [
              "string",
              "null"
            ]
          },
          "renewal_count": {
            "type": "integer"
          },
          "last_renewal_tx_hash": {
            "type": [
              "string",
              "null"
            ]
          },
          "note": {
            "type": "string",
            "description": "Where to go next, in plain language."
          }
        }
      },
      "RenewBody": {
        "type": "object",
        "properties": {
          "periods": {
            "type": "integer",
            "minimum": 1,
            "maximum": 12,
            "default": 1
          }
        }
      },
      "RenewalCreated": {
        "type": "object",
        "properties": {
          "rental_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "renewing"
            ]
          },
          "periods": {
            "type": "integer",
            "description": "Periods actually applied. Compare it against what you asked for before you trust the renewal."
          },
          "price_usdc": {
            "type": "string",
            "description": "What the signed authorization is for, in USDC."
          },
          "current_expires_at": {
            "type": "integer"
          },
          "predicted_expires_at": {
            "type": "integer"
          },
          "manual_step": {
            "type": "string",
            "description": "What you have to do. Normally nothing — the extension is applied automatically. Kept for agents written against the original response shape, when this step needed a human."
          }
        }
      },
      "InstanceStatus": {
        "type": "object",
        "description": "Everything needed to reach the box. `connection` is the preferred shape; `root_user`/`root_password` are kept alongside it for agents written against the original response.",
        "properties": {
          "rental_id": {
            "type": "string",
            "format": "uuid"
          },
          "sku": {
            "type": "string",
            "enum": [
              "sandbox_1h",
              "sandbox_6h",
              "sandbox_24h",
              "server_small_1mo",
              "server_small_12mo",
              "server_medium_1mo",
              "server_medium_12mo",
              "server_large_1mo",
              "server_large_12mo"
            ]
          },
          "region": {
            "type": "string",
            "description": "Region code the box was deployed to."
          },
          "specs": {
            "type": "object",
            "properties": {
              "vcpu": {
                "type": "integer"
              },
              "memory_mb": {
                "type": "integer"
              },
              "disk_gb": {
                "type": "integer"
              },
              "port_mbits": {
                "type": "integer",
                "description": "Uplink speed in Mbit/s."
              },
              "traffic": {
                "type": "string",
                "const": "unmetered",
                "description": "No traffic billing."
              },
              "disk_type": {
                "type": "string",
                "enum": [
                  "SSD",
                  "NVMe"
                ]
              }
            }
          },
          "phase": {
            "type": "string",
            "enum": [
              "provisioning",
              "ready",
              "expired"
            ],
            "description": "Answers 'can I SSH in yet?'. Poll until `ready` (typically 3–5 minutes after /rent)."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending_settlement",
              "active",
              "renewing",
              "expired"
            ],
            "description": "Billing lifecycle. Independent of `phase`: a running box reads `pending_settlement` until the worker settles."
          },
          "running": {
            "type": "boolean",
            "description": "True once TCP :22 answers — the same signal that triggers settlement."
          },
          "ipv4": {
            "type": [
              "string",
              "null"
            ]
          },
          "connection": {
            "type": [
              "object",
              "null"
            ],
            "description": "Null until an IPv4 is assigned.",
            "properties": {
              "host": {
                "type": "string"
              },
              "port": {
                "type": "integer",
                "const": 22
              },
              "user": {
                "type": "string",
                "const": "root"
              },
              "password": {
                "type": "string"
              },
              "command": {
                "type": "string",
                "description": "Paste-ready ssh invocation."
              }
            }
          },
          "root_user": {
            "type": "string",
            "const": "root"
          },
          "root_password": {
            "type": "string"
          },
          "expires_at": {
            "type": "integer",
            "description": "Unix ms. The box is destroyed at this time."
          },
          "expires_in_seconds": {
            "type": "integer"
          },
          "renewal_count": {
            "type": "integer"
          },
          "renewal": {
            "type": [
              "object",
              "null"
            ],
            "description": "Present only while a renewal is in flight. The payment is authorized and held, never captured, until the extension is actually in place — which is why `expires_at` above has not moved. `charged_usdc` is what has actually left your wallet (always 0.00 while this block exists); `charges_on_success_usdc` is what gets captured if it completes. If it never completes you are charged nothing and the rental returns to `active`.",
            "properties": {
              "state": {
                "type": "string",
                "enum": [
                  "awaiting_extension",
                  "finalizing"
                ],
                "description": "`awaiting_extension`: the extension is not in place upstream yet. `finalizing`: it is, and capture follows within a minute or two."
              },
              "periods": {
                "type": "integer"
              },
              "charged_usdc": {
                "type": "string",
                "description": "What has actually left your wallet. 0.00 for as long as this block is visible."
              },
              "charges_on_success_usdc": {
                "type": "string",
                "description": "What is captured if, and only if, the extension completes."
              },
              "current_expires_at": {
                "type": "integer"
              },
              "predicted_expires_at": {
                "type": "integer"
              },
              "abandoned_after": {
                "type": "integer",
                "description": "Unix ms. If the extension isn't in place by then the renewal is dropped, nothing is charged, and the rental returns to `active` so you can retry."
              },
              "note": {
                "type": "string"
              }
            }
          },
          "renew_url": {
            "type": "string",
            "description": "Pre-signed renewal URL for this rental (token included). Carries no `periods` — append &periods=N for N terms."
          },
          "renew_periods_note": {
            "type": "string",
            "description": "How to ask for more than one period on renew_url."
          },
          "note": {
            "type": "string",
            "description": "Human-readable guidance for the current phase."
          }
        }
      }
    }
  }
}
