Plans and billing API
Read the plan, renew, top up, and open Stripe.
Plan changes, renewal, and top-up are organisation routes. Stripe routes open a customer, a checkout session, or the billing portal. Promo code responses return the grant name, not the redemption code.
On this page
Endpoints
| Method | Path | What it does |
|---|---|---|
| GET | /api/clients/:clientId/plan | Read the plan |
| PUT | /api/clients/:clientId/plan | Request a plan change |
| POST | /api/clients/:clientId/plan/renew | Renew a lapsed plan |
| POST | /api/clients/:clientId/plan/topup | Request a manual top-up |
| GET | /api/clients/:clientId/subscriptions | List subscriptions |
| POST | /api/clients/:clientId/stripe_session | Start a Stripe checkout session |
| POST | /api/clients/:clientId/stripe_portal | Open the billing portal |
| GET | /api/clients/:clientId/customer | Read the Stripe customer |
| GET | /api/clients/:clientId/configurations | Read configuration |
| PUT | /api/clients/:clientId/configurations | Update configuration |
| GET | /api/clients/:clientId/promocodes | List applied grants |
| POST | /api/clients/:clientId/promocodes | Redeem a code |
Read the plan
GET /api/clients/:clientId/plan returns the current plan.
Request
curl -X GET "https://api.requestrocket.com/api/clients/:clientId/plan" \
-H "Authorization: u-your-token"import axios from "axios";
const response = await axios.request({
method: "GET",
url: "https://api.requestrocket.com/api/clients/:clientId/plan",
headers: {
"Authorization": "u-your-token",
}
});
console.log(response.status, response.data);import requests
response = requests.request(
"GET",
"https://api.requestrocket.com/api/clients/:clientId/plan",
headers={
"Authorization": "u-your-token",
}
)
print(response.status_code, response.json())package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
var payload io.Reader
req, _ := http.NewRequest("GET", "https://api.requestrocket.com/api/clients/:clientId/plan", payload)
req.Header.Set("Authorization", "u-your-token")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(resp.StatusCode, string(body))
}import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Example {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.requestrocket.com/api/clients/:clientId/plan"))
.method("GET", HttpRequest.BodyPublishers.noBody())
.header("Authorization", "u-your-token")
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
}
}Responses
{
"plan": {
"planTier": "team",
"planStatus": "active",
"planPendingTier": null,
"planPendingInvoiceId": null,
"planPendingEffectiveAt": null,
"planExpiresAt": null,
"planRenewedAt": "2026-11-02T07:30:00.000Z",
"planBillingAnchor": null,
"featureOverrides": [],
"promoOverrides": [],
"effectivePlanTier": "team",
"features": [
"proxy_auth_key",
"mcp_entrypoint"
],
"displayName": "Team",
"prices": {
"basic": 0,
"team": 1
},
"displayNames": {
"basic": "Basic",
"team": "Team",
"enterprise_basic": "Enterprise Basic",
"enterprise_security": "Enterprise Security",
"enterprise_governance": "Enterprise Governance",
"enterprise_custom": "Enterprise Custom"
},
"tiers": [
"basic",
"team",
"enterprise_basic",
"enterprise_security",
"enterprise_governance",
"enterprise_custom"
]
},
"message": "Success"
}{
"message": "Client not found"
}{
"Message": "User is not authorized to access this resource with an explicit deny in an identity-based policy"
}{
"message": "method / endpoint not supported"
}Query parameters
None.
Response
| Attribute | Type | Description |
|---|---|---|
plan.planTier | string | Stored tier: basic, team, enterprise_basic, enterprise_security, enterprise_governance, or enterprise_custom. |
plan.planStatus | string | active, upgrade_requested, downgrade_scheduled, payment_failed, or expired. |
plan.effectivePlanTier | string | Tier in effect. It can differ from planTier while a change is pending. |
plan.displayName | string | Label for the effective tier. |
plan.features | array of string | Feature ids enabled for the effective tier. |
plan.prices | object | Price for each tier id, after any plan rate. |
plan.displayNames | object | Label for every tier id. |
plan.tiers | array of string | Tier ids in order. |
plan.planPendingTier | string or null | Tier waiting to start. |
plan.planPendingInvoiceId | string or null | Stripe invoice for a pending upgrade or renewal. |
plan.planPendingEffectiveAt | string or null | When a scheduled downgrade takes effect. |
plan.planExpiresAt | string or null | End of the current paid period. |
plan.planRenewedAt | string or null | When the current period started. |
plan.planBillingAnchor | number or null | Day of the month used for the billing period. |
plan.featureOverrides | array of string | Extra features assigned to this organisation. |
plan.promoOverrides | array of string | Features granted by promotional codes. |
message | string | Success on a read. |
Request a plan change
PUT /api/clients/:clientId/plan asks to move the organisation to another tier.
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
planTier | string | ✓ | basic, team, enterprise_basic, enterprise_security, enterprise_governance, or enterprise_custom. enterprise_custom is assigned by RequestRocket. |
Returns plan in the same shape as the read call. An upgrade billed through Stripe also returns invoiceId, and planStatus becomes upgrade_requested. A downgrade sets planStatus to downgrade_scheduled and planPendingEffectiveAt to the end of the current period. Sending the current tier while a change is pending cancels that change.
Renew the plan
POST /api/clients/:clientId/plan/renew renews a plan that is expired or payment-failed.
Request
curl -X POST "https://api.requestrocket.com/api/clients/:clientId/plan/renew" \
-H "Authorization: u-your-token"import axios from "axios";
const response = await axios.request({
method: "POST",
url: "https://api.requestrocket.com/api/clients/:clientId/plan/renew",
headers: {
"Authorization": "u-your-token",
}
});
console.log(response.status, response.data);import requests
response = requests.request(
"POST",
"https://api.requestrocket.com/api/clients/:clientId/plan/renew",
headers={
"Authorization": "u-your-token",
}
)
print(response.status_code, response.json())package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
var payload io.Reader
req, _ := http.NewRequest("POST", "https://api.requestrocket.com/api/clients/:clientId/plan/renew", payload)
req.Header.Set("Authorization", "u-your-token")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(resp.StatusCode, string(body))
}import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Example {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.requestrocket.com/api/clients/:clientId/plan/renew"))
.method("POST", HttpRequest.BodyPublishers.noBody())
.header("Authorization", "u-your-token")
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
}
}Responses
{
"plan": {
"planTier": "team",
"planStatus": "active",
"planPendingTier": null,
"planPendingInvoiceId": null,
"planPendingEffectiveAt": null,
"planExpiresAt": null,
"planRenewedAt": "2026-11-02T07:30:00.000Z",
"planBillingAnchor": null,
"featureOverrides": [],
"promoOverrides": [],
"effectivePlanTier": "team",
"features": [
"proxy_auth_key",
"mcp_entrypoint"
],
"displayName": "Team",
"prices": {
"basic": 0,
"team": 1
},
"displayNames": {
"basic": "Basic",
"team": "Team",
"enterprise_basic": "Enterprise Basic",
"enterprise_security": "Enterprise Security",
"enterprise_governance": "Enterprise Governance",
"enterprise_custom": "Enterprise Custom"
},
"tiers": [
"basic",
"team",
"enterprise_basic",
"enterprise_security",
"enterprise_governance",
"enterprise_custom"
]
},
"invoiceId": "in_1Example",
"message": "Renewal invoice created \u2014 pending payment"
}{
"message": "Plan is not expired \u2014 renew is only valid for a lapsed paid plan"
}{
"Message": "User is not authorized to access this resource with an explicit deny in an identity-based policy"
}{
"message": "method / endpoint not supported"
}Request body
This call has no request body.
Returns plan in the same shape as the read call. A Stripe renewal also returns invoiceId. message is Renewal invoice created — pending payment when an invoice is opened, or Plan renewed when the plan is restored without charging Stripe.
Request a top-up
POST /api/clients/:clientId/plan/topup asks billing to top up an organisation whose top-up method is manual.
Request body
This call has no request body.
Returns message (Top-up request submitted to billing).
List subscriptions
GET /api/clients/:clientId/subscriptions lists the organisation's Stripe subscriptions.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
active | string | — | When omitted, the list includes subscriptions whose status is active and that are not cancelled or set to cancel. |
There is no limit, cursor, or page parameter.
Returns subscriptions, an array of Stripe Subscription objects.
Response
| Attribute | Type | Description |
|---|---|---|
subscriptions[].id | string | Stripe subscription id. |
subscriptions[].status | string | Stripe subscription status. |
subscriptions[].customer | string | Stripe customer id. |
subscriptions[].cancel_at_period_end | boolean | Whether the subscription ends at the period boundary. |
subscriptions[].current_period_start | number | Period start, as a Unix timestamp. |
subscriptions[].current_period_end | number | Period end, as a Unix timestamp. |
Start a checkout session
POST /api/clients/:clientId/stripe_session opens a Stripe Checkout session for a subscription. The organisation must already have a Stripe customer.
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
successUrl | string | ✓ | URL Stripe opens after payment. |
cancelUrl | string | ✓ | URL Stripe opens when the customer cancels. |
quantity | number | ✓ | Quantity for prices that are not metered. |
plan | string | ✓ | basic, team, enterprise_basic, enterprise_security, enterprise_governance, enterprise_custom, Basic, Team, or Business. |
metadata | object | — | Extra key-value pairs stored on the Checkout session. plan is always added. |
Returns the Stripe Checkout Session as the response body.
Response
| Attribute | Type | Description |
|---|---|---|
id | string | Checkout session id. |
url | string | URL to send the customer to. |
object | string | checkout.session. |
mode | string | subscription. |
customer | string | Stripe customer id. |
status | string | Session status, such as open. |
success_url | string | URL from successUrl. |
cancel_url | string | URL from cancelUrl. |
Open the billing portal
POST /api/clients/:clientId/stripe_portal opens the Stripe billing portal for the organisation's customer.
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
return_url | string | ✓ | URL Stripe opens when the customer leaves the portal. |
Returns the Stripe Billing Portal session as the response body.
Response
| Attribute | Type | Description |
|---|---|---|
id | string | Portal session id. |
url | string | URL to send the customer to. |
object | string | billing_portal.session. |
customer | string | Stripe customer id. |
return_url | string | URL from the request. |
Read the Stripe customer
GET /api/clients/:clientId/customer returns the Stripe customer linked to the organisation.
Query parameters
None.
Response
| Attribute | Type | Description |
|---|---|---|
customer.id | string | Stripe customer id. |
customer.email | string | Billing email. |
customer.name | string | Customer name. |
customer.address | object | Billing address Stripe has stored. |
customer.phone | string | Phone number, when set. |
customer.metadata | object | Includes clientId and clientName when RequestRocket created the customer. |
Read configuration
GET /api/clients/:clientId/configurations returns one configuration record.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
configurationType | string | ✓ | client, user, or msp. Use client for the organisation. |
Returns configuration and message.
Response
| Attribute | Type | Description |
|---|---|---|
configuration.regionAccess | array of string | Region codes the organisation may use. |
configuration.billingType | string | stripe, marketplace, or invoice. |
configuration.billingTo | string | client or msp. |
configuration.topUpMethod | string | automatic or manual. |
configuration.limits.requestsPerMinute | number | Per-minute request cap. |
configuration.limits.requestsPerDay | number | Per-day request cap. |
configuration.budget.current | number | Current balance. Omitted when an MSP is billed. |
configuration.budget.minimumTopUpThreshold | number | Balance that triggers a top-up. |
configuration.budget.maximumTopUpThreshold | number | Balance a top-up aims for. |
configuration.rates | object | apiSeconds, storageGB, aiTokens, and plan multipliers. Omitted when an MSP is billed. |
configuration.promotionalCodes | array or null | Applied grants. Each item has name, createdAt, and expiresAt. The redemption code is not included. |
configuration.planTier | string | Same tier values as the plan call. |
configuration.planStatus | string | Same status values as the plan call. |
message | string | Success. |
Update configuration
PUT /api/clients/:clientId/configurations merges the body into the stored configuration. The same configurationType query parameter as the read call is required.
All fields are optional. Omit a field to leave it unchanged. Plan fields, promotional codes, feature overrides, billing type, who is billed, rates, top-up method, and sharing are not taken from this body. Change the plan with PUT /api/clients/:clientId/plan. Redeem a code with POST /api/clients/:clientId/promocodes.
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
regionAccess | array of string | — | Region codes the organisation may use. |
limits | object | — | When present, requestsPerMinute and requestsPerDay are both required. Values above 300 per minute or 20,000 per day are stored at those caps. |
budget | object | — | Applied when billing is stripe and the organisation pays for itself. When present, minimumTopUpThreshold and maximumTopUpThreshold are both required. The minimum is at least 5. The maximum is at least 20 and at least 10 above the minimum. budget.current is not taken from the body. |
If you send sharing or rates, include every property those objects require or the call is rejected. A normal caller still does not change the stored sharing or rates.
Returns the saved configuration and message (Configuration updated successfully).
List applied grants
GET /api/clients/:clientId/promocodes returns the grants already on the organisation. The redemption code is not included.
Query parameters
None.
Redeem a code
POST /api/clients/:clientId/promocodes applies a promotional code. The response returns the grant name, not the code.
Request body
| Parameter | Type | Required | Description |
|---|---|---|---|
code | string | ✓ | The redemption code. It is not echoed in the response. |
Returns promotionalCodes, applied, alreadyApplied, and message.
Response
| Attribute | Type | Description |
|---|---|---|
promotionalCodes | array | Grants now on the organisation. Each item has name, createdAt, and expiresAt. |
applied | object | The grant this code matches. Same three fields. |
alreadyApplied | boolean | true when that grant was already on the organisation. |
message | string | Promotional code applied, or Promotional code already applied. |
A 402 on any other call means the organisation is suspended. That code is on Platform limits.
Shared status codes, the 5.5 MB cap, and plan rate limits are on Platform limits.