# Business reporting

Read your business earnings, payout eligibility, and traffic from the CLI or from a server job.

Your business reporting data (earnings, payout eligibility, and traffic per
application) is the same data the business dashboard shows. There are two ways
to read it, and the SDK has no reporting call.

| You are                       | Use                                                                    | Credential                                |
| ----------------------------- | ---------------------------------------------------------------------- | ----------------------------------------- |
| A developer or a coding agent | `enlitt business info` and `enlitt traffic` (see [the CLI](/docs/cli.md)) | Your signed-in CLI session                |
| A scheduled server job        | `GET /v1/business/info`                                                | A Read-only key in `ENLITT_REPORTING_KEY` |

## From the CLI

```bash
npx enlitt business info --json
npx enlitt traffic --days 7 --json
npx enlitt traffic --app <application_id> --days 30 --json
```

`business info` prints earnings, payout eligibility, and the business-wide
traffic totals. `traffic` prints one row per UTC day with visitors, standard
requests, premium requests, and premium earnings. Both need a role that may
view stats; every role has it. See [CLI](/docs/cli.md) for the exact output shapes.

## From a server job

Call the endpoint with a **Read-only key** (`scope: "reporting"` on the wire). Create one with
`npx enlitt key create --scope read-only --name reporting --write-env .env.local`
or under **Keys** in the dashboard (see [API keys](/docs/api-keys.md)). An
App key (`model`) is rejected, and so is a personal session.

```bash
curl -sS "https://api.enlitt.com/v1/business/info?limit=25" \
  -H "Authorization: Bearer $ENLITT_REPORTING_KEY"
```

```ts
// A server-only job. Read the key from the environment; never ship it to a browser.
const reportingKey = process.env.ENLITT_REPORTING_KEY;
if (!reportingKey) throw new Error("Set ENLITT_REPORTING_KEY on the server");

const response = await fetch("https://api.enlitt.com/v1/business/info?limit=25", {
  headers: { Authorization: `Bearer ${reportingKey}` }
});
if (!response.ok) throw new Error(`Enlitt reporting failed with ${response.status}`);
const info = await response.json();
```

Staging uses `https://staging-api.enlitt.com` and a key issued there. Keep
`ENLITT_SECRET_KEY` for your App key and use a separate variable for the
Read-only key.

## Query

| Field         | Meaning                                                                                                              |
| ------------- | -------------------------------------------------------------------------------------------------------------------- |
| `from` / `to` | RFC 3339 bounds of the UTC reporting window. `to` defaults to the current instant and `from` to 30 days before `to`. |
| `cursor`      | The opaque `next_cursor` from a prior page, passed back unchanged. Never inspect or construct one.                   |
| `limit`       | The most `applications` entries to return in this page. A value above the maximum is refused with `invalid_value`.   |

A range wider than the maximum Enlitt can total in full is refused with
`invalid_value` naming the bound. It is never answered with a partial total.

## Response

```ts
interface BusinessDashboardInfo {
  business: { id: string; name: string; email?: string };
  earnings: { earnings_nano_usd: string; payout_pending_nano_usd: string; display_currency: "USD" };
  payout: {
    eligible: boolean;
    connected_account_status: "none" | "onboarding" | "restricted" | "enabled" | "rejected";
    policy_code_version: string;
    minimum_available_nano_usd: string;
    next_eligible_at: string | null;
    blocked_reason?: string; // present exactly when eligible is false
  };
  traffic: {
    daily_visitors: { date: string; count: number }[];
    daily_usage: {
      date: string;
      standard_requests: number;
      premium_requests: number;
      premium_earnings_nano_usd: string;
    }[];
    usage: BusinessApplicationUsage;
  };
  applications: BusinessApplicationSummary[];
  next_cursor: string | null;
  range: { from: string; to: string; timezone: "UTC" };
  data_fresh_at: string;
}
```

* **Amounts** are exact NANOUSD integer strings (`1 USD = 1,000,000,000 NANOUSD`).
  Never parse one into a JavaScript `number`; use `BigInt` for arithmetic.

* **`traffic`** covers every application the business owns, whatever page
  `applications` is on. `daily_usage` omits a day with no calls, so fill zeros.
  `daily_visitors` counts distinct signed-in users with at least one call that
  UTC day. A person using two of your applications that day counts once, so it
  is not the sum of the per-application counts. A person active on several days
  counts on each, so summing days gives visitor-days, not unique visitors.
  There is no unique-visitor total for a week, a month, or any range.

* **`applications`** lists each application's name, environment (`test` or
  `live`), status, public client ID, the safe metadata of its keys (never a
  secret), its daily visitors, and its usage. Usage has a `standard` and a
  `premium` bucket, each with a request count and the amount used; only
  `premium` carries `earnings_nano_usd`, because standard calls earn nothing.

* **`payout.blocked_reason`** is one of `payout_blocked`, `below_minimum`,
  `outstanding_deficit`, `tax_details_required`, `payout_account_incomplete`,
  `monthly_payout_taken`, or `payout_pending`. It names the first rule that
  refused. `next_eligible_at` is `null` when no payout has been taken this month.

* **`payout.minimum_available_nano_usd`** is the smallest payout you can
  request, in NANOUSD. It reflects the published $50 minimum; read the value
  rather than hard-coding it.

* **`payout.connected_account_status`** is the state of your payout account
  with Enlitt's payment provider. The owner acts on it under **Settings** in the
  dashboard; the CLI cannot.

  | Status       | Meaning                                                | What to do                                     |
  | ------------ | ------------------------------------------------------ | ---------------------------------------------- |
  | `none`       | No payout account yet.                                 | Begin payout setup in Settings.                |
  | `onboarding` | Setup was started and not finished.                    | Finish payout setup in Settings.               |
  | `restricted` | The provider needs more information before paying out. | Resolve the requested details in Settings.     |
  | `enabled`    | Ready to receive payouts.                              | Nothing; update details in Settings if needed. |
  | `rejected`   | The provider declined the account.                     | Contact Enlitt support.                        |

* **Freshness:** `data_fresh_at` is the instant the figures were read. Recent
  calls can take a short time to appear, so compare totals over whole days
  rather than expecting the latest call to be counted.

## Earnings per premium credit

The deployed payout is **800,000 NANOUSD (`$0.0008`) received by the business
per premium credit** a user spends in your application. Standard calls earn
nothing. The earnings figures are exact NANOUSD strings computed at this rate.

## Pagination

Follow `next_cursor` until it is `null`. There is no backward cursor: to see a
prior page, issue the original query again.
