# `PushX.Instance`
[🔗](https://github.com/cignosystems/pushx/blob/v0.11.0/lib/push_x/instance.ex#L1)

Runtime management of named push notification instances.

Allows starting, stopping, and reconfiguring APNS and FCM instances
at runtime, enabling multi-provider setups from a database-backed admin panel.

## Usage

    # Start an APNS instance
    PushX.Instance.start(:apns_prod, :apns,
      key_id: "ABC123",
      team_id: "TEAM456",
      private_key: "-----BEGIN EC PRIVATE KEY-----\n...",
      mode: :prod
    )

    # Start an FCM instance
    PushX.Instance.start(:my_fcm, :fcm,
      project_id: "my-project",
      credentials: %{"type" => "service_account", ...}
    )

    # Send via instance
    PushX.push(:apns_prod, token, msg, topic: "com.example.app")

    # Lifecycle management
    PushX.Instance.disable(:apns_prod)
    PushX.Instance.enable(:apns_prod)
    PushX.Instance.reconfigure(:apns_prod, mode: :sandbox)
    PushX.Instance.stop(:apns_prod)

## Credential Rotation Without Restart

Use `reconfigure/2` to hot-swap credentials (e.g., after revoking an APNS
.p8 key or rotating an FCM service account). It stops the old pool and
starts a fresh one with new credentials. In-flight requests on the old pool
get connection errors, which the retry logic handles automatically.

    # Load new key from database/file/env
    new_key = MyApp.Repo.get_latest_apns_key()

    PushX.Instance.reconfigure(:apns_prod,
      key_id: "NEW_KEY_ID",
      private_key: new_key
    )

# `disable`

```elixir
@spec disable(atom()) :: :ok | {:error, :not_found}
```

Disables an instance. New pushes are rejected, but the pool stays warm.

# `enable`

```elixir
@spec enable(atom()) :: :ok | {:error, :not_found}
```

Re-enables a disabled instance.

# `list`

```elixir
@spec list() :: [map()]
```

Lists all running instances.

# `reconfigure`

```elixir
@spec reconfigure(
  atom(),
  keyword()
) :: {:ok, atom()} | {:error, term()}
```

Stops and restarts an instance with updated config.

Merges `new_config` into the existing config. Use this to hot-swap
credentials (e.g., after revoking an APNS .p8 key) without restarting
the application. The old Finch pool is terminated and a new one starts
with fresh connections. In-flight requests on the old pool receive
connection errors, which the retry logic handles automatically.

## Examples

    # Rotate APNS key
    PushX.Instance.reconfigure(:apns_prod,
      key_id: "NEW_KEY_ID",
      private_key: new_pem_string
    )

    # Switch APNS environment
    PushX.Instance.reconfigure(:apns_prod, mode: :sandbox)

# `reconnect`

```elixir
@spec reconnect(atom()) :: :ok | {:error, term()}
```

Restarts the Finch HTTP pool for a named instance.

# `resolve`

```elixir
@spec resolve(atom()) :: {:ok, map()} | {:error, :not_found | :disabled}
```

Resolves an instance name to its info for sending.

Returns `{:error, :disabled}` if the instance exists but is disabled,
`{:error, :not_found}` if it doesn't exist.

# `start`

```elixir
@spec start(atom(), :apns | :fcm, keyword()) :: {:ok, atom()} | {:error, term()}
```

Starts a named instance.

## Arguments

  * `name` - Unique atom name for this instance (e.g., `:apns_prod`)
  * `provider` - `:apns` or `:fcm`
  * `config` - Provider-specific configuration (keyword list)

## APNS Config Keys

  * `:key_id` - (required) Apple Key ID
  * `:team_id` - (required) Apple Team ID
  * `:private_key` - (required) PEM string, `{:file, path}`, or `{:system, "ENV_VAR"}`
  * `:mode` - `:prod` or `:sandbox` (default: `:prod`)
  * `:pool_size` - Finch pool size (default: 2)
  * `:pool_count` - Finch pool count (default: 1)

## FCM Config Keys

  * `:project_id` - (required) Firebase project ID
  * `:credentials` - (required) Service account credentials map or JSON string
  * `:pool_size` - Finch pool size (default: 2)
  * `:pool_count` - Finch pool count (default: 1)

## Returns

  * `{:ok, name}` on success
  * `{:error, :reserved_name}` if name is `:apns` or `:fcm`
  * `{:error, :already_started}` if instance already exists
  * `{:error, {:missing_config, keys}}` if required config is missing

# `status`

```elixir
@spec status(atom()) :: {:ok, map()} | {:error, :not_found}
```

Returns the status of a named instance.

# `stop`

```elixir
@spec stop(atom()) :: :ok | {:error, :not_found}
```

Stops a named instance and cleans up all resources.

---

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