NextCounsel API documentation

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

  1. 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.
  2. Send your first request:
    curl https://issue.nextcounsel.com/api/v1/me \
      -H "Authorization: Bearer nc_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  3. 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.

Lost a token? Revoke it from Settings → API tokens and create a new one — the old one will be rejected immediately.

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

FieldTypeRequiredDescription
titlestringYesOne-line summary, ≤ 200 chars.
descriptionstringYesFull description. Plain text or HTML — sanitized server-side.
typestringNoOne of Bug, Feature Request, Support, Change Request, Incident. Defaults to Support.
prioritystringNoOne of Low, Medium, High, Critical. Defaults to Medium.
reporter_emailstringYesThe 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

FieldTypeRequiredDescription
bodystringYesThe comment text.
author_emailstringYesAn 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"]
}
StatusErrorWhat it means
400validation_failedOne or more fields are missing or invalid — see details.
401missing_authorization · malformed_authorization · invalid_tokenAuthentication problem.
403reporter_not_in_tenant · author_not_in_tenantThe email you supplied doesn't belong to your tenant.
404ticket_not_found · tenant_not_foundRef doesn't exist in this tenant.
503api_disabledAPI 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

  1. In your tenant settings, scroll to Webhooks.
  2. Add the URL we should POST to (e.g. https://hooks.example.com/nc).
  3. Select which events you want — by default all four are enabled.
  4. Save. We generate a signing secret automatically.

Events

EventWhen 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:

HeaderPurpose
Content-Type: application/jsonBody is JSON.
X-Webhook-EventThe event name (same as data.event).
X-Webhook-Signaturesha256=<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');
}
Don't trust the IP address. Webhook origin IPs aren't fixed. Only signature verification proves authenticity.

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.