Start here

Quickstart: capture to verified fix

About 30 minutes. You'll install the CLI, capture a planted bug in one of the demo apps, replay it to REPRODUCED, fix it, and verify to FIXED. Then move the same steps to your own app.

Before you start

From your BugCapsule team you need the project's API URL, ingest key (bci_…) and secret key (bcs_…). The ingest key goes into apps and is treated as public; the secret key is for the CLI only and must never ship in an app.

ForYou need
CLIFlutter 3.35+ (the CLI is a Dart tool shipped with the Flutter package), git
Flutter / iOS demoXcode with an iOS simulator
Android demoAndroid SDK, an emulator (API 35 tested), JDK 17
Web demoNode 20+; your origin allowed by your BugCapsule team (see Web)

1. Install the CLI and log in

dart pub global activate --source git https://github.com/draz26648/bug_capsule_flutter_sdk --git-path packages/bug_capsule
bugcapsule login --url https://api.bugcapsule.com --secret-key bcs_... --ingest-key bci_...
bugcapsule list                      # empty for a new project

Details and alternatives: CLI install.

2. Capture the first capsule

Clone the SDK repository for your platform and pick its demo. The Flutter, iOS and web demos share a planted bug: a coupon SAVE100 that takes a fixed 100 off, so a cart under 100 gets a negative total and fires check("cart_total_non_negative").

cd bug_capsule_flutter_sdk/example/bug_capsule_demo
flutter run -d <simulator> \
  --dart-define=BUGCAPSULE_GIT_SHA=$(git rev-parse HEAD) \
  --dart-define=BUGCAPSULE_BACKEND_URL=https://api.bugcapsule.com \
  --dart-define=BUGCAPSULE_INGEST_KEY=bci_...

Open the cart until the subtotal is below $100, type SAVE100, apply.

cd bug_capsule_android_sdk
export BUGCAPSULE_INGEST_KEY=bci_...
./gradlew :demo:installDebug -PbugcapsuleBackendUrl=https://api.bugcapsule.com

On Android, use the race bug, whose fix toggle verifies cleanly: tap Refresh home until the app crashes (~1 in 4, recommendations arrive before the profile). The crash capsule is written synchronously and uploaded on the next launch, so open the app again. The project folder for the CLI is examples/demo.

cd bug_capsule_ios_sdk/Examples/Demo
xcodebuild test -project BugCapsuleDemo.xcodeproj -scheme BugCapsuleDemo \
  -destination 'id=<simulator udid>' -collect-test-diagnostics never \
  BUGCAPSULE_BACKEND_URL=https://api.bugcapsule.com BUGCAPSULE_INGEST_KEY=bci_... \
  -only-testing:BugCapsuleDemoUITests/CaptureFlowTests/testCaptureNegativeTotal

The UI test drives the app into the bug for you.

cd bug_capsule_web_sdk && npm install && npm run build
node examples/shop/api-server.mjs &              # the demo shop's own API on :4318
cd examples/shop
VITE_BUGCAPSULE_BACKEND_URL=https://api.bugcapsule.com VITE_BUGCAPSULE_INGEST_KEY=bci_... \
VITE_BUGCAPSULE_ALLOW_REPLAY=true npx vite build && npx vite preview --port 4317 --strictPort

Open http://127.0.0.1:4317/, build a cart under 100, apply SAVE100. Stop the preview server before replaying: the CLI refuses to start when something already serves the app URL.

The app writes a capsule and uploads it. The upload waits until the app has fetched its capture settings once, so give it a second.

bugcapsule list                      # status new, eligibility eligible
bugcapsule inspect <id>              # failure, eligibility, network, timeline
bugcapsule inspect <id> --json | less   # check nothing is in there that shouldn't be

3. Replay it

Run from the demo's project folder (or pass -p):

bugcapsule run <id> -n 3             # add -d <simulator|avd> to pick a device

Expected: REPRODUCED 3/3, exit 0, capsule status reproduced. If you get INCONCLUSIVE, the output lists why (for example an action id that isn't registered in the replayed build); see Troubleshooting.

4. Fix and verify

Fix lib/shop.dart: clamp the coupon discount to the subtotal. Then:

bugcapsule verify <id> -d <simulator> -n 5

The demo has build-time fix toggles (race, price, legacy), so no source change is needed. Only race keeps the recorded request sequence; the others change which requests the app makes and verify as INCONCLUSIVE by design.

BUGCAPSULE_ANDROID_GRADLE_ARGS=-PbugcapsuleDemoFix=race bugcapsule verify <id> -p examples/demo -n 3
BUGCAPSULE_IOS_BUILD_SETTINGS='DEMO_FIXES=FIX_NEGATIVE_TOTAL' bugcapsule verify <id> -p Examples/Demo -n 3

Set the demo's fix toggle in the build environment:

VITE_BUGCAPSULE_DEMO_FIX=coupon bugcapsule verify <id> -n 3

Expected: FIXED — failure gone in N/N valid runs, exit 0, status fixed. Anything less than every requested attempt running validly is INCONCLUSIVE (exit 3) and doesn't change the status.

5. Keep it as a regression test

bugcapsule regress add <id> --name "coupon over subtotal"
bugcapsule regress run               # exit 1 if any test fails; wire it into CI

Next: run regressions in CI.

Now do it in your own app