Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.actionlayer.dev/llms.txt

Use this file to discover all available pages before exploring further.

MethodPathPurpose
GET/v1/billing/subscriptionRead current plan, usage, limits, resource counts
POST/v1/billing/checkoutCreate a Stripe Checkout session for upgrade
POST/v1/billing/portalCreate a Stripe Customer Portal session
The subscription endpoint reads only from the database — ActionLayer never queries Stripe in real time for plan enforcement.

Get subscription

curl https://api.actionlayer.dev/v1/billing/subscription \
  -H "Authorization: Bearer $ACTIONLAYER_API_KEY"

Response

{
  "plan": "pro",
  "plan_status": "active",
  "beta_override": false,
  "trial_ends_at": "2026-05-30T00:00:00Z",
  "current_period_start": "2026-04-01T00:00:00Z",
  "current_period_end": "2026-05-01T00:00:00Z",
  "emails_sent_this_period": 142,
  "inbound_received_this_period": 318,
  "identity_count": 3,
  "rule_count": 7,
  "domain_count": 1,
  "limits": {
    "emails_per_period": 5000,
    "inbound_per_period": 10000,
    "max_identities": 10,
    "max_rules": 25,
    "max_domains": 3
  }
}

Fields

FieldNotes
planOne of builder_pack, pro, agency. (agency is the Business tier in marketing copy.)
plan_statusStripe-mirrored: trialing, active, past_due, canceled
beta_overrideIf true, plan limits are bypassed (manual flag set by support; checked first in enforcement)
trial_ends_atNull if not on a trial
current_period_*Stripe billing period; for Builder Pack these are null and quotas are lifetime
emails_sent_this_periodOutbound counter — for Builder Pack this is a lifetime total that never resets
inbound_received_this_periodInbound counter
identity_count / rule_count / domain_countLive counts for the workspace
limitsThe cap for each metric on the current plan

Plans & limits

PlanPriceOutboundInboundIdentitiesRulesDomains
Builder PackFree50 lifetime50 lifetime101
Pro$29/mo5,000/mo10,000/mo10253
Business (agency in API)$79/mo15,000/mo30,000/mo5010010
Builder Pack quotas are a lifetime cap, not a monthly allowance — the counter never resets. Designed for evaluation, not production.

Create checkout session

Initiate an upgrade. Returns a Stripe Checkout URL with a 30-day trial; card is required upfront.
curl -X POST https://api.actionlayer.dev/v1/billing/checkout \
  -H "Authorization: Bearer $ACTIONLAYER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"plan": "pro", "interval": "monthly"}'

Request body

FieldValues
planpro | agency
intervalmonthly | annual

Response

{ "checkout_url": "https://checkout.stripe.com/c/pay/cs_…" }
Redirect the user to checkout_url. On completion they land back at app.actionlayer.dev/billing?session_id=…. The Stripe webhook updates plan and plan_status server-side; the dashboard re-reads /billing/subscription to display the new state.

Create billing portal session

Lets the user manage their subscription, payment method, and invoices.
curl -X POST https://api.actionlayer.dev/v1/billing/portal \
  -H "Authorization: Bearer $ACTIONLAYER_API_KEY"

Response

{ "portal_url": "https://billing.stripe.com/p/session/…" }

Plan-limit errors

When you exceed a quota, write endpoints return 402 with an upgrade_url:
{
  "error": "plan_limit_reached",
  "message": "Outbound quota reached for this billing period.",
  "upgrade_url": "https://app.actionlayer.dev/billing"
}
beta_override: true skips this check — useful for design-partner workspaces.

Errors

HTTPerrorWhen
422invalid_planplan not in {pro, agency}
422invalid_intervalinterval not in {monthly, annual}
422stripe_customer_missingThe workspace’s Stripe customer hasn’t been created yet — retry shortly
502stripe_errorUpstream Stripe API failure