API documentation
Programmatic access to the NextCounsel Issue Tracker — file tickets from your monitoring tools, sync comments back into your support stack, and keep your incident systems in lockstep with NextCounsel's view of your engagement.
The API is REST + JSON. Authentication is via per-tenant bearer tokens. Every endpoint and payload is documented below with copy-paste curl examples. A working sandbox is available at the URL of this docs page itself — generate a token in your tenant settings and start sending requests.
Getting started
- Sign in to your tenant's admin area and open Settings → API tokens. Click Create and give the token a name (e.g. "Datadog incidents"). The token is shown once — copy it somewhere safe.
-
Send your first request:
curl https://issue.nextcounsel.com/api/v1/me \ -H "Authorization: Bearer nc_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -
If the response says
{"ok": true, "tenant": {...}}— you're good. Move on to creating tickets.
Authentication
Every request must carry an Authorization: Bearer <token> header.
Tokens look like nc_ followed by 40 hex characters.
Each token is scoped to a single tenant — it can only see and create tickets that belong to its tenant. Treat tokens like passwords: keep them out of source control, store them in environment variables or a secrets manager, and rotate them when staff leave.
Request format
Base URL: https://issue.nextcounsel.com/api/v1
Content type: application/json for request bodies. Responses
are always application/json; charset=utf-8.
Versioning: the version is in the URL (/api/v1/…). Breaking
changes will ship under /api/v2/… — older versions stay supported for at
least 12 months after a successor lands.
Endpoints
GET /api/v1/me
Returns the tenant your token is scoped to. Useful for verifying a token works.
curl https://issue.nextcounsel.com/api/v1/me \
-H "Authorization: Bearer nc_…"
{
"ok": true,
"tenant": { "id": 1, "slug": "acme", "name": "Acme Corporation" },
"token": { "name": "Datadog incidents", "created_at": "2026-04-20 10:00:00" }
}
GET /api/v1/tickets
List every ticket belonging to your tenant. Sorted by updated_at descending.
curl https://issue.nextcounsel.com/api/v1/tickets \
-H "Authorization: Bearer nc_…"
The ticket object shape is the same as the create response below — but without description and comments (use GET /tickets/{ref} for the full record).
POST /api/v1/tickets
Create a new ticket. The most common API call — typical use is filing tickets from monitoring tools.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | One-line summary, ≤ 200 chars. |
description | string | Yes | Full description. Plain text or HTML — sanitized server-side. |
type | string | No | One of Bug, Feature Request, Support, Change Request, Incident. Defaults to Support. |
priority | string | No | One of Low, Medium, High, Critical. Defaults to Medium. |
reporter_email | string | Yes | The email of an existing user in your tenant. The reporter sees the ticket under "My tickets" and gets every update. |
Example
curl -X POST https://issue.nextcounsel.com/api/v1/tickets \
-H "Authorization: Bearer nc_…" \
-H "Content-Type: application/json" \
-d '{
"title": "Checkout fails on Safari with coupon code",
"description": "Clicking Pay throws a JS error in Safari 17 when a coupon is applied.",
"type": "Bug",
"priority": "High",
"reporter_email": "jane@acme.example"
}'
Response — 201 Created
{
"ok": true,
"ticket": {
"id": 9,
"ref": "NC-ACME-00009",
"tenant": { "id": 1, "slug": "acme" },
"title": "Checkout fails on Safari with coupon code",
"type": "Bug",
"priority": "High",
"status": "New",
"assignee_id": null,
"reporter_id": 10,
"due_date": null,
"created_at": "2026-04-26 17:32:18",
"updated_at": "2026-04-26 17:32:18",
"sla": "pending",
"description": "Clicking Pay throws a JS error...",
"comments": []
}
}
The ref in the response is the ticket reference NextCounsel uses everywhere (e.g. NC-ACME-00009). Pass that ref to subsequent calls.
GET /api/v1/tickets/{ref}
Fetch a single ticket including its full description and all comments.
curl https://issue.nextcounsel.com/api/v1/tickets/NC-ACME-00009 \
-H "Authorization: Bearer nc_…"
POST /api/v1/tickets/{ref}/comments
Add a comment to a ticket on behalf of a user in your tenant.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
body | string | Yes | The comment text. |
author_email | string | Yes | An existing user in your tenant. |
Example
curl -X POST https://issue.nextcounsel.com/api/v1/tickets/NC-ACME-00009/comments \
-H "Authorization: Bearer nc_…" \
-H "Content-Type: application/json" \
-d '{
"body": "Reproduced — pushing a fix now.",
"author_email": "jane@acme.example"
}'
Returns 201 Created with {"ok": true}.
Errors
Error responses always have the shape:
{
"ok": false,
"error": "validation_failed",
"details": ["title required", "invalid priority"]
}
| Status | Error | What it means |
|---|---|---|
400 | validation_failed | One or more fields are missing or invalid — see details. |
401 | missing_authorization · malformed_authorization · invalid_token | Authentication problem. |
403 | reporter_not_in_tenant · author_not_in_tenant | The email you supplied doesn't belong to your tenant. |
404 | ticket_not_found · tenant_not_found | Ref doesn't exist in this tenant. |
503 | api_disabled | API is turned off (operator changed api.enabled in config). |
Webhooks
Webhooks let you receive a callback the moment something happens to a ticket — useful for paging on-call, auto-updating your incident system, or syncing into Slack/Teams.
Setup
- In your tenant settings, scroll to Webhooks.
- Add the URL we should POST to (e.g.
https://hooks.example.com/nc). - Select which events you want — by default all four are enabled.
- Save. We generate a signing secret automatically.
Events
| Event | When it fires |
|---|---|
ticket.created |
A ticket is opened (UI or API) |
ticket.updated |
Status, priority, assignee, or due date changes |
ticket.commented |
A comment is added |
ticket.resolved |
Status moves to Resolved or Closed |
Payload format
Every event POSTs the following JSON body:
{
"event": "ticket.created",
"tenant_id": 1,
"data": {
"id": 9,
"ref": "NC-ACME-00009",
"title": "Checkout fails on Safari with coupon code",
"type": "Bug",
"priority": "High",
"status": "New",
"reporter_id": 10,
"created_at": "2026-04-26 17:32:18"
},
"extra": {},
"sent_at": "2026-04-26T17:32:19+00:00"
}
Headers we send:
| Header | Purpose |
|---|---|
Content-Type: application/json | Body is JSON. |
X-Webhook-Event | The event name (same as data.event). |
X-Webhook-Signature | sha256=<hex> — see below. |
Verifying signatures
The X-Webhook-Signature header is an HMAC-SHA256 of the raw request body using
your endpoint's secret. Verify it before trusting any webhook payload — that's how you tell
a real call from a forged one.
PHP receiver
$body = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$signature = preg_replace('/^sha256=/', '', $header);
$expected = hash_hmac('sha256', $body, $WEBHOOK_SECRET);
if (!hash_equals($expected, $signature)) {
http_response_code(401);
exit('bad signature');
}
$payload = json_decode($body, true);
// payload['event'], payload['data'], …
Node.js receiver
const crypto = require('crypto');
const body = JSON.stringify(req.body); // or use raw-body middleware
const sig = (req.headers['x-webhook-signature'] || '').replace(/^sha256=/, '');
const expect = crypto.createHmac('sha256', WEBHOOK_SECRET).update(body).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expect))) {
return res.status(401).send('bad signature');
}
Rate limits
Tokens are best-effort capped at 60 requests/minute.
Sustained traffic well above that may be temporarily throttled with a 429
response. Burst-friendly — short spikes are fine.
If you need a higher limit, contact your NextCounsel admin and we'll raise the cap for your token.
Changelog
- v1.0 — initial release. Tickets list/get/create, comment add, webhooks for created/updated/commented/resolved.
Need something not in the API? Tell your NextCounsel admin — we add endpoints based on client demand and will work with you to figure out the right shape.