Get started
Install
Install by framework, paste the project key, send the first event, then name the person.
You are integrating Bugwalk into an existing application. Bugwalk keeps events, errors, and logs on one timeline per person (after identify()), so an AI debug engineer can explain a failure from that visit and the connected repositories.
Ask these questions one at a time and wait for each answer unless it was already provided:
1. Project key (DSN) from Bugwalk Setup? (placeholder in docs: YOUR_PROJECT_KEY)
2. Frontend stack? Options include: Angular, Next.js, Nuxt, React, Script tag, SvelteKit, Vue
3. Backend stack, if any? Options include: Django, Echo, Express, FastAPI, Fastify, Flask, Gin, Hono, Koa, NestJS, net/http, or none
… full prompt includes every framework snippet.
Run the wizard
In the app you want to watch, run the wizard. It finds the framework, installs the package, and writes the project key from Setup.
The wizard opens a browser tab so you can approve the terminal. Approve it, go back to the terminal, and let it finish. If you would rather paste, use the snippets below. They are the same edits the wizard makes.
- 01
From the app you want to watch
npx @bugwalk/wizard - 02
Replace the project key
Every snippet uses YOUR_PROJECT_KEY. Copy the real key from Setup and paste it in place of that string.
Frontend integrations
Install the package, drop the project key in, and wrap or register the SDK once at startup. Use the same key on the backend half of the app.
Angular
TypeScriptNo paste snippet here yet. Run npx @bugwalk/wizard in this repo and it writes the file for you.
Install
npx @bugwalk/wizardNext.js
TypeScriptInstall
npm install @bugwalk/nextAdd to app/layout.tsx
import { BugwalkProvider } from '@bugwalk/next';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<BugwalkProvider dsn="YOUR_PROJECT_KEY">{children}</BugwalkProvider>
</body>
</html>
);
}Nuxt
TypeScriptNo paste snippet here yet. Run npx @bugwalk/wizard in this repo and it writes the file for you.
Install
npx @bugwalk/wizardReact
TypeScriptInstall
npm install @bugwalk/reactAdd to src/main.tsx
import { BugwalkProvider } from '@bugwalk/react';
createRoot(document.getElementById('root')!).render(
<BugwalkProvider dsn="YOUR_PROJECT_KEY">
<App />
</BugwalkProvider>,
);Script tag
HTMLInstall
<script src="https://cdn.bugwalk.dev/v1.js" data-dsn="YOUR_PROJECT_KEY" async></script>Add to index.html
<script src="https://cdn.bugwalk.dev/v1.js" data-dsn="YOUR_PROJECT_KEY" async></script>SvelteKit
TypeScriptNo paste snippet here yet. Run npx @bugwalk/wizard in this repo and it writes the file for you.
Install
npx @bugwalk/wizardVue
TypeScriptInstall
npm install @bugwalk/vueAdd to src/main.ts
import { bugwalk } from '@bugwalk/vue';
createApp(App).use(bugwalk, { dsn: 'YOUR_PROJECT_KEY' }).mount('#app');Backend integrations
Register middleware or a module before your routes. Requests from an identified browser session attach to the same person automatically.
Django
PythonInstall
pip install "bugwalk-sdk[django]"Add to settings.py
BUGWALK_DSN = "YOUR_PROJECT_KEY"
MIDDLEWARE = [
"bugwalk.django.BugwalkMiddleware",
*MIDDLEWARE,
]Echo
GoNo paste snippet here yet. Run npx @bugwalk/wizard in this repo and it writes the file for you.
Install
go get github.com/bugwalk-dev/bugwalk/sdks/go/echoExpress
TypeScriptInstall
npm install @bugwalk/expressAdd to src/server.ts
import { bugwalk } from '@bugwalk/express';
const app = express();
app.use(bugwalk({ dsn: 'YOUR_PROJECT_KEY' }));FastAPI
PythonInstall
pip install "bugwalk-sdk[fastapi]"Add to main.py
from bugwalk.fastapi import BugwalkMiddleware
app.add_middleware(BugwalkMiddleware, dsn="YOUR_PROJECT_KEY")Fastify
TypeScriptInstall
npm install @bugwalk/fastifyAdd to src/server.ts
import { bugwalk } from '@bugwalk/fastify';
const app = fastify();
await app.register(bugwalk, { dsn: 'YOUR_PROJECT_KEY' });Flask
PythonNo paste snippet here yet. Run npx @bugwalk/wizard in this repo and it writes the file for you.
Install
pip install "bugwalk-sdk[flask]"Gin
GoInstall
go get github.com/bugwalk-dev/bugwalk/sdks/go/ginAdd to main.go
import bugwalkgin "github.com/bugwalk-dev/bugwalk/sdks/go/gin"
router.Use(bugwalkgin.Middleware("YOUR_PROJECT_KEY"))Hono
TypeScriptNo paste snippet here yet. Run npx @bugwalk/wizard in this repo and it writes the file for you.
Install
npx @bugwalk/wizardKoa
TypeScriptNo paste snippet here yet. Run npx @bugwalk/wizard in this repo and it writes the file for you.
Install
npx @bugwalk/wizardNestJS
TypeScriptInstall
npm install @bugwalk/nestjsAdd to src/app.module.ts
import { BugwalkModule } from '@bugwalk/nestjs';
@Module({
imports: [BugwalkModule.forRoot({ dsn: 'YOUR_PROJECT_KEY' })],
})
export class AppModule {}net/http
GoNo paste snippet here yet. Run npx @bugwalk/wizard in this repo and it writes the file for you.
Install
go get github.com/bugwalk-dev/bugwalk/sdks/goJava, .NET, Ruby, and PHP
No Bugwalk package goes into those services. Point the OpenTelemetry agent you already run at Bugwalk and send the project key in a header.
Java, .NET, Ruby, and PHP
OpenTelemetryInstall
OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.bugwalk.devAdd to environment
OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.bugwalk.dev
OTEL_EXPORTER_OTLP_HEADERS=x-bugwalk-key=YOUR_PROJECT_KEYHow the first event arrives
You do not click a button to send the first event. The SDK sends a batch in the background after the app starts. A page view is usually enough.
Setup polls every few seconds. While it is waiting, the corner says Waiting for the first event. When a batch lands, that becomes Open the dashboard. The first page view is often there within a few seconds of the app starting.
If nothing arrives, the app is not running, the project key is a different project, or the request never left the browser. The key on the Setup page has to be the one in the snippet.
Name the person
Events record even if you never say who they belong to. They show up on Overview as sessions. They do not show up under People, and they are not a journey, until you call identify().
Call it once you know the id or email, usually right after sign-in. Events before and after that call join the same person, including requests the backend made during the visit.
People shows the email when you sent one, otherwise the id. Search accepts either, including a partial email.
Call once you know who the person is. Everything before and after joins them.
// Same call from React, Vue, Next.js, and the script tag SDK.
bugwalk.identify({ id: user.id, email: user.email });When you have an internal id but no email yet.
bugwalk.identify({ id: user.id });When email is what support already has.
bugwalk.identify({ email: user.email });After the next event, People looks like this. Email is the title when you sent one.
sam@example.com
u_42
3 sessions · 2 h ago
u_8841
1 session · just now
Search on People accepts sam@ or u_42. Open a row to see their journey.
Background work
A cron, a queue message, a webhook, or a script answers two questions, and either one can be left out. What is the work? Pass a stable id and a kind: cron, queue, or task. Who is it for? Pass the same id or email you would pass to identify().
withWork names the run and starts a new session. forPerson attributes one iteration to a person, then clears that person so the next iteration does not inherit them. Call forPerson inside withWork when the job runs for many people. Call forPerson alone when the work is for one person and has no job name.
A run with no person shows on Overview as a session. It does not show under People. A run that calls forPerson shows each person under People, the same way a signed-in visit does. An error is still an issue. It names the person when one was set, and it keeps the job when the run was named.
Leave identify() for web requests. Calling it with an id such as cron:daily-digest files that string under People.
Nobody owns this run. It shows on Overview as a session. It does not show under People.
import { withWork } from '@bugwalk/node';
await withWork({ id: 'temp-cleanup', kind: 'cron' }, async () => {
await deleteOldFiles();
});The job is the whole run. forPerson uses the same id and email as identify(), on that person’s own session, then clears them.
import { forPerson, withWork } from '@bugwalk/node';
await withWork({ id: 'daily-digest', kind: 'cron' }, async () => {
for (const person of people) {
await forPerson({ id: person.id, email: person.email }, async () => {
await sendDigest(person);
});
}
});kind is queue. The person is still forPerson, not a fake identify() id.
await withWork({ id: 'process-order', kind: 'queue' }, async () => {
await forPerson({ id: message.personId }, async () => {
await fulfill(message);
});
});An invoice, a webhook, or a retry. forPerson alone puts them under People. Add withWork with kind task when you also want a name on the run.
await forPerson({ id: person.id, email: person.email }, async () => {
await sendInvoice(person);
});
await withWork({ id: 'send-invoice', kind: 'task' }, async () => {
await forPerson({ id: person.id }, () => sendInvoice(person));
});@Cron sits above @BugwalkJob so the scheduler calls the wrapped method. The decorator does not walk the list. The loop calls forPerson.
import { BugwalkJob, forPerson } from '@bugwalk/nestjs';
@Cron('0 2 * * *')
@BugwalkJob({ id: 'daily-digest', kind: 'cron' })
async sendDigests() {
for (const person of await this.people.due()) {
await forPerson({ id: person.id, email: person.email }, () => this.mailer.send(person));
}
}with_work and for_person. The person does not stick to the process.
def send(person):
bugwalk_sdk.capture("digest_sent")
def run(_run):
for person in people:
bugwalk_sdk.for_person(
lambda person=person: send(person),
id=person["id"],
email=person["email"],
)
bugwalk_sdk.with_work(run, id="daily-digest", kind="cron")Pass the context into Capture. Identify on the client would leak across goroutines.
err := bugwalk.WithWork(ctx, bugwalk.Work{ID: "daily-digest", Kind: "cron"}, func(ctx context.Context, _ bugwalk.Run) error {
for _, person := range people {
if err := bugwalk.ForPerson(ctx, bugwalk.Person{ID: person.ID, Email: person.Email}, func(ctx context.Context) error {
return sendDigest(ctx, person)
}); err != nil {
return err
}
}
return nil
})Java, .NET, Ruby, and PHP set bugwalk.job.id on the span. bugwalk.job.runner is cron, queue, or task. Person attributes stay as they are. One span per person is the same record as one forPerson call.