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.
Collector and API Keys
The hosted collector is https://rewindrewind.com. Self-hosted Workers can use their own Worker origin in the same SDK options.
- Use a
browser_publickey for browser apps and configure allowed origins. - Use a
server_secretkey for backend services, workers, jobs, and source-map uploads. - Never put a
server_secretkey in browser JavaScript, HTML, mobile bundles, or source maps. - Set stable
environmentvalues such asproduction,staging, andpreview. - Set
releaseto the deployed artifact version, package version, build number, or git SHA.
REWIND_ENDPOINT=https://rewindrewind.com
REWIND_API_KEY=rr_xxx_secret
REWIND_BROWSER_API_KEY=rr_xxx_public
REWIND_ENVIRONMENT=production
REWIND_RELEASE=web@1.14.0
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 }
]
},
"user": { "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_API_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_BROWSER_API_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.
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.setUser({ id: user.id, email: user.email });
rewind.setTags({ plan: user.plan });
rewind.setUser(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_BROWSER_API_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_API_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_API_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_API_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
A Python package is not published yet. Until then, send RewindRewind-native JSON with a small helper around urllib.request.
from rewind_rewind import rewind
try:
process_invoice(invoice_id)
except Exception as error:
rewind.capture_exception(error, {"invoice_id": invoice_id})
raise
rewind.user = {"id": user.id, "email": user.email}
rewind.capture_event("invoice.paid", {"invoice_id": invoice.id, "total": invoice.total})
Flask
Connect got_request_exception and capture the exception with method and path metadata.
Django
Add middleware with process_exception near the top of MIDDLEWARE.
FastAPI and ASGI
Wrap the ASGI app and capture exceptions before re-raising.
Direct HTTP Capture
curl https://rewindrewind.com/v1/exceptions \
-H "Authorization: Bearer $REWIND_API_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 Authorization: Bearer rr_xxx or ?sentry_key=rr_xxx.
curl "https://rewindrewind.com/v1/sentry/envelope?sentry_key=$REWIND_API_KEY" \
-H "Content-Type: application/x-sentry-envelope" \
--data-binary @envelope.txt
Source Maps
Upload JavaScript source maps with a backend server_secret key and use the same release value as the browser SDK.
curl https://rewindrewind.com/v1/source-maps \
-H "Authorization: Bearer $REWIND_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @- <<'JSON'
{
"release": "web@1.14.0",
"file_name": "assets/app.js.map",
"content": "..."
}
JSON
Verification Checklist
- Trigger a handled test exception in every runtime.
- Trigger an unhandled browser exception and unhandled promise rejection.
- Trigger an unhandled backend exception in a non-production environment.
- Confirm events land in the intended project and environment.
- Send the same exception twice and confirm issue grouping.
- Confirm
release,service, andruntimetags are present. - Confirm browser requests are rejected from disallowed origins.
- Confirm no server key is present in built frontend assets.
- Upload a source map and verify stack frames resolve for that release.
Operational Guidance
- Initialize SDKs before application code that may throw.
- Capture at runtime boundaries: browser entrypoints, route handlers, job runners, message consumers, cron handlers, and CLI command wrappers.
- Rethrow after capture unless the application intentionally handles the error.
- Await capture or call
flush()before process exit, job completion, or page unload when losing the event would matter. - Keep tags low-cardinality. Put unique IDs in
extraor event properties, not tags. - Configure blocked property names for tokens, passwords, cookies, and secrets.
The repository also includes the longer Markdown guide at docs/exception-capture-sdk.md for maintainers.