RedSwitches API Private beta Reference
Menu

Ordering guide

Order an instant server end to end: get ready, pick an offer, check the total with a dry run, order, and wait until the server is ready.

Before your first order

Ordering is off until the account owner sets it up. In the client area, on Settings → API:

  1. Make a key with the order permission. It needs a monthly spend limit for that key: a whole amount in the account's currency, 1 to 100000. Orders that would take the key over it in 30 days are refused. The limit cannot be raised later; make a new key instead. An IP list on ordering keys is strongly suggested.
  2. Turn on Ordering through the API (a recent sign-in and an emailed code, as for keys).
  3. Wait 24 hours. Ordering opens 24 hours after it was turned on and after a new ordering key was made. Until then orders answer 403 cooling_off, with details.retry_after in seconds.
  4. Add funds. Orders through the API are paid from account credit only. With read_billing, GET /account shows the balance.

Account limits

On top of each key's own monthly limit, the account has limits counted across all its keys over rolling windows:

LimitDefaultdetails.cap when reached
Servers ordered in a day3servers_day
Servers ordered in 30 days10servers_month
Orders paid but not delivered yet, at once2open
Money spent in 30 daysthe equivalent of €2,000money_month
Monthly renewals added in 30 daysthe equivalent of €2,000renewal_month
The key's own monthly limitset by the ownerkey_month

The account limits are set by RedSwitches for each account; the defaults are above. Contact support if you need a higher one. Raising a limit does not pause ordering. The key's own limit is set when the key is made and cannot be raised: make a new key instead.

Over a limit the order is refused with 403 cap_reached and nothing is ordered. The details say which limit, its value and its window:

{
  "error": {
    "code": "cap_reached",
    "message": "This order would go over a limit of this key or of the account. Nothing was ordered.",
    "request_id": "01JCX7Q2M8ZK4T6V9B3N5R1W2Y",
    "details": { "cap": "servers_day", "limit": 3, "window": "day" }
  }
}

For a money limit, limit is an amount in your account's currency, in minor units: {"cap": "money_month", "limit": {"amount": "200000", "currency": "EUR"}, "window": "30_days"} is 2,000.00 EUR in 30 days. window is day, 30_days, or open for orders paid but not delivered yet.

1. Pick an offer

curl -sS https://api.redswitches.com/v1/offers \
  -H "Authorization: Bearer $REDSWITCHES_API_KEY"
const { data: offers } = await rs.offers.list();
const offer = offers.find((o) => o.location?.city === "Amsterdam" && o.stock !== 0)!;
const os = offer.os.find((x) => x.name.startsWith("Ubuntu"))!;
offers = client.offers.list().data
offer = next(o for o in offers if o.location and o.location.city == "Amsterdam")
os_choice = next(x for x in offer.os if x.name.startswith("Ubuntu"))
offers, err := client.Offers.List(ctx)
if err != nil {
	log.Fatal(err)
}
offer := offers.Data[0]
osID := offer.OS[0].ID
{
  "data": [
    {
      "id": "ofr_12",
      "name": "Instant Dedicated Server E-2388G",
      "location": { "city": "Amsterdam", "country": "NL" },
      "cpu": "Intel Xeon E-2388G (8 cores, 3.2 GHz)",
      "ram": "64 GB DDR4",
      "disks": "2 x 960 GB NVMe SSD",
      "port_speed": "1 Gbps",
      "traffic": "50 TB",
      "currency": "USD",
      "prices": [{ "cycle": "monthly", "price": "129.00", "setup": "0.00" }],
      "os": [{ "id": "os_31", "name": "Ubuntu 24.04" }],
      "stock": "5+",
      "as_of": "2026-11-15T09:30:00Z"
    }
  ]
}

Offer prices are before tax. The amount you pay comes from the dry run.

2. Dry run

A dry run checks everything a real order checks (offer, OS, SSH key, stock, limits, credit) and returns the exact total. Nothing is ordered or held.

curl -sS -X POST "https://api.redswitches.com/v1/orders?dry_run=true" \
  -H "Authorization: Bearer $REDSWITCHES_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"offer_id":"ofr_12","billing_cycle":"monthly","os_id":"os_31","ssh_key_id":"sshkey_7"}'
const body = { offer_id: offer.id, billing_cycle: "monthly", os_id: os.id, ssh_key_id: "sshkey_7" } as const;
const quote = await rs.orders.create(body, { dryRun: true });
console.log("Will pay", quote.total.amount, quote.total.currency, "(minor units)");
body = dict(offer_id=offer.id, billing_cycle="monthly", os_id=os_choice.id, ssh_key_id="sshkey_7")
quote = client.orders.create(**body, dry_run=True)
print("Will pay", quote.total.amount, quote.total.currency, "(minor units)")
req := &redswitches.OrderCreateRequest{OfferID: offer.ID, BillingCycle: "monthly", OSID: osID, SSHKeyID: "sshkey_7"}
quote, err := client.Orders.Create(ctx, req, &redswitches.OrderOptions{DryRun: true})
{
  "dry_run": true,
  "offer_id": "ofr_12",
  "billing_cycle": "monthly",
  "total": { "amount": "15480", "currency": "USD" },
  "recurring": { "amount": "12900", "currency": "USD" },
  "paid_from_credit": { "amount": "15480", "currency": "USD" }
}

"15480" is 154.80 USD. Show it to whoever approves the spend.

3. Order

Send the same body with expected_total set to the dry run's total, and a new Idempotency-Key that you keep until you have the answer. If the price moved in between, the order is refused with 409 price_changed and the new total in details.total; nothing is charged.

curl -sS -X POST https://api.redswitches.com/v1/orders \
  -H "Authorization: Bearer $REDSWITCHES_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7d0e4b1c-2f3a-4c5d-8e9f-0a1b2c3d4e5f" \
  -d '{"offer_id":"ofr_12","billing_cycle":"monthly","os_id":"os_31","ssh_key_id":"sshkey_7","expected_total":{"amount":"15480","currency":"USD"}}'
import { randomUUID } from "node:crypto";

const idempotencyKey = randomUUID(); // store it with your job until the answer arrives
const order = await rs.orders.create({ ...body, expected_total: quote.total }, { idempotencyKey });
import uuid

key = str(uuid.uuid4())  # store it with your job until the answer arrives
order = client.orders.create(**body, expected_total=quote.total, idempotency_key=key)
req.ExpectedTotal = quote.Total
order, err := client.Orders.Create(ctx, req, &redswitches.OrderOptions{IdempotencyKey: uuid.NewString()})
{
  "id": "ord_456",
  "invoice_id": "inv_789",
  "server_ids": ["srv_123"],
  "status": "paid",
  "delivery": "queued",
  "billing_cycle": "monthly",
  "total": { "amount": "15480", "currency": "USD" },
  "recurring": { "amount": "12900", "currency": "USD" },
  "paid_from_credit": { "amount": "15480", "currency": "USD" },
  "poll_after": 30
}

201 means the order exists and is paid from credit. The invoice is already settled.

If the answer is lost

Send exactly the same request again, with the same Idempotency-Key and the same body. You get:

  • 201 with the same order, if it went through;
  • the same error, if it was refused;
  • 409 in_progress while it is still running: wait details.retry_after seconds and ask again.

You never get a second server. Never change the body under the same key: that is 422 idempotency_conflict.

4. Wait for delivery

Poll the order, waiting poll_after seconds between requests, until every server's delivery is ready.

while true; do
  r=$(curl -sS https://api.redswitches.com/v1/orders/ord_456 \
    -H "Authorization: Bearer $REDSWITCHES_API_KEY")
  echo "$r" | jq -c '.services'
  wait=$(echo "$r" | jq -r '.poll_after // empty')
  [ -z "$wait" ] && break
  sleep "$wait"
done
const ready = await rs.orders.waitUntilReady(order.id);
const server = await rs.servers.get(ready.services[0].id);
console.log(server.primary_ip);
ready = client.orders.wait_until_ready(order.id)
server = client.servers.get(ready.services[0].id)
print(server.primary_ip)
for {
	o, err := client.Orders.Get(ctx, order.ID)
	if err != nil {
		log.Fatal(err)
	}
	if o.PollAfter == nil {
		break
	}
	time.Sleep(time.Duration(*o.PollAfter) * time.Second)
}
{
  "id": "ord_456",
  "date": "2026-11-15T09:30:00Z",
  "status": "active",
  "payment": "paid",
  "invoice": "inv_789",
  "services": [{ "id": "srv_123", "delivery": "ready" }],
  "poll_after": null
}

Delivery goes assigning → installing → ready. poll_after becomes null when nothing is still being delivered. If a server shows attention, our staff are already looking at it; do not order again.

When it is ready, the welcome email with the login details is in the account owner's mailbox. The root password never appears in the API. If you ordered with ssh_key_id, you can log in with that key straight away.

When an order is refused

AnswerWhat happenedWhat to do
402 insufficient_creditNot enough credit. details.needed is the shortfall in minor units.Add funds, then order with a new Idempotency-Key.
409 price_changedThe total moved. details.total is the new one.Confirm it, then order with a new key and the new expected_total.
409 sold_outThe last unit went.Pick another offer.
403 cap_reachedA limit would be passed. details.cap, details.limit and details.window say which.Wait for the window to pass, or ask support to raise an account limit.
403 cooling_offOrdering is not open yet.Wait details.retry_after seconds.
403 ordering_not_enabledOrdering is off for the account.The owner turns it on.
403 not_eligibleThe account may not order now.The owner sees why on the API page.
403 ordering_pausedRedSwitches paused API ordering.Try later.
409 order_not_placedNothing was created or charged.You may try again with a new key.
409 order_under_reviewThe order exists but staff must check it before it is paid or delivered. details.order is its id.Do not order again. We will contact you.
422 not_orderableOffer, OS, SSH key or cycle not allowed.Check the ids against GET /offers and GET /ssh-keys.