A valid request URL is required to generate request examples{
"checkoutUrl": "<string>",
"orderId": "<string>",
"expiresAt": "2023-11-07T05:31:56Z",
"quantity": 2,
"fulfilmentIds": [
"<string>"
]
}{
"error": {
"code": "backend_resolution_failed",
"message": "<string>",
"retryable": true
}
}{
"error": {
"code": "unauthorized",
"reason": "missing_credentials",
"hint": "<string>"
}
}{
"error": {
"code": "backend_resolution_failed",
"message": "<string>",
"retryable": true
}
}{
"error": {
"code": "backend_resolution_failed",
"message": "<string>",
"retryable": true
}
}{
"error": {
"code": "payload_too_large",
"message": "<string>"
}
}{
"error": {
"code": "rate_limited",
"message": "<string>"
}
}{
"error": {
"code": "admission_unavailable",
"message": "<string>"
}
}Start provisioning this organisation
Begins a purchase and returns somewhere to send the browser.
WHAT IS BEING BOUGHT is an optional {sku, billing} body. Send it to buy a named thing at the price GET /v1/skus published for that pair. Send no body at all, as a caller with nothing to choose between does, and the deployment’s single default price applies, which is what this route did for every caller before it read a body. A body naming a pair this deployment holds no price for answers 503 rather than quietly charging the default: taking the wrong amount is worse than taking none.
WHO MAY CALL IT. A signed-in org ADMIN: a member cannot commit the organisation to a subscription and gets 403. A deployment may additionally require the admin’s recent identity check, refusing with reverification_required; whether it does is configuration. OR an API key holding orders:place, for PREPAID plans only (pay as you go answers 403). Only an admin’s session holds that scope and a key never carries more than its minter, so such a key was created or approved by an admin; a key has no sign-in to check, so no identity check is asked of it. A key without the scope gets 403.
An organisation that is ALREADY provisioned gets 409, whatever the deployment’s configuration gaps are: telling a customer with a working subscription that provisioning is unavailable would be a worse answer than the truth. The state of the organisation is therefore checked before the state of the deployment.
SEND AN Idempotency-Key AND A RETRY COSTS NOTHING. With one, a repeat within 24 hours is answered with the FIRST attempt’s answer, the same checkout url and the same order id with no new session, and the response carries Idempotent-Replayed: true so you can tell. That is what makes a lost response safe to retry: the 2xx you never received is handed to you rather than reproduced.
WITHOUT ONE, calling it again while an attempt is open RESTARTS that attempt where the earlier one can still be made unpayable, and refuses with 409 checkout_in_progress where it cannot, which is the case that matters: an order the customer has already paid for is never abandoned out from under an inbound payment. Restarting is safe for your money and costs you the session already open in the browser, which is the whole reason to send a key.
Every reason this deployment cannot provision, one that is not finished being set up and one with no payment provider configured alike, answers one 503 code. From a caller’s side they are one situation and splitting them would only make the console branch on a distinction it cannot act on.
WHAT A KEY CAN AND CANNOT DO HERE. It opens a Checkout page and returns its url; a PERSON still has to pay on that page, so a program cannot spend money while nobody is watching. Cancelling or returning an order, the billing portal and pay as you go stay with a signed-in person, and no scope grants them.
Hosted deployments only. A local checkout does not mount this route, so calling it there is a 404.
A valid request URL is required to generate request examples{
"checkoutUrl": "<string>",
"orderId": "<string>",
"expiresAt": "2023-11-07T05:31:56Z",
"quantity": 2,
"fulfilmentIds": [
"<string>"
]
}{
"error": {
"code": "backend_resolution_failed",
"message": "<string>",
"retryable": true
}
}{
"error": {
"code": "unauthorized",
"reason": "missing_credentials",
"hint": "<string>"
}
}{
"error": {
"code": "backend_resolution_failed",
"message": "<string>",
"retryable": true
}
}{
"error": {
"code": "backend_resolution_failed",
"message": "<string>",
"retryable": true
}
}{
"error": {
"code": "payload_too_large",
"message": "<string>"
}
}{
"error": {
"code": "rate_limited",
"message": "<string>"
}
}{
"error": {
"code": "admission_unavailable",
"message": "<string>"
}
}Authorizations
The control surface credential. Send Authorization: Bearer <token>.
Two kinds of token are accepted and they are told apart by shape, not by a separate header. A token beginning pbk_ is an org scoped API key, whose public half and secret half are generated together and of which only a hash of the secret is ever stored; anything else is treated as an OAuth 2.1 access token and verified against the authorization server's keys.
Both resolve to the same context: an org, a principal and a set of scopes. Nothing downstream branches on which channel you used, with one deliberate exception, key management, which requires a signed-in person so that a key can never mint another key.
Scopes are enforced when MCP tools are REGISTERED rather than when they are called, so a tool your credential cannot use is absent from tools/list rather than refused mid gesture.
Headers
A key of your own, unique to this attempt, so a retry cannot buy twice. Send the SAME value on every retry of one purchase; up to 255 printable ASCII characters, and a random uuid is the usual choice.
With a key, a retry within 24 hours is answered with the ORIGINAL answer, the same checkout url and the same order id, and nothing new is created. Without one, a retry supersedes: the session you may already have open is expired and a different one is minted, which is safe for your money and inconvenient for whoever is looking at the old tab.
Reusing a key you already sent with a DIFFERENT purchase is refused rather than answered, because the alternative is handing you a link to the wrong thing.
255Body
What to buy, or nothing at all. See the description for what an absent body means.
What to buy. Fields this service does not know are IGNORED, not refused: the route reads sku, billing, model, quoteId, region, quantity and addOns and drops everything else.
What to buy. Must be one this deployment prices; see GET /v1/skus.
emulator, cloud_phone, phone_robot_android, phone_robot_ios How to pay for it.
subscription, payg, day Which model of that sku, by the id of one of the models the matching GET /v1/skus row listed. Required for monthly Cloud phone checkout; other pairs may omit it. Send an id the deployment did not declare and the answer is 400 invalid_argument. A supplier-listed monthly Cloud model with backorderable: true may be paid for while sold out. An unresolved supplier goodId is refused before payment. Validated against the current server catalog, never the copy a client holds.
1 - 64The quoteId published beside the selected model in GET /v1/skus. Required for a priced model; the server checks it against its current Price before creating an order or a payment session. Never an amount and never trusted as the charge source.
Add-on intents, Cloud phone with a model only. STRICT, unlike the body: an unknown key or a non-boolean value is 400 invalid_argument. Recorded on the order; nothing here changes the charge.
Show child attributes
Show child attributes
How many phones of the chosen model, 1 to 20. Optional, legal only beside a model; absent means one. Respect the matching catalog entry's maxQuantity: modern Cloud first-month payments support up to three; all other plans support one. A larger value is refused before an order or payment exists. One Stripe payment creates separately tracked units, each with its own delivery, term and bounded refund.
1 <= x <= 20Where to place that model, by the code of one of the regions the chosen model listed on GET /v1/skus: an upper-case ISO 3166-1 alpha-2 code. Optional, and legal only beside a model that lists regions: send it for a model with no regions and the answer is 400 invalid_argument ("No region can be chosen for this model; omit region."); send a code the model did not list and the answer is 400 naming the codes it does. A listed sold-out region may be purchased as a Cloud backorder. Omit it and the deployment's default region for that model applies; an unlisted default is refused before payment.
^[A-Z]{2}$Response
A started purchase. A named sku is fulfilled from a unit of THAT kind and no other, so a customer is never handed something other than what they paid for; if none is free the purchase still succeeds and the device is promised (see shipsNow on GET /v1/skus).
A started purchase.
Where to send the browser to complete the purchase. Single use, and it expires.
This attempt's order id, echoed so a caller can correlate.
After this the URL stops being payable and a fresh attempt is needed.
1 <= x <= 3Stable opaque per-phone identifiers, committed as fulfillments only after verified payment. Quantity-one keeps its existing order identifier.
1 - 3 elements