# `Accrue.Billing.MeterEventActions`
[🔗](https://github.com/szTheory/accrue/blob/accrue-v1.0.0/lib/accrue/billing/meter_event_actions.ex#L1)

Metered billing write surface.

Implements `Accrue.Billing.report_usage/3` as a sync-through with a
transactional-outbox audit table:

  1. `Repo.transact/2` inserts a `pending` `%MeterEvent{}` + an
     `accrue_events` ledger row, then commits.
  2. **Outside** the transaction, calls the configured processor's
     `report_meter_event/1` callback.
  3. On `{:ok, _}` → flips the row to `reported` (and stamps
     `reported_at`). On `{:error, _}` → `Accrue.Billing.MeterEvents`
     performs a guarded `pending` → `failed` transition and emits
     `[:accrue, :ops, :meter_reporting_failed]` at most once for that
     transition (`source: :sync`).

Crashes between step 1 and step 2 leave a durable `pending` row that
`Accrue.Jobs.MeterEventsReconciler` retries on its next cron tick.

Idempotency: the caller's `operation_id` (via `Accrue.Actor`) + event
name + value + timestamp derive a deterministic `identifier`. The
unique index on `accrue_meter_events.identifier` dedupes at the audit
layer; Stripe's body-level `identifier` + HTTP-level `idempotency_key`
(forwarded in `Accrue.Processor.Stripe`) dedupe at the wire.

# `report_usage`

```elixir
@spec report_usage(Accrue.Billing.Customer.t() | String.t(), String.t(), keyword()) ::
  {:ok, Accrue.Billing.MeterEvent.t()} | {:error, term()}
```

Reports a metered usage event. See `Accrue.Billing.report_usage/3` for
the public entry point; this function is the implementation target.

# `report_usage!`

```elixir
@spec report_usage!(Accrue.Billing.Customer.t() | String.t(), String.t(), keyword()) ::
  Accrue.Billing.MeterEvent.t()
```

Bang variant of `report_usage/3`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
