> ## Documentation Index
> Fetch the complete documentation index at: https://docs.memory.anonalabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Slack

> Ask a question in a Slack channel and get an answer from that channel's memory space.

Anona's Slack app answers questions in the channels you choose, from the memory
space you point each channel at, and can **save a thread into that space** when
somebody asks it to.

**It reads and writes, and each channel chooses which.** A channel is bound
**Read only** by default; set it to **Read & write** and
[`@Anona save this conversation`](#saving-a-thread-to-memory) works there for
members who can already write to the space. Everything else the bot does is a
read.

Access follows your Anona space membership, not the channel. Binding a channel
invites the people in it to that space, and the bot answers each person as
themselves once they accept. Somebody who has not accepted is not answered, and
nothing in the space is shown to them.

## Install

1. Open **Integrations** in the dashboard.
2. On the **Slack** row, choose **Connect**.
3. Approve the install in your workspace.

The app asks for nine scopes and no more: `app_mentions:read` so it can see
questions addressed to it, `chat:write` so it can answer, `commands` for the
`/anona` command, `channels:history` plus `groups:history` so it can read the
thread a question was asked in, `channels:read` plus `groups:read` for the
channel's member list, `users:read` to tell people from bots, and `im:write` to
send you an invitation by direct message.

It does not ask for `users:read.email`, and that is deliberate. Matching a Slack
user to the Anona account with the same address would be the obvious shortcut,
and it is a way in: a workspace admin can change somebody's email address. Your
identity is proved by a private message plus a signed-in Anona session instead,
and neither half is derived from the other.

Installing does not make anything readable. Until you bind a channel, the bot is
silent everywhere.

## Connect your Anona account

Required, and the first thing to do. Run this in Slack:

```
/anona link
```

Open the private link it replies with **while signed in to Anona**, and the
Slack identity that ran the command is connected to the account you are signed
in as. The page says so, naming the Slack user it connected.

The bot answers **as you**, against the spaces you can reach and your
organization's credits. So until Slack knows which Anona account is yours, it
cannot bind a channel on your behalf, cannot answer your questions, and cannot
attribute a saved thread. Each of those refuses with an explanation rather than
failing quietly, but the fix is always this step.

Running `/anona link` again from a different Anona account moves the connection.

Linking does not filter what a channel returns. A channel answers from the space
it is bound to, for everyone in it who has accepted. Filtering per person would
mean posting one person's private memories into a shared channel, which is a
leak rather than a feature.

## Bind a channel

Run this in the channel you want to use:

```
/anona bind
```

Slack replies with one link, visible only to you. If you have not connected your
account yet it carries both steps at once: opening it connects the account and
then fills the bind form with this channel. Open it **while signed in to Anona**,
or the channel details are lost on the way through sign-in and you will be
retyping the channel id.

The Slack row opens on arrival with the channel already filled. Choose:

**Space** `string` *required*

Which memory space this channel reads from. Everyone in the channel can see
anything the bot answers from it.

**API key** `string` *required*

The key the bot presents. Revoking it stops the bot in this channel. Only live
keys are offered: a revoked or expired one is refused by the API, so the
picker leaves it out and tells you when there is nothing to choose.

**Model** `string`

Which model answers here. Leave it on **Inherit from the space** to follow the
space's own default.

Binding the same channel again edits it rather than creating a second one.

## Change or review a channel's settings

On the **Slack** row in Integrations, click any bound channel. Its settings open
inside that channel's own row: space, API key, what the channel may do, and
model. Change what you need and choose **Save changes**, or **Cancel** to leave
it as it was. Clicking the row again closes it, and only one channel is open at
a time.

**What the channel may do** is the channel's ceiling, not a grant. **Read only**
answers questions. **Read & write** also allows saving a thread from that
channel, and only for people who can write to the bound space themselves. A
channel left on Read only refuses a save and says so.

The channel ID is fixed while editing. A channel's ID is what identifies the
binding, so changing it would not move the binding, it would create a second one
against a different channel.

To stop the bot in a channel entirely, use the delete button on its row.

## Ask

Mention the bot in a bound channel:

```
@Anona what did we decide about the pricing change?
```

The bot posts **Thinking...** in the thread as soon as it starts, and that
message becomes the answer when it lands. An answer is a multi step reasoning
loop and takes tens of seconds, so the marker is how you tell a bot that is
working from one that is down.

The answer arrives in a thread on your message. It is the same synthesis
`POST /v1/reason` returns, so it costs credits from **the organization of the
person who asked**, at that model's rate — see [Costs](#costs).

Answers arrive formatted for Slack, so bold, lists, links and code blocks read
the way they should rather than showing their markup.

### Asking inside a thread

Mention the bot part-way through a thread and it reads the conversation before
answering, so "so what did we land on?" works without restating the question.

The thread is used only to understand what is being asked. The answer still
comes from the memories in the bound space, and nothing in the thread is
recorded. Asking never writes. Saving a thread is a separate and explicit
action, described further down.

A very long thread is trimmed to its most recent messages, and the bot says when
that happened rather than answering from half a conversation as though it were
the whole one.

One question runs at a time per channel. Ask a second while the first is still
working and the bot says so rather than queueing it.

## Permissions

Your Anona space membership is the permission boundary.

* The bot answers only in channels someone deliberately bound.
* It answers you as **you**. Your question runs against your own access to that
  space, and it costs your organization's credits.
* Somebody who has not accepted their invitation is not answered at all.
* **Answers are public only when everybody in the channel has accepted.** If
  even one person has not, the answer is shown only to whoever asked, and a copy
  is sent to them by direct message. The channel is left with "Replied to
  @someone in a DM", so the room can tell a private answer from a bot that is
  down. It says only that, never why: whether the reply was an answer or a
  refusal is about one person. Once everybody accepts, answers go back to being
  posted for the channel.
* Only an owner or an admin can connect or disconnect a Slack workspace.
* You can only bind a channel you are in, and you can only bind a space for
  writing if you can write to it yourself.

### Accepting an invitation

When a channel is bound, everybody in it gets a direct message from the bot with
a link. Opening it signs you in to Anona and connects your Slack identity in one
step. That is the whole of the setup for a person.

Those invitations go out only when the person who bound the channel is an
**owner or an admin of the organization that owns the space**. Handing a space to
a roomful of people is giving access away, so it takes the same authority as
inviting somebody through the dashboard. A bind by anybody else still works and
the channel still answers them, but nobody is invited — if a channel went quiet
and no DMs arrived, that is the first thing to check. An owner or admin can send
them with **Resend**.

If you ignore it, nothing breaks. The channel keeps working for everyone who has
accepted, and your colleagues' answers simply stay private to them until you do.

### Checking where you stand

Run this anywhere:

```
/anona whoami
```

It tells you which Anona account this Slack identity is connected to, which
space the channel reads, and whether answers here are public or private right
now, naming who has not accepted yet.

It shows the email address on purpose. Connecting the wrong account is otherwise
silent: everything keeps working, against the wrong spaces and the wrong credit
balance, and nothing would ever tell you.

## When it cannot answer

The bot always replies, even when the answer is that it cannot:

| What you see | What happened |
| - | - |
| out of credits | The organization's credit balance is spent. |
| the API key was revoked | Click the channel on the Integrations page and pick a new key. |
| that space no longer exists | The bound space was deleted. Re-bind the channel. |
| nothing on that yet | The space has no memories matching the question. |
| connect your Anona account | Your Slack identity is not linked yet. Open the invitation the bot sent you, or run `/anona link`. |
| you have not been given access | You are connected, but not a member of the space this channel reads. |

## Costs

**One rule, for everything the bot does: the person who asked pays.** An answer
is a `reason` call and a save is a `record`, and both are billed to the Anona
organization of the Slack user who triggered them, at the rate of whichever
model ran. For an ordinary team that is the organization that owns the
workspace, because everybody is in it. Somebody using a personal Anona account
spends their own credits.

This is the same rule the API follows on a shared space: the space's owner
supplies the tenancy, and the caller supplies the credits. The organization that
owns the workspace is never charged for somebody else's question on the strength
of owning the install.

Leaving the model on **Inherit from the space** means a channel follows the
space's own default, so you set the model once rather than per channel.

## Disconnect

**Disconnect** on the Slack row revokes the workspace and removes its bindings.
Your spaces and memories are untouched.

To stop the bot in one channel only, unbind that channel instead.

## Seeing why a channel is answering privately

A channel answering privately says so in the channel, so it no longer reads as
a broken bot. What it does not say is why, and the reason lives on the
Integrations page.

Each bound channel shows:

* **Public** or **Private**, and the reason. Private reads "answers are visible
  only to whoever asked, because 2 of 9 have not accepted their invitation",
  naming them.
* **Resend invitations**, which sends the direct message again to everybody
  outstanding, whatever state their invitation is in. Ignoring it, letting it
  expire, or declining it are all recoverable without rebinding the channel.
  Nobody who has already accepted is messaged, because they have nothing left to
  accept. Owner or admin only, because it sends a direct message to real people,
  and on a large channel that is a lot of them at once.
* A warning when somebody in the channel uses an Anona account outside your
  organization. They can read the space once they accept, but their questions
  spend their own credits. Invite them to the organization instead if that is
  not what you want.

`/anona whoami` reports the same thing from inside Slack.

## Seeing it from the space instead

The Integrations page answers "what is this channel doing". The space answers
"who was asked, and who got in", which is the same question from the other end.

A space's **Members** tab lists everyone a bound Slack channel offered it to who
has not accepted, under **Invited from Slack**. Each row names the person, the
channel they were asked in, the role they were offered, and where the offer got
to:

| Status | What it means |
| - | - |
| Invited | The direct message went out and they have not answered. |
| Declined | They said no. The offer is recorded rather than deleted, so refusal and silence stay distinguishable. |
| Expired | Fourteen days passed without an answer. |

Somebody who accepts leaves this list and appears in the member table above it
as an ordinary member, so nobody is counted twice.

There is no Cancel button on these rows, and that is deliberate rather than
missing. A channel re-offers to everyone standing in it on its next roster sync,
so cancelling one offer would only make it come back. To stop one, remove the
person from the Slack channel or re-point the binding.

The block is owner or admin only, like the member directory it sits under, and
it is absent entirely for a space no Slack channel points at.

> [!NOTE]
>
> Anona stores a Slack display name, never a Slack email address. The bot asks for
> `users:read` and deliberately not `users:read.email`, because a workspace admin
> can change a member's address, and an identity resting on one is an identity an
> admin can hand themselves. A roster synced before names were stored shows the
> raw Slack id instead; asking one question in the channel refreshes it.

## The endpoints behind the page

Everything the Integrations page does is also callable with a **dashboard
session token** (never an API key: `401 session_required`):

| Method and path | What it does |
| - | - |
| `GET /v1/slack/status` | The workspace connection and every channel binding. |
| `GET /v1/slack/install` | The Slack consent URL to send the browser to. Returns JSON rather than redirecting, because a browser navigation cannot carry your session header. Owner or admin. |
| `POST /v1/slack/bindings` | Binds a channel to a space (what the `/anona bind` link submits). |
| `POST /v1/slack/invites/accept` | Accepts a space invitation sent over Slack. |
| `POST /v1/slack/bindings/{binding_id}/resend` | Re-invites everyone in a channel who has not accepted. Owner or admin. |
| `POST /v1/slack/links` | Connects a Slack identity to your account, from the `/anona link` deep link. |
| `DELETE /v1/slack/links/{link_id}` | Disconnects one Slack identity. |
| `DELETE /v1/slack/bindings/{binding_id}` | Unbinds one channel. |
| `DELETE /v1/slack/installations/{installation_id}` | Disconnects the workspace and removes its bindings. |
| `GET /v1/spaces/{space_id}/slack-invites` | Everyone a bound channel offered this space to who has not accepted, with their state. Owner or admin. |

`/v1/slack/events`, `/v1/slack/commands`, `/v1/slack/interactions` and
`/v1/slack/oauth/callback` are Slack's to call, not yours.

## Saving a thread to memory

Two ways, both saving the whole thread rather than one message: a decision
lives in the back and forth, and keeping only the conclusion throws away the
reason for it.

**Ask the bot.** In the thread you want to keep:

```
@Anona save this conversation
```

Also understood: `save this`, `record this chat`, `remember the above`,
`store this discussion`, and similar. The instruction has to start with the
verb, so a question like *"did we ever record that decision?"* is still
answered as a question rather than quietly saving something.

**Or use the menu.** Open the `...` menu on any message and choose
**Save to Anona**.

Four things have to be true, and a save that fails says which:

1. The channel is set to **Read & write** on the Integrations page. This setting
   is a **ceiling rather than a grant**, so a space you can write to can still
   be bound read only into a wide channel.
2. **You** have write access to the space yourself — a Read Only member of the
   space cannot save, however the channel is set. The channel setting can only
   narrow your own access, never widen it.
3. Your Slack identity is linked to your Anona account. If it is not, the bot
   tells you to run `/anona link`.
4. The workspace has not spent its hourly save budget (see below).

The default is read only, because saving changes what the space recalls:
consolidation synthesizes across the saved conversation and whatever your agents
have written, together.

Saving the same thread twice does nothing the second time. The bot says so
rather than making a duplicate.

### The hourly cap

Each workspace can save ten threads an hour, and the bot says so when that is
spent.

The cap is real rather than arbitrary. Extraction runs on a queue shared by
every Anona customer, and a thread is many pieces of work in one click, so a
team importing a backlog at once would hold up everyone else's uploads. It will
rise once that queue is per customer rather than shared.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.