Akteora API docs

Plans, limits and billing

What an organisation's plan allows, how the API says "not on this plan", and the routes behind the dashboard's Billing page.

When a request is over the plan

Anything that would create something the plan does not cover returns 402 with the problem type plan_limit_exceeded, and a limit member you can act on:

{
  "type": "https://docs.akteora.com/errors/plan_limit_exceeded",
  "title": "Plan limit reached",
  "status": 402,
  "detail": "The Free plan includes 1 brand, and this organization has 1. Upgrade to Starter for 3 brands.",
  "request_id": "01J…",
  "limit": {
    "name": "brands",
    "plan": "free",
    "allowed": 1,
    "requested": 2,
    "upgrade_to": "starter",
    "upgrade_url": "https://app.akteora.com/billing?upgrade=starter"
  }
}
limit.name Refused when Counted
brands creating a brand brands that exist now
submissions_per_month a respondent opens a link, or starts a testimonial testimonials sent this calendar month (UTC)
storage_bytes the same every file kept: recordings, renditions, posters, reels
render_seconds_per_month rendering a reel CPU core-seconds rendered this month; a render minute is 60
exports_per_month starting an export exports started this month
submissions_per_export, media_files_per_export starting an export that big the export's own size
media_exports an MP4, MP3 or ZIP export on Free
widget_attribution_removal hiding the widget's attribution link on Free
payment_past_due anything gated, while read-only

A 402 never removes anything. A lower plan, or a lapsed one, limits what can be added; every testimonial, brand, widget and public page already there stays, and keeps working.

What a respondent sees

When an organisation cannot take a testimonial — the month's testimonials or its storage used up, or the account read-only — its collect links close with the reason paused:

{
  "is_open": false,
  "reason": "paused",
  "message": "This link isn’t taking testimonials at the moment. Please try again later.",
  "branding": { "brand_name": "Northwind Coffee", "…": "…" }
}

GET /public/links/{slug} returns it with 410, before anyone records, and creating a submission is refused the same way. The message says nothing about plans or payments; the organisation's owners are emailed the reason, once a month.

The plans

GET /v1/billing lists every plan as sold, so you never hard-code a limit:

curl -H "$AUTH" https://api.akteora.com/v1/billing
{
  "plan": "starter",
  "features": ["widget_attribution_removal", "media_exports"],
  "access": "full",
  "past_due_since": null,
  "read_only_from": null,
  "subscription": {
    "status": "active",
    "plan": "starter",
    "current_period_end": "2026-10-01T00:00:00.000Z",
    "cancels_at": null
  },
  "available": true,
  "plans": [
    {
      "plan": "core",
      "name": "Core",
      "price_cents": 7900,
      "currency": "USD",
      "quotas": {
        "brands": 15,
        "submissions_per_month": 500,
        "render_seconds_per_month": 3600,
        "storage_bytes": 100000000000,
        "exports_per_month": 200
      },
      "features": ["widget_attribution_removal", "media_exports", "api_access", "custom_domain"]
    }
  ]
}

Usage

GET /v1/billing/usage gives each quota against the plan: this calendar month for the monthly ones, and the total now for brands and storage.

{
  "plan": "starter",
  "period_start": "2026-09-01",
  "period_ends_at": "2026-10-01T00:00:00.000Z",
  "quotas": [
    {
      "quota": "submissions_per_month",
      "used": 85,
      "allowed": 100,
      "level": "approaching",
      "resets_at": "2026-10-01T00:00:00.000Z"
    }
  ],
  "history": [{ "period_start": "2026-09-01", "submissions": 85, "render_seconds": 126, "exports": 3 }]
}

level is approaching from 80% and reached at 100%. history covers up to twelve months, newest first.

Paying and changing plan

These three are the organisation owner's alone, and are used by the dashboard with the owner's session. Paddle, the merchant of record, takes the payment; no card or address ever passes through this API.

Route Does
POST /v1/billing/checkout { "plan": "core" } Returns { "url" }, a Paddle-hosted checkout. Send the browser there; Paddle returns it to the billing page, and the plan changes when Paddle confirms the payment, usually within seconds. 409 if the organisation already has a subscription
POST /v1/billing/portal Returns { "url" }, Paddle's customer portal for this organisation: the payment method, invoices, cancelling. Short-lived; ask each time
POST /v1/billing/change-plan { "plan": "plus" } Moves the subscription to another paid plan at once, prorated. "free" cancels at the end of the period already paid for. Returns the billing state; read GET /v1/billing again to see the new plan once Paddle confirms

A server that takes no payments answers checkout and the portal with 409 billing_unavailable, and available is false. If Paddle fails to answer, the response is 502 billing_provider_error, and nothing has been charged.