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.com

All 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/complaints
🔑
A missing, malformed, or revoked key returns 401. Keys are available on the Professional and Enterprise plans.

List complaints

GET/api/v1/complaints

Returns your complaints, newest first. Supports filtering and pagination.

Query paramDescription
statusFilter by status, e.g. Open, Escalated, Resolved.
stageFilter by stage, e.g. Intake, Escalation, Closure.
escalation_levelFilter by level, e.g. L1L9.
pagePage number, starting at 1 (default 1).
limitItems per page, 1100 (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

GET/api/v1/complaints/:escalatrId

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-00010

Event feed

GET/api/v1/events

The same events you can receive by webhook, available to poll. Each row carries the parsed event data and its delivery state.

Query paramDescription
eventFilter by event name, e.g. complaint.escalated.
statusDelivery status: pending, success, failed, dead.
escalatr_idOnly events for one complaint.
page, limitPagination, as above.
ℹ️
The feed is built from webhook delivery records, so an event appears here once it has been dispatched to a registered webhook. If you integrate by polling only, watch List complaints for state changes and add a webhook when you want the event stream.

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"
  }
}
HeaderValue
X-Escalatr-SignatureHMAC-SHA256 of the raw request body, hex-encoded.
X-Escalatr-EventThe event name, e.g. complaint.resolved.
Content-Typeapplication/json
User-AgentEscalatr-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.

EventFires whenNotable data
complaint.escalatedA complaint moves up a level — by the consumer or automatically.to_level, trigger (manual / auto_action_wait / auto_sla)
complaint.resolvedThe consumer marks the complaint resolved.resolution_type, amount_recovered
complaint.withdrawnThe consumer voluntarily withdraws.reason_code
complaint.delegatedThe consumer delegates the complaint to someone else.to_email, immediate_transfer
settlement.respondedThe consumer accepts, rejects, or counters a settlement offer.action, offer_id
message.newThe consumer posts a chat message to your team.message_id, directed_to
evidence.uploadedThe consumer uploads a new piece of evidence.evidence_id, file_name
followup.repliedThe consumer answers a follow-up question.all_answered
action.draftedThe consumer drafts a resolution action.action_id, draft_id
action.filedThe consumer marks an action as filed.draft_id
social.postedThe consumer marks a social post as published.draft_id
webhook.testYou press Test on a webhook.message