Servers
Section titled “Servers”The Servers API lets you browse the rental marketplace, rent physical servers and delivery-lead-time offers, manage active rentals and reservations, and organize servers into pools for team collaboration. Use these endpoints to discover hardware, rent for a chosen duration, extend an active rental, and look up live CPU, memory, and disk metrics for any server you currently hold.
Marketplace
Section titled “Marketplace”GET /api/v1/servers/available
Section titled “GET /api/v1/servers/available”Browse the rental marketplace. Returns servers that can be rented immediately (in stock). Apply query filters to narrow by location, hardware specs, network capacity, and price.
curl -X GET "https://api.hoody.icu/api/v1/servers/available?country=US&min_cpu_cores=32&featured_only=true" \ -H "Authorization: Bearer <token>"await client.api.serverRental.browseIterator({ country: 'US', min_cpu_cores: 32, featured_only: true,});{ "statusCode": 200, "message": "Available servers retrieved successfully", "data": [ { "id": "507f1f77bcf86cd799439014", "name": "node-us-nyc-1", "country": "US", "region": "us-east", "city": "New York", "datacenter": "NYC-DC1", "model": "Intel Xeon E5-2680 v4", "is_vm": false, "category": "compute", "featured": true, "popularity_rank": 1, "setup_time_minutes": 15, "delivery_hours": 0, "pricing": { "prices": { "1": "12.00", "7": "70.00", "30": "250.00" }, "setup_fee": "25.00", "setup_fee_cents": 2500, "price_tiers": { "1": { "price": "12.00", "hold_days": 1, "total_first_payment": "37.00" }, "7": { "price": "70.00", "hold_days": 2, "total_first_payment": "95.00" } } }, "specs": { "cpu": { "model": "AMD EPYC 7763", "cores": 64, "threads": 128, "score": 48500, "score_type": "passmark" }, "ram": { "capacity_gb": 256, "type": "ECC DDR5", "speed_mhz": 4800 }, "disks": { "config": [ { "count": 2, "capacity_gb": 1000, "type": "NVMe", "interface": "PCIe 4.0" }, { "count": 6, "capacity_gb": 15000, "type": "NVMe", "interface": "PCIe 4.0" } ], "total_gb": 92000, "summary": "2x1TB NVMe + 6x15TB NVMe" }, "network": { "bandwidth_mbps": 10000, "bandwidth_formatted": "10 Gbps", "traffic_tb": 100, "traffic_unlimited": false }, "additional": { "ipv4_count": 5, "ipv6_enabled": true } } } ]}Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
country | query | string | No | Filter by country code (e.g., US, DE) |
region | query | string | No | Filter by region (e.g., us-east, eu-central) |
max_price_per_day | query | number | No | Maximum price per day in USD |
available_durations | query | array | No | Filter servers that support these rental durations (days) |
min_cpu_cores | query | number | No | Minimum CPU cores |
min_cpu_score | query | number | No | Minimum CPU benchmark score |
cpu_score_type | query | string | No | CPU benchmark type for score filtering. Allowed values: passmark, geekbench_single, geekbench_multi |
min_ram_gb | query | number | No | Minimum RAM in GB |
ram_types | query | array | No | Filter by RAM types |
min_total_storage_gb | query | number | No | Minimum total storage in GB |
disk_types | query | array | No | Filter servers with these disk types |
min_bandwidth_mbps | query | number | No | Minimum network bandwidth in Mbps |
min_traffic_tb | query | number | No | Minimum monthly traffic allowance in TB |
unlimited_traffic_only | query | boolean | No | Show only servers with unlimited traffic |
category | query | string | No | Filter by server category. Allowed values: compute, memory, storage, general, gpu |
featured_only | query | boolean | No | Show only featured servers |
Response 200
Section titled “Response 200”Success.
GET /api/v1/offers
Section titled “GET /api/v1/offers”Browse machines available to order with a delivery lead time. Distinguished from in-stock listings by delivery_hours: in-stock servers report 0, while offers report 1..720.
curl -X GET "https://api.hoody.icu/api/v1/offers" \ -H "Authorization: Bearer <token>"await client.api.serverRental.listServerOffers();{ "statusCode": 200, "message": "Offers retrieved successfully", "data": [ { "id": "507f1f77bcf86cd799439020", "name": "node-de-fra-provisioning", "country": "DE", "region": "eu-central", "city": "Frankfurt", "datacenter": "FRA-DC2", "cpu_model": "AMD EPYC 7402P", "cpu_cores": 24, "cpu_threads": 48, "ram_gb": 128, "disks": [ { "count": 2, "capacity_gb": 960, "type": "NVMe", "interface": "PCIe 3.0" } ], "bandwidth_mbps": 1000, "traffic_tb": 50, "traffic_unlimited": false, "ipv4_count": 1, "pricing_rules": { "7": 12000, "30": 40000 }, "hold_rules": { "7": 2, "30": 5 }, "setup_fee_cents": 5000, "delivery_hours": 48, "setup_time_minutes": 90, "stock": 3 } ]}This endpoint takes no parameters.
Response 200
Section titled “Response 200”Success.
POST /api/v1/offers/{id}/reserve
Section titled “POST /api/v1/offers/{id}/reserve”Reserve an offer. Charges immediately. Provide an idempotency_key to make the request safely retryable — replaying the same key returns the original reservation, unpaid twice.
curl -X POST "https://api.hoody.icu/api/v1/offers/507f1f77bcf86cd799439020/reserve" \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "days": 30, "max_charge_cents": 45000, "idempotency_key": "reserve-2025-01-21-001" }'await client.api.serverRental.reserveServerOffer( '507f1f77bcf86cd799439020', { days: 30, max_charge_cents: 45000, idempotency_key: 'reserve-2025-01-21-001', },);{ "statusCode": 201, "message": "Reservation created successfully", "data": { "reservation": { "id": "507f1f77bcf86cd799439350", "offer_id": "507f1f77bcf86cd799439020", "days": 30, "state": "pending", "ready_by": "2025-01-23T22:00:00.000Z", "delivery_hours_quoted": 48, "setup_time_minutes_quoted": 90, "hold_days_quoted": 5, "server_id": null, "rental_id": null, "created_at": "2025-01-21T22:00:00.000Z", "rental_cents": 40000, "setup_fee_cents": 5000, "total_paid_cents": 45000, "offer_snapshot": {} }, "replayed": false }}{ "statusCode": 409, "error": "CHARGE_EXCEEDS_MAX", "message": "The price changed — please review the current total and confirm again.", "data": { "rental_cents": 40000, "setup_fee_cents": 5000, "total_cents": 45000, "max_charge_cents": 40000 }}| Error Code | Title | Description | Resolution |
|---|---|---|---|
OFFER_UNAVAILABLE | Offer has been withdrawn | The listing is no longer active because it was withdrawn. Withdrawing an offer never affects reservations that were already placed against it. | Re-read GET /api/v1/offers and present what is currently purchasable. |
OUT_OF_STOCK | Offer is sold out | Stock is exhausted. This is also what a caller sees when another customer took the last unit a moment earlier — nothing was charged. | Re-read GET /api/v1/offers; the offer may return to stock later. |
IDEMPOTENCY_INTENT_MISMATCH | Idempotency key reused for a different purchase | The supplied idempotency_key was already used for a reservation with different terms — a different offer, duration, pool or confirmed total. Returning the original would silently give the caller something other than what this request asked for, so it fails instead. | Use a fresh idempotency_key for a genuinely new purchase. |
SETUP_FEE_CONFIRMATION_REQUIRED | One-time setup fee must be confirmed | This server carries a one-time setup fee, charged once when the rental starts. The request omitted max_charge_cents, so the total was never confirmed and nothing was charged. | Read pricing.setup_fee_cents and pricing.price_tiers[rental_days].total_first_payment, show the customer the one-time fee, then retry with max_charge_cents set to the confirmed total. |
CHARGE_CONFIRMATION_REQUIRED | The total must be confirmed before money moves | The request would debit an account and carried no max_charge_cents, so there was nothing to bind the quoted price to the charged one. Nothing was charged. | Read the current total, show it to the customer, and retry with max_charge_cents set to the value they confirmed. Never auto-retry with the total from this error. |
CHARGE_EXCEEDS_MAX | Price changed since the quote | The live total (rental price plus any one-time setup fee) is higher than the max_charge_cents ceiling supplied with the request, so nothing was charged. | Re-read the listing, show the user the new total, and retry with an updated max_charge_cents once they confirm. Do NOT auto-retry with the returned actual value. |
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Offer ID |
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
days | number | Yes | Must be one of the offer’s pricing_rules keys. |
max_charge_cents | number | No | Ceiling on the total debit (rent + one-time setup fee), in integer cents. Required whenever a setup fee applies. |
idempotency_key | string | Yes | Caller-generated key. Replaying it returns the original reservation, unpaid twice. |
pool_id | string | No | Must be a pool you own. Defaults to your default pool. |
Reservations
Section titled “Reservations”GET /api/v1/reservations
Section titled “GET /api/v1/reservations”List your reservations for ordered-but-not-yet-delivered offers.
curl -X GET "https://api.hoody.icu/api/v1/reservations?limit=20&offset=0" \ -H "Authorization: Bearer <token>"await client.api.serverRental.listMyReservations({ limit: 20, offset: 0 });{ "statusCode": 200, "message": "Reservations retrieved successfully", "data": [ { "id": "507f1f77bcf86cd799439350", "offer_id": "507f1f77bcf86cd799439020", "days": 30, "state": "pending", "ready_by": "2025-01-23T22:00:00.000Z", "delivery_hours_quoted": 48, "setup_time_minutes_quoted": 90, "hold_days_quoted": 5, "server_id": null, "rental_id": null, "created_at": "2025-01-21T22:00:00.000Z", "rental_cents": 40000, "setup_fee_cents": 5000, "total_paid_cents": 45000, "offer_snapshot": {} } ], "total": 1, "limit": 20, "offset": 0}Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit | query | integer | No | Maximum number of reservations to return. Default: 50. |
offset | query | integer | No | Number of reservations to skip. Default: 0. |
Response 200
Section titled “Response 200”Success.
GET /api/v1/reservations/{id}
Section titled “GET /api/v1/reservations/{id}”Get one of your reservations by ID.
curl -X GET "https://api.hoody.icu/api/v1/reservations/507f1f77bcf86cd799439350" \ -H "Authorization: Bearer <token>"await client.api.serverRental.getMyReservation('507f1f77bcf86cd799439350');{ "statusCode": 200, "message": "Reservation retrieved successfully", "data": { "id": "507f1f77bcf86cd799439350", "offer_id": "507f1f77bcf86cd799439020", "days": 30, "state": "pending", "ready_by": "2025-01-23T22:00:00.000Z", "delivery_hours_quoted": 48, "setup_time_minutes_quoted": 90, "hold_days_quoted": 5, "server_id": null, "rental_id": null, "created_at": "2025-01-21T22:00:00.000Z", "rental_cents": 40000, "setup_fee_cents": 5000, "total_paid_cents": 45000, "offer_snapshot": {} }}Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Reservation ID |
Response 200
Section titled “Response 200”Success.
Servers
Section titled “Servers”GET /api/v1/servers
Section titled “GET /api/v1/servers”List your rented servers. Alias for /api/v1/rentals.
curl -X GET "https://api.hoody.icu/api/v1/servers" \ -H "Authorization: Bearer <token>"await client.api.serverRental.listIterator();{ "statusCode": 200, "message": "Rentals retrieved successfully", "data": [ { "id": "507f1f77bcf86cd799439320", "rental_start": "2025-01-21T22:00:00.000Z", "rental_end": "2025-01-28T22:00:00.000Z", "status": "active", "amount": "70.00", "setup_fee_cents": 2500, "total_paid_cents": 9500, "renewal_pricing_frozen": { "30": 8000 }, "remaining_days": 6, "server_id": "507f1f77bcf86cd799439014", "pool_id": "507f1f77bcf86cd799439300", "is_free_tier": false, "server": { "id": "507f1f77bcf86cd799439014" } } ]}This endpoint takes no parameters.
Response 200
Section titled “Response 200”Success.
GET /api/v1/servers/{id}
Section titled “GET /api/v1/servers/{id}”Get details about one of your rented servers. Alias for /api/v1/rentals/{id}.
curl -X GET "https://api.hoody.icu/api/v1/servers/507f1f77bcf86cd799439320" \ -H "Authorization: Bearer <token>"await client.api.serverRental.get('507f1f77bcf86cd799439320');{ "statusCode": 200, "message": "Rental details retrieved successfully", "data": { "id": "507f1f77bcf86cd799439320", "rental_start": "2025-01-21T22:00:00.000Z", "rental_end": "2025-01-28T22:00:00.000Z", "hold_days": 2, "status": "active", "amount": "70.00", "setup_fee_cents": 2500, "total_paid_cents": 9500, "renewal_pricing_frozen": { "30": 8000 }, "remaining_days": 6, "usage_days": 1, "server_id": "507f1f77bcf86cd799439014", "pool_id": "507f1f77bcf86cd799439300", "is_free_tier": false, "server": { "id": "507f1f77bcf86cd799439014", "name": "node-us-nyc-1", "country": "US", "region": "us-east", "city": "New York", "datacenter": "NYC-DC1", "model": "Intel Xeon E5-2680 v4", "is_vm": false, "specs": { "cpu": { "model": "AMD EPYC 7763", "cores": 64, "threads": 128, "score": 48500, "score_type": "passmark" }, "ram": { "capacity_gb": 256, "type": "ECC DDR5", "speed_mhz": 4800 }, "disks": { "config": [ { "count": 2, "capacity_gb": 1000, "type": "NVMe", "interface": "PCIe 4.0" }, { "count": 6, "capacity_gb": 15000, "type": "NVMe", "interface": "PCIe 4.0" } ], "total_gb": 92000, "summary": "2x1TB NVMe + 6x15TB NVMe" }, "network": { "bandwidth_mbps": 10000, "bandwidth_formatted": "10 Gbps", "traffic_tb": 100, "traffic_unlimited": false }, "additional": { "ipv4_count": 5, "ipv6_enabled": true } } }, "transaction": { "id": "507f1f77bcf86cd799439321", "amount": 70, "currency": "USD", "created_at": "2025-01-21T22:00:00.000Z" } }}Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Rental/Server ID |
Response 200
Section titled “Response 200”Success.
POST /api/v1/servers/{id}/rent
Section titled “POST /api/v1/servers/{id}/rent”Rent an available server for a specified duration. Some servers carry a one-time setup fee charged only on this first payment (never on extension); when they do, the request must confirm the total via max_charge_cents.
curl -X POST "https://api.hoody.icu/api/v1/servers/507f1f77bcf86cd799439014/rent" \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "rental_days": 7, "max_charge_cents": 9500, "pool_id": "507f1f77bcf86cd799439300" }'await client.api.serverRental.rent( '507f1f77bcf86cd799439014', { rental_days: 7, max_charge_cents: 9500, pool_id: '507f1f77bcf86cd799439300', },);{ "statusCode": 201, "message": "Server rented successfully", "data": { "rental": { "id": "507f1f77bcf86cd799439320", "server_id": "507f1f77bcf86cd799439014", "rental_start": "2025-01-21T22:00:00.000Z", "rental_end": "2025-01-28T22:00:00.000Z", "hold_days": 2, "actual_usage_days": 0, "status": "active", "setup_fee_cents": 2500, "total_paid_cents": 12500, "renewal_pricing_frozen": { "30": 8000 } }, "transaction": { "id": "507f1f77bcf86cd799439321", "amount": 70, "currency": "USD" } }}{ "statusCode": 400, "error": "INSUFFICIENT_BALANCE", "message": "Insufficient balance for rental", "data": { "rental_cents": 20000, "setup_fee_cents": 2500, "required_cents": 22500, "available_cents": 18000 }}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INSUFFICIENT_BALANCE | Wallet balance too low | The wallet balance is below the total required for this rental. The response data breaks the total down into rental_cents, setup_fee_cents, required_cents and available_cents. | Top up the wallet to at least required_cents, then retry. |
INVALID_DURATION | Rental duration not offered | rental_days must match a duration the server actually prices. There is no interpolation. | Pick one of pricing.available_durations and retry. |
NO_PRICING | Server has no pricing configured | The server has no pricing row, or none covering the requested duration. | Choose a different server, or contact support to have pricing configured for it. |
{ "statusCode": 409, "error": "CHARGE_EXCEEDS_MAX", "message": "The price changed — please review the current total and confirm again.", "data": { "rental_cents": 20000, "setup_fee_cents": 2500, "total_cents": 22500, "max_charge_cents": 20000 }}| Error Code | Title | Description | Resolution |
|---|---|---|---|
SERVER_UNAVAILABLE | Server not available for rental | The server is not rentable: it is already rented, not ready, or not offered for rental. Also returned when a concurrent request won the race for it. | Re-read GET /api/v1/servers/available and pick a currently available server. |
SETUP_FEE_CONFIRMATION_REQUIRED | One-time setup fee must be confirmed | This server carries a one-time setup fee, charged once when the rental starts. The request omitted max_charge_cents, so the total was never confirmed and nothing was charged. | Read pricing.setup_fee_cents and pricing.price_tiers[rental_days].total_first_payment, show the customer the one-time fee, then retry with max_charge_cents set to the confirmed total. |
CHARGE_CONFIRMATION_REQUIRED | The total must be confirmed before money moves | The request would debit an account and carried no max_charge_cents, so there was nothing to bind the quoted price to the charged one. Nothing was charged. | Read the current total, show it to the customer, and retry with max_charge_cents set to the value they confirmed. Never auto-retry with the total from this error. |
CHARGE_EXCEEDS_MAX | Price changed since the quote | The live total (rental price plus any one-time setup fee) is higher than the max_charge_cents ceiling supplied with the request, so nothing was charged. | Re-read the listing, show the user the new total, and retry with an updated max_charge_cents once they confirm. Do NOT auto-retry with the returned actual value. |
{ "statusCode": 500, "error": "PRICING_INVALID", "message": "Server pricing is misconfigured and cannot be charged. Please contact support."}| Error Code | Title | Description | Resolution |
|---|---|---|---|
PRICING_INVALID | Stored pricing is misconfigured | The server’s stored pricing did not pass validation when the charge was attempted. The request fails closed and nothing is charged. | This server cannot be rented until its pricing is corrected — please contact support. Retrying beforehand cannot succeed. |
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Server ID |
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
pool_id | string | No | Must match ^[0-9a-f]{24}$. |
rental_days | integer | Yes | Number of days to rent (must match server pricing durations, max 3650). |
max_charge_cents | integer | No | Ceiling on the total debit (rental price + one-time setup fee), in integer cents. Required for every paid rental. |
Rentals
Section titled “Rentals”GET /api/v1/rentals
Section titled “GET /api/v1/rentals”List all rentals for the authenticated user.
curl -X GET "https://api.hoody.icu/api/v1/rentals" \ -H "Authorization: Bearer <token>"await client.api.rentals.listIterator();{ "statusCode": 200, "message": "Rentals retrieved successfully", "data": [ { "id": "507f1f77bcf86cd799439320", "rental_start": "2025-01-21T22:00:00.000Z", "rental_end": "2025-01-28T22:00:00.000Z", "status": "active", "amount": "70.00", "setup_fee_cents": 2500, "total_paid_cents": 9500, "renewal_pricing_frozen": { "30": 8000 }, "remaining_days": 6, "server_id": "507f1f77bcf86cd799439014", "pool_id": "507f1f77bcf86cd799439300", "is_free_tier": false, "server": { "id": "507f1f77bcf86cd799439014" } } ]}This endpoint takes no parameters.
Response 200
Section titled “Response 200”Success.
GET /api/v1/rentals/{id}
Section titled “GET /api/v1/rentals/{id}”Get details about a specific rental.
curl -X GET "https://api.hoody.icu/api/v1/rentals/507f1f77bcf86cd799439320" \ -H "Authorization: Bearer <token>"await client.api.rentals.get('507f1f77bcf86cd799439320');{ "statusCode": 200, "message": "Rental retrieved successfully", "data": { "id": "507f1f77bcf86cd799439320", "rental_start": "2025-01-21T22:00:00.000Z", "rental_end": "2025-01-28T22:00:00.000Z", "hold_days": 2, "status": "active", "amount": "70.00", "setup_fee_cents": 2500, "total_paid_cents": 9500, "renewal_pricing_frozen": { "30": 8000 }, "remaining_days": 6, "usage_days": 1, "server_id": "507f1f77bcf86cd799439014", "pool_id": "507f1f77bcf86cd799439300", "is_free_tier": false, "server": { "id": "507f1f77bcf86cd799439014", "name": "node-us-nyc-1", "country": "US", "region": "us-east", "city": "New York", "datacenter": "NYC-DC1", "model": "Intel Xeon E5-2680 v4", "is_vm": false, "specs": { "cpu": { "model": "AMD EPYC 7763", "cores": 64, "threads": 128, "score": 48500, "score_type": "passmark" }, "ram": { "capacity_gb": 256, "type": "ECC DDR5", "speed_mhz": 4800 }, "disks": { "config": [ { "count": 2, "capacity_gb": 1000, "type": "NVMe", "interface": "PCIe 4.0" }, { "count": 6, "capacity_gb": 15000, "type": "NVMe", "interface": "PCIe 4.0" } ], "total_gb": 92000, "summary": "2x1TB NVMe + 6x15TB NVMe" }, "network": { "bandwidth_mbps": 10000, "bandwidth_formatted": "10 Gbps", "traffic_tb": 100, "traffic_unlimited": false }, "additional": { "ipv4_count": 5, "ipv6_enabled": true } } }, "transaction": { "id": "507f1f77bcf86cd799439321", "amount": 70, "currency": "USD", "created_at": "2025-01-21T22:00:00.000Z" } }}Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Rental ID |
Response 200
Section titled “Response 200”Success.
POST /api/v1/rentals/{id}/extend
Section titled “POST /api/v1/rentals/{id}/extend”Extend an existing rental for additional days.
curl -X POST "https://api.hoody.icu/api/v1/rentals/507f1f77bcf86cd799439320/extend" \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "expected_rental_end": "2025-01-28T22:00:00.000Z", "additional_days": 7, "max_charge_cents": 9500 }'await client.api.rentals.extend( '507f1f77bcf86cd799439320', { expected_rental_end: '2025-01-28T22:00:00.000Z', additional_days: 7, max_charge_cents: 9500, },);{ "statusCode": 200, "message": "Rental extended successfully", "data": { "rental": { "id": "507f1f77bcf86cd799439320", "rental_end": "2025-02-04T22:00:00.000Z", "status": "active", "amount": "140.00", "setup_fee_cents": 2500, "total_paid_cents": 9500, "renewal_pricing_frozen": { "30": 8000 }, "remaining_days": 13 }, "transaction": { "id": "507f1f77bcf86cd799439322", "amount": 70, "currency": "USD" } }}{ "statusCode": 409, "error": "EXTENSION_ALREADY_APPLIED", "message": "This rental has already been extended. Re-read it and confirm the new total before extending again.", "data": { "rental_id": "000000000000000000000001", "rental_end": "2026-09-01T00:00:00.000Z", "expected_rental_end": "2026-08-01T00:00:00.000Z" }}| Error Code | Title | Description | Resolution |
|---|---|---|---|
EXTENSION_ALREADY_APPLIED | That extension has already been applied | The rental’s end date no longer matches the expected_rental_end sent with the request, so the extension was not applied again. Nothing was charged. | Re-read the rental. If its end date already reflects the extension you wanted, you are done. Otherwise retry with expected_rental_end set to the value you just read. Never retry with the original value. |
CHARGE_CONFIRMATION_REQUIRED | The total must be confirmed before money moves | The request would debit an account and carried no max_charge_cents, so there was nothing to bind the quoted price to the charged one. Nothing was charged. | Read the current total, show it to the customer, and retry with max_charge_cents set to the value they confirmed. Never auto-retry with the total from this error. |
CHARGE_EXCEEDS_MAX | Price changed since the quote | The live total (rental price plus any one-time setup fee) is higher than the max_charge_cents ceiling supplied with the request, so nothing was charged. | Re-read the listing, show the user the new total, and retry with an updated max_charge_cents once they confirm. Do NOT auto-retry with the returned actual value. |
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Rental ID |
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
expected_rental_end | string | Yes | The rental’s CURRENT rental_end, exactly as the API returned it. The extension is applied only if it still matches. |
additional_days | integer | Yes | Number of additional days to extend (must match server pricing durations, max 3650). |
max_charge_cents | integer | No | The total you confirmed, in whole cents. The extension is refused if it would cost more. Required when the rental has no frozen renewal price. |
Runtime
Section titled “Runtime”GET /api/v1/rentals/{id}/runtime
Section titled “GET /api/v1/rentals/{id}/runtime”Get live runtime info for a rented server or subserver. Served from a short-TTL cache so it can be polled frequently. First-party JWT only. Physical scope requires the server to have no subservers; a shared/system host returns 409. Subserver scope may be disabled on this deployment.
curl -X GET "https://api.hoody.icu/api/v1/rentals/507f1f77bcf86cd799439320/runtime" \ -H "Authorization: Bearer <token>"await client.api.serverRental.getRentalRuntime('507f1f77bcf86cd799439320');{ "statusCode": 200, "message": "Runtime info retrieved successfully", "data": { "scope": "server", "ts": "2025-01-21T22:00:00.000Z", "cache_age_ms": 1200, "stale": false, "uptime_s": 86400, "cores": 64, "load": { "1": 0.42, "5": 0.38, "15": 0.31 }, "cpu": { "busy_pct": 12.5 }, "mem": { "used_bytes": 134217728, "total_bytes": 274877906944, "pct": 0.05 }, "swap": { "used_bytes": 0, "total_bytes": 0 }, "storage": { "used_bytes": 1200000000000, "total_bytes": 92000000000000, "pct": 1.3 } }}{ "statusCode": 400, "error": "Bad Request", "message": "Invalid ID format"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INVALID_ID_FORMAT | Invalid ID format | The provided ID must be a 24-character hexadecimal string. | Ensure the ID is exactly 24 characters long and contains only hexadecimal characters (0-9, a-f). |
{ "statusCode": 401, "error": "Unauthorized", "message": "Authentication token required"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
MISSING_TOKEN | Authentication token missing | No authentication token was provided in the request. | Include a valid JWT token in the Authorization header as Bearer <token>. |
INVALID_TOKEN | Invalid authentication token | The provided authentication token is malformed or invalid. | Obtain a new token by logging in again or using a valid auth token. |
{ "statusCode": 403, "error": "Forbidden", "message": "Insufficient permissions"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INSUFFICIENT_PERMISSIONS | Insufficient permissions | You do not have the required permissions to perform this action. | Contact the resource owner or administrator to request access. |
{ "statusCode": 404, "error": "Not Found", "message": "Resource not found"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
RESOURCE_NOT_FOUND | Resource not found | The requested resource does not exist or has been deleted. | Verify the resource ID and ensure it exists. |
{ "statusCode": 409, "error": "Conflict", "message": "Host runtime not available for a shared server"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
RUNTIME_HOST_SHARED | Host runtime not available for a shared server | Host-level runtime info is not exposed for a physical server that hosts subservers or is a system server, because it would reveal co-tenant activity. | Query runtime for the specific subserver you rent instead of the whole physical host. |
{ "statusCode": 429, "error": "Too Many Requests", "message": "Rate limit exceeded"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
RATE_LIMIT_EXCEEDED | Rate limit exceeded | You have exceeded the rate limit for this endpoint. | Wait before making additional requests, or upgrade your plan for higher limits. |
{ "statusCode": 503, "error": "Service Unavailable", "message": "Runtime info temporarily unavailable"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
RUNTIME_UNAVAILABLE | Runtime info temporarily unavailable | Live runtime info could not be collected for this server and no recent snapshot is available. | Retry shortly. If it persists, the host may be unreachable or under maintenance. |
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Rental ID |
GET /api/v1/servers/{id}/runtime
Section titled “GET /api/v1/servers/{id}/runtime”Alias for /api/v1/rentals/{id}/runtime. Returns live runtime info for the server by rental/alias ID.
curl -X GET "https://api.hoody.icu/api/v1/servers/507f1f77bcf86cd799439320/runtime" \ -H "Authorization: Bearer <token>"await client.api.serverRental.getServerRuntime('507f1f77bcf86cd799439320');{ "statusCode": 200, "message": "Runtime info retrieved successfully", "data": { "scope": "server", "ts": "2025-01-21T22:00:00.000Z", "cache_age_ms": 1200, "stale": false, "uptime_s": 86400, "cores": 64, "load": { "1": 0.42, "5": 0.38, "15": 0.31 }, "cpu": { "busy_pct": 12.5 }, "mem": { "used_bytes": 134217728, "total_bytes": 274877906944, "pct": 0.05 }, "swap": { "used_bytes": 0, "total_bytes": 0 }, "storage": { "used_bytes": 1200000000000, "total_bytes": 92000000000000, "pct": 1.3 } }}{ "statusCode": 400, "error": "Bad Request", "message": "Invalid ID format"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INVALID_ID_FORMAT | Invalid ID format | The provided ID must be a 24-character hexadecimal string. | Ensure the ID is exactly 24 characters long and contains only hexadecimal characters (0-9, a-f). |
{ "statusCode": 401, "error": "Unauthorized", "message": "Authentication token required"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
MISSING_TOKEN | Authentication token missing | No authentication token was provided in the request. | Include a valid JWT token in the Authorization header as Bearer <token>. |
INVALID_TOKEN | Invalid authentication token | The provided authentication token is malformed or invalid. | Obtain a new token by logging in again or using a valid auth token. |
{ "statusCode": 403, "error": "Forbidden", "message": "Insufficient permissions"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
INSUFFICIENT_PERMISSIONS | Insufficient permissions | You do not have the required permissions to perform this action. | Contact the resource owner or administrator to request access. |
{ "statusCode": 404, "error": "Not Found", "message": "Resource not found"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
RESOURCE_NOT_FOUND | Resource not found | The requested resource does not exist or has been deleted. | Verify the resource ID and ensure it exists. |
{ "statusCode": 409, "error": "Conflict", "message": "Host runtime not available for a shared server"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
RUNTIME_HOST_SHARED | Host runtime not available for a shared server | Host-level runtime info is not exposed for a physical server that hosts subservers or is a system server, because it would reveal co-tenant activity. | Query runtime for the specific subserver you rent instead of the whole physical host. |
{ "statusCode": 429, "error": "Too Many Requests", "message": "Rate limit exceeded"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
RATE_LIMIT_EXCEEDED | Rate limit exceeded | You have exceeded the rate limit for this endpoint. | Wait before making additional requests, or upgrade your plan for higher limits. |
{ "statusCode": 503, "error": "Service Unavailable", "message": "Runtime info temporarily unavailable"}| Error Code | Title | Description | Resolution |
|---|---|---|---|
RUNTIME_UNAVAILABLE | Runtime info temporarily unavailable | Live runtime info could not be collected for this server and no recent snapshot is available. | Retry shortly. If it persists, the host may be unreachable or under maintenance. |
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Server/Rental ID |
Pools let you group servers and teammates. Every authenticated user has at least one default pool. Pools have three roles: owner, admin, and user. New invitations can only be granted admin or user; owners cannot be invited.
GET /api/v1/pools
Section titled “GET /api/v1/pools”List pools the user owns or is a member of.
curl -X GET "https://api.hoody.icu/api/v1/pools" \ -H "Authorization: Bearer <token>"await client.api.pools.listIterator();{ "statusCode": 200, "message": "Pools retrieved successfully", "data": [ { "id": "507f1f77bcf86cd799439300", "name": "Development Team", "description": "Pool for development team resources", "is_default": false, "settings": { "auto_approve": false, "max_servers": 10 }, "created_at": "2025-01-15T10:00:00.000Z", "updated_at": "2025-01-20T14:30:00.000Z", "owner_id": "507f1f77bcf86cd799439011", "user_role": "owner", "member_count": 5, "server_count": 3 } ]}This endpoint takes no parameters.
Response 200
Section titled “Response 200”Success.
GET /api/v1/pools/{id}
Section titled “GET /api/v1/pools/{id}”Get detailed information about a specific pool, including its members and the servers assigned to it.
curl -X GET "https://api.hoody.icu/api/v1/pools/507f1f77bcf86cd799439300" \ -H "Authorization: Bearer <token>"await client.api.pools.get('507f1f77bcf86cd799439300');{ "statusCode": 200, "message": "Pool retrieved successfully", "data": { "id": "507f1f77bcf86cd799439300", "name": "Development Team", "description": "Pool for development team resources", "is_default": false, "settings": {}, "created_at": "2025-01-15T10:00:00.000Z", "updated_at": "2025-01-20T14:30:00.000Z", "owner_id": "507f1f77bcf86cd799439011", "members": [ { "id": "507f1f77bcf86cd799439310", "role": "admin", "is_authorized": true, "joined_at": "2025-01-16T11:00:00.000Z", "user": { "id": "507f1f77bcf86cd799439012", "username": "jane_admin", "alias": "Jane Smith" } } ], "servers": [ { "id": "507f1f77bcf86cd799439014", "name": "node-us-nyc-1", "rental_status": "active", "is_ready": true } ] }}Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Pool ID |
Response 200
Section titled “Response 200”Success.
POST /api/v1/pools
Section titled “POST /api/v1/pools”Create a new pool for team collaboration.
curl -X POST "https://api.hoody.icu/api/v1/pools" \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "name": "Production Team", "description": "Pool for production environment management", "settings": { "auto_approve": true, "max_servers": 20 } }'await client.api.pools.create({ name: 'Production Team', description: 'Pool for production environment management', settings: { auto_approve: true, max_servers: 20 },});{ "statusCode": 201, "message": "Pool created successfully", "data": { "id": "507f1f77bcf86cd799439301", "name": "Production Team", "description": "Pool for production environment management", "owner_id": "507f1f77bcf86cd799439011", "is_default": false, "settings": { "auto_approve": true, "max_servers": 20 }, "created_at": "2025-01-21T22:00:00.000Z" }}This endpoint takes no path, query, or header parameters.
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Pool name. Max length: 100 characters. |
description | string | No | Pool description. Max length: 500 characters. |
settings | object | No | Arbitrary pool-level settings. |
Response 201
Section titled “Response 201”Pool created successfully.
PUT /api/v1/pools/{id}
Section titled “PUT /api/v1/pools/{id}”Update pool details (owner only).
curl -X PUT "https://api.hoody.icu/api/v1/pools/507f1f77bcf86cd799439301" \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "description": "Updated pool description", "settings": { "auto_approve": true, "max_servers": 50 } }'await client.api.pools.update('507f1f77bcf86cd799439301', { description: 'Updated pool description', settings: { auto_approve: true, max_servers: 50 },});{ "statusCode": 200, "message": "Pool updated successfully", "data": { "success": true }}Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Pool ID |
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
description | string | No | New pool description. Max length: 500 characters. |
settings | object | No | New pool-level settings. |
Response 200
Section titled “Response 200”Pool updated successfully.
DELETE /api/v1/pools/{id}
Section titled “DELETE /api/v1/pools/{id}”Delete a pool. Owner only; the default pool cannot be deleted.
curl -X DELETE "https://api.hoody.icu/api/v1/pools/507f1f77bcf86cd799439301" \ -H "Authorization: Bearer <token>"await client.api.pools.delete('507f1f77bcf86cd799439301');{ "statusCode": 200, "message": "Pool deleted successfully", "data": { "success": true }}Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Pool ID |
Response 200
Section titled “Response 200”Pool deleted successfully.
Pool Members
Section titled “Pool Members”POST /api/v1/pools/{id}/members
Section titled “POST /api/v1/pools/{id}/members”Invite a user to join the pool. Admin or above required.
curl -X POST "https://api.hoody.icu/api/v1/pools/507f1f77bcf86cd799439300/members" \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "username": "jane_admin", "role": "admin" }'await client.api.poolMembers.invite('507f1f77bcf86cd799439300', { username: 'jane_admin', role: 'admin',});{ "statusCode": 201, "message": "Member invited successfully", "data": { "id": "507f1f77bcf86cd799439311", "role": "user", "is_authorized": false, "invited_at": "2025-01-21T22:15:00.000Z" }}Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Pool ID |
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
username | string | Yes | Username of the user to invite. Must match ^[a-zA-Z0-9_-]+$. Length: 1–100 characters. |
role | string | Yes | Role to grant. Allowed values: admin, user. |
Response 201
Section titled “Response 201”Member invited successfully.
PUT /api/v1/pools/{id}/members/{userId}
Section titled “PUT /api/v1/pools/{id}/members/{userId}”Update a member’s role in the pool. Owner only.
curl -X PUT "https://api.hoody.icu/api/v1/pools/507f1f77bcf86cd799439300/members/507f1f77bcf86cd799439311" \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{ "role": "user" }'await client.api.poolMembers.updateRole( '507f1f77bcf86cd799439300', '507f1f77bcf86cd799439311', { role: 'user' },);{ "statusCode": 200, "message": "Member role updated successfully", "data": { "success": true }}Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Pool ID |
userId | path | string | Yes | User ID |
Request Body
Section titled “Request Body”| Field | Type | Required | Description |
|---|---|---|---|
role | string | Yes | New role. Allowed values: admin, user. |
Response 200
Section titled “Response 200”Member role updated successfully.
DELETE /api/v1/pools/{id}/members/{userId}
Section titled “DELETE /api/v1/pools/{id}/members/{userId}”Remove a member from the pool. Admin or above required.
curl -X DELETE "https://api.hoody.icu/api/v1/pools/507f1f77bcf86cd799439300/members/507f1f77bcf86cd799439311" \ -H "Authorization: Bearer <token>"await client.api.poolMembers.remove( '507f1f77bcf86cd799439300', '507f1f77bcf86cd799439311',);{ "statusCode": 200, "message": "Member removed successfully", "data": { "success": true }}Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Pool ID |
userId | path | string | Yes | User ID |
Response 200
Section titled “Response 200”Member removed successfully.
Pool Invitations
Section titled “Pool Invitations”GET /api/v1/pools/invitations/pending
Section titled “GET /api/v1/pools/invitations/pending”List all pending pool invitations for the authenticated user.
curl -X GET "https://api.hoody.icu/api/v1/pools/invitations/pending" \ -H "Authorization: Bearer <token>"await client.api.poolInvitations.list();{ "statusCode": 200, "message": "Pending invitations retrieved successfully", "data": [ { "id": "507f1f77bcf86cd799439340", "pool_id": "507f1f77bcf86cd799439300", "pool_name": "Development Team", "pool_description": "Pool for development team resources", "role": "admin", "invited_by": { "id": "507f1f77bcf86cd799439011", "username": "team_lead", "alias": "Team Lead" }, "invited_at": "2025-01-21T22:00:00.000Z" } ]}This endpoint takes no parameters.
Response 200
Section titled “Response 200”Success.
POST /api/v1/pools/{id}/accept
Section titled “POST /api/v1/pools/{id}/accept”Accept an invitation to join a pool.
curl -X POST "https://api.hoody.icu/api/v1/pools/507f1f77bcf86cd799439340/accept" \ -H "Authorization: Bearer <token>"await client.api.poolInvitations.accept('507f1f77bcf86cd799439340');{ "statusCode": 200, "message": "Invitation accepted successfully", "data": { "success": true }}Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | Invitation ID |
Response 200
Section titled “Response 200”Invitation accepted successfully.
POST /api/v1/pools/{id}/reject
Section titled “POST /api/v1/pools/{id}/reject”Reject an invitation to join a pool.
curl -X POST "https://api.hoody.icu/api/v1/pools/507f1f77bcf86cd799439340/reject" \ -H "Authorization: Bearer <token>"await client.api.poolInvitations.reject('507f1f77bcf86cd799439340');{ "statusCode": 200, "message": "Invitation rejected successfully", "data": { "success": true }}Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | Yes | id path parameter |
Response 200
Section titled “Response 200”Invitation rejected successfully.