Manual Failure Capture

Capture API failures without using the Dio interceptor — ideal for custom request routing or non-Dio HTTP clients.

When to Use Manual Capture

Basic Usage

Use captureFailure() to manually report a failed request:

await EndpointVault.instance.captureFailure(
  method: 'POST',
  url: 'https://api.example.com/items',
  statusCode: 500,
  errorMessage: 'Internal Server Error',
  requestBody: {'name': 'Item 1', 'price': 29.99},
  requestHeaders: {'Authorization': 'Bearer ***'},
  responseBody: {'error': 'Database connection failed'},
);

Full Parameter Reference

await EndpointVault.instance.captureFailure(
  // Required
  method: 'POST',                    // HTTP method
  url: 'https://api.example.com/endpoint',
  statusCode: 500,                   // Response status code (nullable)

  // Optional - Error details
  errorType: 'server_error',         // Category of error
  errorMessage: 'Internal Server Error',

  // Optional - Request data (will be redacted & encrypted)
  requestHeaders: {'Content-Type': 'application/json'},
  requestBody: {'key': 'value'},

  // Optional - Response data (will be redacted & encrypted)
  responseHeaders: {'X-Request-Id': 'abc123'},
  responseBody: {'error': 'Something went wrong'},

  // Optional - Timing
  duration: Duration(milliseconds: 1500),

  // Optional - Extra metadata (not encrypted, for filtering)
  extra: {
    'userId': 123,
    'feature': 'checkout',
    'retryCount': 2,
  },
);

Integration with Offline Queue

A common pattern is to capture failures when requests fail after being retried from an offline queue:

class OfflineQueueManager {
  Future<void> processRequest(QueuedRequest request) async {
    try {
      final response = await executeRequest(request);
      await handleSuccess(request, response);
    } catch (e) {
      final statusCode = (e is DioException) ? e.response?.statusCode : null;

      // Check if this is a non-retryable error
      if (!isRetryable(statusCode)) {
        await quarantineFailedRequest(request, statusCode);
      }
    }
  }

  Future<void> quarantineFailedRequest(
    QueuedRequest request,
    int? statusCode,
  ) async {
    // Capture failure with EndpointVault
    await EndpointVault.instance.captureFailure(
      method: request.method,
      url: request.url,
      statusCode: statusCode,
      requestBody: request.data,
      requestHeaders: request.headers,
      extra: {
        'requestType': request.type.name,
        'queuedAt': request.createdAt.toIso8601String(),
      },
    );

    // Remove from local queue
    await removeFromQueue(request);
  }
}
Tip: The extra field is useful for storing metadata for filtering in the dashboard, like user IDs or feature names.

Enabling Replay Support

To enable device-side replay for manually captured failures, configure onReplayRequest during SDK initialization. This callback handles all replay requests — including post-replay operations like file uploads:

await EndpointVault.init(
  apiKey: 'your-api-key',
  encryptionKey: 'your-32-char-key',
  onReplayRequest: (eventId, request) async {
    // Re-execute the request
    final response = await dio.request(
      request.url,
      data: request.requestBody,
      options: Options(method: request.method),
    );

    // Handle any post-replay operations here
    // For example, if the response contains upload URLs:
    if (response.data['uploadUrls'] != null) {
      await uploadPendingFiles(response.data['uploadUrls']);
    }

    return response;
  },
);

See Device-side Replay for more details on the replay flow.