RewindRewind SDK Docs

Exception Capture SDK Setup

Capture frontend and backend exceptions with RewindRewind's native SDKs, or use Sentry-compatible envelope tooling where an existing Sentry SDK is already installed.

Set up with an AI agent

Hand this runbook to Claude or Codex. It runs the CLI, confirms an admin key (and asks you for one if missing, driving account creation when needed), then wires up all three surfaces: front-end exceptions, back-end exceptions, and app events. The same guide is served raw at http://rewindrewind.com/agents.md, so an agent can fetch and follow it directly.

# Set up RewindRewind with an AI agent

RewindRewind captures back-end and front-end exceptions plus custom product events.
This guide is self-contained; follow it top to bottom. Never invent an API key.

## 1. Make sure there is an account and an admin key
Ask the user for their RewindRewind admin key (it starts with `rr_`).

If they do not have an account yet:
1. Ask the user for their email address.
2. Create the account (this emails them a sign-in link):
   ```
   curl -X POST http://rewindrewind.com/agents/signup -H 'content-type: application/json' -d '{"email":"USER_EMAIL"}'
   ```
3. Tell the user: open your inbox and click the RewindRewind sign-in link.
4. Then have them mint an admin key: go to http://rewindrewind.com/organization/keys, click
   "New key", copy the `rr_…` secret (shown only once), and paste it back to me.

## 2. Configure the CLI (or call the API directly)
Run the CLI straight from the public repo, no install needed:
```
npx github:bananatron/rewindrewindcli configure --api-key rr_xxx --base-url http://rewindrewind.com
npx github:bananatron/rewindrewindcli init            # finds the project and fetches its public key
```
No project yet? `npx github:bananatron/rewindrewindcli projects create --name "My App"`
Using it often? Install it once with `npm install -g github:bananatron/rewindrewindcli` for a persistent `rewindrewind` (alias `rr`) command, and set long-term auth so you never pass the key again: `rewindrewind config set api-key-file /path/to/key` (or export `REWINDREWIND_API_KEY`).
Prefer raw HTTP? Send `Authorization: Bearer rr_xxx` to http://rewindrewind.com/api/* (see OpenAPI below).

## 3. Wire the three surfaces
- Front-end exceptions: add one `<script>` to your `<head>` (order-independent; safe to re-run on SPA / Hotwire-Turbo navigations):
  ```html
  <script>
    (function (w, d) {
      var r = (w.RewindRewind = w.RewindRewind || { _q: [] });
      ["init","captureException","captureEvent","captureMessage","addBreadcrumb","setIdentity","setTags","setContext","flush"]
        .forEach(function (m) { r[m] = r[m] || function () { (r._q = r._q || []).push([m, arguments]); }; });
      if (!r._loading) { r._loading = 1; var s = d.createElement("script"); s.async = 1; s.src = "http://rewindrewind.com/sdk/v1/rewind.js"; d.head.appendChild(s); }
    })(window, document);
    RewindRewind.init({ key: "rrpub_xxx", environment: "production" });
  </script>
  ```
  The public key (`rrpub_xxx`) is safe to embed, like a Sentry DSN.
- Back-end exceptions: send with the public key, or `npx github:bananatron/rewindrewindcli exceptions send …`
- App events: `rewind.captureEvent("checkout.completed", { total: 42 })`
  or `npx github:bananatron/rewindrewindcli events send --type checkout.completed --properties '{"total":42}'`

## 4. Verify
```
npx github:bananatron/rewindrewindcli verify
```

## Reference
- Full SDK docs: http://rewindrewind.com/docs/exception-capture-sdk
- OpenAPI (every endpoint): http://rewindrewind.com/openapi.json
- All CLI commands: `npx github:bananatron/rewindrewindcli --help`

Collector and keys

The hosted collector is https://rewindrewind.com. Self-hosted Workers can use their own Worker origin in the same SDK options.

REWIND_ENDPOINT=https://rewindrewind.com
REWIND_PROJECT_KEY=rrpub_xxx        # public project key for all ingestion
REWIND_ENVIRONMENT=production
REWIND_RELEASE=web@1.14.0
# Only where you call the management API / CLI (never in client code):
REWIND_ADMIN_KEY=rr_xxx

Fastest setup: the CLI

One command finds your project, reads its public project key, and prints copy-paste setup for all three surfaces: front-end exceptions, back-end exceptions, and app events:

npx github:bananatron/rewindrewindcli init --api-key rr_your_admin_key
npx github:bananatron/rewindrewindcli verify

verify sends test data and confirms it lands. The CLI can also drive the entire management API; run npx github:bananatron/rewindrewindcli --help.

Reaching for it often? Install it once for a persistent rewindrewind (and rr) command:

npm install -g github:bananatron/rewindrewindcli
rewindrewind --help

For long-term auth, point the CLI at a file holding your admin key instead of passing it each time (rewindrewind config set api-key-file /path/to/key), or set REWINDREWIND_API_KEY in the environment. See the CLI README for the full resolution order.

Payload Shape

Native SDKs send Sentry-shaped JSON to POST /v1/exceptions and custom product events to POST /v1/events.

{
  "environment": "production",
  "release": "web@1.14.0",
  "platform": "javascript",
  "level": "error",
  "message": "Checkout failed",
  "exception": {
    "type": "TypeError",
    "value": "Checkout failed",
    "stacktrace": [
      { "filename": "app.js", "function": "submitCheckout", "line": 120, "column": 19, "in_app": true }
    ]
  },
  "identity": { "id": "user_123", "email": "person@example.com" },
  "tags": { "service": "checkout" },
  "extra": { "cart_id": "cart_123" }
}

Event Capture

Send product, workflow, and audit moments to POST /v1/events.

await rewind.captureEvent("checkout.button_clicked", {
  path: window.location.pathname,
  plan: user.plan,
});
curl https://rewindrewind.com/v1/events \
  -H "Authorization: Bearer $REWIND_PROJECT_KEY" \
  -H "Content-Type: application/json" \
  --data '{
    "type": "checkout.button_clicked",
    "environment": "production",
    "release": "web@1.14.0",
    "properties": { "path": "/checkout", "plan": "team" }
  }'

JavaScript Browser Apps

npm install @rewindrewind/sdk
import { initRewind } from "@rewindrewind/sdk/browser";

export const rewind = initRewind({
  endpoint: import.meta.env.VITE_REWIND_ENDPOINT ?? "https://rewindrewind.com",
  apiKey: import.meta.env.VITE_REWIND_PROJECT_KEY,
  environment: import.meta.env.MODE === "production" ? "production" : "preview",
  release: import.meta.env.VITE_REWIND_RELEASE,
  tags: { service: "web" },
});

The browser SDK captures window.error and window.unhandledrejection by default.

Breadcrumbs wrap fetch. To record network breadcrumbs the SDK instruments window.fetch, so rewind.js can appear in the call stack of requests it did not originate, e.g. a Facebook Pixel or analytics call that your CSP connect-src blocks. Seeing rewind.js in such a stack does not mean RewindRewind made the request; it is a bystander the stack trace passes through.

Because those third-party failures aren't your code, the SDK treats them as such: a failed cross-origin fetch is recorded as a warning breadcrumb and its TypeError: Failed to fetch rejection is dropped rather than reported as a phantom exception. A failed same-origin request is kept and capturable, since it can signal a real outage.

For your Content Security Policy, the only host the SDK talks to is your collector, so connect-src needs just https://rewindrewind.com (or your self-hosted Worker origin), never the rotating serverless hosts a third-party tracker may post to.

try {
  await submitCheckout();
} catch (error) {
  await rewind.captureException(error, {
    route: window.location.pathname,
    action: "submit_checkout",
  });
  throw error;
}

await rewind.captureEvent("checkout.button_clicked", {
  path: window.location.pathname,
});

Attach users after login and clear them on logout.

rewind.setIdentity({ id: user.id, email: user.email });
rewind.setTags({ plan: user.plan });
rewind.setIdentity(undefined);

Sentry Browser Tooling

If a browser app already uses Sentry, keep the SDK and tunnel event envelopes through RewindRewind.

npm install @sentry/browser
import * as Sentry from "@sentry/browser";

Sentry.init({
  dsn: "https://rewind-public@rewindrewind.com/1",
  tunnel: `https://rewindrewind.com/v1/sentry/envelope?sentry_key=${encodeURIComponent(
    import.meta.env.VITE_REWIND_PROJECT_KEY,
  )}`,
  environment: import.meta.env.MODE === "production" ? "production" : "preview",
  release: import.meta.env.VITE_REWIND_RELEASE,
});

The tunnel endpoint currently ingests Sentry envelope items with "type": "event". Use native SDKs when you also want RewindRewind product events.

Node.js Apps

npm install @rewindrewind/sdk
import { initRewind } from "@rewindrewind/sdk/node";

export const rewind = initRewind({
  endpoint: process.env.REWIND_ENDPOINT ?? "https://rewindrewind.com",
  apiKey: process.env.REWIND_PROJECT_KEY!,
  environment: process.env.REWIND_ENVIRONMENT ?? "production",
  release: process.env.REWIND_RELEASE,
  tags: { service: "api", runtime: "node" },
  autoCapture: true,
});

autoCapture: true captures uncaughtException and unhandledRejection. Capture route and job boundaries explicitly so request metadata is preserved.

app.use((err, req, _res, next) => {
  void rewind
    .captureException(err, {
      request: { method: req.method, url: req.originalUrl },
    })
    .finally(() => next(err));
});

Bun Apps

bun add @rewindrewind/sdk
import { initRewind } from "@rewindrewind/sdk/bun";

const rewind = initRewind({
  endpoint: Bun.env.REWIND_ENDPOINT ?? "https://rewindrewind.com",
  apiKey: Bun.env.REWIND_PROJECT_KEY!,
  environment: Bun.env.REWIND_ENVIRONMENT ?? "production",
  release: Bun.env.REWIND_RELEASE,
  tags: { service: "edge-api", runtime: "bun" },
});

Bun.serve({
  async fetch(req) {
    try {
      return await handleRequest(req);
    } catch (error) {
      await rewind.captureException(error, {
        request: { method: req.method, url: new URL(req.url).pathname },
      });
      return new Response("Internal Server Error", { status: 500 });
    }
  },
});

Ruby Apps

gem install rewind_rewind
require "rewind_rewind"

REWIND = RewindRewind::Client.new(
  api_key: ENV.fetch("REWIND_PROJECT_KEY"),
  endpoint: ENV.fetch("REWIND_ENDPOINT", "https://rewindrewind.com"),
  environment: ENV.fetch("RACK_ENV", "production"),
  release: ENV["REWIND_RELEASE"],
  tags: { service: "web", runtime: "ruby" }
)
begin
  charge_customer(customer_id)
rescue => error
  REWIND.capture_exception(error, {
    action: "charge_customer",
    customer_id: customer_id
  })
  raise
end

Rack apps can wrap the application with REWIND.rack.call(app). Background jobs can use REWIND.job_wrapper(queue: "critical").

Python Apps

The framework-agnostic rewind-rewind package installs from the hosted index (no PyPI account involved) and depends only on the standard library. It imports as rewind_rewind.

pip install --index-url https://rewindrewind.com/pypi/simple/ rewind-rewind
# requirements.txt:
#   --extra-index-url https://rewindrewind.com/pypi/simple/
#   rewind-rewind>=1.0.0
import rewind_rewind

rewind_rewind.init(
    project_key=os.environ["REWIND_PROJECT_KEY"],  # rrpub_...
    environment="production",
    release=os.environ.get("GIT_SHA"),
)

try:
    process_invoice(invoice_id)
except Exception as error:
    rewind_rewind.capture_exception(error, extra={"invoice_id": invoice_id})
    raise

rewind_rewind.capture_event("invoice.paid", properties={"total": invoice.total})

Flask / WSGI

Wrap the app: app.wsgi_app = RewindMiddleware(app.wsgi_app) (from rewind_rewind.wsgi) to auto-capture unhandled request exceptions with request context.

Plain scripts & workers

Call rewind_rewind.capture_exception(e) in your except blocks. Captures never raise and run on a short timeout, so they won't stall a job.

in_app frames

Set project_root (defaults to the cwd) so culprits point at your code, not site-packages or the stdlib.

Direct HTTP Capture

curl https://rewindrewind.com/v1/exceptions \
  -H "Authorization: Bearer $REWIND_PROJECT_KEY" \
  -H "Content-Type: application/json" \
  --data '{
    "environment": "production",
    "release": "api@1.0.0",
    "platform": "custom",
    "level": "error",
    "message": "Example failure",
    "exception": { "type": "ExampleError", "value": "Example failure", "stacktrace": [] },
    "tags": { "service": "api" },
    "extra": { "source": "curl" }
  }'

Sentry Envelope Endpoint

Send Sentry event envelopes to POST /v1/sentry/envelope. Authenticate with your public project key: Authorization: Bearer rrpub_xxx or ?sentry_key=rrpub_xxx.

curl "https://rewindrewind.com/v1/sentry/envelope?sentry_key=$REWIND_PROJECT_KEY" \
  -H "Content-Type: application/x-sentry-envelope" \
  --data-binary @envelope.txt

Source Maps

Upload JavaScript source maps with your project's public key (rrpub_…) and use the same release value as the browser SDK.

curl https://rewindrewind.com/v1/source-maps \
  -H "Authorization: Bearer $REWIND_PROJECT_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @- <<'JSON'
{
  "release": "web@1.14.0",
  "file_name": "assets/app.js.map",
  "content": "..."
}
JSON

Verification Checklist

  1. Trigger a handled test exception in every runtime.
  2. Trigger an unhandled browser exception and unhandled promise rejection.
  3. Trigger an unhandled backend exception in a non-production environment.
  4. Confirm events land in the intended project and environment.
  5. Send the same exception twice and confirm issue grouping.
  6. Confirm release, service, and runtime tags are present.
  7. Confirm browser requests are rejected from disallowed origins.
  8. Confirm no server key is present in built frontend assets.
  9. Upload a source map and verify stack frames resolve for that release.

Operational Guidance

The repository also includes the longer Markdown guide at docs/exception-capture-sdk.md for maintainers.