Skip to content

Billing

Programmatic access to your tenant's credit balance, transactions, invoices, per-call cost breakdowns, and spend alerts. The dashboard's Billing & Usage screen is built entirely on these endpoints.

Rymi bills per minute of connected call audio against a prepaid credit balance. Failed calls (busy, no-answer) don't bill — only calls that connect and exchange audio.

Get Pricing Rates

http
GET /v1/billing/agent-roles

Returns per-role pricing rates. Same rates apply across all plans; Free / Hobby / Pro determine which roles are unlocked, not the rate.

Response 200

json
{
  "roles": {
    "operator": {
      "rate_per_minute": 0.05,
      "rate_per_second": 0.00083333,
      "display_name": "Operator"
    },
    "specialist": { "rate_per_minute": 0.12, "rate_per_second": 0.002, "display_name": "Specialist" }
  }
}

Get Balance

http
GET /v1/billing/balance

Returns the tenant's current credit balance plus composition signals the dashboard uses to render the right headline format (single-role tenant, runway estimate, role range, etc.).

Response 200

json
{
  "tenant_id": "550e8400-...",
  "balance_seconds": 18000,
  "balance_usd": 15.0,
  "estimated_minutes_remaining": 300,
  "roles": { "operator": 0.05, "specialist": 0.12 },
  "is_active": true,
  "tone": "healthy",
  "tenant_state": "has_calls",
  "primary_role": "specialist",
  "agent_roles_in_use": [],
  "recent_daily_spend_usd": 1.20,
  "runway_days_estimate": 12
}
FieldDescription
balance_secondsRemaining seconds of call time at the cheapest rate
balance_usdSame balance in USD
is_activeWhether the tenant can dispatch live calls (false when balance ≤ 0)
toneUI hint — healthy, low, critical, or depleted
tenant_stateno_agents, agents_no_calls, or has_calls
primary_roleMost-used role across the last 30 days, or null if no role accounts for >50% of recent calls
recent_daily_spend_usd7-day rolling daily-spend average (only set after ≥7 days of activity)
runway_days_estimatebalance_usd / recent_daily_spend_usd, only set when both are populated

List Transactions

http
GET /v1/billing/transactions

Per-tenant credit ledger — top-ups, monthly grants, refunds, and per-call deductions.

Query Parameters

ParameterTypeDefaultDescription
limitinteger50Max records (cap 200)
fromISO dateInclusive lower bound on created_at
toISO dateInclusive upper bound on created_at (extended to end of day)

Response 200

json
{
  "transactions": [
    {
      "id": "tx_...",
      "amount_seconds": 36000,
      "amount_usd": 30.0,
      "transaction_type": "stripe_topup",
      "description": "Top-up via Stripe",
      "created_at": "2026-04-01T10:00:00Z"
    }
  ]
}

List Invoices

http
GET /v1/billing/invoices

Cursor-paginated list of invoices for the tenant.

Query Parameters

ParameterTypeDefaultDescription
limitinteger25Max records (cap 100)
cursorstringResume token returned in next_cursor

Response 200

json
{
  "invoices": [
    { "id": "inv_...", "total_usd": 30.0, "status": "paid", "created_at": "..." }
  ],
  "next_cursor": null
}

Get Invoice

http
GET /v1/billing/invoices/:id

Returns the invoice plus its line items.

Response 200

json
{
  "invoice": { "id": "inv_...", "total_usd": 30.0, "status": "paid" },
  "items": [ { "description": "Top-up", "amount_usd": 30.0 } ]
}
StatusMeaning
404Invoice does not exist for this tenant

Per-Call Cost Breakdown

http
GET /v1/billing/calls

Lists recent calls with cost / margin metadata. Useful for reconciliation against your own records.

Query Parameters

ParameterTypeDefaultDescription
limitinteger50Max records (cap 200)
fromISO dateInclusive lower bound on started_at
toISO dateInclusive upper bound on started_at
agent_idstringFilter to a single agent

Response 200

json
{
  "calls": [
    {
      "id": "call_...",
      "started_at": "2026-04-01T10:00:00Z",
      "agent_id": "agent_...",
      "agent_name": "Support Bot",
      "agent_role": "specialist",
      "duration_seconds": 145,
      "customer_revenue": 0.29,
      "provider_cost": 0.07,
      "gross_margin": 0.22
    }
  ]
}

Export CSV

http
GET /v1/billing/export.csv

Downloads up to 5,000 calls as CSV. Filterable by date range, agent, and call status.

Query Parameters

ParameterTypeDescription
fromISO dateInclusive lower bound on started_at
toISO dateInclusive upper bound on started_at
agent_idstringFilter to a single agent
statusstringFilter by call status (completed, failed, …)

Returns text/csv with header row:

call_id,started_at,agent_id,agent_role,duration_seconds,customer_revenue_usd,provider_cost_usd,gross_margin_usd

Spend Alerts

http
GET /v1/billing/alerts
PUT /v1/billing/alerts

Configure threshold-based spend alerts.

PUT Request Body

FieldTypeDescription
thresholds_usdnumber[]Spend thresholds in USD (max 10)
low_balance_pctinteger (0–100)Alert when remaining balance ≤ this percent of last top-up
email_enabledbooleanWhether to email the alert

Response 200

json
{ "thresholds_usd": [50, 100], "low_balance_pct": 20, "email_enabled": true }

Spend-Velocity Alerts

http
GET /v1/billing/spend-alerts
POST /v1/billing/spend-alerts/:id/acknowledge

Returns un-acknowledged spend-velocity alerts (Phase 2B safety net) and the most recent ten acknowledged ones, plus an endpoint to dismiss a banner.

GET Response 200

json
{
  "unacknowledged": [
    { "id": "alert_...", "alert_type": "spend_velocity", "severity": "critical", "amount_usd": 12.5, "threshold_usd": 10.0 }
  ],
  "recent": []
}

Estimate

http
POST /v1/billing/estimate

Estimates cost for a given role / duration. Used by the studio's call-planner.

Request Body

FieldTypeDescription
tierstringoperator, specialist, executive, or concierge
duration_secondsnumberOptional duration; defaults to a typical call

Response 200

json
{ "rate_per_minute": 0.12, "estimated_usd": 0.29, "duration_seconds": 145 }

Plan & Subscription

http
GET /v1/billing/plan
GET /v1/billing/subscription
POST /v1/billing/plan/upgrade
POST /v1/billing/plan/checkout/start
POST /v1/billing/plan/cancel

The dashboard's plan-selector modal uses these to read the current plan, kick off a checkout, and cancel.

GET /billing/plan Response 200

json
{
  "plan_id": "free",
  "plan": { "id": "free", "display_name": "Free", "monthly_credit_cents": 100, "byok_allowed": false },
  "monthly_credit_remaining_cents": 80,
  "monthly_credit_resets_at": "2026-05-01T00:00:00Z",
  "topup_balance_cents": 1500,
  "preferred_gateway": "stripe",
  "preferred_currency": "USD"
}

POST /billing/plan/upgrade Body

FieldTypeRequiredDescription
target_planenumyesfree, hobby, pro, or enterprise
billing_cycleenumnomonthly or annual (default monthly)

free downgrades immediately. enterprise returns 501 sales_led with a contact email. hobby / pro route through /billing/plan/checkout/start, which returns a Razorpay short URL (IN-resident tenants) or a Stripe Checkout URL (everyone else).

POST /billing/plan/cancel

Schedules cancellation at the end of the current billing period when an active subscription exists; immediately reverts to free otherwise.

Errors

StatusMeaning
409A non-terminal subscription already exists — cancel before starting a new checkout
501Subscription billing not yet enabled in this environment, or sales-led plan
503Gateway not configured (missing Stripe / Razorpay env)