Escalatr Platform API · v1
Build on complaint resolution.
Read your complaints over a simple REST API, or subscribe to webhooks and receive a signed event the moment something changes. Same data, two ways to consume it.
Overview
The Escalatr API gives a company programmatic access to the complaints filed against it. Every endpoint is scoped to your company by the API key you authenticate with — you only ever see your own data.
Base URL:
https://escalatr-api.onrender.comAll responses are JSON in the shape { "success": true, "data": ... }. The REST API is read-only today; writes happen in the dashboard.
Authentication
Create a key under Settings → Integrations → API Keys (admins and owners only). The full key — it begins with esc_ — is shown once, at creation. Store it somewhere safe; it can't be retrieved again, only revoked.
Send it on every request as a bearer token:
# Authorization header (recommended)
curl -H "Authorization: Bearer esc_your_key_here" \
https://escalatr-api.onrender.com/api/v1/complaints
# or the X-API-Key header
curl -H "X-API-Key: esc_your_key_here" \
https://escalatr-api.onrender.com/api/v1/complaints401. Keys are available on the Professional and Enterprise plans.List complaints
Returns your complaints, newest first. Supports filtering and pagination.
| Query param | Description |
|---|---|
status | Filter by status, e.g. Open, Escalated, Resolved. |
stage | Filter by stage, e.g. Intake, Escalation, Closure. |
escalation_level | Filter by level, e.g. L1 … L9. |
page | Page number, starting at 1 (default 1). |
limit | Items per page, 1–100 (default 25). |
// 200 OK
{
"success": true,
"page": 1, "limit": 25, "total": 142,
"data": [
{
"escalatr_id": "ESC-VOLT-260417-00010",
"complaint_category": "Consumer Durables",
"status": "Escalated", "stage": "Escalation",
"escalation_level": "L2", "priority": "high",
"amount_involved": 42000, "currency": "INR",
"is_overdue": false, "created_at": "2026-04-17T09:21:00Z"
}
]
}Get a complaint
Returns the full record for one complaint, including the description and resolution detail. Responds 404 if the id isn't one of yours.
curl -H "Authorization: Bearer esc_your_key_here" \
https://escalatr-api.onrender.com/api/v1/complaints/ESC-VOLT-260417-00010Event feed
The same events you can receive by webhook, available to poll. Each row carries the parsed event data and its delivery state.
| Query param | Description |
|---|---|
event | Filter by event name, e.g. complaint.escalated. |
status | Delivery status: pending, success, failed, dead. |
escalatr_id | Only events for one complaint. |
page, limit | Pagination, as above. |
Set up a webhook
Add an endpoint under Settings → Integrations → Webhooks, choose the events you care about, and save. You'll get a signing secret that starts with whsec_ — used to verify that deliveries really came from Escalatr. Use the Test button to send a sample webhook.test event, and the deliveries view to see every attempt.
Payload & headers
Escalatr POSTs a JSON body to your URL. Every event shares the same envelope:
{
"event": "complaint.escalated",
"timestamp": "2026-06-13T07:45:12.004Z",
"data": {
"escalatr_id": "ESC-VOLT-260417-00010",
"status": "Escalated",
"escalation_level": "L2",
"to_level": "L2", "trigger": "manual"
}
}| Header | Value |
|---|---|
X-Escalatr-Signature | HMAC-SHA256 of the raw request body, hex-encoded. |
X-Escalatr-Event | The event name, e.g. complaint.resolved. |
Content-Type | application/json |
User-Agent | Escalatr-Webhooks/1.0 |
Verify signatures
Compute the HMAC of the raw request body with your whsec_ secret and compare it to X-Escalatr-Signature. Verify before parsing — re-serializing JSON first can change the bytes and break the match.
// Node.js / Express
const crypto = require('crypto');
// capture the raw body for this route
app.use('/webhooks/escalatr', express.raw({ type: 'application/json' }));
app.post('/webhooks/escalatr', (req, res) => {
const secret = process.env.ESCALATR_WEBHOOK_SECRET; // whsec_...
const expected = crypto.createHmac('sha256', secret)
.update(req.body) // raw Buffer
.digest('hex');
const sig = req.get('X-Escalatr-Signature');
if (!sig || !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
return res.status(401).send('bad signature');
}
const payload = JSON.parse(req.body.toString());
// handle payload.event ...
res.sendStatus(200); // ack quickly
});Retries & delivery
Reply with any 2xx to acknowledge. Anything else — or a timeout past 10 seconds — counts as a failure and Escalatr retries on a backoff schedule:
attempt 1 fails -> retry in 1 min
attempt 2 fails -> retry in 5 min
attempt 3 fails -> retry in 30 min
attempt 4 fails -> retry in 2 hr
attempt 5 fails -> retry in 6 hr
attempt 6 fails -> marked dead (no further attempts)Deliveries are at-least-once, so make your handler idempotent — key off data.escalatr_id and the event. The first attempt is near-instant; retries are picked up every minute.
Event catalog
Subscribe to any subset. Every payload includes escalatr_id, status, and escalation_level plus the fields below.
| Event | Fires when | Notable data |
|---|---|---|
complaint.escalated | A complaint moves up a level — by the consumer or automatically. | to_level, trigger (manual / auto_action_wait / auto_sla) |
complaint.resolved | The consumer marks the complaint resolved. | resolution_type, amount_recovered |
complaint.withdrawn | The consumer voluntarily withdraws. | reason_code |
complaint.delegated | The consumer delegates the complaint to someone else. | to_email, immediate_transfer |
settlement.responded | The consumer accepts, rejects, or counters a settlement offer. | action, offer_id |
message.new | The consumer posts a chat message to your team. | message_id, directed_to |
evidence.uploaded | The consumer uploads a new piece of evidence. | evidence_id, file_name |
followup.replied | The consumer answers a follow-up question. | all_answered |
action.drafted | The consumer drafts a resolution action. | action_id, draft_id |
action.filed | The consumer marks an action as filed. | draft_id |
social.posted | The consumer marks a social post as published. | draft_id |
webhook.test | You press Test on a webhook. | message |