Skip to content
For developers

Billing API

REST endpoints for billing — request and response reference with examples.

Subscription plans and checkout. The public-plans and public-checkout endpoints require no authentication; the rest operate on the authenticated account. Pricing on www.actuallycare.com/pricing comes from these endpoints.

Base URL: https://api.actuallycare.com/v1 · Errors use the standard envelope — see Errors.

Get public plan pricing#

GET/v1/billing/public-plans

Returns the current subscription plans with pricing in cents. This endpoint requires no authentication and is the source of truth for pricing shown on the marketing site. Responses are cacheable (5 minutes in browsers, 1 hour at the CDN edge).

Responses#

StatusMeaning
200Available plans
429Rate limit exceeded — too many requests in the current window. Wait for the window to reset (see Retry-After) before retrying.
500Internal server error

Example request#

cURL
curl "https://api.actuallycare.com/v1/billing/public-plans"

Example response (200)#

{
  "success": true,
  "data": {
    "plans": [
      {
        "key": "agent",
        "name": "ActuallyCare+ Agent",
        "priceMonthly": 10000,
        "priceYearly": 120000,
        "trialDays": 30,
        "seats": 1,
        "features": [
          "example features"
        ]
      }
    ],
    "currency": "usd",
    "version": "example version"
  }
}

Start checkout without an account#

POST/v1/billing/public-checkout

Creates a Stripe Checkout session for a new customer who doesn't have an account yet. Only the agent plan is available through this endpoint; team and brokerage subscriptions require an account and the authenticated checkout endpoint. Returns the Stripe-hosted checkout URL to redirect the visitor to.

Request body#

FieldTypeRequiredDescription
emailstringYesEmail address for the new customer and Stripe Checkout session · Format: email
planKeyenumNoSubscription plan tier (only agent is available without an account) · One of: agent · Default: "agent"
intervalenumNoBilling interval (defaults to monthly) · One of: monthly, yearly · Default: "monthly"

Responses#

StatusMeaning
200Checkout session created
400Invalid request data
429Rate limit exceeded — too many requests in the current window. Wait for the window to reset (see Retry-After) before retrying.
500Internal server error

Example request#

cURL
curl -X POST "https://api.actuallycare.com/v1/billing/public-checkout" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]"
  }'

Example response (200)#

{
  "success": true,
  "data": {
    "url": "example url",
    "sessionId": "example sessionId"
  }
}

Get plans with checkout identifiers#

GET/v1/billing/plans

Returns the subscription plans for the in-app billing page. Same plans as the public endpoint, plus the identifiers needed to start an authenticated checkout. Prices here are in dollars.

Responses#

StatusMeaning
200Available plans
401Authentication required
429Rate limit exceeded — too many requests in the current window. Wait for the window to reset (see Retry-After) before retrying.
500Internal server error

Example request#

cURL
curl "https://api.actuallycare.com/v1/billing/plans" \
  -H "Authorization: Bearer YOUR_JWT"

Example response (200)#

{
  "success": true,
  "data": [
    {
      "key": "agent",
      "name": "ActuallyCare+ Agent",
      "priceId": "price_1QxT4kGH2vSNn8LKcAgentMo",
      "priceIds": {
        "monthly": "price_1QxT4kGH2vSNn8LKcAgentMo",
        "yearly": "price_1QxT5bGH2vSNn8LKcAgentYr"
      },
      "price": 100,
      "priceMonthly": 100,
      "priceYearly": 1000,
      "seats": 1,
      "escrowsPerMonth": -1,
      "includes": "base",
      "features": [
        "1 agent seat",
        "Unlimited data",
        "Base AI included (~20 requests/session)"
      ]
    }
  ]
}

Get current subscription status#

GET/v1/billing/subscription

Returns the authenticated user's subscription state at each scope (personal, team, brokerage) plus which scope currently provides coverage.

Responses#

StatusMeaning
200Subscription status per scope
401Authentication required
429Rate limit exceeded — too many requests in the current window. Wait for the window to reset (see Retry-After) before retrying.
500Internal server error

Example request#

cURL
curl "https://api.actuallycare.com/v1/billing/subscription" \
  -H "Authorization: Bearer YOUR_JWT"

Example response (200)#

{
  "success": true,
  "data": {
    "personal": {
      "status": "active",
      "tier": "agent",
      "currentPeriodEnd": "2026-07-15T14:32:10.000Z",
      "canceledAt": "2026-07-15T14:32:10.000Z"
    },
    "team": {},
    "brokerage": {},
    "effective_coverage": "brokerage"
  }
}

Start checkout for a subscription#

POST/v1/billing/checkout

Creates a Stripe Checkout session for the authenticated user at the requested scope. Valid scope and plan combinations are personal+agent, team+team, brokerage+team, and brokerage+broker. Eligibility depends on your role and how your brokerage pays for seats; ineligible requests return 400 with a specific reason.

Request body#

FieldTypeRequiredDescription
scopeenumYesSubscription scope the seat applies to (personal, team, or brokerage) · One of: personal, team, brokerage
planKeyenumYesSubscription plan tier to subscribe to · One of: agent, team, broker
intervalenumNoBilling interval (defaults to monthly) · One of: monthly, yearly · Default: "monthly"

Responses#

StatusMeaning
200Checkout session created
400Invalid scope/plan combination or eligibility check failed (the message explains why)
401Authentication required
404Team or brokerage not found
429Rate limit exceeded — too many requests in the current window. Wait for the window to reset (see Retry-After) before retrying.
500Internal server error

Example request#

cURL
curl -X POST "https://api.actuallycare.com/v1/billing/checkout" \
  -H "Authorization: Bearer YOUR_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "scope": "personal",
    "planKey": "agent"
  }'

Example response (200)#

{
  "success": true,
  "data": {
    "url": "example url",
    "sessionId": "example sessionId"
  }
}

Open the billing portal#

POST/v1/billing/portal

Creates a Stripe customer portal session where the user can manage payment methods, view invoices, and change or cancel their subscription. Requires an existing billing account (returns 400 if the user has never subscribed).

Responses#

StatusMeaning
200Portal session created
400No billing account found — subscribe to a plan first
401Authentication required
429Rate limit exceeded — too many requests in the current window. Wait for the window to reset (see Retry-After) before retrying.
500Internal server error

Example request#

cURL
curl -X POST "https://api.actuallycare.com/v1/billing/portal" \
  -H "Authorization: Bearer YOUR_JWT"

Example response (200)#

{
  "success": true,
  "data": {
    "url": "example url"
  }
}