Documentation
Verify and Observe
Check Status
RevclustStatus | What it means for the integration |
|---|---|
disabled | The SDK has not been initialized in this app process yet. |
initializing | Setup is still in progress. |
ready | The SDK is ready to capture and upload. |
degraded | Setup is unhealthy but not fully broken. Upload connectivity may be down, or local capture may have failed during startup. |
uploadBlocked | The SDK can still queue locally, but it cannot upload right now. |
misconfigured | The SDK key is missing, malformed, or otherwise invalid for bootstrap. |
notProvisioned | The SDK key is well-formed but unavailable or revoked. |
captureInvariantFailure(...) is always allowed in ready and uploadBlocked. In degraded, it only works if the problem is upload connectivity. If local capture failed during startup, captureInvariantFailure(...) is still blocked. It is always blocked in disabled, initializing, misconfigured, and notProvisioned.
Check Upload Activity
uploadSnapshot.pendingCount tells you how many captures are waiting to upload. uploadSnapshot.uploadingCount tells you how many captures are actively uploading, and uploadSnapshot.lastErrorCode gives the best-known coarse queue or upload error after a failure. A first healthy run usually moves from one queued capture to one active upload and then back to zero.
final RevclustUploadSnapshot snapshot = revclust.uploadSnapshot;
final int pendingCount = snapshot.pendingCount;
final int uploadingCount = snapshot.uploadingCount;
final RevclustUploadErrorCode? lastErrorCode = snapshot.lastErrorCode;
Watch Upload Events
RevclustUploadStarted means a queued capture has started uploading. RevclustUploadAccepted means the backend accepted it. RevclustUploadRejected means the backend rejected it. RevclustTransportFailure means upload failed before the backend could accept or reject it.
revclust.uploadEvents.listen((RevclustUploadEvent event) {
if (event is RevclustUploadAccepted) {
final RevclustAcceptedResult result = event.result;
final String packId = result.packId;
final Uri? viewerUrl = result.viewerUrl;
}
if (event is RevclustUploadRejected) {
final RevclustRejectionCode code = event.code;
final String? message = event.message;
}
if (event is RevclustTransportFailure) {
final int? statusCode = event.statusCode;
final bool retryable = event.retryable;
final String? message = event.message;
}
});
Use One Simple First-Pass Observer
For the first pass, use one simple loop: subscribe before capture, keep the returned captureId, replay any early events, ignore unrelated captures, and stop waiting after a clear timeout.
import "dart:async";
import "package:flutter/foundation.dart";
import "package:revclust_flutter_sdk/revclust_flutter.dart";
Future<void> verifyFirstAcceptedIncident({
required Revclust revclust,
required RevclustInvariantFailure failure,
}) async {
debugPrint("Revclust status=${revclust.status.name}");
final Completer<void> finished = Completer<void>();
final List<RevclustUploadEvent> bufferedEvents = <RevclustUploadEvent>[];
String? captureId;
void finish() {
if (!finished.isCompleted) {
finished.complete();
}
}
void handleEvent(RevclustUploadEvent event) {
final String? activeCaptureId = captureId;
if (activeCaptureId == null) {
bufferedEvents.add(event);
return;
}
if (event.captureId != activeCaptureId) {
return;
}
if (event is RevclustUploadAccepted) {
debugPrint("Accepted packId=${event.result.packId}");
debugPrint("Accepted viewerUrl=${event.result.viewerUrl}");
finish();
return;
}
if (event is RevclustUploadRejected) {
debugPrint("Rejected code=${event.code} message=${event.message}");
finish();
return;
}
if (event is RevclustTransportFailure && !event.retryable) {
debugPrint(
"Transport failure statusCode=${event.statusCode} message=${event.message}",
);
finish();
}
}
late final StreamSubscription<RevclustUploadEvent> subscription;
subscription = revclust.uploadEvents.listen(handleEvent);
try {
final RevclustCaptureOutcome outcome =
await revclust.captureInvariantFailure(failure);
debugPrint("Immediate capture outcome=${outcome.runtimeType}");
if (outcome is! RevclustCaptureQueued) {
return;
}
captureId = outcome.captureId;
for (final RevclustUploadEvent event
in List<RevclustUploadEvent>.of(bufferedEvents)) {
handleEvent(event);
}
bufferedEvents.clear();
await finished.future.timeout(const Duration(seconds: 30), onTimeout: () {
final RevclustUploadSnapshot snapshot = revclust.uploadSnapshot;
debugPrint("Timed out waiting for acceptance");
debugPrint("pending=${snapshot.pendingCount}");
debugPrint("uploading=${snapshot.uploadingCount}");
debugPrint("lastErrorCode=${snapshot.lastErrorCode}");
});
} finally {
await subscription.cancel();
}
}
Keep that observer next to the shared Revclust client. If you do not see an accepted event within thirty seconds, move to Troubleshooting.
What Success Looks Like
Success looks like this: Revclust.initialize(...) returns a client, status settles at ready, the first invariant failure returns RevclustCaptureQueued, upload activity drains back to zero, and uploadEvents emits RevclustUploadAccepted. Open the incident page with the direct incident link (viewerUrl) or the incident ID (packId).
When To Stop And Fix Setup
Stop and fix setup if status stays at misconfigured or notProvisioned, the first invariant failure returns RevclustCaptureBlocked, uploadSnapshot.lastErrorCode stays non-null after repeated attempts, you see RevclustUploadRejected, or you have an accepted packId but the right Revclust account still cannot open it.
If the happy path does not complete, include the visible status, the immediate capture result, lastErrorCode, and any upload event code in your report.