# Notifications
`GhEx.Notifications` wraps the GitHub Notifications REST API for consuming the
authenticated user's inbox: list threads, mark them read or done, and read
GitHub's suggested poll interval. It is a set of thin `GhEx.REST` wrappers, so it
returns the usual `{:ok, body, meta}` / `{:error, reason}` shape and passes
`opts` through to `Req`.
```elixir
{:ok, threads, _meta} = GhEx.Notifications.list(client, params: [participating: true])
for t <- threads do
IO.puts("#{t["reason"]}: #{t["subject"]["title"]}")
end
```
A thread is a pointer, not a payload. It tells you *that* something happened
(`reason`, `subject.type`, `subject.url`) but not the details. To act on it,
fetch the subject:
```elixir
{:ok, pr, _} = GhEx.REST.get(client, thread["subject"]["url"])
```
## Polling politely
The inbox has no webhook, so consuming it means polling. GitHub asks you to do
three things, and gh_ex gives you each piece:
1. **Poll conditionally.** Send the previous response's validator so an unchanged
inbox is cheap and does not count against your rate limit. A `304` comes back
as `{:ok, :not_modified, meta}` (distinct from a normal `{:ok, body, meta}`).
2. **Respect `X-Poll-Interval`.** `poll_interval/2` reads the header, the seconds
GitHub wants you to wait before the next poll.
3. **Back off on the rate limit.** `GhEx.RateLimit.delay_until_reset/2` is the
backstop when the bucket runs low.
gh_ex supplies these pieces; the loop is yours. A minimal poller:
```elixir
defmodule Inbox do
def poll(client, last_modified \\ nil) do
headers = if last_modified, do: [{"if-modified-since", last_modified}], else: []
case GhEx.Notifications.list(client, headers: headers, params: [participating: true]) do
{:ok, :not_modified, meta} ->
wait_and_repeat(client, last_modified, meta)
{:ok, threads, meta} ->
Enum.each(threads, &handle/1)
wait_and_repeat(client, meta.last_modified, meta)
end
end
defp wait_and_repeat(client, last_modified, meta) do
Process.sleep(GhEx.Notifications.poll_interval(meta) * 1000)
poll(client, last_modified)
end
defp handle(thread), do: IO.inspect(thread["subject"]["title"])
end
```
In a job system the same shape is non-blocking: return `{:snooze,
poll_interval(meta)}` instead of sleeping, and store `meta.last_modified` (or
`meta.etag`) on the job for the next run.
## What is not here
`GhEx.Notifications` is a client, not a consumer engine: no poll loop, dedup, or
state ship with it.
Thread subscriptions are supported: `get_thread_subscription/3`,
`set_thread_subscription/4` (`%{ignored: true}` to mute, `%{ignored: false}` to
subscribe), and `delete_thread_subscription/3`. Repository watch subscriptions,
which govern what enters the inbox at the repo level, belong to the Watching API
and are tracked separately.