# `IdempotencyKit.Store.Ecto`
[🔗](https://github.com/metacircu1ar/idempotency_kit/blob/main/lib/idempotency_kit/store/ecto.ex#L1)

Generic Ecto/Postgres helper functions for idempotency stores.

This module is adapter glue and does not implement a behaviour by itself.
Host applications typically create a thin module that implements their local
store behaviour and delegates to these functions with app-specific `Repo`,
request schema, and config.

`claim_request/7` options:

- `:processing_stale_after_seconds` (positive integer, default `300`)
- `:retention_days` (positive integer, default `14`)
- `:create_changeset_fun` optional function `(schema_module, attrs) -> Ecto.Changeset.t()`
  used to build the insert changeset.

# `claim_request`

```elixir
@spec claim_request(
  module(),
  module(),
  integer(),
  String.t(),
  String.t(),
  String.t(),
  keyword()
) ::
  {:execute, IdempotencyKit.Store.request_record()}
  | {:processing, IdempotencyKit.Store.request_record()}
  | {:replay, IdempotencyKit.Store.request_record()}
  | {:error,
     :invalid_key
     | :invalid_scope
     | :invalid_request_hash
     | :payload_mismatch
     | :idempotency_unavailable}
```

# `complete_request`

```elixir
@spec complete_request(
  module(),
  module(),
  IdempotencyKit.Store.request_record(),
  String.t(),
  pos_integer(),
  map()
) ::
  {:ok, IdempotencyKit.Store.request_record()}
  | {:error, :idempotency_unavailable}
```

# `purge_stale_requests`

```elixir
@spec purge_stale_requests(module(), module(), keyword()) ::
  {non_neg_integer(), nil | [term()]}
```

# `replay_candidate?`

```elixir
@spec replay_candidate?(module(), module(), integer(), String.t(), String.t(), term()) ::
  boolean()
```

Return whether a request is an exact idempotency-key + payload match.

This is a read-only pre-check for callers that need policy decisions before
claiming, for example skipping rate-limit consumption for an exact retry. It
returns `false` for invalid identifiers, missing records, and payload
mismatches.

The result is advisory. Call `claim_request/7` for the authoritative lifecycle
result.

# `request_hash`

```elixir
@spec request_hash(term()) :: String.t()
```

---

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