Crash Reporting
Real-time error monitoring and crash reporting via REST API.
Overview
The Crash Reporting tool helps you monitor errors and crashes in your applications. Submit crashes via our REST API from any platform or language.
Features
- Real-time crash monitoring
- Smart crash grouping by stack trace
- Session tracking and crash-free metrics
- Custom events and breadcrumbs
- File attachments (screenshots, logs)
- Email and webhook alerts
- Configurable data retention
Getting Started
- Open your project in SureProgramming
- In the Tools section, enable "Crashes"
- Go to Settings > API Keys and copy your SDK key
- Integrate the REST API into your application
API Base URL
https://sureprogramming.com/api/crashes
Authentication
Include your SDK key in the Authorization header:
Authorization: Bearer sp_xxxxxxxx_yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
Submit a Crash
POST to /api/crashes/submit
Request Body
{
"exception_type": "NullPointerException",
"message": "Cannot read property 'x' of null",
"stack_trace": "Error: Cannot read property 'x' of null\n at MyClass.method (app.js:42:15)\n at main (app.js:10:5)",
"app_version": "1.2.3",
"environment": "production",
"os_name": "iOS",
"os_version": "17.0",
"device_model": "iPhone 15",
"user_identifier": "user_123",
"session_id": "sess_abc123",
"breadcrumbs": [
{"type": "navigation", "message": "Opened settings", "timestamp": "2026-09-14T10:30:00Z"},
{"type": "user", "message": "Tapped save button", "timestamp": "2026-09-14T10:30:05Z"}
],
"custom_data": {
"subscription_tier": "premium",
"feature_flags": {"dark_mode": true}
}
}
Response
{
"success": true,
"data": {
"crash_id": 12345,
"issue_id": 67,
"is_new_issue": false,
"attachments": []
}
}
With Attachments
Use multipart/form-data to include files:
curl -X POST https://sureprogramming.com/api/crashes/submit \
-H "Authorization: Bearer YOUR_SDK_KEY" \
-F "exception_type=Error" \
-F "message=App crashed" \
-F "stack_trace=..." \
-F "attachments[]=@screenshot.png" \
-F "attachments[]=@debug.log"
Sessions
Track user sessions to calculate crash-free rates.
Start Session
POST to /api/crashes/sessions/start
{
"session_id": "sess_unique_id",
"app_version": "1.2.3",
"os_name": "Android",
"os_version": "14",
"environment": "production",
"user_identifier": "user_123"
}
End Session
POST to /api/crashes/sessions/end
{
"session_id": "sess_unique_id"
}
Events
Track non-fatal events, warnings, and custom telemetry.
POST to /api/crashes/events
{
"name": "payment_failed",
"category": "billing",
"level": "warning",
"message": "Card declined",
"session_id": "sess_abc123",
"properties": {
"payment_method": "credit_card",
"error_code": "insufficient_funds"
}
}
Event levels: debug, info, warning, error
Platform Examples
JavaScript (Browser)
<script>
window.SureProgrammingConfig = {
sdkKey: 'YOUR_SDK_KEY',
appVersion: '1.0.0',
environment: 'production'
};
</script>
<script src="https://sureprogramming.com/assets/js/crash-reporter.js"></script>
<script>
// Errors are captured automatically
// Manual reporting:
SureProgramming.reportError(new Error('Something went wrong'), {
userId: 'user_123',
context: 'checkout_flow'
});
// Set user
SureProgramming.setUser('user_123');
// Add breadcrumb
SureProgramming.addBreadcrumb('user', 'Clicked checkout');
</script>
Swift (iOS)
This is a minimal, working REST transport — it has no retry queue, offline persistence, or native crash capture. Adapt it or copy the .NET example's approach into your own crash-handling code.
import UIKit
class CrashReporter {
static let shared = CrashReporter(apiKey: "YOUR_SDK_KEY")
private let apiKey: String
private let baseURL = URL(string: "https://sureprogramming.com/api/crashes")!
private let sessionId = UUID().uuidString
init(apiKey: String) {
self.apiKey = apiKey
startSession()
}
func report(_ error: Error, customData: [String: Any]? = nil) {
post(path: "submit", body: [
"exception_type": String(describing: type(of: error)),
"message": error.localizedDescription,
"stack_trace": Thread.callStackSymbols.joined(separator: "\n"),
"session_id": sessionId,
"custom_data": customData ?? [:]
])
}
private func startSession() {
post(path: "sessions/start", body: [
"session_id": sessionId,
"os_name": "iOS",
"os_version": UIDevice.current.systemVersion
])
}
private func post(path: String, body: [String: Any]) {
var request = URLRequest(url: baseURL.appendingPathComponent(path))
request.httpMethod = "POST"
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try? JSONSerialization.data(withJSONObject: body)
URLSession.shared.dataTask(with: request) { _, response, error in
if let error = error {
print("Crash report submission failed: \(error)")
return
}
if let http = response as? HTTPURLResponse, !(200...299).contains(http.statusCode) {
print("Crash report rejected with status \(http.statusCode)")
}
}.resume()
}
}
Kotlin (Android)
Uses HttpURLConnection so the snippet has no extra
dependency; swap in OkHttp/Retrofit if your app already uses one.
Run post() off the main thread (e.g. from a background
dispatcher) — it blocks on network I/O as written.
class CrashReporter(private val apiKey: String) {
private val baseUrl = "https://sureprogramming.com/api/crashes"
private val sessionId: String = UUID.randomUUID().toString()
init {
startSession()
Thread.setDefaultUncaughtExceptionHandler { _, throwable ->
reportCrash(throwable)
}
}
fun reportCrash(throwable: Throwable, customData: Map<String, Any>? = null) {
val payload = JSONObject().apply {
put("exception_type", throwable.javaClass.name)
put("message", throwable.message ?: "Unknown error")
put("stack_trace", Log.getStackTraceString(throwable))
put("session_id", sessionId)
put("custom_data", JSONObject(customData ?: emptyMap<String, Any>()))
}
post("/submit", payload)
}
private fun startSession() {
val payload = JSONObject().apply {
put("session_id", sessionId)
put("os_name", "Android")
put("os_version", Build.VERSION.RELEASE)
}
post("/sessions/start", payload)
}
private fun post(path: String, payload: JSONObject) {
val connection = URL(baseUrl + path).openConnection() as HttpURLConnection
connection.requestMethod = "POST"
connection.doOutput = true
connection.setRequestProperty("Authorization", "Bearer $apiKey")
connection.setRequestProperty("Content-Type", "application/json")
connection.outputStream.use { it.write(payload.toString().toByteArray()) }
val status = connection.responseCode
if (status !in 200..299) {
Log.w("CrashReporter", "Crash report rejected with status $status")
}
connection.disconnect()
}
}
C# (.NET)
See the downloadable CrashReportingExample.csproj for a
fuller, compiled/tested version of this. Notably it builds the
request body as buffered StringContent rather than
PostAsJsonAsync — in some hosting/runtime combinations,
PostAsJsonAsync negotiates chunked transfer encoding with
no Content-Length header, which this API does not parse
(the request is treated as empty and every field is rejected as
missing, even though the JSON was fully populated).
public class CrashReporter
{
private readonly HttpClient _client;
private readonly string _sessionId = Guid.NewGuid().ToString();
public CrashReporter(string apiKey)
{
_client = new HttpClient();
_client.BaseAddress = new Uri("https://sureprogramming.com/api/crashes/");
_client.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}");
StartSession();
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
}
public async Task<bool> ReportAsync(Exception ex, Dictionary<string, object>? customData = null)
{
var payload = new
{
exception_type = ex.GetType().Name,
message = ex.Message,
stack_trace = ex.StackTrace,
session_id = _sessionId,
custom_data = customData
};
return await PostAsync("submit", payload);
}
private void StartSession()
{
_ = PostAsync("sessions/start", new
{
session_id = _sessionId,
os_name = Environment.OSVersion.Platform.ToString(),
os_version = Environment.OSVersion.VersionString
});
}
// Buffered StringContent, not PostAsJsonAsync — see note above.
private async Task<bool> PostAsync(string path, object payload)
{
using var content = new StringContent(
JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
var response = await _client.PostAsync(path, content);
if (!response.IsSuccessStatusCode)
{
Console.Error.WriteLine($"Crash report rejected with status {(int)response.StatusCode}");
}
return response.IsSuccessStatusCode;
}
private void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
if (e.ExceptionObject is Exception ex)
ReportAsync(ex).Wait();
}
}
Python
import requests
import traceback
import uuid
import sys
class CrashReporter:
def __init__(self, api_key):
self.api_key = api_key
self.base_url = "https://sureprogramming.com/api/crashes"
self.session_id = str(uuid.uuid4())
self._start_session()
sys.excepthook = self._handle_exception
def report(self, exception, custom_data=None):
payload = {
"exception_type": type(exception).__name__,
"message": str(exception),
"stack_trace": traceback.format_exc(),
"session_id": self.session_id,
"custom_data": custom_data or {}
}
self._post("/submit", payload)
def _start_session(self):
import platform
self._post("/sessions/start", {
"session_id": self.session_id,
"os_name": platform.system(),
"os_version": platform.release()
})
def _handle_exception(self, exc_type, exc_value, exc_tb):
self.report(exc_value)
sys.__excepthook__(exc_type, exc_value, exc_tb)
def _post(self, path, payload):
response = requests.post(
f"{self.base_url}{path}",
json=payload,
headers={"Authorization": f"Bearer {self.api_key}"}
)
if not response.ok:
print(f"Crash report rejected with status {response.status_code}")
Alerts & Integrations
Configure email alerts and webhook integrations in your project settings:
- Email alerts - Get notified of new crashes, regressions, and spikes
- Slack - Post crash notifications to a channel
- Discord - Send alerts to Discord webhooks
- Custom webhooks - Integrate with any service
Best Practices
- Start sessions early - Call session start at app launch for accurate crash-free metrics
- Include context - Add user IDs, breadcrumbs, and custom data to help debug issues
- Use environments - Separate development, staging, and production crashes
- Handle offline - Queue crashes locally and submit when connectivity returns