Integração Notion Database

Configure a integração com Notion para salvar resultados de tarefas em databases

Nesta Página

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

  1. Abra o Notion e acesse seu workspace
  2. Crie uma nova página
  3. Digite /database e selecione Database - Full page
  4. Nomeie a database (ex: "Tarefas AI - Logs")
  5. Configure as colunas:

Estrutura recomendada para Logs:

ColunaTipoDescrição
NameTitleNome da tarefa
StatusSelectsuccess, error, pending
Executado emDateData/hora de execução
DuraçãoNumberTempo de execução (ms)
ResultadoTextOutput da tarefa
ErroTextMensagem de erro (se houver)
UsuárioPersonQuem criou a tarefa
TagsMulti-selectCategorias

2. Criar Integration no Notion

  1. Acesse notion.so/my-integrations
  2. Clique em + New integration
  3. Preencha os dados:
    • Name: Tarefa AI
    • Logo: Upload opcional
    • Associated workspace: Selecione seu workspace
  4. Em Capabilities, habilite:
    • ✅ Read content
    • ✅ Update content
    • ✅ Insert content
  5. Clique em Submit
  6. Copie o Internal Integration Token (começa com secret_)

3. Conectar Database à Integration

  1. Abra a database que você criou
  2. Clique nos 3 pontos (...) no canto superior direito
  3. Vá em Add connections
  4. Selecione Tarefa AI (sua integration)
  5. Clique em Confirm

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-id

Produção (Vercel/Railway/Render)

Vercel:

# Via CLI
vercel env add NOTION_API_KEY
vercel env add NOTION_DATABASE_ID
 
# Ou no dashboard
# Settings → Environment Variables

Railway:

railway variables set NOTION_API_KEY=secret_sua_token
railway variables set NOTION_DATABASE_ID=seu-database-id

Docker:

# docker-compose.yml
environment:
  - NOTION_API_KEY=${NOTION_API_KEY}
  - NOTION_DATABASE_ID=${NOTION_DATABASE_ID}

6. Instalar SDK do Notion

npm install @notionhq/client

7. 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.ts

Exemplos 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:

  1. Verifique se o Database ID está correto
  2. 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 corretas

Erro: "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-integrations

Pá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 corretos

Desempenho 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

Comunidade

Exemplos


Próximos Passos

Agora que você configurou o Notion, explore:

Precisa de ajuda?

Consulte nosso guia completo de troubleshooting

Learn more