Start a paid-plan checkout

Create an invoice and payment session for a paid Receptionist plan, and get back the URL to send the dealer to. The checkout is for the dealer group and rooftop named in the body; the rooftop in the token is not used.

POSThttps://api.spyne.ai/agent-marketplace/billing/checkout
AuthBearer token (console user); the token must carry a dealer group and rooftopRate limitNot documentedProcessingSynchronous; waits for the billing serviceWarningStarts a real paid checkout for the rooftop in enterprise_id and team_id. Replace the placeholders only if you mean to bill that rooftop.

The subscription is written as PENDING on the chosen plan. It moves to ACTIVE only when a payment-success callback arrives, which happens outside this call.

Auth: Bearer token of a signed-in user; the token must carry a dealer group and rooftop · Rate limit: not documented · Processing: synchronous, waits for the billing service

Before you call it

  • Get plan_key from List Receptionist plans. Use a plan with kind: "PAID".
  • If the rooftop already has an active, trialing or past-due subscription on a paid plan for this product, the call returns 409. A free-plan subscription, or an earlier checkout left pending, does not block it.
  • Pass the rooftop you are billing in enterprise_id and team_id. Do not rely on the token for this. The route does not check that the caller belongs to them.
  • success_url and cancel_url must be valid URLs. On success, the billing service adds invoice details to success_url as query parameters.
  • Unknown body fields are rejected with 422.
  • The amount billed today is prorated to month end for monthly plans.
  • The onboarding console does not call this route today. Its unused checkout helper targets POST /agent-marketplace/billing/products/receptionist_agent/subscription/checkout, which does not exist. The route on this page is the one the backend serves.

Request body

Requiredalways send
Request checklist
  1. Headers: set Authorization: Bearer YOUR_TOKEN and Content-Type: application/json.
  2. Pick the plan: set agent_key to receptionist_agent and plan_key to the chosen plan.
  3. Set the rest: success_url, cancel_url, enterprise_id and team_id.
  4. Send it, then send the dealer to checkout_url with a full page navigation.
ParameterTypeRequiredDescription
agent_keystringRequiredBilling product key, receptionist_agent. Minimum length 1.
plan_keystringRequiredPlan key from the plan list. Minimum length 1.
success_urlstringRequiredURL to return to after payment. Must be a URL; a top-level domain is not required.
cancel_urlstringRequiredURL to return to if the dealer cancels. Same rule.
enterprise_idstringRequiredDealer group being billed. Minimum length 1.
team_idstringRequiredRooftop being billed. Minimum length 1.

Sample request

curl -X POST "https://api.spyne.ai/agent-marketplace/billing/checkout" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_key": "receptionist_agent",
    "plan_key": "<planKey>",
    "success_url": "https://console.example.com/reception?enterprise_id=<enterpriseId>&team_id=<teamId>",
    "cancel_url": "https://console.example.com/reception?enterprise_id=<enterpriseId>&team_id=<teamId>",
    "enterprise_id": "<enterpriseId>",
    "team_id": "<teamId>"
  }'
import requests

res = requests.post(
    "https://api.spyne.ai/agent-marketplace/billing/checkout",
    headers={"Authorization": "Bearer YOUR_TOKEN"},
    json={
        "agent_key": "receptionist_agent",
        "plan_key": "<planKey>",
        "success_url": "https://console.example.com/reception",
        "cancel_url": "https://console.example.com/reception",
        "enterprise_id": "<enterpriseId>",
        "team_id": "<teamId>",
    },
)
print(res.status_code, res.json())
const res = await fetch('https://api.spyne.ai/agent-marketplace/billing/checkout', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer YOUR_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    agent_key: 'receptionist_agent',
    plan_key: '<planKey>',
    success_url: 'https://console.example.com/reception',
    cancel_url: 'https://console.example.com/reception',
    enterprise_id: '<enterpriseId>',
    team_id: '<teamId>',
  }),
});
console.log(res.status, await res.json());

Sample response

{
  "checkout_url": "<checkoutUrl>",
  "checkout_session_id": "<checkoutSessionId>",
  "invoice_url": "<invoiceUrl>",
  "invoice_number": "<invoiceNumber>",
  "total_amount": "<amount>",
  "currency": "usd",
  "invoice_id": "<invoiceId>"
}
{
  "code": "BILLING_CHECKOUT_FAILED",
  "message": "Could not start checkout. Please try again.",
  "statusCode": 400
}
{
  "message": "Invalid or expired Bearer token",
  "error": "Unauthorized",
  "statusCode": 401,
  "code": "UNAUTHORIZED"
}
{
  "code": "TENANT_UNRESOLVED",
  "message": "Your account has no enterprise or rooftop associated with it, so this request cannot be scoped.",
  "statusCode": 403
}
{
  "code": "BILLING_PLAN_NOT_FOUND",
  "message": "No plan '<planKey>' on product 'receptionist_agent'.",
  "statusCode": 404
}
{
  "code": "BILLING_SUBSCRIPTION_ALREADY_ACTIVE",
  "message": "This rooftop already has an active subscription for this product.",
  "statusCode": 409
}
{
  "code": "VALIDATION_ERROR",
  "message": "1 field(s) failed validation.",
  "errors": [
    { "field": "agent_key", "code": "<code>", "message": "<message>" }
  ],
  "statusCode": 422
}

Response parameters (201)

ParameterTypeDescription
checkout_urlstringSend the dealer here with a full page navigation.
checkout_session_idstringPayment session ID.
invoice_urlstring or nullInvoice raised for this checkout.
invoice_numberstring or nullInvoice number.
total_amountnumberAmount billed today.
currencystringCurrency code.
invoice_idstring or nullInvoice ID.

Errors

StatusCodeWhen it happens
400BILLING_CHECKOUT_REJECTEDThe billing service rejected the request, for example because a payment is already in progress for the dealer group. Its message is passed through.
400BILLING_CHECKOUT_FAILEDThe payment session could not be created.
400BILLING_ZOHO_CUSTOMER_FAILEDThe billing customer record could not be created.
400BILLING_CREATE_PAYMENTS_FAILEDThe payment step failed.
400BILLING_UPSTREAM_ERRORThe billing service could not be reached.
401UNAUTHORIZEDMissing or invalid Bearer token.
403TENANT_UNRESOLVEDThe token has no dealer group or rooftop.
404BILLING_PLAN_NOT_FOUNDNo such plan on this product.
409BILLING_SUBSCRIPTION_ALREADY_ACTIVEThe rooftop already has a paid subscription for this product, or a parallel checkout won a race.
422VALIDATION_ERRORA field is missing or invalid, or the body has an unknown field. All failing fields are listed.
500INTERNAL_ERRORUnexpected error.

What happens next

After payment, the dealer lands on success_url with invoice details in the query string. The subscription stays PENDING until the payment-success callback marks it ACTIVE. You can read it with GET /agent-marketplace/billing/products/receptionist_agent/subscription.

Then buy the phone number.


Did this page help you?