ErzyCall API
    ErzyCall API

    Getting Started

    Quick StartAuthentication

    Guides

    Rate LimitingError HandlingWebhooks

    MCP

    OverviewChat assistantsCoding toolsAutomation platformsTools & permissionsTroubleshooting

    API Reference

    CallsContactsCasesAssistantsPhone NumbersContact GroupsUsageWebhook EndpointsWhatsApp

    Usage

    Read your organization's plan, minute balance, spend, and usage history.

    Usage endpoints are read-only. They report what the call pipeline has already recorded — plans, balances, and minutes are changed through the dashboard and Stripe, never through the API.

    Get Usage Summary

    GET /api/v1/usage

    Required scope: usage:read

    Returns the current plan, minutes left on the balance, minutes spent in the current billing period, and how many days are left before that period ends.

    Example Request

    curl -X GET "https://app.erzycall.com/api/v1/usage" \
      -H "X-API-Key: ek_live_abc123"

    Example Response

    {
      "data": {
        "plan": {
          "id": "outreach_growth",
          "name": "Outreach Caller Growth",
          "productType": "outreach",
          "status": "active",
          "includedMins": 300,
          "isTrial": false,
          "isComplimentary": false,
          "cancelAtPeriodEnd": false,
          "cancelAt": null,
          "features": {
            "allowInbound": false,
            "allowOutbound": true,
            "allowWhatsApp": false
          }
        },
        "balanceMins": 212,
        "isLowBalance": false,
        "period": {
          "start": "2026-08-01T00:00:00.000Z",
          "end": "2026-09-01T00:00:00.000Z",
          "daysRemaining": 12,
          "source": "subscription"
        },
        "currentPeriodUsage": {
          "totalMins": 88,
          "deductedMins": 88,
          "totalCalls": 61,
          "inbound": { "calls": 24, "mins": 40 },
          "outbound": { "calls": 37, "mins": 48 },
          "truncated": false
        },
        "trial": {
          "isActive": false,
          "hasEnded": true,
          "expiredWithoutPlan": false,
          "startedAt": "2026-06-01T09:00:00.000Z",
          "endsAt": "2026-06-04T09:00:00.000Z",
          "daysRemaining": null,
          "grantedMins": 800
        },
        "subscriptions": [
          {
            "id": "bs_001",
            "planId": "outreach_growth",
            "planName": "Outreach Caller Growth",
            "productType": "outreach",
            "status": "active",
            "currentPeriodStart": "2026-08-01T00:00:00.000Z",
            "currentPeriodEnd": "2026-09-01T00:00:00.000Z",
            "cancelAtPeriodEnd": false,
            "cancelAt": null
          }
        ]
      }
    }

    Reading the numbers

    FieldMeaning
    balanceMinsMinutes left to spend right now
    currentPeriodUsage.totalMinsTalk time this period, whether or not it came off the balance
    currentPeriodUsage.deductedMinsThe part of that talk time that actually reduced the balance
    period.daysRemainingWhole days until the period ends, rounded up, floored at 0
    plan.includedMinsMinutes the plan grants each renewal — not a live balance
    plan.cancelAtWhen the plan actually ends. null while it keeps renewing — period.end is the renewal date

    totalMins and deductedMins differ when a plan covers a call outright: inbound calls on AI Receptionist and every call on Complimentary consume talk time without reducing the balance.

    trial.hasEnded is the literal fact that the trial's end date has passed — it stays true for an organization that went on to subscribe. trial.expiredWithoutPlan is the narrower signal that the trial ended and nothing replaced it, which is when the organization has no entitlement left.

    period.source says what the window means:

    • subscription — the active subscription's billing period.
    • trial — the free-trial window, for an organization still inside it.
    • rolling_30d — the organization has no period this API can name, so usage is summed over the last 30 days.

    period.end is never a date that has already passed. An organization whose subscription lapsed or whose trial expired falls to rolling_30d rather than reporting the window that has already closed.

    A null end means two different things depending on the source. Under subscription it means the period's end was never recorded — some legacy and manually linked subscriptions carry a start with no end — so the window is real but open-ended. Under rolling_30d it means there is no billing period at all. period.daysRemaining is null in both cases, and period.start always states the exact window the usage totals cover.

    currentPeriodUsage.truncated is true only for an organization with more than 10,000 billing rows in one period. The totals then cover the most recent rows and are a floor — page /usage/history for the exact ledger.

    List Usage History

    GET /api/v1/usage/history

    Required scope: usage:read

    Every balance movement, newest first: call usage, plan renewals, minute top-ups, coupon redemptions, and admin adjustments.

    Query Parameters

    ParameterTypeDescription
    limitintegerResults per page, 1–100. Defaults to 25.
    cursorstringCursor from the previous page of this same query.
    typestringReturn only one kind of movement — see below.
    fromstringISO 8601 timestamp; only rows created at or after it.
    tostringISO 8601 timestamp; only rows created at or before it.

    Valid type values: inbound_call, outbound_call, monthly_renewal, addon_purchase, admin_adjustment, coupon_redemption, trial_activation, trial_conversion, trial_expiration.

    A cursor belongs to the exact filter set that produced it. Change type, from, or to and start over without a cursor — reusing one across a filter change returns 400 INVALID_CURSOR.

    Example Request

    curl -X GET "https://app.erzycall.com/api/v1/usage/history?type=outbound_call&limit=2" \
      -H "X-API-Key: ek_live_abc123"

    Example Response

    {
      "data": [
        {
          "id": "bt_002",
          "type": "outbound_call",
          "amountMins": -2,
          "balanceAfterMins": 212,
          "durationMins": 2,
          "includedInPlan": false,
          "description": "Outbound call usage: 2 min(s)",
          "productType": "outreach",
          "providerCallId": "b7c2f4e1-2a55-4d0e-9c31-6f8a2d1e4b90",
          "createdAt": "2026-08-19T14:05:00.000Z"
        },
        {
          "id": "bt_001",
          "type": "outbound_call",
          "amountMins": -1,
          "balanceAfterMins": 214,
          "durationMins": 1,
          "includedInPlan": false,
          "description": "Outbound call usage: 1 min(s)",
          "productType": "outreach",
          "providerCallId": "3f9a1c08-7d64-4b12-8e05-1a7c9b3d2f45",
          "createdAt": "2026-08-19T11:42:00.000Z"
        }
      ],
      "pagination": {
        "cursor": "u1a2b3c4.eyJ...",
        "hasMore": true,
        "pageSize": 2
      }
    }

    Row fields

    FieldMeaning
    amountMinsSigned as stored — negative consumed minutes, positive granted them
    balanceAfterMinsThe balance immediately after this movement
    durationMinsTalk time for a call row; null for grants and adjustments
    includedInPlantrue when the plan covered the call, so amountMins is 0 while durationMins is not
    providerCallIdThe telephony provider's ID for the call this row bills. null on non-call rows

    Correlating a spend row with its call

    providerCallId is the same value the calls resource publishes as apiCallId — match on that to find the call behind a charge:

    curl -X GET "https://app.erzycall.com/api/v1/calls?from=2026-08-01T00:00:00Z" \
      -H "X-API-Key: ek_live_abc123"
    # then match calls[].apiCallId against providerCallId from the history row

    providerCallId is not a Convex document ID, so GET /calls/{callId} will not accept it — that endpoint takes the id field from the calls resource. The ledger stores the provider's reference, and translating it here would mean reading a full page of call documents (which carry transcripts and raw provider payloads) on every request, so the API publishes the reference it has rather than paying that cost.

    Rows are returned newest first. When type is set, a page can come back shorter than limit while hasMore is still true — keep following the cursor until hasMore is false.

    Contact Groups

    List contact groups for your organization.

    Webhook Endpoints

    Create, update, and manage webhook endpoint subscriptions.

    On this page

    Get Usage SummaryExample RequestExample ResponseReading the numbersList Usage HistoryQuery ParametersExample RequestExample ResponseRow fieldsCorrelating a spend row with its call