Developers

Ten minutes from API key to a working widget.

Embed the widget, create tickets from your backend, receive signed webhooks, and drive Sortd from your coding assistant.

Embed

Add the widget in three steps.

Your API key stays on your server. The browser only ever sees a short-lived session id.

1

Create an app

In the portal, create an app for your product and copy its API key into your server's environment.

# .env (server only)
SORTD_API_KEY=sk_app_…
2

Create a session for the signed-in user

Call this from your backend when a user loads your app. Pass a tenant id so a customer's admins can see their whole company's tickets.

const res = await fetch('https://sortd.sneh.dev/api/embed/sessions', {
  method: 'POST',
  headers: {
    authorization: `Bearer ${process.env.SORTD_API_KEY}`,
    'content-type': 'application/json',
  },
  body: JSON.stringify({
    externalUserId: user.id,
    email: user.email,
    appTenantId: user.companyId,
  }),
});
const { sessionId } = await res.json();
3

Render the script tag

Choose which tabs to show and where the launcher sits. Colours, font and logo are set in the portal.

<script
  src="https://sortd.sneh.dev/embed.js"
  data-session-id="{sessionId}"
  data-position="bottom-right"
  defer></script>
REST API

Your backend can talk to Sortd too.

Every endpoint uses your app's Bearer key. Ticket updates stream over server-sent events, so you never need to poll.

  • POST/api/tickets · create a ticket
  • POST/api/conversations · ask on a user's behalf
  • GET/api/tickets/:id/stream · live updates
  • POST/api/errors · report an exception
  • PUT/api/monitors/:key · monitors as code
  • POST/api/monitors/heartbeat/:key · cron heartbeat
curl -X POST https://sortd.sneh.dev/api/tickets \
  -H "Authorization: Bearer $SORTD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "endUser": { "externalId": "u_831", "email": "ana@acme.co" },
    "issueType": "bug",
    "subject": "Export disabled for Q3 range",
    "description": "Export button is greyed out for Q3",
    "reportedFrom": { "url": "/invoices?range=q3" }
  }'

# 201 Created
{ "ticket": { "displayId": "LED-0142", "status": "open" } }
// Verify a Sortd webhook (Node)
const ts  = req.headers['x-sortd-timestamp'];
const sig = req.headers['x-sortd-signature'];
const expected = crypto
  .createHmac('sha256', process.env.SORTD_WEBHOOK_SECRET)
  .update(`${ts}.${rawBody}`)
  .digest('hex');

// Reject stale timestamps and mismatched signatures
const a = Buffer.from(sig);
const b = Buffer.from(expected);
const fresh = Math.abs(Date.now() / 1000 - Number(ts)) <= 300;
if (!fresh || a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
  return res.status(401).end();
}
Webhooks & notifications

Know the moment something changes.

Sortd sends events to your endpoint when tickets are created or resolved, monitors go down or recover, and generated knowledge base articles are ready. Each one is HMAC-SHA256 signed, timestamped and retried with backoff.

  • ticket.created
  • ticket.resolved
  • conversation.resolved
  • monitor.down
  • monitor.recovered
  • error.new_group
  • kb_gen.ready_for_review

Prefer chat? Send the same events to Slack, Microsoft Teams or Discord, choosing which ones per channel.

MCP

Work your support queue from your coding assistant.

One MCP endpoint over OAuth, with no key to copy around. Ask about tickets, errors and monitors, or set up a new app, without leaving your editor.

Claude Code

claude plugin marketplace add \
  https://sortd.sneh.dev/marketplace.json
claude plugin install sortd

Claude.ai and ChatGPT

Add a custom connector and paste the endpoint. You'll sign in to Sortd once.

https://sortd.sneh.dev/mcp

Cursor, Gemini CLI and others

Any streamable-HTTP MCP client that supports OAuth.

{ "mcpServers": { "sortd": {
  "url": "https://sortd.sneh.dev/mcp" } } }

"Which bugs did users report this week, and are any of them linked to an error group?"

All MCP clients →

Get an API key.

Request access and we'll set up your first app with you.