# `Tayori.Inbox`
[🔗](https://github.com/hattoriai/tayori/blob/v0.1.0/lib/tayori/inbox.ex#L1)

The changes that shipped, before they become entries: what the app's
sources bring (`Tayori.Source`), what its CI, GitHub and its own code
add, what an agent adds over MCP. The admin reads them in the inbox,
puts aside what readers need not know, and a digest makes an entry of
the rest (`Tayori.Digest`).

A change is the source's and its ref's: adding it again changes
nothing, so a source can give all it has each time it is read.

    Tayori.Inbox.add(%{ref: "pr-1234", title: "Exports in two clicks", kind: "added"},
      source: "app"
    )

A change that does not say its audience takes the `:inbox` config's
(`"public"`); one that does not say its category takes its kind's
(the config's `:kinds`, or the category whose key is the kind).

# `add`

```elixir
@spec add(map(), keyword()) :: {:ok, Tayori.Change.t()} | {:error, Ecto.Changeset.t()}
```

Adds a change, or finds the one with the same source and ref. Options:
`:source` (`"app"`). Returns `{:ok, change}`, or `{:error, changeset}`.

# `add_many`

```elixir
@spec add_many([map()], keyword()) :: %{
  added: non_neg_integer(),
  existing: non_neg_integer(),
  errors: [{non_neg_integer(), Ecto.Changeset.t()}]
}
```

Adds changes, each as `add/2` would. Returns how many were new, how
many were there already, and the errors of those that could not be
added, `{index, changeset}`.

# `counts`

```elixir
@spec counts() :: %{required(String.t()) =&gt; non_neg_integer()}
```

How many changes are in each state.

# `get`

```elixir
@spec get(Ecto.UUID.t()) :: Tayori.Change.t() | nil
```

A change by its id, or nil.

# `hide`

```elixir
@spec hide(Tayori.Change.t()) :: {:ok, Tayori.Change.t()}
```

Puts a change aside: no digest takes it.

# `ingest`

```elixir
@spec ingest() :: %{required(String.t()) =&gt; {:ok, map()} | {:error, term()}}
```

Reads every source of the `:inbox` config, and adds their changes.
Returns each source's result, by its name: `{:ok, counts}` (as
`add_many/2`), or `{:error, reason}`, logged. With the `:digest`
config's `every: :ingest`, new changes start a digest
(`Tayori.Digest.after_ingest/1`).

# `list`

```elixir
@spec list(keyword()) :: [Tayori.Change.t()]
```

The changes, the newest first. Options: `:state` (`"new"`, `"drafted"`,
`"hidden"`), `:source`, `:audience`, `:entry_id`, `:ids`, `:search`
(words of the title), `:limit` (100).

# `pending`

```elixir
@spec pending(keyword()) :: [Tayori.Change.t()]
```

The changes waiting for a digest: new, and for the public. Options:
`:ids` (only these), `:since` and `:until` (when they happened).

# `pending_count`

```elixir
@spec pending_count() :: non_neg_integer()
```

How many changes are waiting for a digest: new, and for the public.

# `restore`

```elixir
@spec restore(Tayori.Change.t()) :: {:ok, Tayori.Change.t()}
```

Brings back a change put aside, or one drafted whose entry is gone.

# `update`

```elixir
@spec update(Tayori.Change.t(), map()) ::
  {:ok, Tayori.Change.t()} | {:error, Ecto.Changeset.t()}
```

Changes what a change says: its title, body, category, audience, segments, highlight.

---

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