Quick Start
From zero to your first incident alert in under 5 minutes. No SDK required — SyncGuard is a plain REST API.
1. Create a workspace
curl -X POST https://api.syncguard.io/api/v1/workspaces \ -H "Authorization: Bearer <your_jwt>" \ -H "Content-Type: application/json" \ -d '{"name": "My Team"}'
{ "isSuccess": true, "data": { "workspace": { "id": "3fa1c2d4-...", "name": "My Team", "apiKeyPrefix": "sg_a3f9d2c", "isActive": true }, // Store this — it will not be shown again "apiKey": "sg_a3f9d2c814e77b9f5a6d3e2c0b1f8e4d7a9c5b3" } }
2. Send your first event
curl -X POST https://api.syncguard.io/api/v1/events \ -H "X-Api-Key: sg_a3f9d2c814e77b9f5a6d3e2c0b1f8e4d7a9c5b3" \ -H "Content-Type: application/json" \ -d '{ "integrationKey": "payments-service", "eventType": "token_refresh_failed", "errorCode": "401", "tenantId": "acme-corp" }'
{ "isSuccess": true, "data": { "eventId": "b2e3a1f4-...", "incidentId": "inc_8f2a1c9d-...", "incidentStatus": "Open", "patternId": "auth_failure", "confidence": "High", "isNewIncident": true } }
auth_failure pattern, opened an incident, and will alert your configured channels automatically.Authentication
All client API calls are authenticated with an API key. You get your key when you create a workspace — it is shown only once, so store it securely.
API Key
Pass the key in the X-Api-Key header on every request. Keys always start with sg_ followed by 48 hex characters.
curl -X POST https://api.syncguard.io/api/v1/events \ -H "X-Api-Key: sg_a3f9d2c814e77b9f5a6d3e2c0b1f8e4d7a9c5b3" \ -H "Content-Type: application/json" \ -d '{ ... }'
Rotate your key
If a key is compromised, rotate it immediately. The old key stops working the instant the new one is issued.
curl -X POST https://api.syncguard.io/api/v1/workspaces/rotate-key \ -H "X-Api-Key: sg_a3f9d2c814e77b9f5a6d3e2c0b1f8e4d7a9c5b3"
{ "isSuccess": true, "data": { "workspace": { "id": "3fa1c2d4-...", "name": "My Team", "apiKeyPrefix": "sg_b9e1f3a" }, // New key — shown once, store it now "apiKey": "sg_b9e1f3a0c2d4e6f8a0b2c4d6e8f0a2b4c6d8e0f2" } }
SYNCGUARD_API_KEY=sg_...Send Events
POST /api/v1/events is the core endpoint. It runs the 3-stage pipeline: resolve tenant → classify pattern → open or update incident.
Request body
{ // Required "integrationKey": "payments-service", // groups related events into incidents "eventType": "webhook_failed", // matched against pattern triggers "tenantId": "acme-corp", // your customer's external ID // Optional — improves confidence scoring "errorCode": "503", "rawPayload": { // any additional context (stored as JSONB) "endpoint": "/v2/webhooks/deliver", "durationMs": 12043, "attemptNumber": 3 } }
Successful response — new incident opened
{ "isSuccess": true, "data": { "eventId": "b2e3a1f4-7c8d-4e9f-a1b2-c3d4e5f6a7b8", "incidentId": "9d8c7b6a-5e4f-3d2c-1b0a-f9e8d7c6b5a4", "incidentStatus": "Open", "patternId": "webhook_failure_spike", "confidence": "High", "isNewIncident": true, "affectedTenants": ["acme-corp"] } }
Event grouped into existing incident
{ "isSuccess": true, "data": { "eventId": "a1b2c3d4-...", "incidentId": "9d8c7b6a-...", // same incident "incidentStatus": "Open", "patternId": "webhook_failure_spike", "confidence": "High", "isNewIncident": false, // grouped, not opened again "affectedTenants": ["acme-corp", "beta-ltd"] // now 2 tenants } }
Rate limit exceeded
{ "isSuccess": false, "statusCode": 429, "message": "Rate limit exceeded. Try again in 43 seconds." // Retry-After header is also set }
Patterns
Twelve built-in patterns cover the most common integration failure modes. Evaluated in priority order — first match wins. You enable/disable per workspace and optionally set custom alert thresholds.
| Pattern ID | Priority | Triggers on |
|---|---|---|
| auth_failure | 1 | token_refresh_failed · auth_failed · errorCode 401/403/invalid_grant/token_expired |
| webhook_failure_spike | 2 | webhook_failed · webhook_timeout · webhook_undeliverable · ≥10 events / 5 min |
| rate_limit_exceeded | 3 | rate_limit_exceeded · throttled · errorCode 429/rate_limit/too_many_requests |
| timeout_spike | 4 | request_timeout · connection_timeout · gateway_timeout · ≥5 events / 3 min |
| task_canceled_spike | 5 | task_canceled · operation_canceled · request_canceled · ≥5 events / 3 min |
| validation_failure_spike | 6 | validation_failed · schema_validation_failed · payload_invalid · errorCode 400/422 |
| server_error_spike | 7 | server_error · bad_gateway · service_unavailable · errorCode 500/502/503/504 |
| connection_failure | 8 | connection_failed · connection_refused · connection_reset · host_unreachable · errorCode econnrefused/econnreset · ≥3 events / 2 min |
| service_unavailable | 9 | service_unavailable · dependency_failed · circuit_open · upstream_unavailable · errorCode 503/circuit_open |
| data_sync_failure | 10 | sync_failed · sync_error · data_sync_failed · record_sync_failed · batch_sync_failed · errorCode sync_error/data_mismatch/mapping_error/duplicate_record |
| ssl_certificate_error | 11 | ssl_error · tls_error · certificate_error · tls_handshake_failed · certificate_expired · errorCode ssl_error/certificate_expired/hostname_mismatch |
| quota_exceeded | 12 | quota_exceeded · limit_reached · storage_full · seats_exhausted · resource_exhausted · errorCode quota_exceeded/storage_full/over_quota/account_limit |
Confidence scoring
// High — both eventType AND errorCode match // Medium — only one of the two matches // (Spike patterns are always High confidence) { "eventType": "auth_failed", // ✓ matches auth_failure "errorCode": "401" // ✓ matches auth_failure // → confidence: "High" } { "eventType": "auth_failed", // ✓ matches auth_failure "errorCode": "500" // ✗ not an auth error code // → confidence: "Medium" }
Set a custom alert threshold
curl -X PATCH https://api.syncguard.io/api/v1/patterns/webhook_failure_spike \ -H "X-Api-Key: sg_..." \ -H "Content-Type: application/json" \ -d '{ "isEnabled": true, "thresholdCount": 20, "thresholdWindowSeconds": 300 }'
With a threshold set, the incident opens silently. The alert fires only once the event count inside the window reaches thresholdCount.
Incidents
Incidents are opened automatically by the pipeline. They follow a strict lifecycle managed by background timers — no manual state flipping required.
Lifecycle
// Open → new event matched a pattern, threshold met // Recovering → no new events for RecoveryWindow (default 5 min) // Closed → stable during CloseWindow (default 10 min) // Reopened → new event arrives within CooldownWindow (15 min) after Close // → same incident is reused, ReopenCount++ // Manually resolved at any time via POST /incidents/{id}/resolve
Full incident object
{ "id": "9d8c7b6a-5e4f-3d2c-1b0a-f9e8d7c6b5a4", "workspaceId": "3fa1c2d4-...", "integrationKey": "payments-service", "patternId": "auth_failure", "patternName": "Auth Failure", "status": "Open", // Open | Recovering | Closed "confidence": "High", "openedAt": "2025-03-26T08:14:22Z", "recoveringAt": null, "closedAt": null, "reopenedAt": null, "reopenCount": 0, "eventCount": 14, "tenantCount": 3, "isChronic": false, // true when reopenCount >= 3 "affectedTenants": [ { "tenantId": "...", "tenantName": "acme-corp", "eventCount": 9, "firstSeenAt": "...", "lastSeenAt": "..." }, { "tenantId": "...", "tenantName": "beta-ltd", "eventCount": 4, "firstSeenAt": "...", "lastSeenAt": "..." }, { "tenantId": "...", "tenantName": "gamma-inc", "eventCount": 1, "firstSeenAt": "...", "lastSeenAt": "..." } ] }
List incidents with filters
# Active (Open + Recovering) — default curl "https://api.syncguard.io/api/v1/incidents" \ -H "X-Api-Key: sg_..." # Closed, paginated curl "https://api.syncguard.io/api/v1/incidents?status=closed&page=1&pageSize=25" \ -H "X-Api-Key: sg_..." # Filter by integration key curl "https://api.syncguard.io/api/v1/incidents?integrationKey=payments-service" \ -H "X-Api-Key: sg_..." # Chronic incidents only (reopened 3+ times) curl "https://api.syncguard.io/api/v1/incidents?chronic=true" \ -H "X-Api-Key: sg_..."
Incident timeline
// GET /incidents/{id}/timeline [ { "fromStatus": null, "toStatus": "Open", "reason": "Pattern matched: auth_failure", "occurredAt": "2025-03-26T08:14:22Z" }, { "fromStatus": "Open", "toStatus": "Recovering", "reason": "No events for 5 min", "occurredAt": "2025-03-26T08:19:22Z" }, { "fromStatus": "Recovering", "toStatus": "Open", "reason": "New event during recovery", "occurredAt": "2025-03-26T08:21:05Z" }, { "fromStatus": "Open", "toStatus": "Recovering", "reason": "No events for 5 min", "occurredAt": "2025-03-26T08:26:05Z" }, { "fromStatus": "Recovering", "toStatus": "Closed", "reason": "Stable for 10 min", "occurredAt": "2025-03-26T08:36:05Z" } ]
Alerts
SyncGuard fires alerts automatically at each lifecycle transition. Configure channels once per workspace — no per-incident wiring needed.
Configure Slack
curl -X PUT https://api.syncguard.io/api/v1/workspaces/me/channels/slack \ -H "X-Api-Key: sg_..." \ -H "Content-Type: application/json" \ -d '{ "config": { "webhookUrl": "https://hooks.slack.com/services/T.../B.../..." }, "isEnabled": true }'
Configure PagerDuty
curl -X PUT https://api.syncguard.io/api/v1/workspaces/me/channels/pagerduty \ -H "X-Api-Key: sg_..." \ -H "Content-Type: application/json" \ -d '{ "config": { "routingKey": "R01ABCDEF1234567890ABCDEF12345678" }, "isEnabled": true }'
Configure OpsGenie
curl -X PUT https://api.syncguard.io/api/v1/workspaces/me/channels/opsgenie \ -H "X-Api-Key: sg_..." \ -H "Content-Type: application/json" \ -d '{ "config": { "apiKey": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" }, "isEnabled": true }'
Incident openedFirst time a pattern threshold is met
Incident reopenedNew event during Recovering state
Incident resolvedClosed after stable CloseWindow
Blast radius expandedA new tenant is affected in the current incident cycle
API Reference
All endpoints share the base URL https://api.syncguard.io/api/v1. Responses use OperationResult<T> — always check isSuccess.
| Method | Path | Description |
|---|---|---|
| POST | /auth/register | Register a new user account |
| POST | /auth/login | Login and get a workspace-scoped JWT |
| POST | /auth/token | Exchange JWT for a different workspace |
| POST | /workspaces | Create a workspace (JWT required) |
| GET | /workspaces/me | Get current workspace info + channels |
| PATCH | /workspaces/me | Update workspace name or timing windows |
| POST | /workspaces/rotate-key | Rotate API key (old key immediately invalid) |
| GET | /workspaces/stats | Open incidents, MTTR, reopen rate, top patterns |
| GET | /workspaces/trends | Per-pattern incident buckets with trend direction |
| PUT | /workspaces/me/channels/{provider} | Upsert notification channel (slack/pagerduty/opsgenie/teams) |
| DELETE | /workspaces/me/channels/{provider} | Remove a notification channel |
| POST | /events | Ingest an event — runs the 3-stage pipeline |
| GET | /events | List events for an incident (paginated) |
| GET | /incidents | List active/closed incidents with filters |
| GET | /incidents/{id} | Full incident detail with affected tenants |
| POST | /incidents/{id}/resolve | Manually resolve an open incident |
| GET | /incidents/{id}/timeline | State transition history ordered by time |
| GET | /patterns | All patterns with enabled status and thresholds |
| PATCH | /patterns/{id} | Enable/disable a pattern or set alert threshold |
| GET | /tenants | List all tenants in the workspace |
| POST | /tenants | Create a new tenant |
| DELETE | /tenants/{id} | Soft-deactivate a tenant |
| POST | /tenants/{id}/activate | Re-activate a deactivated tenant |
Response envelope
{ "isSuccess": true, "errorCode": 0, // 0 = Success "message": "...", "data": { ... } // present on success responses } // Error example { "isSuccess": false, "errorCode": 4, // ValidationError "message": "integrationKey is required." }