# `Jido.Messaging.Persistence`
[🔗](https://github.com/agentjido/jido_messaging/blob/v1.0.0/lib/jido_messaging/persistence.ex#L1)

Behaviour for Jido.Messaging storage adapters.

Adapters provide persistence for rooms, participants, threads, and messages.
Each adapter instance maintains its own state (e.g., ETS table references)
to enable multiple isolated messaging instances in the same BEAM.

## Implementing an Adapter

    defmodule MyApp.CustomAdapter do
      @behaviour Jido.Messaging.Persistence

      @impl true
      def init(opts) do
        # Initialize adapter state
        {:ok, %{}}
      end

      # ... implement other callbacks
    end

# `bridge_id`

```elixir
@type bridge_id() :: String.t()
```

# `channel`

```elixir
@type channel() :: atom()
```

# `directory_query`

```elixir
@type directory_query() :: map()
```

# `directory_target`

```elixir
@type directory_target() :: :participant | :room
```

# `external_id`

```elixir
@type external_id() :: String.t()
```

# `message_id`

```elixir
@type message_id() :: String.t()
```

# `onboarding_flow`

```elixir
@type onboarding_flow() :: map()
```

# `onboarding_id`

```elixir
@type onboarding_id() :: String.t()
```

# `participant_id`

```elixir
@type participant_id() :: String.t()
```

# `room_id`

```elixir
@type room_id() :: String.t()
```

# `state`

```elixir
@type state() :: term()
```

# `create_room_binding`

```elixir
@callback create_room_binding(
  state(),
  room_id(),
  channel(),
  bridge_id(),
  external_id(),
  attrs :: map()
) :: {:ok, Jido.Messaging.RoomBinding.t()} | {:error, term()}
```

Create a binding between an internal room and an external platform room.

# `delete_bridge_config`

```elixir
@callback delete_bridge_config(state(), String.t()) :: :ok | {:error, :not_found}
```

Delete bridge config.

# `delete_ingress_subscription`
*optional* 

```elixir
@callback delete_ingress_subscription(state(), bridge_id(), String.t()) ::
  :ok | {:error, :not_found}
```

Delete stored ingress subscription metadata.

# `delete_message`

```elixir
@callback delete_message(state(), message_id()) :: :ok | {:error, term()}
```

Delete a message by ID

# `delete_participant`

```elixir
@callback delete_participant(state(), participant_id()) :: :ok | {:error, term()}
```

Delete a participant by ID

# `delete_room`

```elixir
@callback delete_room(state(), room_id()) :: :ok | {:error, term()}
```

Delete a room by ID

# `delete_room_binding`

```elixir
@callback delete_room_binding(state(), binding_id :: String.t()) :: :ok | {:error, term()}
```

Delete a room binding by ID.

# `delete_routing_policy`

```elixir
@callback delete_routing_policy(state(), String.t()) :: :ok | {:error, :not_found}
```

Delete routing policy by room id.

# `directory_lookup`

```elixir
@callback directory_lookup(
  state(),
  directory_target(),
  directory_query(),
  opts :: keyword()
) ::
  {:ok, map()} | {:error, :not_found | {:ambiguous, [map()]} | term()}
```

Lookup a single directory entry by target and query.

Returns `{:error, {:ambiguous, matches}}` when multiple entries satisfy the query.

# `directory_search`

```elixir
@callback directory_search(
  state(),
  directory_target(),
  directory_query(),
  opts :: keyword()
) ::
  {:ok, [map()]} | {:error, term()}
```

Search directory entries by target and query.

# `get_bridge_config`

```elixir
@callback get_bridge_config(state(), String.t()) ::
  {:ok, Jido.Messaging.BridgeConfig.t()} | {:error, :not_found}
```

Fetch bridge config by id.

# `get_message`

```elixir
@callback get_message(state(), message_id()) ::
  {:ok, Jido.Messaging.Message.t()} | {:error, :not_found}
```

Get a message by ID

# `get_message_by_external_id`

```elixir
@callback get_message_by_external_id(state(), channel(), bridge_id(), external_id()) ::
  {:ok, Jido.Messaging.Message.t()} | {:error, :not_found}
```

Get a message by its external ID within a channel/instance context.

Used for resolving reply_to references from external platforms.

# `get_messages`

```elixir
@callback get_messages(state(), room_id(), opts :: keyword()) ::
  {:ok, [Jido.Messaging.Message.t()]}
```

Get messages for a room with options (limit, before, after)

# `get_onboarding`

```elixir
@callback get_onboarding(state(), onboarding_id()) ::
  {:ok, onboarding_flow()} | {:error, :not_found}
```

Fetch onboarding flow state by onboarding ID.

# `get_or_create_participant_by_external_id`

```elixir
@callback get_or_create_participant_by_external_id(
  state(),
  channel(),
  external_id(),
  attrs :: map()
) :: {:ok, Jido.Chat.Participant.t()}
```

Get or create a participant by external ID.

Used when receiving messages from external channels to map external
user IDs to internal participant IDs.

# `get_or_create_room_by_external_binding`

```elixir
@callback get_or_create_room_by_external_binding(
  state(),
  channel(),
  bridge_id(),
  external_id(),
  attrs :: map()
) :: {:ok, Jido.Chat.Room.t()}
```

Get or create a room by external binding.

Used when receiving messages from external channels to map external
chat IDs to internal room IDs.

# `get_participant`

```elixir
@callback get_participant(state(), participant_id()) ::
  {:ok, Jido.Chat.Participant.t()} | {:error, :not_found}
```

Get a participant by ID

# `get_room`

```elixir
@callback get_room(state(), room_id()) :: {:ok, Jido.Chat.Room.t()} | {:error, :not_found}
```

Get a room by ID

# `get_room_by_external_binding`

```elixir
@callback get_room_by_external_binding(state(), channel(), bridge_id(), external_id()) ::
  {:ok, Jido.Chat.Room.t()} | {:error, :not_found}
```

Get a room by its external binding.

Returns the room if a binding exists, otherwise :not_found.

# `get_routing_policy`

```elixir
@callback get_routing_policy(state(), String.t()) ::
  {:ok, Jido.Messaging.RoutingPolicy.t()} | {:error, :not_found}
```

Fetch routing policy by room id.

# `get_thread`

```elixir
@callback get_thread(state(), String.t()) ::
  {:ok, Jido.Messaging.Thread.t()} | {:error, :not_found}
```

Get a thread by ID

# `get_thread_by_external_id`

```elixir
@callback get_thread_by_external_id(state(), room_id(), String.t()) ::
  {:ok, Jido.Messaging.Thread.t()} | {:error, :not_found}
```

Get a thread by room and external thread ID

# `get_thread_by_root_message`

```elixir
@callback get_thread_by_root_message(state(), room_id(), message_id()) ::
  {:ok, Jido.Messaging.Thread.t()} | {:error, :not_found}
```

Get a thread by root message ID

# `init`

```elixir
@callback init(opts :: keyword()) :: {:ok, state()} | {:error, term()}
```

Initialize the adapter with options. Returns adapter state.

# `list_bridge_configs`

```elixir
@callback list_bridge_configs(
  state(),
  keyword()
) :: {:ok, [Jido.Messaging.BridgeConfig.t()]}
```

List bridge configs with optional filters.

# `list_ingress_subscriptions`
*optional* 

```elixir
@callback list_ingress_subscriptions(state(), bridge_id(), keyword()) ::
  {:ok, [Jido.Messaging.IngressSubscription.t()]}
```

List stored ingress subscription metadata for a bridge.

# `list_room_bindings`

```elixir
@callback list_room_bindings(state(), room_id()) ::
  {:ok, [Jido.Messaging.RoomBinding.t()]}
```

List all bindings for a room.

# `list_rooms`

```elixir
@callback list_rooms(state(), opts :: keyword()) :: {:ok, [Jido.Chat.Room.t()]}
```

List rooms with optional filters

# `list_threads`

```elixir
@callback list_threads(state(), room_id(), opts :: keyword()) ::
  {:ok, [Jido.Messaging.Thread.t()]}
```

List threads for a room

# `save_bridge_config`

```elixir
@callback save_bridge_config(state(), Jido.Messaging.BridgeConfig.t()) ::
  {:ok, Jido.Messaging.BridgeConfig.t()} | {:error, term()}
```

Persist bridge config.

# `save_ingress_subscription`
*optional* 

```elixir
@callback save_ingress_subscription(state(), Jido.Messaging.IngressSubscription.t()) ::
  {:ok, Jido.Messaging.IngressSubscription.t()} | {:error, term()}
```

Persist normalized ingress subscription metadata.

# `save_message`

```elixir
@callback save_message(state(), Jido.Messaging.Message.t()) ::
  {:ok, Jido.Messaging.Message.t()} | {:error, term()}
```

Save a message

# `save_onboarding`

```elixir
@callback save_onboarding(state(), onboarding_flow()) ::
  {:ok, onboarding_flow()} | {:error, term()}
```

Persist onboarding flow state.

# `save_participant`

```elixir
@callback save_participant(state(), Jido.Chat.Participant.t()) ::
  {:ok, Jido.Chat.Participant.t()} | {:error, term()}
```

Save a participant (insert or update)

# `save_room`

```elixir
@callback save_room(state(), Jido.Chat.Room.t()) ::
  {:ok, Jido.Chat.Room.t()} | {:error, term()}
```

Save a room (insert or update)

# `save_routing_policy`

```elixir
@callback save_routing_policy(state(), Jido.Messaging.RoutingPolicy.t()) ::
  {:ok, Jido.Messaging.RoutingPolicy.t()} | {:error, term()}
```

Persist routing policy.

# `save_thread`

```elixir
@callback save_thread(state(), Jido.Messaging.Thread.t()) ::
  {:ok, Jido.Messaging.Thread.t()} | {:error, term()}
```

Save a thread

# `update_message_external_id`

```elixir
@callback update_message_external_id(state(), message_id(), external_id()) ::
  {:ok, Jido.Messaging.Message.t()} | {:error, term()}
```

Update a message's external_id after successful channel delivery.

Used to record the external platform's message ID after sending.

---

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