Integração Notion Database
A integração com Notion permite que você salve automaticamente resultados de tarefas, logs, relatórios e análises em databases do Notion, criando um sistema de documentação e tracking poderoso.
Visão Geral
A integração com Notion permite:
- Salvar resultados de tarefas em databases
- Criar logs estruturados de execuções
- Gerar relatórios automáticos em páginas
- Sincronizar dados com seu workspace Notion
- Criar dashboards visuais de acompanhamento
Pré-requisitos
- Conta no Notion
- Workspace do Notion (gratuito funciona)
- Database(s) criada(s) no Notion
- Variáveis de ambiente configuradas
Configuração Passo a Passo
1. Criar Database no Notion
- Abra o Notion e acesse seu workspace
- Crie uma nova página
- Digite
/databasee selecione Database - Full page - Nomeie a database (ex: "Tarefas AI - Logs")
- Configure as colunas:
Estrutura recomendada para Logs:
| Coluna | Tipo | Descrição |
|---|---|---|
| Name | Title | Nome da tarefa |
| Status | Select | success, error, pending |
| Executado em | Date | Data/hora de execução |
| Duração | Number | Tempo de execução (ms) |
| Resultado | Text | Output da tarefa |
| Erro | Text | Mensagem de erro (se houver) |
| Usuário | Person | Quem criou a tarefa |
| Tags | Multi-select | Categorias |
Dica: Você pode adicionar mais colunas depois, como "Priority", "Project", "Cost", etc.
2. Criar Integration no Notion
- Acesse notion.so/my-integrations
- Clique em + New integration
- Preencha os dados:
- Name: Tarefa AI
- Logo: Upload opcional
- Associated workspace: Selecione seu workspace
- Em Capabilities, habilite:
- ✅ Read content
- ✅ Update content
- ✅ Insert content
- Clique em Submit
- Copie o Internal Integration Token (começa com
secret_)
Importante: Salve o token imediatamente. Ele não será exibido novamente.
3. Conectar Database à Integration
- Abra a database que você criou
- Clique nos 3 pontos (...) no canto superior direito
- Vá em Add connections
- Selecione Tarefa AI (sua integration)
- Clique em Confirm
Sem este passo, sua integration não terá acesso à database!
4. Obter Database ID
Opção A: Via URL
A URL da sua database tem este formato:
https://www.notion.so/workspace/7b5a8f9c12ab4321bcde56789f0a1b2c?v=...
O Database ID é a parte entre / e ?:
7b5a8f9c12ab4321bcde56789f0a1b2c
Opção B: Via API
curl -X GET 'https://api.notion.com/v1/search' \
-H 'Authorization: Bearer secret_sua_integration_token' \
-H 'Notion-Version: 2022-06-28' \
-H 'Content-Type: application/json' \
-d '{
"query": "Tarefas AI - Logs",
"filter": {
"value": "database",
"property": "object"
}
}'O ID estará em results[0].id.
5. Configurar Variáveis de Ambiente
Desenvolvimento Local (.env.local)
# Notion Configuration
NOTION_API_KEY=secret_sua_integration_token_aqui
NOTION_DATABASE_ID=7b5a8f9c12ab4321bcde56789f0a1b2c
# Optional: IDs de outras databases
NOTION_REPORTS_DATABASE_ID=outro-id-aqui
NOTION_ANALYTICS_DATABASE_ID=mais-um-idProdução (Vercel/Railway/Render)
Vercel:
# Via CLI
vercel env add NOTION_API_KEY
vercel env add NOTION_DATABASE_ID
# Ou no dashboard
# Settings → Environment VariablesRailway:
railway variables set NOTION_API_KEY=secret_sua_token
railway variables set NOTION_DATABASE_ID=seu-database-idDocker:
# docker-compose.yml
environment:
- NOTION_API_KEY=${NOTION_API_KEY}
- NOTION_DATABASE_ID=${NOTION_DATABASE_ID}6. Instalar SDK do Notion
npm install @notionhq/client7. Verificar Instalação
Teste a conexão:
// scripts/test-notion.ts
import { Client } from '@notionhq/client';
const notion = new Client({
auth: process.env.NOTION_API_KEY,
});
async function testNotion() {
try {
// Buscar database
const database = await notion.databases.retrieve({
database_id: process.env.NOTION_DATABASE_ID!,
});
console.log('Conexão bem-sucedida!');
console.log('Database:', database.title[0]?.plain_text);
// Criar página de teste
const page = await notion.pages.create({
parent: {
database_id: process.env.NOTION_DATABASE_ID!,
},
properties: {
Name: {
title: [
{
text: {
content: 'Teste de Integração',
},
},
],
},
Status: {
select: {
name: 'success',
},
},
},
});
console.log('Página criada:', page.url);
} catch (error) {
console.error('Erro:', error);
}
}
testNotion();Execute:
npm run test:notion
# ou
npx tsx scripts/test-notion.tsExemplos Práticos
Exemplo 1: Salvar Resultado de Tarefa
// src/lib/notion/save-task-result.ts
import { Client } from '@notionhq/client';
const notion = new Client({
auth: process.env.NOTION_API_KEY,
});
export async function saveTaskResultToNotion({
taskName,
status,
result,
duration,
error,
}: {
taskName: string;
status: 'success' | 'error' | 'pending';
result?: string;
duration?: number;
error?: string;
}) {
try {
const page = await notion.pages.create({
parent: {
database_id: process.env.NOTION_DATABASE_ID!,
},
properties: {
Name: {
title: [
{
text: {
content: taskName,
},
},
],
},
Status: {
select: {
name: status,
},
},
'Executado em': {
date: {
start: new Date().toISOString(),
},
},
Duração: {
number: duration || 0,
},
Resultado: {
rich_text: [
{
text: {
content: result || 'Sem resultado',
},
},
],
},
...(error && {
Erro: {
rich_text: [
{
text: {
content: error,
},
},
],
},
}),
},
});
console.log('Salvo no Notion:', page.url);
return { success: true, pageId: page.id, url: page.url };
} catch (error) {
console.error('Erro ao salvar no Notion:', error);
return { success: false, error: error.message };
}
}
// Uso em tarefa
const result = await executeTask();
await saveTaskResultToNotion({
taskName: 'Análise de Sentimento',
status: 'success',
result: JSON.stringify(result, null, 2),
duration: 1234,
});Exemplo 2: Criar Relatório com Blocos
// src/lib/notion/create-report.ts
export async function createReportPage({
title,
summary,
data,
}: {
title: string;
summary: string;
data: any[];
}) {
// 1. Criar página
const page = await notion.pages.create({
parent: {
database_id: process.env.NOTION_REPORTS_DATABASE_ID!,
},
properties: {
Name: {
title: [
{
text: {
content: title,
},
},
],
},
},
});
// 2. Adicionar conteúdo (blocos)
await notion.blocks.children.append({
block_id: page.id,
children: [
// Heading
{
type: 'heading_1',
heading_1: {
rich_text: [
{
text: {
content: title,
},
},
],
},
},
// Resumo
{
type: 'paragraph',
paragraph: {
rich_text: [
{
text: {
content: summary,
},
},
],
},
},
// Divider
{
type: 'divider',
divider: {},
},
// Tabela de dados
{
type: 'table',
table: {
table_width: 3,
has_column_header: true,
has_row_header: false,
children: [
// Header
{
type: 'table_row',
table_row: {
cells: [
[{ text: { content: 'Tarefa' } }],
[{ text: { content: 'Status' } }],
[{ text: { content: 'Duração' } }],
],
},
},
// Dados
...data.map((item) => ({
type: 'table_row',
table_row: {
cells: [
[{ text: { content: item.name } }],
[{ text: { content: item.status } }],
[{ text: { content: `${item.duration}ms` } }],
],
},
})),
],
},
},
],
});
return page;
}
// Uso
await createReportPage({
title: 'Relatório Diário - 17/10/2024',
summary: 'Resumo das execuções do dia',
data: [
{ name: 'Tarefa 1', status: 'success', duration: 1234 },
{ name: 'Tarefa 2', status: 'error', duration: 567 },
],
});Exemplo 3: Atualizar Página Existente
// src/lib/notion/update-page.ts
export async function updateTaskStatus({
pageId,
status,
result,
}: {
pageId: string;
status: 'success' | 'error' | 'pending';
result: string;
}) {
await notion.pages.update({
page_id: pageId,
properties: {
Status: {
select: {
name: status,
},
},
Resultado: {
rich_text: [
{
text: {
content: result,
},
},
],
},
},
});
}Exemplo 4: Buscar e Filtrar Dados
// src/lib/notion/query-tasks.ts
export async function getTasksByStatus(status: string) {
const response = await notion.databases.query({
database_id: process.env.NOTION_DATABASE_ID!,
filter: {
property: 'Status',
select: {
equals: status,
},
},
sorts: [
{
property: 'Executado em',
direction: 'descending',
},
],
});
return response.results.map((page: any) => ({
id: page.id,
name: page.properties.Name.title[0]?.plain_text,
status: page.properties.Status.select?.name,
executedAt: page.properties['Executado em'].date?.start,
duration: page.properties.Duração.number,
url: page.url,
}));
}
// Uso
const failedTasks = await getTasksByStatus('error');
console.log('Tarefas com erro:', failedTasks);Exemplo 5: Criar Dashboard com Estatísticas
// src/lib/notion/create-dashboard.ts
export async function createDashboardPage() {
// Buscar estatísticas
const allTasks = await notion.databases.query({
database_id: process.env.NOTION_DATABASE_ID!,
});
const stats = {
total: allTasks.results.length,
success: allTasks.results.filter((p: any) =>
p.properties.Status.select?.name === 'success'
).length,
error: allTasks.results.filter((p: any) =>
p.properties.Status.select?.name === 'error'
).length,
avgDuration: calculateAvgDuration(allTasks.results),
};
// Criar página de dashboard
const page = await notion.pages.create({
parent: {
database_id: process.env.NOTION_REPORTS_DATABASE_ID!,
},
properties: {
Name: {
title: [
{
text: {
content: `Dashboard - ${new Date().toLocaleDateString('pt-BR')}`,
},
},
],
},
},
});
// Adicionar blocos com estatísticas
await notion.blocks.children.append({
block_id: page.id,
children: [
{
type: 'heading_1',
heading_1: {
rich_text: [{ text: { content: 'Dashboard de Tarefas' } }],
},
},
{
type: 'callout',
callout: {
icon: { emoji: '📊' },
rich_text: [
{
text: {
content: `Total de execuções: ${stats.total}\nSucesso: ${stats.success}\nErros: ${stats.error}\nDuração média: ${stats.avgDuration}ms`,
},
},
],
},
},
{
type: 'heading_2',
heading_2: {
rich_text: [{ text: { content: 'Taxa de Sucesso' } }],
},
},
{
type: 'paragraph',
paragraph: {
rich_text: [
{
text: {
content: `${((stats.success / stats.total) * 100).toFixed(1)}%`,
},
},
],
},
},
],
});
return page;
}Exemplo 6: Sincronizar Tags do Notion com App
// src/lib/notion/sync-tags.ts
export async function syncTagsFromNotion() {
const database = await notion.databases.retrieve({
database_id: process.env.NOTION_DATABASE_ID!,
});
// Extrair opções de tags
const tagsProperty = database.properties.Tags as any;
const tags = tagsProperty.multi_select.options.map((option: any) => ({
name: option.name,
color: option.color,
}));
// Salvar no banco local
for (const tag of tags) {
await prisma.tag.upsert({
where: { name: tag.name },
update: { color: tag.color },
create: { name: tag.name, color: tag.color },
});
}
return tags;
}Estruturas de Database Recomendadas
Database: Task Executions (Logs)
Name (Title)
Status (Select): success, error, pending, running
Executado em (Date)
Duração (Number) ms
Resultado (Text)
Erro (Text)
Usuário (Person)
Tags (Multi-select): automation, ai, notification, report
Priority (Select): high, medium, low
Project (Relation) → Projects database
Database: Daily Reports
Name (Title): "Relatório - DD/MM/YYYY"
Date (Date)
Total Tasks (Number)
Success Rate (Number) %
Summary (Text)
Created by (Person)
Status (Select): draft, published
Database: AI Prompts
Name (Title)
Prompt (Text)
Model (Select): gpt-4, claude-3, gemini-pro
Category (Multi-select)
Last Used (Date)
Usage Count (Number)
Avg Cost (Number) $
Otimização e Boas Práticas
1. Rate Limiting da API Notion
A API do Notion tem limite de 3 requisições por segundo.
// src/lib/notion/rate-limiter.ts
import pLimit from 'p-limit';
const limit = pLimit(3); // Max 3 concurrent requests
export async function batchCreatePages(pages: any[]) {
const promises = pages.map((pageData) =>
limit(() =>
notion.pages.create(pageData)
)
);
return Promise.all(promises);
}2. Cache de Databases
// src/lib/notion/cache.ts
import { Redis } from '@upstash/redis';
const redis = new Redis({
url: process.env.UPSTASH_REDIS_URL!,
token: process.env.UPSTASH_REDIS_TOKEN!,
});
export async function getDatabaseWithCache(databaseId: string) {
const cacheKey = `notion:db:${databaseId}`;
// Verificar cache
const cached = await redis.get(cacheKey);
if (cached) return JSON.parse(cached as string);
// Buscar da API
const database = await notion.databases.retrieve({
database_id: databaseId,
});
// Armazenar em cache por 1 hora
await redis.setex(cacheKey, 3600, JSON.stringify(database));
return database;
}3. Tratamento de Erros
// src/lib/notion/error-handler.ts
export async function safeNotionCreate(data: any) {
try {
return await notion.pages.create(data);
} catch (error: any) {
if (error.code === 'object_not_found') {
throw new Error('Database não encontrada. Verifique o ID e permissões.');
} else if (error.code === 'validation_error') {
throw new Error(`Dados inválidos: ${error.message}`);
} else if (error.code === 'rate_limited') {
// Aguardar e tentar novamente
await new Promise((resolve) => setTimeout(resolve, 1000));
return notion.pages.create(data);
}
throw error;
}
}4. Batch Operations
// src/lib/notion/batch.ts
export async function batchUpdatePages(updates: Array<{ pageId: string; properties: any }>) {
const BATCH_SIZE = 10;
for (let i = 0; i < updates.length; i += BATCH_SIZE) {
const batch = updates.slice(i, i + BATCH_SIZE);
await Promise.all(
batch.map((update) =>
notion.pages.update({
page_id: update.pageId,
properties: update.properties,
})
)
);
// Delay entre batches
if (i + BATCH_SIZE < updates.length) {
await new Promise((resolve) => setTimeout(resolve, 500));
}
}
}Troubleshooting
Erro: "object_not_found"
Causa: Database não encontrada ou integration sem acesso
Solução:
- Verifique se o Database ID está correto
- Conecte a integration à database:
- Abra a database no Notion
- Clique em "..." → "Add connections"
- Selecione sua integration
Erro: "validation_error"
Causa: Propriedades inválidas ou tipos incorretos
Solução:
// Verificar propriedades da database primeiro
const database = await notion.databases.retrieve({
database_id: process.env.NOTION_DATABASE_ID!,
});
console.log('Propriedades disponíveis:', Object.keys(database.properties));
// Ajustar o código para usar as propriedades corretasErro: "unauthorized"
Causa: Integration token inválido ou expirado
Solução:
# Verificar token
echo $NOTION_API_KEY
# Deve começar com "secret_"
# Se necessário, gere um novo token em:
# https://www.notion.so/my-integrationsPáginas Criadas mas Vazias
Causa: properties não correspondem à estrutura da database
Solução:
// Sempre verificar estrutura antes
const db = await notion.databases.retrieve({
database_id: process.env.NOTION_DATABASE_ID!,
});
// Ver tipos de cada propriedade
Object.entries(db.properties).forEach(([name, prop]: [string, any]) => {
console.log(`${name}: ${prop.type}`);
});
// Ajustar código para usar os tipos corretosDesempenho Lento
Causa: Muitas requisições sequenciais
Solução:
// ❌ Lento (sequencial)
for (const task of tasks) {
await saveToNotion(task);
}
// ✅ Rápido (paralelo com limite)
import pLimit from 'p-limit';
const limit = pLimit(3);
await Promise.all(
tasks.map((task) =>
limit(() => saveToNotion(task))
)
);Recursos Adicionais
Documentação Oficial
Ferramentas Úteis
- Notion API Explorer - Teste requests
- Postman Collection - Exemplos prontos
Comunidade
Exemplos
Próximos Passos
Agora que você configurou o Notion, explore:
- Integração com Email - Envie links do Notion por email
- Webhooks API - Trigger tarefas quando database mudar
- OpenRouter AI - Analise dados do Notion com IA
Precisa de ajuda?
Consulte nosso guia completo de troubleshooting