# Slack — Workspace Token Cache

Deixa o token e o usuário do bot de cada workspace do Slack prontos para outros workflows, atualizados do Tooling a cada 5 min.

| Platform | Workflow ID | Status | Version | Last saved | Nodes | Systems |
| --- | --- | --- | --- | --- | --- | --- |
| n8n | aqaoUgxhGVLTnKUR | Active | 565b640c-345d-4880-b4c7-4622c45b3d95 | 2026-11-06T19:41:23.849Z | 18 | Tooling and Access |

O Slack — Workspace Token Cache deixa o bot do Slack de cada workspace pronto para os workflows que postam no Slack. Ele lê as contas de bot do Tooling and Access, guarda uma linha por workspace do Slack numa Data Table do n8n por 15 minutos e responde aos outros workflows com o token e o usuário do bot do workspace pedido. Ele atualiza todos os workspaces a cada cinco minutos, então quem chama normalmente recebe a resposta sem chegar ao Airtable.

Ele não grava em nenhum sistema de negócio. A única saída dele é a Data Table `slack_workspace_token_cache`, que é armazenamento dentro do n8n, e a resposta que ele devolve ao workflow que o chamou. O maintainer do Tooling and Access cuida das contas de bot que ele lê.

## Fluxo

Gerado da versão do n8n do quadro acima: cada caixa é um nó e cada seta é uma ligação no n8n. Pílulas rosa são gatilhos, hexágonos amarelos são nós If e Switch com as saídas nas setas, cilindros azuis leem (Airtable, Data Table, HTTP GET), cilindros verdes gravam, caixas roxas chamam outro workflow e setas tracejadas são saídas de erro.

```mermaid
flowchart TD
  subgraph g0["Gatilhos"]
    n2(["Every 5 Minutes"]):::trigger
    n11(["Refresh Manually"]):::trigger
    n9["Refresh All Workspaces"]:::logic
    n16(["Token Request"]):::trigger
    n4["Normalize Request"]:::logic
    n10{{"Refresh All?"}}:::decision
  end
  subgraph g1["Responder do cache"]
    n8[("Read Workspace Cache<br/><small>get n8n Data Table slack_workspace_token_cache</small>")]:::read
    n1["Evaluate Cache"]:::logic
    n12{{"Refresh Needed?"}}:::decision
    n14["Return Cached Result"]:::logic
  end
  subgraph g2["Atualizar do Airtable"]
    n7[("Read Cache for Refresh<br/><small>get n8n Data Table slack_workspace_token_cache</small>")]:::read
    n6[("Read Bot Accounts<br/><small>GET Tooling Tool Account</small>")]:::read
    n0["Build Cache Rows"]:::logic
    n3{{"Has Cache Rows?"}}:::decision
    n5[("Persist Workspace Tokens<br/><small>upsert n8n Data Table slack_workspace_token_cache</small>")]:::write
    n15["Return Refreshed Result"]:::logic
  end
  subgraph g3["Erros"]
    n13["Return Cache Error"]:::logic
  end
  n2 --> n9
  n9 --> n4
  n11 --> n9
  n16 --> n4
  n4 --> n10
  n4 -.->|erro| n13
  n8 --> n1
  n8 -.->|erro| n13
  n1 --> n12
  n1 -.->|erro| n13
  n7 --> n6
  n7 -.->|erro| n13
  n6 --> n0
  n6 -.->|erro| n13
  n0 --> n3
  n0 -.->|erro| n13
  n5 --> n15
  n5 -.->|erro| n13
  n10 -->|sim| n7
  n10 -->|não| n8
  n12 -->|sim| n7
  n12 -->|não| n14
  n3 -->|sim| n5
  n3 -->|não| n15
  n15 -.->|erro| n13
  classDef trigger fill:#fde7f3,stroke:#d6409f,color:#111
  classDef decision fill:#fff7e0,stroke:#c99a00,color:#111
  classDef read fill:#e8f1ff,stroke:#3b82f6,color:#111
  classDef write fill:#e7f8ee,stroke:#16a34a,color:#111
  classDef subflow fill:#f3e8ff,stroke:#9333ea,color:#111
  classDef wait fill:#f1f1f1,stroke:#888,color:#111
  classDef logic fill:#ffffff,stroke:#999,color:#111
```

## Sistemas que ele toca

| Sistema | Tabela | Lê | Grava |
| --- | --- | --- | --- |
| Tooling and Access | Tool Account | Read Bot Accounts | Nada |
| n8n (não é sistema de negócio) | Data Table `slack_workspace_token_cache` | Read Workspace Cache, Read Cache for Refresh | Persist Workspace Tokens |

O Read Bot Accounts é um nó HTTP Request com a credencial de token do Airtable "Airtable PAT (Fully Scoped)". O filtro e os campos que ele lê usam IDs de campo, então renomear Kind, Access Token, External Identifier ou Workspace (External Identifier) no Tooling não o quebra. Veja os registros de Tool Account em [Automações do Tooling and Access](/pt-br/systems/tooling/automations).

Dois workflows o chamam pelo Token Request e esperam a resposta, salvo onde indicado:

| Workflow que chama | Nó que chama | Entradas enviadas |
| --- | --- | --- |
| [Daily Digest](/pt-br/automations/daily-digest) | Get Cached Notification Bot | workspaceId; cacheOnly, forceRefresh e refreshAll falsos |
| [Reading Reply](/pt-br/automations/reading-reply) | Get Cached Bot | workspaceId; cacheOnly verdadeiro quando a rota da requisição do Slack é `open`, falso nos outros casos |
| [Reading Reply](/pt-br/automations/reading-reply) | Refresh Workspace in Background | workspaceId; forceRefresh verdadeiro; não espera a resposta |

## Gatilhos

| Gatilho | Quando | O que roda |
| --- | --- | --- |
| Every 5 Minutes | A cada cinco minutos | Refresh All Workspaces, depois o caminho de atualização para todos os workspaces |
| Refresh Manually | Alguém roda no editor | O mesmo que Every 5 Minutes |
| Token Request | Outro workflow o chama | O caminho escolhido pelas entradas |

O Normalize Request tira espaços do `workspaceId` e só considera `cacheOnly`, `forceRefresh` e `refreshAll` verdadeiros quando quem chama mandou o booleano true. Sem `refreshAll`, o ID do workspace precisa ter cara de ID de time do Slack (`T` seguido de letras maiúsculas e dígitos); senão ele lança INVALID_WORKSPACE e quem chama recebe a resposta de erro.

O Refresh All? manda `refreshAll` para o caminho de atualização e todo o resto para o caminho do cache.

## Responder do cache

O Read Workspace Cache busca as linhas da Data Table cujo `workspace_id` é o workspace pedido. O Evaluate Cache só aceita a linha quando existe exatamente uma, ela tem `access_token`, não tem `cache_error` e o `expires_at` está a mais de cinco segundos.

| Situação | Resposta |
| --- | --- |
| Linha válida e sem `forceRefresh` | `ok` verdadeiro, `workspaceId`, `botSecret`, `botUserId`, `expiresAt`, `source` cache. O Return Cached Result a devolve |
| Duas linhas para o workspace | Erro CACHE_DUPLICATE |
| Nenhuma linha | Erro CACHE_MISS |
| Linha com `cache_error` | Esse código de erro |
| Linha perto de expirar, ou `forceRefresh` | Erro CACHE_EXPIRED |

Num erro, o Refresh Needed? vai para o caminho de atualização, a menos que quem chama tenha mandado `cacheOnly`: aí o Return Cached Result devolve o erro na hora, sem Airtable. A nota de contrato no canvas reserva `cacheOnly` para cliques interativos do Slack, que precisam de resposta rápida.

## Atualizar do Airtable

1. O Read Cache for Refresh lê todas as linhas da Data Table, mesmo quando só um workspace é atualizado.
2. O Read Bot Accounts lê as linhas de Tool Account cujo Kind é bot, 100 por página, seguindo todas as páginas, uma vez por execução, com timeout de 15 segundos por requisição.
3. O Build Cache Rows agrupa as contas de bot por cada valor de Workspace (External Identifier) que tem cara de ID de time do Slack. Para cada workspace a atualizar ele monta uma linha, ou um código de erro:

| Código | Quando |
| --- | --- |
| AMBIGUOUS_BOT | Mais de uma conta de bot para o workspace |
| BOT_NOT_CONFIGURED | Nenhuma conta de bot para o workspace |
| BOT_TOKEN_MISSING | O Access Token não começa com o prefixo de token de bot do Slack |
| BOT_USER_MISSING | O External Identifier não é um ID de usuário do Slack (`U` seguido de letras maiúsculas e dígitos) |

Os workspaces a atualizar são o pedido ou, com `refreshAll`, todos os encontrados em Tool Account mais todos os que já estão na Data Table. Um workspace que só está na Data Table recebe BOT_NOT_CONFIGURED, o que apaga o token dele.

4. O Has Cache Rows? pula a gravação quando não há nada a gravar (só acontece com `refreshAll` e nenhum workspace em lugar nenhum).
5. O Persist Workspace Tokens faz upsert de cada linha por `workspace_id`.
6. O Return Refreshed Result responde a quem chamou. Depois de `refreshAll` ele devolve contagens: `workspaces`, `ready` e `invalid`. Para um workspace ele devolve a mesma resposta de um acerto válido no cache com `source` refresh, ou o código de erro da linha, ou CACHE_EXPIRED.

## Campos gravados

### Campos de Tool Account lidos (Tooling and Access)

| Campo | Usado para |
| --- | --- |
| Kind | Só linhas com bot são lidas |
| Workspace (External Identifier) | Os IDs de workspace do Slack a que o bot pertence |
| Access Token | O token do bot copiado para o cache |
| External Identifier | O ID de usuário do bot no Slack |

### Colunas da Data Table gravadas (Persist Workspace Tokens)

| Coluna | Valor |
| --- | --- |
| workspace_id | ID do workspace do Slack; a chave do upsert |
| access_token | Token do bot, ou vazio quando há código de erro |
| bot_user_id | ID de usuário do bot no Slack, ou vazio quando há código de erro |
| source_record_id | ID do registro de Tool Account, ou vazio quando nenhum bateu |
| cache_error | Vazio, ou um dos códigos de [Atualizar do Airtable](#refresh-from-airtable) |
| expires_at | Hora da gravação mais 15 minutos, em milissegundos; 0 quando há código de erro |

## Nós

O workflow tem 18 nós: 17 que executam e uma nota adesiva. Num nó If, a saída 0 é verdadeiro e a saída 1 é falso. Os nós configurados para seguir por uma saída de erro (Normalize Request, Read Workspace Cache, Evaluate Cache, Read Cache for Refresh, Read Bot Accounts, Build Cache Rows, Persist Workspace Tokens e Return Refreshed Result) mandam as falhas pela saída 1, desenhada com seta tracejada no fluxo.

| Nó | Tipo | O que faz |
| --- | --- | --- |
| Every 5 Minutes | scheduleTrigger | Gatilho agendado, a cada cinco minutos |
| Refresh Manually | manualTrigger | Gatilho manual no editor |
| Refresh All Workspaces | set | Define refreshAll = true para os dois gatilhos de atualização |
| Token Request | executeWorkflowTrigger | Chamado por outro workflow com workspaceId, cacheOnly, forceRefresh, refreshAll |
| Normalize Request | code | Valida o ID do workspace e normaliza as flags |
| Refresh All? | if | refreshAll ou um único workspace |
| Read Workspace Cache | dataTable | Linhas da Data Table do workspace |
| Evaluate Cache | code | Devolve o bot do cache quando válido; senão diz por quê |
| Refresh Needed? | if | Atualizar agora ou responder como está |
| Return Cached Result | noOp | Devolve a resposta ao chamador sem mudança |
| Read Cache for Refresh | dataTable | Todas as linhas da Data Table |
| Read Bot Accounts | httpRequest | Linhas de Tool Account com Kind bot, todas as páginas |
| Build Cache Rows | code | Uma linha de cache por workspace, ou um código de erro |
| Has Cache Rows? | if | Algo a gravar ou não |
| Persist Workspace Tokens | dataTable | Faz upsert das linhas por workspace_id |
| Return Refreshed Result | code | Responde ao chamador depois de uma atualização |
| Return Cache Error | code | Responde TOKEN_CACHE_UNAVAILABLE com a etapa |
| Cache Contract | stickyNote | Nota de contrato no canvas; não executa |

## Ligações

O Return Refreshed Result não tem ligação de saída na saída 0: o item dele é a resposta devolvida a quem chamou.

| De | Saída | Para |
| --- | --- | --- |
| Every 5 Minutes | 0 | Refresh All Workspaces |
| Refresh Manually | 0 | Refresh All Workspaces |
| Refresh All Workspaces | 0 | Normalize Request |
| Token Request | 0 | Normalize Request |
| Normalize Request | 0 | Refresh All? |
| Normalize Request | 1 | Return Cache Error |
| Refresh All? | 0 | Read Cache for Refresh |
| Refresh All? | 1 | Read Workspace Cache |
| Read Workspace Cache | 0 | Evaluate Cache |
| Read Workspace Cache | 1 | Return Cache Error |
| Evaluate Cache | 0 | Refresh Needed? |
| Evaluate Cache | 1 | Return Cache Error |
| Refresh Needed? | 0 | Read Cache for Refresh |
| Refresh Needed? | 1 | Return Cached Result |
| Read Cache for Refresh | 0 | Read Bot Accounts |
| Read Cache for Refresh | 1 | Return Cache Error |
| Read Bot Accounts | 0 | Build Cache Rows |
| Read Bot Accounts | 1 | Return Cache Error |
| Build Cache Rows | 0 | Has Cache Rows? |
| Build Cache Rows | 1 | Return Cache Error |
| Has Cache Rows? | 0 | Persist Workspace Tokens |
| Has Cache Rows? | 1 | Return Refreshed Result |
| Persist Workspace Tokens | 0 | Return Refreshed Result |
| Persist Workspace Tokens | 1 | Return Cache Error |
| Return Refreshed Result | 1 | Return Cache Error |

## Configurações e tratamento de erro

| Configuração | Valor |
| --- | --- |
| Ordem de execução | v1 |
| Fuso horário | America/Recife |
| Timeout de execução | 60 segundos |
| Salvar execuções com sucesso | Não |
| Salvar execuções com falha | Não |
| Salvar execuções manuais | Não |
| Salvar progresso da execução | Não |
| Workflow de erro | Nenhum |
| Retry on fail | Desligado em todos os nós |
| Caller policy | Workflows do mesmo dono |
| Disponível no MCP | Sim |

Toda falha dentro do fluxo vai para o Return Cache Error, que responde `ok` falso, erro TOKEN_CACHE_UNAVAILABLE e `stage`: o nome do último nó que ele acha executado, conferido do fim do caminho de atualização para trás. As falhas nos nós com saída de erro viram essa resposta em vez de uma execução com falha; quem chama decide o que fazer com ela.

## Lacunas conhecidas

Estas vêm da configuração dos nós. As execuções não são salvas, então o n8n não guarda histórico das rodadas para comparar.

| Lacuna | Efeito |
| --- | --- |
| A resposta leva o token do bot em `botSecret` | Qualquer workflow do mesmo dono que o chama recebe o token nos próprios dados de execução |
| A Data Table guarda o token do bot em texto puro em `access_token` | Quem abre a Data Table no n8n consegue ler todos os tokens em cache |
| Nenhuma execução é salva e os erros viram uma resposta normal | Uma atualização agendada quebrada não deixa rastro no n8n; só aparece quando quem chama recebe CACHE_EXPIRED ou TOKEN_CACHE_UNAVAILABLE |
| O `stage` da resposta de erro é o último nó executado encontrado, não o nó que falhou | Aponta a área certa, nem sempre o nó exato |
| Uma atualização de um workspace lê a Data Table inteira e todas as contas de bot | Cada atualização forçada custa tantas chamadas ao Airtable quanto uma completa |
| Duas atualizações podem rodar ao mesmo tempo (agendamento e quem chama) | As duas fazem upsert nas mesmas linhas; a última gravação vence, com os mesmos dados |
| O cache nunca pergunta ao Slack se o token ainda funciona | Um token revogado no Slack continua sendo servido até alguém mudar o Access Token em Tool Account |

## Recuperar uma gravação parcial

A Data Table pode ser reconstruída a qualquer momento, então recuperar é atualizar.

| O que aconteceu | O que fazer |
| --- | --- |
| Quem chama recebe CACHE_EXPIRED ou CACHE_MISS para um workspace que tem bot | Rode o Refresh Manually; se o erro continuar, confira a linha de Tool Account do workspace |
| Um workspace responde AMBIGUOUS_BOT | Deixe uma única Tool Account com Kind bot para esse workspace e atualize |
| Um workspace responde BOT_TOKEN_MISSING ou BOT_USER_MISSING | Corrija o Access Token ou o External Identifier da Tool Account dele e atualize |
| Uma atualização parou no meio | Rode o Refresh Manually; o upsert substitui todas as linhas |
| Duas linhas para um workspace (CACHE_DUPLICATE) | Apague uma linha na Data Table e atualize |

## Manter esta página em dia

O quadro no topo mostra a versão do n8n que esta página descreve. Abra o workflow no n8n: quando o último salvamento dele é mais novo que a data do quadro, esta página está desatualizada até a próxima revisão. Todos os nós e todas as ligações dessa versão estão em [Nós](#nodes) e [Ligações](#connections).