# `LatticeStripe.Account`
[🔗](https://github.com/szTheory/lattice_stripe/blob/v1.1.0/lib/lattice_stripe/account.ex#L1)

Operations on Stripe Connect Account objects.

A Connect Account represents a user's connected Stripe account that your
platform transacts on behalf of. Phase 17 delivers the full account lifecycle
(`create/3`, `retrieve/3`, `update/4`, `delete/3`, `reject/4`, `list/3`,
`stream!/3`) plus the companion onboarding URL modules
`LatticeStripe.AccountLink` and `LatticeStripe.LoginLink`.

## Acting on behalf of a connected account

LatticeStripe already threads the `Stripe-Account` header end-to-end in
`LatticeStripe.Client`. You can set a connected account either per-client OR
per-request:

    # Per-client (platform holds the key, acts on one connected account)
    client = LatticeStripe.Client.new!(
      api_key: "sk_test_platform",
      finch: MyApp.Finch,
      stripe_account: "acct_connected"
    )
    LatticeStripe.Customer.create(client, %{email: "c@example.test"})

    # Per-request (platform holds the key, switches connected account per-call)
    LatticeStripe.Customer.create(client, %{email: "c@example.test"},
      stripe_account: "acct_connected")

The per-request opt takes precedence over the per-client value. See the
Connect guide for idiomatic patterns.

## D-01 nested struct budget reframing

Phase 17 amends Phase 16's D1 nested-struct budget rule: the 5-module budget
now counts DISTINCT nested struct modules, not promoted parent fields. This
resource exercises the reframing — `LatticeStripe.Account.Requirements` is
defined once and reused at both `%Account{}.requirements` and
`%Account{}.future_requirements`. Subsequent phases should treat the rule as
"up to 5 distinct nested struct modules per resource, with reuse encouraged."

## Requesting capabilities

LatticeStripe does NOT provide a `request_capability/4` helper (rejected per
Phase 17 D-04b as fake ergonomics — the capability name set is an open,
growing string enum). Use `update/4` with the nested map idiom:

    LatticeStripe.Account.update(client, "acct_123", %{
      capabilities: %{
        "card_payments" => %{requested: true},
        "transfers" => %{requested: true}
      }
    })

## Rejecting a connected account

`reject/4` calls `POST /v1/accounts/:id/reject` with a single `reason` atom.
The reason is guarded at the function head:

    LatticeStripe.Account.reject(client, "acct_123", :fraud)
    LatticeStripe.Account.reject(client, "acct_123", :terms_of_service)
    LatticeStripe.Account.reject(client, "acct_123", :other)

Any other atom raises `FunctionClauseError` at call time. This is an
irreversible action — once rejected, the connected account cannot accept
charges or transfers. Wire `account.application.deauthorized` and
`account.updated` webhooks in `LatticeStripe.Webhook` rather than driving
state from the SDK response.

## Webhook handoff

**Drive your application state from webhook events, not SDK responses.** An
SDK response reflects the account state at the moment of the call, but
Stripe may transition the account a moment later (capability activation,
requirements update, payouts enablement). Wire `account.updated`,
`account.application.authorized`, and `account.application.deauthorized`
into your webhook handler.

## Stripe API Reference

See the [Stripe Accounts API](https://docs.stripe.com/api/accounts).

# `t`

```elixir
@type t() :: %LatticeStripe.Account{
  business_profile: LatticeStripe.Account.BusinessProfile.t() | nil,
  business_type: String.t() | nil,
  capabilities:
    %{optional(String.t()) =&gt; LatticeStripe.Account.Capability.t()} | nil,
  charges_enabled: boolean() | nil,
  company: LatticeStripe.Account.Company.t() | nil,
  controller: map() | nil,
  country: String.t() | nil,
  created: integer() | nil,
  default_currency: String.t() | nil,
  details_submitted: boolean() | nil,
  email: String.t() | nil,
  external_accounts: map() | nil,
  extra: map(),
  future_requirements: LatticeStripe.Account.Requirements.t() | nil,
  id: String.t() | nil,
  individual: LatticeStripe.Account.Individual.t() | nil,
  livemode: boolean() | nil,
  metadata: map() | nil,
  object: String.t(),
  payouts_enabled: boolean() | nil,
  requirements: LatticeStripe.Account.Requirements.t() | nil,
  settings: LatticeStripe.Account.Settings.t() | nil,
  tos_acceptance: LatticeStripe.Account.TosAcceptance.t() | nil,
  type: String.t() | nil
}
```

A Stripe Connect Account object.

# `create`

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

Creates a new Connect Account.

Sends `POST /v1/accounts`.

## Parameters

- `client` - `%LatticeStripe.Client{}`
- `params` - Map of account attributes. Common keys:
  - `"type"` - Account type: `"custom"`, `"express"`, or `"standard"`
  - `"country"` - Two-letter country code (e.g., `"US"`)
  - `"email"` - Account email address
- `opts` - Per-request overrides (e.g., `[idempotency_key: "..."]`)

## Returns

- `{:ok, %Account{}}` on success
- `{:error, %LatticeStripe.Error{}}` on failure

# `create!`

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

Like `create/3` but raises on failure.

# `delete`

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

Deletes a Connect Account.

Sends `DELETE /v1/accounts/:id`. Stripe returns a deletion stub
`%{"id" => ..., "object" => "account", "deleted" => true}`. The returned
`%Account{}` will have `extra: %{"deleted" => true}`.

> #### Irreversible {: .warning}
>
> Deletion cannot be undone. Only custom and express accounts may be deleted;
> standard accounts must be rejected via `reject/4` instead.

# `delete!`

```elixir
@spec delete!(LatticeStripe.Client.t(), String.t(), keyword()) :: t()
```

Like `delete/3` but raises on failure.

# `list`

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

Lists Connect Accounts with optional filters.

Sends `GET /v1/accounts`.

# `list!`

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

Like `list/3` but raises on failure.

# `reject`

```elixir
@spec reject(
  LatticeStripe.Client.t(),
  String.t(),
  :fraud | :terms_of_service | :other,
  keyword()
) ::
  {:ok, t()} | {:error, LatticeStripe.Error.t()}
```

Rejects a Connect account.

Dispatches to `POST /v1/accounts/:id/reject` with the atom reason converted to
its Stripe string form. The `reason` MUST be one of `:fraud`,
`:terms_of_service`, or `:other` — any other atom raises `FunctionClauseError`
at the call site.

## Example

    Account.reject(client, "acct_123", :fraud)

## Irreversibility

Rejection is one-way. Once rejected, the connected account cannot be
re-activated. This is why `reject/4` guards the reason at the function head —
a typo like `:fruad` fails loudly at compile-time (for literal atoms) or
runtime, rather than silently sending an invalid payload to Stripe.

# `reject!`

```elixir
@spec reject!(
  LatticeStripe.Client.t(),
  String.t(),
  :fraud | :terms_of_service | :other,
  keyword()
) ::
  t()
```

Like `reject/4` but raises on failure.

# `retrieve`

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

Retrieves a Connect Account by ID.

Sends `GET /v1/accounts/:id`.

# `retrieve!`

```elixir
@spec retrieve!(LatticeStripe.Client.t(), String.t(), keyword()) :: t()
```

Like `retrieve/3` but raises on failure.

# `stream!`

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

Returns a lazy stream of all Connect Accounts matching the given params.

Auto-paginates via `LatticeStripe.List.stream!/2`. Raises on fetch failure.

# `update`

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

Updates a Connect Account by ID.

Sends `POST /v1/accounts/:id`.

To request capabilities, use the nested map idiom — do not look for a
`request_capability/4` helper, which does not exist (D-04b). See the
"Requesting capabilities" section in the module doc.

# `update!`

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

Like `update/4` but raises on failure.

---

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