# `Accrue.Jobs.MeterEventsReconciler`
[🔗](https://github.com/szTheory/accrue/blob/accrue-v1.0.0/lib/accrue/jobs/meter_events_reconciler.ex#L1)

Metered billing outbox reconciler.

Scans `accrue_meter_events` for rows stuck in `stripe_status = "pending"`
more than 60 seconds and retries the Stripe call. Closes the "row
committed but the process died before the synchronous Stripe call
returned" gap in `Accrue.Billing.MeterEventActions.report_usage/3`.

## Wiring

Accrue does not start its own Oban — the host app schedules this
worker in its Oban cron config:

    config :my_app, Oban,
      plugins: [
        {Oban.Plugins.Cron,
         crontab: [
           {"* * * * *", Accrue.Jobs.MeterEventsReconciler}
         ]}
      ]

The worker runs on queue `:accrue_meters` (host must declare the
queue in its Oban config). Each tick processes up to 1 000 rows.

## Failure handling

On Stripe error the row flips to `failed` — the reconciler does NOT
keep retrying the same row in the same tick, avoiding the
"stuck row infinite retry" footgun. Failures emit
`[:accrue, :ops, :meter_reporting_failed]` with `source: :reconciler`
so ops can distinguish sync request path vs reconciler vs webhook failures.

# `reconcile`

```elixir
@spec reconcile() :: {:ok, non_neg_integer()}
```

Runs a single reconciliation pass. Returns `{:ok, count}` where
`count` is the number of pending rows this pass considered. Public
so tests can drive it without Oban's job struct.

---

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