rollfuse
← Voltar aos tutoriais

Consultando a trilha de auditoria e decidindo aprovações pendentes

Consultando a trilha de auditoria e decidindo aprovações pendentes

Toda ação sensível em uma Organização deixa rastro, e algumas ações só entram em vigor depois que uma segunda pessoa dá o aval. Este tutorial mostra como ler e exportar a trilha de auditoria, e depois como decidir uma aprovação pendente.

As duas áreas exigem uma sessão de um Membro com a permissão certa: ler a trilha de auditoria pede audit:read, e decidir aprovações pede a permissão de gerenciamento de roles.

Filtrando a trilha de auditoria

GET /v1/audit-events retorna os eventos da sua Organização, do mais recente para o mais antigo. Você nunca passa um id de organização: a Organização usada é sempre a vinculada à sua sessão. Filtre por qualquer combinação de tipo de evento, sujeito e uma janela de data.

const orgId = "018f2f3a-6000-7000-9c3a-1f2b3c4d5e6f";
const token = process.env.ROLLFUSE_SESSION_TOKEN;

const params = new URLSearchParams({
  event_type: "role.granted",
  subject_type: "member",
  occurred_after: "2026-08-01T00:00:00Z",
  occurred_before: "2026-09-01T00:00:00Z",
  limit: "50",
});

const auditRes = await fetch(
  `https://api.rollfuse.com/v1/audit-events?${params}`,
  { headers: { Authorization: `Bearer ${token}` } }
);

const { audit_events, limit, offset } = await auditRes.json();
audit_events.forEach((event) =>
  console.log(event.event_type, event.subject_id, event.actor)
);

A resposta devolve a paginação que foi realmente aplicada, então se você não passar limit, dá para ler o valor padrão direto da resposta em vez de tentar adivinhar. Uma janela invertida (occurred_after depois de occurred_before) ou um timestamp mal formado é rejeitada de cara, em vez de simplesmente devolver uma lista vazia.

Exportando a trilha

Quando você precisa da trilha completa para uma revisão de compliance, em vez de uma fatia paginada, exporte. O endpoint respeita os mesmos filtros da listagem, mas ignora a paginação e devolve um único documento.

const exportRes = await fetch(
  `https://api.rollfuse.com/v1/audit-events/export?format=csv&occurred_after=2026-08-01T00:00:00Z`,
  { headers: { Authorization: `Bearer ${token}` } }
);

const csv = await exportRes.text();

format tem csv como padrão; passe format=json se preferir já receber algo pronto para parsear. No CSV, a coluna payload de cada linha carrega o documento JSON do evento como um campo entre aspas, então quem consome o arquivo ainda precisa fazer o parse dessa coluna como JSON, mesmo o arquivo em si sendo CSV.

Decidindo uma aprovação pendente

Algumas ações, como conceder a role admin a um Membro, não entram em vigor na hora: elas geram um ApprovalRequest em vez disso. Liste as que ainda esperam uma decisão:

const pendingRes = await fetch(
  "https://api.rollfuse.com/v1/approval-requests",
  { headers: { Authorization: `Bearer ${token}` } }
);

const pending = await pendingRes.json();

Cada item traz requested_by_member_id e o target_member_id a quem a mudança se aplicaria. Aprove ou rejeite pelo id, usando uma sessão de um Membro diferente de quem solicitou:

const decisionRes = await fetch(
  `https://api.rollfuse.com/v1/approval-requests/${pending[0].id}/approve`,
  { method: "POST", headers: { Authorization: `Bearer ${token}` } }
);

const decided = await decisionRes.json();
console.log(decided.status); // "approved"

Troque o trecho final da URL para reject se quiser descartar a solicitação; rejeitar nunca cria a atribuição de role que a solicitação pedia. Duas proteções são aplicadas no servidor: quem decide não pode ser quem solicitou a mudança, e uma solicitação já decidida não pode ser decidida de novo. Os dois casos voltam como conflito em vez de simplesmente ter sucesso duas vezes.