message.comDevelopers

Billing Live

Read the workspace subscription, start a Stripe checkout for a plan or add-on change, open the customer portal, and list past invoices. Workspaces billed through Shopify App Pricing use a separate, webhook-free reconcile path; the Stripe endpoints below refuse to act on a Shopify-billed row. Admin-only.

Current subscription

GET/api/v1/billing/subscriptionAuth: Bearer

Returns the subscription row plus resolved entitlements: the effective plan, whether it's locked (a plan the account is no longer eligible to renew into), days left in trial, and the addon set.

Code samples

cURL
curl 'https://app.message.com/api/v1/billing/subscription' \
  -H 'Authorization: Bearer YOUR_WORKSPACE_JWT'
JavaScript
const res = await fetch('https://app.message.com/api/v1/billing/subscription', {
  headers: { Authorization: 'Bearer ' + token }
});
const subscription = await res.json();
Python
import requests
r = requests.get(
    "https://app.message.com/api/v1/billing/subscription",
    headers={"Authorization": f"Bearer {token}"},
)
subscription = r.json()
Ruby
require "net/http"
require "json"
uri = URI("https://app.message.com/api/v1/billing/subscription")
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
  req = Net::HTTP::Get.new(uri)
  req["Authorization"] = "Bearer #{token}"
  http.request(req)
end
subscription = JSON.parse(res.body)
PHP
<?php
$ctx = stream_context_create([
  "http" => ["method" => "GET", "header" => "Authorization: Bearer $token"]
]);
$subscription = json_decode(file_get_contents("https://app.message.com/api/v1/billing/subscription", false, $ctx), true);
200 OK
{
  "subscription": {
    "id": "uuid",
    "plan": "support",
    "addons": ["ai"],
    "status": "active",
    "billingInterval": "monthly",
    "billingSource": "stripe",
    "currentPeriodStart": "2026-05-01T00:00:00Z",
    "currentPeriodEnd": "2026-05-31T23:59:59Z",
    "cancelAtPeriodEnd": false,
    "createdAt": "2025-11-10T12:00:00Z"
  },
  "planId": "support",
  "locked": false,
  "daysLeftInTrial": null,
  "addons": ["ai"],
  "billingSource": "stripe"
}

Invoice history

GET/api/v1/billing/invoicesAuth: Bearer

Returns the workspace's most recent 100 invoice records. There is no separate "upcoming invoice" preview endpoint.

Start checkout / change plan

POST/api/v1/billing/checkoutAuth: Bearer

There is no separate "change plan" action. This one endpoint both starts a plan and changes it: if the workspace already has a live Stripe subscription it's updated in place (prorated); otherwise it returns a hosted Stripe Checkout URL to redirect the admin to. Refused with 409 on a Shopify-billed workspace, which manages its plan through the Shopify listing instead.

Body
{
  "planId": "support",
  "interval": "monthly",
  "aiAddon": true,
  "successUrl": "https://app.message.com/settings/billing?checkout=success",
  "cancelUrl": "https://app.message.com/settings/billing"
}

planId is one of chat, support, business. Enterprise has no Stripe price and is not a checkout target; it's sales-assisted.

Toggle add-ons

POST/api/v1/billing/addonsAuth: Bearer

Sets the desired full add-on set for the workspace (idempotent, not a delta). Today the only toggleable key is ai ($5/mo). Body: { "addons": ["ai"] }.

Customer portal

POST/api/v1/billing/portalAuth: Bearer

Returns a Stripe billing-portal URL for the workspace. Lets admins update the card on file, download invoices, and view tax IDs without leaving Stripe's hosted portal.

Cancel

POST/api/v1/billing/cancelAuth: Bearer

Schedules cancellation at period end (or, on an active trial, ends it immediately). Also refused on a Shopify-billed workspace.

Errors

CodeWhen
403 admin_requiredAgent role is not admin.
400 invalid_bodyPayload failed schema validation.
409Checkout or cancel attempted on a Shopify-billed workspace.

Common pitfalls

  • Looking for a GET payment-method endpoint. There isn't one. Card details live entirely in Stripe; send admins to the customer portal to view or change them.
  • Hard-coding plan IDs. Use chat, support, or business, not Stripe price IDs. Prices are looked up by interval server-side.
  • Assuming Stripe endpoints work for every workspace. A Shopify-billed workspace manages its subscription through the Shopify App Pricing listing; checkout and cancel both refuse to act on it.