# `LatticeStripe.Billing.MeterEvent`
[🔗](https://github.com/szTheory/lattice_stripe/blob/v2.2.2/lib/lattice_stripe/billing/meter_event.ex#L1)

Stripe Billing MeterEvent — hot-path usage reporting. Create-only; Stripe
exposes no retrieve/list operations for events. See `guides/metering.md`
for the full `AccrueLike.UsageReporter` recipe and the two-layer
idempotency contract.

# `t`

```elixir
@type t() :: %LatticeStripe.Billing.MeterEvent{
  created: integer() | nil,
  event_name: String.t() | nil,
  identifier: String.t() | nil,
  livemode: boolean() | nil,
  payload: map() | nil,
  timestamp: integer() | nil
}
```

# `create`

```elixir
@spec create(LatticeStripe.Client.t(), map(), keyword()) ::
  {:ok, t()} | {:error, LatticeStripe.Error.t()}
```

Report a metered usage event to Stripe.

## Params

- `event_name` (required, string) — must match a `Billing.Meter.event_name`
- `payload` (required, map) — carries the customer-mapping key and, for
  `sum`/`last` meters, the value key named by the meter's
  `value_settings.event_payload_key` (default `"value"`) — **plus any number of
  additional custom dimension keys of your choosing**, which Stripe stores and
  this library passes through without filtering. Pass decimal values as
  **strings**: a float goes through Elixir's default stringifier, which can put
  the value on the wire in scientific notation. See `guides/metering.md` → "The
  payload contract" for the coercion table, the flat-only and cardinality rules,
  and the fact that dimensions cannot be read back grouped on the generally
  available API.
- `timestamp` (optional, integer — Unix seconds) — when the usage occurred;
  must be within the 35-day backdating window and no more than 5 minutes in
  the future
- `identifier` (optional, string, ≤100 chars) — **body-level (business-layer)
  idempotency**; Stripe dedups on this for 24 hours. Use a stable
  domain-derived value (e.g. `"inv_123:item_456"`)

## Opts

- `idempotency_key:` (optional, string) — **transport-layer (HTTP header)
  idempotency**; replays the exact previous response for the same key on
  network retries. **Orthogonal to body `identifier`.** Set BOTH in production
  for full safety: `identifier` protects against duplicate domain events,
  `idempotency_key:` protects against network-level retries.
- `stripe_account:` (optional, string) — `acct_*` for Connect scenarios

## Return value — IMPORTANT

`{:ok, %MeterEvent{}}` means Stripe **accepted for processing**
— it does **not** mean the event has been recorded against a customer.
Customer-mapping validation happens **asynchronously** in Stripe's billing
pipeline. The ONLY way to detect customer-mapping failures, invalid values,
or a missing payload key is to subscribe to the
`v1.billing.meter.error_report_triggered` webhook. See
`guides/metering.md` → "Reconciliation via webhooks" for the error code
table and remediation patterns.

## Example

    {:ok, event} = LatticeStripe.Billing.MeterEvent.create(client, %{
      "event_name" => "api_call",
      "payload" => %{"stripe_customer_id" => "cus_123", "value" => "1"},
      "identifier" => "req_abc"
    }, idempotency_key: "req_abc")

# `create!`

```elixir
@spec create!(LatticeStripe.Client.t(), map(), keyword()) :: t()
```

Bang variant of `create/3`. Raises `LatticeStripe.Error` on failure.

# `from_map`

```elixir
@spec from_map(map()) :: t()
```

Decode a Stripe-shaped string-keyed map into a `%MeterEvent{}`.

MeterEvent has no nested sub-objects. Unknown keys are silently dropped
(no `:extra` field) per EVENT-05 minimal-struct contract.

---

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