recalled.dev
For AI assistants

The full Recalled docs in one markdown file

Paste the link below into Claude Code, Codex, Cursor or any AI agent to give it the complete docs. Or download the .md and feed it to your model as context.

Direct link
/en/docs/recalled.md
Getting started

Recalled

Audit logs as a Service for B2B and B2C products.

Recalled stores "who did what, when, from where" for every action your users take, and gives you a signed, searchable, exportable log you can show to your customers, your auditors and your legal team.

Why

  • Compliance ready: SOC 2, ISO 27001, GDPR ask for an audit trail. Recalled gives you one out of the box with EU hosting, AES-256 at rest and a cryptographic hash chain.
  • No main-DB pollution: audit logs grow fast and slow down production queries. Recalled stores events off-site, indexed for search, with configurable retention.
  • Embeddable UI: an internal admin widget (React component) for your support engineers, ops team, SRE and compliance reviewers. Drop it inside the back-office your team already uses to operate the product so they can browse "who did what" without leaving their workflow.

How it works

  1. Create a project in the dashboard.
  2. Generate an API key.
  3. Install the npm SDK or hit the REST API from any language.
  4. Send events: client.events.create({ action, actor, targets, metadata }).
  5. Read them back via the dashboard, the API, or the embeddable component.

Ship your first event in 2 minutes

ts
import { Recalled } from "@recalled/sdk";

const client = new Recalled({
  apiKey: process.env.RECALLED_API_KEY!,
});

await client.events.create({
  action: "invoice.deleted",
  actor: { id: "user_123", email: "alice@example.com" },
  organization: "org_abc",
  targets: [{ type: "invoice", id: "inv_42" }],
  metadata: { reason: "duplicate" },
});

That's it. You're logging.

Setup with an LLM

If you are integrating Recalled with the help of Claude, Cursor, ChatGPT or any other AI coding assistant, paste the prompt below into the assistant's context first. It tells the assistant the rules of what to log, what to skip, how to name actions, and what to put in metadata, so it stops asking you 50 follow-up questions and ships a clean integration on the first try.

text
You are integrating Recalled (audit logs as a service) into an existing app.

# Rules
1. Log state changes, not reads. A user reading a dashboard 50 times is not an audit event. A user changing their email is.
2. Log actions with consequences, not technical noise. Health checks, cache misses, 304 responses do not belong here. They belong in APM.
3. Log what tells a story. In 6 months, someone will ask "who did this and when". The answer must come from a single event with actor, target, reason, IP, time.

# What to log

Authentication: user.signed_up, user.logged_in, user.logged_out, user.login_failed, user.password_changed, user.password_reset_requested, user.password_reset_completed, user.email_changed, user.two_factor_enabled, user.two_factor_disabled, user.session_revoked, magic_link.sent, magic_link.consumed, oauth.linked, oauth.unlinked.

Authorisation: member.invited, member.joined, member.removed, member.role_changed, team.created, team.deleted, permission.granted, permission.revoked, api_key.created, api_key.revoked, sharing.granted, sharing.revoked, ownership.transferred.

Data lifecycle on every business object (invoice, project, document, etc.): <object>.created, <object>.updated, <object>.deleted, <object>.archived, <object>.restored, <object>.published, <object>.unpublished, <object>.duplicated, <object>.moved.

Money: subscription.created, subscription.updated, subscription.canceled, subscription.plan_changed, invoice.created, invoice.paid, invoice.failed, invoice.refunded, payment.succeeded, payment.failed, refund.issued, refund.completed, coupon.applied, coupon.expired, payment_method.added, payment_method.removed, payment_method.set_default.

Admin actions (always, no exception): admin.impersonation_started, admin.impersonation_ended, admin.user_unlocked, admin.user_locked, admin.feature_toggled, admin.data_overridden, admin.support_intervention.

Exports and imports: export.started, export.completed, export.failed, import.started, import.completed, import.failed, bulk_delete.requested, bulk_delete.completed, gdpr.access_request, gdpr.erasure_request.

Integrations: integration.connected, integration.disconnected, webhook.created, webhook.updated, webhook.deleted, webhook.delivery_failed (only after retries are exhausted).

Security: security.brute_force_detected, security.suspicious_login, security.rate_limit_exceeded (only when persistent), security.csp_violation_reported, security.api_key_leaked.

System and background jobs (only when meaningful): cron.<name>.completed, cron.<name>.failed (one per run, not per item), migration.applied, migration.rolled_back, backup.created, backup.restored.

# What to skip

- GET requests, page views, dashboard reads, scroll events
- Auto-saves and draft updates if saved every few seconds
- Token refresh, health checks, CSRF verifications
- Permission checks (every API request runs them)
- Each successful webhook delivery, each batch job iteration, each cache invalidation
- Heartbeats and liveness probes
- Anything bigger than ~2 KB in metadata

# Naming convention

Format: <domain>.<subject>.<verb_past_tense>, all lowercase, dot-separated, snake_case inside a segment if needed. Past tense verbs always (.created not .create, .deleted not .delete). Be consistent: do not mix .deleted and .removed for the same domain.

# Metadata

Always include when relevant: source (web, mobile, api, admin_panel, automation, import, webhook), reason (free text if user provided one), request_id (correlation id), result (success or failure).

For updates: changed_fields array of field names, plus before/after only for small diffs.

For money: amount_cents (integer, never float), currency, provider id (stripe_payment_intent_id or equivalent).

For failures: result: "failure", reason, code.

# Antipatterns

Never put in metadata: plaintext secrets, passwords, full tokens, full credit card numbers, full document bodies, file contents, blobs, PII you do not need, stack traces, SQL queries, anything bigger than ~2 KB.

# How to add the SDK call

Use @recalled/sdk:
  import { Recalled } from "@recalled/sdk";
  const client = new Recalled({ apiKey: process.env.RECALLED_API_KEY });
  client.events.emit({ action: "...", actor: {...}, organization: "...", targets: [...], metadata: {...} });

Use emit() (resilient, non blocking) by default. Use create() (throws on failure) only when the audit log is part of the request's success condition.

Now go through the codebase, find the spots that match the catalogue above, and add the appropriate client.events.emit calls. Skip everything that does not match.

For the full opinionated guide on what to log, see What to log. The MCP server also exposes this as a tool: get_setup_guide returns the same prompt so AI agents can read it on demand.