> ## Documentation Index
> Fetch the complete documentation index at: https://docs.goyappr.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Affiliate stats

> Your referral code, your commission rates, lifetime totals, and a trailing
month-by-month breakdown. Always scoped to the API key's own workspace —
there is no path, query or body parameter for a company id, and supplying
`company_id` returns `400`. Required scope `affiliates:read`.

**Money is integer US cents.** Every `*_cents` field, including
`payout_sent_cents`, is denominated in US cents no matter which currency a
payout was actually wired in. Divide by 100 for dollars.

**Earned vs payable.** `earned_cents` is everything you have accrued.
`payable_cents` is the part that has aged past `hold_days` and is claimable
now; it is the number payouts are drawn from. `outstanding_cents` is
`payable_cents - paid_cents` and **may be negative** when you have been paid
ahead of the hold window — that is a real state, not an error.

**Reconciliation.** `earned_before_window_cents` plus the sum of
`months[].commission_cents` equals `summary.earned_cents` exactly, so the
table on screen can always be made to add up to the lifetime total.


Your referral code, your commission rates, lifetime totals, and a month-by-month breakdown of what you earned and what has been paid out.

Required scope: `affiliates:read`.

## Scoping

This endpoint always reports on the workspace that issued the API key. There is no path, query or body parameter for a company id, and passing `company_id` returns `400` rather than silently reporting your own numbers back to you. To read a different workspace's affiliate stats, use a key issued by that workspace.

## Query parameters

| Parameter | Type    | Default | Notes                                                                                                  |
| --------- | ------- | ------- | ------------------------------------------------------------------------------------------------------ |
| `months`  | integer | `12`    | How many trailing months `months[]` covers, 1–60. Does not affect `summary`, which is always lifetime. |

There is no request body.

## Money is integer US cents

Every field ending in `_cents` is a whole number of **US cents**. Divide by 100 for dollars; never parse it as a decimal. That includes `payout_sent_cents` and `paid_cents` — a payout wired in another currency is converted at the rate recorded on that payout and stored in US cents, so the whole payload is one currency.

## Earned vs payable — the distinction that matters

| Field               | What it is                                                                      |
| ------------------- | ------------------------------------------------------------------------------- |
| `earned_cents`      | Everything you have accrued, from the moment the customer paid.                 |
| `payable_cents`     | The part of `earned_cents` that has aged past `hold_days` and is claimable now. |
| `paid_cents`        | What has actually been sent to you.                                             |
| `outstanding_cents` | `payable_cents - paid_cents`. **The number you are owed today.**                |

Commission is earned at the moment a referred customer pays, but it only becomes payable after `hold_days` have elapsed — that window covers card disputes and refunds, so money that could still be reversed never appears as owed. A recent month therefore reads `commission_cents` higher than `payable_commission_cents`; the gap closes on its own as the hold elapses. Nothing is lost.

<Warning>
  `outstanding_cents` **can be negative.** That means a payout ran ahead of the hold window — you have been paid for commission that has not cleared yet. It is a real state, not an error, and it is deliberately not clamped to zero so an overpayment stays visible. It works itself off as more commission becomes payable. Render it as an "overpaid" state rather than as a negative currency amount.
</Warning>

## Response fields

| Field                                | Type    | Meaning                                                                                                                                                |
| ------------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `ref_code`                           | string  | Your referral code. Your link is `https://app.goyappr.com/signup?ref=<ref_code>`.                                                                      |
| `commission_rate_pct`                | number  | Your **direct** (tier-1) rate today, as a percentage. See [Rates are not retroactive](#rates-are-not-retroactive).                                     |
| `override_rate_pct`                  | number  | Your **override** (tier-2) rate on revenue from your sub-affiliates' customers, today. `0` when you have no tier-2 terms — which is the normal case.   |
| `hold_days`                          | integer | Days a commission ages before becoming payable. `0` means immediately payable.                                                                         |
| `months_window_months`               | integer | Echo of the `months` parameter.                                                                                                                        |
| `summary.clients_invited`            | integer | Companies that signed up through your link, **including** ones that have never paid.                                                                   |
| `summary.sub_affiliates`             | integer | Enrolled partners you earn an override through. `0` for most workspaces. Itemised by [`GET /affiliates/downline`](/api-reference/affiliates/downline). |
| `summary.referred_revenue_cents`     | integer | Lifetime tier-1 billed revenue — what your direct referrals paid us.                                                                                   |
| `summary.override_revenue_cents`     | integer | Lifetime tier-2 billed revenue — what your sub-affiliates' customers paid us.                                                                          |
| `summary.earned_cents`               | integer | Lifetime commission, both tiers. Equals `direct_earned_cents + override_earned_cents`.                                                                 |
| `summary.direct_earned_cents`        | integer | Tier-1 share of `earned_cents`.                                                                                                                        |
| `summary.override_earned_cents`      | integer | Tier-2 share of `earned_cents`.                                                                                                                        |
| `summary.payable_cents`              | integer | The part of `earned_cents` past the hold window.                                                                                                       |
| `summary.paid_cents`                 | integer | Lifetime payouts in US cents. Voided payouts are excluded.                                                                                             |
| `summary.outstanding_cents`          | integer | `payable_cents - paid_cents`. May be negative.                                                                                                         |
| `summary.earned_before_window_cents` | integer | Commission earned before the first month in `months[]`.                                                                                                |
| `months[]`                           | array   | Newest first, `months_window_months` entries, zero-filled.                                                                                             |

### Month rows

| Field                       | Type    | Meaning                                                                         |
| --------------------------- | ------- | ------------------------------------------------------------------------------- |
| `month`                     | string  | `YYYY-MM`.                                                                      |
| `clients_invited`           | integer | Signups through your link that month.                                           |
| `referred_revenue_cents`    | integer | Tier-1 billed revenue that month.                                               |
| `commission_cents`          | integer | Commission earned that month, both tiers.                                       |
| `direct_commission_cents`   | integer | Tier-1 share.                                                                   |
| `override_commission_cents` | integer | Tier-2 share.                                                                   |
| `payable_commission_cents`  | integer | The part past the hold window. Lower than `commission_cents` for recent months. |
| `payout_sent_cents`         | integer | Payouts recorded against that month, in US cents.                               |

<Warning>
  Any `*_cents` field in a month row **can be negative.** A refund or chargeback is booked in the month it happens, and it reverses the commission at the rate that applied when the customer originally paid — so a month with more refunds than sales reads negative. The `summary` totals already account for it. Do not clamp these to zero; a table that hides a reversal will not add up to `earned_cents`.
</Warning>

## Rates are not retroactive

`commission_rate_pct` and `override_rate_pct` are the rates **in force today**. Each commission was priced at the rate in force on the day that customer paid, and a later rate change never re-prices what you already earned — up or down.

So this does **not** hold, and you should not present it as if it did:

```
referred_revenue_cents * commission_rate_pct / 100   ≠   direct_earned_cents
```

If your rate moved from 5% to 10% last month, revenue from before then is still priced at 5% and the two numbers will disagree by a wide margin. `direct_earned_cents` and `override_earned_cents` are the authoritative figures; the rate fields tell you what the *next* dollar earns.

For the same reason, the per-line rounding is done at the (customer, month) grain, so `sum(months[].commission_cents)` can differ by a cent or two from a rate applied to a lifetime revenue total. The reconciliation identity below is the one that always holds.

## Reconciling the table to the lifetime total

`summary` is lifetime and unbounded; `months[]` is a trailing window. They differ, and the payload tells you by exactly how much:

```
earned_before_window_cents + sum(months[].commission_cents) == summary.earned_cents
```

Render `earned_before_window_cents` as an "Earlier" row and your table always adds up. Widen the window with `months` if you want the older months itemised.

## Example request

```bash theme={null}
curl "https://api.goyappr.com/affiliates/stats?months=2" \
  -H "Authorization: Bearer $YAPPR_API_KEY"
```

## Example response

Read on 2026-09-15, for an affiliate on 10% direct, 2.5% override, 30-day hold, whose rates have never changed — which is why revenue times rate happens to reproduce the earned figures here. Do not rely on that in general; see [Rates are not retroactive](#rates-are-not-retroactive).

```json theme={null}
{
  "ref_code": "AB12CD34",
  "commission_rate_pct": 10,
  "override_rate_pct": 2.5,
  "hold_days": 30,
  "months_window_months": 2,
  "summary": {
    "clients_invited": 3,
    "sub_affiliates": 1,
    "referred_revenue_cents": 26500,
    "override_revenue_cents": 40000,
    "earned_cents": 3650,
    "direct_earned_cents": 2650,
    "override_earned_cents": 1000,
    "payable_cents": 1450,
    "paid_cents": 700,
    "outstanding_cents": 750,
    "earned_before_window_cents": 1450
  },
  "months": [
    {
      "month": "2026-09",
      "clients_invited": 0,
      "referred_revenue_cents": 12000,
      "commission_cents": 2200,
      "direct_commission_cents": 1200,
      "override_commission_cents": 1000,
      "payable_commission_cents": 0,
      "payout_sent_cents": 0
    },
    {
      "month": "2026-08",
      "clients_invited": 1,
      "referred_revenue_cents": 0,
      "commission_cents": 0,
      "direct_commission_cents": 0,
      "override_commission_cents": 0,
      "payable_commission_cents": 0,
      "payout_sent_cents": 700
    }
  ]
}
```

Read that payload in the right order. `earned_cents` is 3,650¢, but all 2,200¢ of it earned in September — a 1,200¢ direct commission and a 1,000¢ override — is younger than the 30-day hold, so `payable_commission_cents` is `0` for that month and `payable_cents` is only the 1,450¢ accrued before the window. 700¢ of that has been paid, so the amount actually owed today is `outstanding_cents` = 750¢. Come 2026-10-05 the September lines age past the hold and `payable_cents` jumps to 3,650¢ with no new revenue at all.

The reconciliation also holds: `earned_before_window_cents` (1,450) + `2200 + 0` = `earned_cents` (3,650).

## Errors

| HTTP | Code                  | When                                                                       |
| ---- | --------------------- | -------------------------------------------------------------------------- |
| 400  | —                     | `months` is not an integer 1–60, or a `company_id` parameter was supplied. |
| 401  | `INSUFFICIENT_SCOPE`  | API key lacks `affiliates:read`.                                           |
| 401  | `INVALID_KEY`         | Key not found, revoked, or expired.                                        |
| 403  | `AFFILIATE_FORBIDDEN` | Affiliate data is not readable for this workspace.                         |
| 429  | `RATE_LIMIT`          | 60 requests/minute exceeded.                                               |
| 500  | —                     | The figures could not be computed. Safe to retry.                          |


## OpenAPI

````yaml GET /affiliates/stats
openapi: 3.1.0
info:
  title: Yappr API
  description: >
    Create and manage AI voice agents, purchase phone numbers, configure tools,
    and initiate calls — all via REST.
  version: 1.0.0
  contact:
    url: https://goyappr.com
servers:
  - url: https://api.goyappr.com
    description: Production
security:
  - apiKey: []
paths:
  /affiliates/stats:
    get:
      tags:
        - Affiliates
      summary: Get affiliate stats
      description: >
        Your referral code, your commission rates, lifetime totals, and a
        trailing

        month-by-month breakdown. Always scoped to the API key's own workspace —

        there is no path, query or body parameter for a company id, and
        supplying

        `company_id` returns `400`. Required scope `affiliates:read`.


        **Money is integer US cents.** Every `*_cents` field, including

        `payout_sent_cents`, is denominated in US cents no matter which currency
        a

        payout was actually wired in. Divide by 100 for dollars.


        **Earned vs payable.** `earned_cents` is everything you have accrued.

        `payable_cents` is the part that has aged past `hold_days` and is
        claimable

        now; it is the number payouts are drawn from. `outstanding_cents` is

        `payable_cents - paid_cents` and **may be negative** when you have been
        paid

        ahead of the hold window — that is a real state, not an error.


        **Reconciliation.** `earned_before_window_cents` plus the sum of

        `months[].commission_cents` equals `summary.earned_cents` exactly, so
        the

        table on screen can always be made to add up to the lifetime total.
      operationId: getAffiliateStats
      parameters:
        - in: query
          name: months
          schema:
            type: integer
            minimum: 1
            maximum: 60
            default: 12
          description: >-
            How many trailing months `months[]` should cover. Does not affect
            `summary`, which is lifetime.
      responses:
        '200':
          description: Affiliate stats
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AffiliateStats'
        '400':
          description: '`months` is out of range, or a `company_id` parameter was supplied.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized, or the key is missing `affiliates:read`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: >-
            `AFFILIATE_FORBIDDEN` — affiliate data is not readable for this
            workspace.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    AffiliateStats:
      type: object
      description: >
        Affiliate commission summary for the API key's own workspace. All money
        is

        integer **US cents** — divide by 100 for dollars, never parse as a
        float.
      properties:
        ref_code:
          type: string
          example: AB12CD34
          description: >-
            Your referral code. Your link is
            `https://app.goyappr.com/signup?ref=<ref_code>`.
        commission_rate_pct:
          type: number
          example: 10
          description: >
            Your **direct** (tier-1) commission rate as a percentage, *as it
            stands today*. Every past commission was priced at the rate in force
            on the day that customer paid, so after a rate change
            `referred_revenue_cents * commission_rate_pct` will NOT reproduce
            `direct_earned_cents`. Use it to say what new revenue will earn —
            never to re-derive history.
        override_rate_pct:
          type: number
          example: 2.5
          description: >
            Your **override** (tier-2) rate on revenue from your sub-affiliates'
            customers, as it stands today. `0` when you have no tier-2 terms.
            Same caveat as `commission_rate_pct`: it does not re-derive
            `override_earned_cents`.
        hold_days:
          type: integer
          example: 30
          description: >
            Days a commission must age before it becomes payable. Covers the
            card-dispute window. `0` means commission is payable as soon as it
            is earned.
        months_window_months:
          type: integer
          example: 12
          description: >-
            How many trailing months `months[]` covers — echoes the `months`
            query parameter.
        summary:
          type: object
          description: Lifetime totals. Unbounded — not limited to `months_window_months`.
          properties:
            clients_invited:
              type: integer
              description: >-
                Companies that signed up through your link, including those that
                have never paid.
            sub_affiliates:
              type: integer
              description: >-
                Enrolled sub-affiliates you earn an override through. `0` for
                most workspaces.
            referred_revenue_cents:
              type: integer
              description: Lifetime tier-1 billed revenue.
            override_revenue_cents:
              type: integer
              description: Lifetime tier-2 billed revenue.
            earned_cents:
              type: integer
              description: >-
                Lifetime commission earned across both tiers. Equals
                `direct_earned_cents + override_earned_cents`.
            direct_earned_cents:
              type: integer
            override_earned_cents:
              type: integer
            payable_cents:
              type: integer
              description: >
                The part of `earned_cents` past the hold window. **This is the
                number payouts are drawn from** — `earned_cents` is what you
                have accrued, `payable_cents` is what is currently claimable.
            paid_cents:
              type: integer
              description: >-
                Lifetime payouts, converted to US cents at the rate recorded on
                each payout. Voided payouts are excluded.
            outstanding_cents:
              type: integer
              description: >
                `payable_cents - paid_cents`. **Can be negative** — that means
                you have been paid ahead of the hold window, and the balance
                works itself off as more commission becomes payable. It is not
                an error and it is not clamped to zero.
            earned_before_window_cents:
              type: integer
              description: >
                Commission earned before the first month in `months[]`. Added to
                the sum of `months[].commission_cents` it reconciles exactly to
                `earned_cents`.
        months:
          type: array
          description: >-
            Newest month first, `months_window_months` entries, zero-filled for
            months with no activity.
          items:
            $ref: '#/components/schemas/AffiliateMonth'
    Error:
      type: object
      properties:
        error:
          type: string
        code:
          type: string
    AffiliateMonth:
      type: object
      description: >
        One calendar month of affiliate activity. Every `*_cents` field is an
        integer

        number of **US cents**.


        Any `*_cents` field here **can be negative**. A refund or chargeback is
        booked in

        the month it happens and reverses the commission at the rate that
        originally

        applied, so a month with more refunds than sales reads negative. That is
        correct,

        not a bug — the lifetime totals in `summary` already account for it.
      properties:
        month:
          type: string
          example: 2026-07
          description: Calendar month, `YYYY-MM`.
        clients_invited:
          type: integer
          description: Companies that signed up through your link in this month.
        referred_revenue_cents:
          type: integer
          description: >-
            Tier-1 billed revenue — what the customers you referred directly
            paid us. Negative if refunds outweighed sales.
        commission_cents:
          type: integer
          description: >-
            Total commission earned in this month, both tiers. Equals
            `direct_commission_cents + override_commission_cents`. May be
            negative.
        direct_commission_cents:
          type: integer
          description: Tier-1 share of `commission_cents`.
        override_commission_cents:
          type: integer
          description: Tier-2 share of `commission_cents`.
        payable_commission_cents:
          type: integer
          description: >
            The part of `commission_cents` that has cleared the hold window (see
            `hold_days`) and is therefore payable. Recent months read lower than
            `commission_cents`; the gap closes on its own as the hold elapses.
        payout_sent_cents:
          type: integer
          description: >-
            Payouts recorded against this month, in US cents regardless of the
            currency actually wired.
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      bearerFormat: API Key
      description: >-
        Your Yappr API key (e.g. `ypr_live_...`). Generate one in the dashboard
        under **Settings → API Keys**.

````