New Instance Platform
v1.0.0New Instance Platform - Developer API
The complete external integration reference for the New Instance platform: every supported REST endpoint, the SDK and CLI directory, webhook contracts, the MCP agent endpoint, error catalogue and the go-live path.
Platform products covered:
- BugWatch - error/log ingest, SDKs for 6 platforms, OpenTelemetry, Prometheus, Jaeger, RUM, debug symbols, CI deployments
- Support Tickets - sign-in links, ticket CRUD, comments, outbound ticket webhooks
- Secret Manager - encrypted MEK + variable fetch,
nismCLI - Chat - embeddable web widget + Android/iOS/Flutter/React Native SDKs
- Docs Portal - dashboard-managed docs publishing
- AI Agent Access - MCP endpoint for agent tooling
Base URLs (production - there is no separate sandbox host)link
- REST API:
https://api.newinstance.cloud({{baseUrl}}) - MCP agent endpoint:
https://service.newinstance.cloud({{serviceUrl}}) - Test mode: use a
sk_test_…key on the same URLs - test keys are isolated from live data server-side
Authenticationlink
All requests use x-api-key: {{apiKey}}. Client-side delivery (browser session tokens, mobile device-signed tokens) is handled inside the SDKs and documented in their guides.
Getting Started
1 endpoint/api/auth/verifyVerify API key
Verify that your API key is valid and inspect its metadata.
Run this first after generating a key to confirm it is active and check the environment (test vs live).
Authentication
- Header:
x-api-key: {{apiKey}}
Success - 200 OK
{
"success": true,
"message": "API key is valid",
"business": { "id": "org_abc123", "name": "Acme Corp", "slug": "acme-corp" },
"key": { "id": "sk_test_abc123", "environment": "test" },
"timestamp": "2026-06-26T10:00:00.000Z"
}Common errors
401- key missing, malformed, or revoked403- key found but inactive
Headers
x-api-keyResponses
200 – Key valid
{
"success": true,
"message": "API key is valid",
"business": {
"id": "org_abc123",
"name": "Acme Corp",
"slug": "acme-corp"
},
"key": {
"id": "sk_test_abc123",
"environment": "test"
},
"timestamp": "2026-06-26T10:00:00.000Z"
}https://api.newinstance.cloud/api/auth/verifyHeaders
x-api-keyCode samples
curl -X GET 'https://api.newinstance.cloud/api/auth/verify'Browser Ingest
1 endpoint/api/v1/bugwatch/ingest/browserBrowser ingest - error event (session token)
Send a browser event authenticated with a session token minted by Server Ingest API → Mint browser session token (run that request first: it stores sessionToken). Same payload contract as server ingest. The browser's Origin header is checked against the project's allowed origins: a mismatch returns 403 origin_not_allowed; an expired or invalid token returns 401 and the SDK re-mints automatically.
Headers
x-bugwatch-sessionRequest body
application/json{
"level": 50,
"time": {{nowMs}},
"message": "Uncaught TypeError: Cannot read properties of undefined",
"release": "web@3.1.0",
"eventId": "evt_browser_abc123",
"tags": { "surface": "spa", "route": "/checkout" },
"user": { "id": "usr_456" },
"contexts": { "cart": { "id": "cart_42", "items": 3 } },
"breadcrumbs": [
{ "timestamp": {{nowMs}}, "category": "navigation", "message": "Navigated to /checkout", "level": 30 }
],
"exception": {
"type": "TypeError",
"value": "Cannot read properties of undefined",
"stacktrace": { "frames": [ { "filename": "checkout.js", "function": "calculateTotal", "lineno": 42, "colno": 15 } ] }
}
}Responses
202 – Accepted
{
"ingested": 1,
"skipped": 0,
"deduped": 0
}https://api.newinstance.cloud/api/v1/bugwatch/ingest/browserHeaders
x-bugwatch-sessionRequest body
Code samples
curl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/ingest/browser' \
-H 'Content-Type: application/json' \
--data-raw '{
"level": 50,
"time": {{nowMs}},
"message": "Uncaught TypeError: Cannot read properties of undefined",
"release": "web@3.1.0",
"eventId": "evt_browser_abc123",
"tags": { "surface": "spa", "route": "/checkout" },
"user": { "id": "usr_456" },
"contexts": { "cart": { "id": "cart_42", "items": 3 } },
"breadcrumbs": [
{ "timestamp": {{nowMs}}, "category": "navigation", "message": "Navigated to /checkout", "level": 30 }
],
"exception": {
"type": "TypeError",
"value": "Cannot read properties of undefined",
"stacktrace": { "frames": [ { "filename": "checkout.js", "function": "calculateTotal", "lineno": 42, "colno": 15 } ] }
}
}'Server Ingest API
2 endpoints/api/v1/bugwatch/browser-sessionMint browser session token
Mint a short-lived browser session token. Call this from your backend using the secret key - the token is then passed to your frontend. The secret never reaches the client.
Scope: ingest:write
Request
- Method:
POST - Header:
x-api-key: {{apiKey}} - Body: empty
Success - 200 OK
{ "token": "<session-token>", "expiresAt": "2026-06-26T12:15:00.000Z" }Common errors
401- invalid/missingx-api-key403- key lacksingest:writescope
Headers
x-api-keyResponses
200 – Token minted
{
"token": "eyJhbGciOiJIUzI1NiJ9.example",
"expiresAt": "2026-06-26T12:15:00.000Z"
}https://api.newinstance.cloud/api/v1/bugwatch/browser-sessionHeaders
x-api-keyRequest body
Code samples
curl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/browser-session'/api/v1/bugwatch/ingestIngest – log event (JSON)
Ingest logs and error events from server-side or CI environments.
Scope: ingest:write
Request
- Header:
x-api-key: {{apiKey}} - Content-Type:
application/json(single object or array) orapplication/x-ndjson(one event per line) - Max body: 2 MB
Event fields
| Field | Type | Notes |
|---|---|---|
level | number | 10/20/30/40/50/60 |
time | number | Unix ms timestamp |
message | string | Human-readable text |
release | string | App build version |
environment | string | production / staging / development |
eventId | string | Optional; enables deduplication within 10-min window |
tags | object | Flat key-value labels (≤50 keys) |
user | object | {id, email, username, ip} - only these four keys; others are silently dropped |
traceId | string | Hex distributed trace id (≤32 chars) |
spanId | string | Span id |
exception | object | {type, value, stacktrace: {frames}} |
breadcrumbs | array | Leading events |
Success - 202 Accepted
{ "ingested": 1, "skipped": 0, "deduped": 0 }Headers
x-api-keyRequest body
application/json{
"level": 30,
"time": {{nowMs}},
"message": "User signed in",
"release": "1.0.0",
"environment": "production",
"tags": { "region": "eu-west-1" },
"user": { "id": "usr_123", "email": "alice@example.com" }
}Responses
202 – Event ingested
{
"ingested": 1,
"skipped": 0,
"deduped": 0
}https://api.newinstance.cloud/api/v1/bugwatch/ingestHeaders
x-api-keyRequest body
Code samples
curl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/ingest' \
-H 'Content-Type: application/json' \
--data-raw '{
"level": 30,
"time": {{nowMs}},
"message": "User signed in",
"release": "1.0.0",
"environment": "production",
"tags": { "region": "eu-west-1" },
"user": { "id": "usr_123", "email": "alice@example.com" }
}'OpenTelemetry, Prometheus & Tracing
10 endpoints/v1/logsOTLP/HTTP – ingest logs (JSON)
Send OpenTelemetry logs to BugWatch via OTLP/HTTP.
Scope: ingest:write
Content-Type options
application/json- JSON body (used here)application/x-protobuf- protobufExportLogsServiceRequest(used by the OTel Collector)
Request body (JSON form)
{
"resourceLogs": [{
"resource": { "attributes": [{ "key": "service.name", "value": { "stringValue": "my-service" } }] },
"scopeLogs": [{
"logRecords": [{
"timeUnixNano": "1750924800000000000",
"severityNumber": 9,
"severityText": "INFO",
"body": { "stringValue": "User signed in" },
"attributes": [{ "key": "user.id", "value": { "stringValue": "usr_123" } }]
}]
}]
}]
}Success - 200 OK (empty body on full success)
{}On partial rejection:
{ "partialSuccess": { "rejectedLogRecords": 2, "errorMessage": "some records rejected" } }Headers
x-api-keyRequest body
application/json{
"resourceLogs": [
{
"resource": {
"attributes": [
{
"key": "service.name",
"value": {
"stringValue": "my-service"
}
}
]
},
"scopeLogs": [
{
"logRecords": [
{
"timeUnixNano": "{{nowNano}}",
"severityNumber": 9,
"severityText": "INFO",
"body": {
"stringValue": "User signed in"
},
"attributes": [
{
"key": "user.id",
"value": {
"stringValue": "usr_123"
}
}
]
}
]
}
]
}
]
}Responses
200 – All accepted
nullhttps://api.newinstance.cloud/v1/logsHeaders
x-api-keyRequest body
Code samples
curl -X POST 'https://api.newinstance.cloud/v1/logs' \
-H 'Content-Type: application/json' \
--data-raw '{
"resourceLogs": [
{
"resource": {
"attributes": [
{
"key": "service.name",
"value": {
"stringValue": "my-service"
}
}
]
},
"scopeLogs": [
{
"logRecords": [
{
"timeUnixNano": "{{nowNano}}",
"severityNumber": 9,
"severityText": "INFO",
"body": {
"stringValue": "User signed in"
},
"attributes": [
{
"key": "user.id",
"value": {
"stringValue": "usr_123"
}
}
]
}
]
}
]
}
]
}'/v1/tracesOTLP/HTTP – ingest traces (JSON)
Send OpenTelemetry traces to BugWatch via OTLP/HTTP.
Scope: ingest:write
Content-Type options
application/json- JSON body (used here)application/x-protobuf- protobufExportTraceServiceRequest(used by the OTel Collector)
Request body (JSON form)
{
"resourceSpans": [{
"resource": { "attributes": [{ "key": "service.name", "value": { "stringValue": "my-service" } }] },
"scopeSpans": [{
"spans": [{
"traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
"spanId": "00f067aa0ba902b7",
"name": "HTTP GET /api/users",
"kind": 2,
"startTimeUnixNano": "1750924800000000000",
"endTimeUnixNano": "1750924800050000000",
"status": { "code": 1 }
}]
}]
}]
}Success - 200 OK (empty body on full success; partialSuccess on rejection)
Headers
x-api-keyRequest body
application/json{
"resourceSpans": [
{
"resource": {
"attributes": [
{
"key": "service.name",
"value": {
"stringValue": "my-service"
}
}
]
},
"scopeSpans": [
{
"spans": [
{
"traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
"spanId": "00f067aa0ba902b7",
"name": "HTTP GET /api/users",
"kind": 2,
"startTimeUnixNano": "{{nowNano}}",
"endTimeUnixNano": "{{nowNano}}",
"status": {
"code": 1
},
"attributes": [
{
"key": "http.method",
"value": {
"stringValue": "GET"
}
},
{
"key": "http.status_code",
"value": {
"intValue": 200
}
}
]
}
]
}
]
}
]
}Responses
200 – All accepted
nullhttps://api.newinstance.cloud/v1/tracesHeaders
x-api-keyRequest body
Code samples
curl -X POST 'https://api.newinstance.cloud/v1/traces' \
-H 'Content-Type: application/json' \
--data-raw '{
"resourceSpans": [
{
"resource": {
"attributes": [
{
"key": "service.name",
"value": {
"stringValue": "my-service"
}
}
]
},
"scopeSpans": [
{
"spans": [
{
"traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
"spanId": "00f067aa0ba902b7",
"name": "HTTP GET /api/users",
"kind": 2,
"startTimeUnixNano": "{{nowNano}}",
"endTimeUnixNano": "{{nowNano}}",
"status": {
"code": 1
},
"attributes": [
{
"key": "http.method",
"value": {
"stringValue": "GET"
}
},
{
"key": "http.status_code",
"value": {
"intValue": 200
}
}
]
}
]
}
]
}
]
}'/v1/metricsOTLP/HTTP – ingest metrics (JSON)
Send OpenTelemetry metrics to BugWatch via OTLP/HTTP.
Scope: ingest:write
Content-Type options
application/json- JSON body (used here)application/x-protobuf- protobufExportMetricsServiceRequest(used by the OTel Collector)
Request body (JSON form)
{
"resourceMetrics": [{
"resource": { "attributes": [{ "key": "service.name", "value": { "stringValue": "my-service" } }] },
"scopeMetrics": [{
"metrics": [{
"name": "http.server.request.duration",
"unit": "ms",
"gauge": {
"dataPoints": [{
"timeUnixNano": "1750924800000000000",
"asDouble": 123.4,
"attributes": [{ "key": "http.method", "value": { "stringValue": "GET" } }]
}]
}
}]
}]
}]
}Success - 200 OK (empty on full success; partialSuccess on rejection)
Headers
x-api-keyRequest body
application/json{
"resourceMetrics": [
{
"resource": {
"attributes": [
{
"key": "service.name",
"value": {
"stringValue": "my-service"
}
}
]
},
"scopeMetrics": [
{
"metrics": [
{
"name": "http.server.request.duration",
"unit": "ms",
"gauge": {
"dataPoints": [
{
"timeUnixNano": "{{nowNano}}",
"asDouble": 123.4,
"attributes": [
{
"key": "http.method",
"value": {
"stringValue": "GET"
}
}
]
}
]
}
}
]
}
]
}
]
}Responses
200 – All accepted
nullhttps://api.newinstance.cloud/v1/metricsHeaders
x-api-keyRequest body
Code samples
curl -X POST 'https://api.newinstance.cloud/v1/metrics' \
-H 'Content-Type: application/json' \
--data-raw '{
"resourceMetrics": [
{
"resource": {
"attributes": [
{
"key": "service.name",
"value": {
"stringValue": "my-service"
}
}
]
},
"scopeMetrics": [
{
"metrics": [
{
"name": "http.server.request.duration",
"unit": "ms",
"gauge": {
"dataPoints": [
{
"timeUnixNano": "{{nowNano}}",
"asDouble": 123.4,
"attributes": [
{
"key": "http.method",
"value": {
"stringValue": "GET"
}
}
]
}
]
}
}
]
}
]
}
]
}'/api/v1/prom/writePrometheus remote-write (DOCUMENTATION ONLY)
Prometheus remote-write ingestion endpoint.
Scope: ingest:write
This endpoint is NOT called directly by humans. It is invoked by the Prometheus remote-write adapter or the
bugwatch-otel-collector. The body is a Snappy-compressed protobufWriteRequest- you cannot send a readable JSON body here.
To use Prometheus remote-write with BugWatch:
- Deploy the
bugwatch-otel-collector(or configure Prometheusremote_write) - Set the remote-write URL to:
{{baseUrl}}/api/v1/prom/write - Add header:
x-api-key: sk_live_KEYID:secret
Success - 204 No Content
Common errors
400- malformed Snappy/protobuf body401- invalid API key429- rate limited
Headers
x-api-keyX-Prometheus-Remote-Write-VersionRequest body
text/plain<<< BINARY SNAPPY-COMPRESSED PROTOBUF - sent by Prometheus/collector, not a manual JSON request >>>Responses
204 – Accepted
nullhttps://api.newinstance.cloud/api/v1/prom/writeHeaders
x-api-keyX-Prometheus-Remote-Write-VersionRequest body
Code samples
curl -X POST 'https://api.newinstance.cloud/api/v1/prom/write' \
-H 'Content-Type: application/json' \
--data-raw '<<< BINARY SNAPPY-COMPRESSED PROTOBUF - sent by Prometheus/collector, not a manual JSON request >>>'/api/v1/prom/queryPrometheus query (instant)
Query stored Prometheus metrics using a PromQL-compatible selector.
Scope: ingest:write (same key used for write is accepted for query)
Query params
query(required) - metric name or label selector e.g.http_requests_totalor{service="api-gateway"}time(optional) - Unix timestamp (seconds, float); defaults to now
Success - 200 OK
{
"status": "success",
"data": {
"resultType": "vector",
"result": [{ "metric": { "__name__": "http_requests_total", "service": "api" }, "value": [1750924800, "42"] }]
}
}Headers
x-api-keyParameters
queryquerystringdefault: Metric name or label selectortimequerystringdefault: Unix timestamp seconds (optional, defaults to now)Responses
200 – Instant vector
{
"status": "success",
"data": {
"resultType": "vector",
"result": [
{
"metric": {
"__name__": "http_requests_total",
"service": "api"
},
"value": [
1788741982,
"42"
]
}
]
}
}https://api.newinstance.cloud/api/v1/prom/queryQuery parameters
querytimeHeaders
x-api-keyCode samples
curl -X GET 'https://api.newinstance.cloud/api/v1/prom/query'/api/v1/prom/query_rangePrometheus query_range (over time)
Query stored Prometheus metrics over a time range.
Scope: ingest:write
Query params
query(required) - metric selectorstart(required) - range start as Unix seconds (float)end(required) - range end as Unix seconds (float)
Success - 200 OK
{
"status": "success",
"data": {
"resultType": "matrix",
"result": [{ "metric": { "__name__": "http_requests_total" }, "values": [[1750924800, "42"], [1750924860, "47"]] }]
}
}Headers
x-api-keyParameters
queryquerystringdefault: Metric name or label selectorstartquerystringdefault: Range start Unix secondsendquerystringdefault: Range end Unix secondsResponses
200 – Matrix result
{
"status": "success",
"data": {
"resultType": "matrix",
"result": [
{
"metric": {
"__name__": "http_requests_total"
},
"values": [
[
1788738382,
"38"
],
[
1788741982,
"42"
]
]
}
]
}
}https://api.newinstance.cloud/api/v1/prom/query_rangeQuery parameters
querystartendHeaders
x-api-keyCode samples
curl -X GET 'https://api.newinstance.cloud/api/v1/prom/query_range'/api/v1/prom/label/__name__/valuesPrometheus label __name__ values (list metric names)
List all distinct metric names stored for this project+environment.
Scope: ingest:write
Success - 200 OK
{ "status": "success", "data": ["http_requests_total", "rum.lcp", "rum.cls"] }Headers
x-api-keyResponses
200 – Metric names
{
"status": "success",
"data": [
"http_requests_total",
"rum.lcp",
"rum.cls",
"rum.inp"
]
}https://api.newinstance.cloud/api/v1/prom/label/__name__/valuesHeaders
x-api-keyCode samples
curl -X GET 'https://api.newinstance.cloud/api/v1/prom/label/__name__/values'/jaeger/api/servicesJaeger – list service names
List distinct service names that have sent traces to this BugWatch project.
Scope: ingest:write (same API key)
Success - 200 OK
{ "data": ["api-gateway", "payment-service", "user-service"], "total": 3, "limit": 3, "offset": 0, "errors": null }Headers
x-api-keyResponses
200 – Services
{
"data": [
"api-gateway",
"payment-service",
"user-service"
],
"total": 3,
"limit": 3,
"offset": 0,
"errors": null
}https://api.newinstance.cloud/jaeger/api/servicesHeaders
x-api-keyCode samples
curl -X GET 'https://api.newinstance.cloud/jaeger/api/services'/jaeger/api/tracesJaeger – list traces
List recent distributed traces for this project+environment.
Scope: ingest:write
Query params
service(optional) - filter by service namelimit(optional, default 20, max 100)lookback(optional) -1h|24h|7d|30d(default1h)
Success - 200 OK - returns Jaeger-compatible trace list with spans.
Headers
x-api-keyParameters
limitquerystringdefault: Max traces (default 20, max 100)lookbackquerystringdefault: 1h | 24h | 7d | 30dResponses
200 – Traces list
{
"data": [],
"total": 0,
"limit": 20,
"offset": 0,
"errors": null
}https://api.newinstance.cloud/jaeger/api/tracesQuery parameters
limitlookbackHeaders
x-api-keyCode samples
curl -X GET 'https://api.newinstance.cloud/jaeger/api/traces'/api/v1/rumRUM – ingest web-vitals
Ingest Real User Monitoring (RUM) web-vitals metrics (LCP, CLS, INP, FID, TTFB).
Auth: Either x-bugwatch-session (browser) or x-api-key (server-side / testing)
Scope (for x-api-key): ingest:write
Request body - three accepted shapes:
- Wrapped array:
{ "vitals": [{ "name": "LCP", "value": 1250.5, "url": "https://example.com/", "rating": "good" }] }- Bare array:
[{ "name": "CLS", "value": 0.05, "rating": "good" }, { "name": "INP", "value": 180, "rating": "needs-improvement" }]- Single vital object:
{ "name": "TTFB", "value": 320, "url": "https://example.com/checkout", "rating": "needs-improvement" }Vital field schema:
| Field | Type | Notes |
|---|---|---|
name | string (required) | LCP, CLS, INP, FID, TTFB |
value | number (required) | Metric value in ms (or unitless for CLS) |
url | string (optional) | Page URL |
rating | string (optional) | good |
id | string (optional) | Client-side unique id |
Success - 202 Accepted
{ "ingested": 2 }Stored as rum.<name_lower> metric points (e.g. rum.lcp, rum.cls).
Common errors
400- malformed JSON401- invalid session token or API key403- origin not inallowedOrigins(when using session token)429- rate limited
Headers
x-api-keyOr use x-bugwatch-session for browser callsRequest body
application/json{
"vitals": [
{
"name": "LCP",
"value": 1250.5,
"url": "https://example.com/",
"rating": "good"
},
{
"name": "CLS",
"value": 0.05,
"url": "https://example.com/",
"rating": "good"
},
{
"name": "INP",
"value": 180,
"url": "https://example.com/",
"rating": "needs-improvement"
}
]
}Responses
202 – Vitals ingested
{
"ingested": 3
}https://api.newinstance.cloud/api/v1/rumHeaders
x-api-keyRequest body
Code samples
curl -X POST 'https://api.newinstance.cloud/api/v1/rum' \
-H 'Content-Type: application/json' \
--data-raw '{
"vitals": [
{
"name": "LCP",
"value": 1250.5,
"url": "https://example.com/",
"rating": "good"
},
{
"name": "CLS",
"value": 0.05,
"url": "https://example.com/",
"rating": "good"
},
{
"name": "INP",
"value": 180,
"url": "https://example.com/",
"rating": "needs-improvement"
}
]
}'Source Maps & Symbols
6 endpoints/api/v1/bugwatch/artifacts/presignArtifact upload · step 1 – presign
Step 1 of 2 - request a presigned URL for a text symbolication artifact.
Scope: symbols:upload · Auth: project secret key (keyId:secret)
artifactType | File | Platform |
|---|---|---|
r8 / proguard | mapping.txt | android |
sourcemap | .map | ios · react-native · android |
dart-symbols | --split-debug-info output | flutter |
dsym | Apple dSYM referenced as an artifact | ios |
The file never passes through this API. You presign, PUT the bytes straight to storage, then confirm. That is why uploads are not bound by request-size limits - the previous raw-body routes were rejected by the edge proxy for any real symbol file.
1. POST …/presign → { uploadId, uploadUrl, expiresAt }
2. PUT <uploadUrl> → the raw bytes, streamed to storage
3. POST …/uploads/{uploadId}/complete → verified, then queued
The API key goes on steps 1 and 3 only - never to the storage host, which is authorised by the presigned URL's own signature.
Uploads are idempotent per (release, platform, artifactType) - completing a new
upload replaces the previous artifact and deletes the superseded file.
The release you declare must match the release your SDK reports at runtime, or the
artifact exists but nothing matches it and stacks stay unreadable.
Most integrators should use the CLI, which performs all three steps, streams the file (flat memory regardless of size) and retries transient failures:
# Binary debug-symbol archives - Apple dSYM, Android native .so
npx @newinstance/bugwatch-cli symbols upload MyApp.xcarchive \
--release "1.4.2" --build-number "318"
# Text artifacts - R8/ProGuard mapping, source map, Dart symbols
npx @newinstance/bugwatch-cli artifacts upload mapping.txt \
--release "1.4.2" --platform android --type r8The two commands are not interchangeable: symbols upload accepts binary archives
only and validates magic bytes, so a mapping.txt or .map is rejected.
Headers
x-api-keyRequest body
application/json{
"release": "1.0.0",
"platform": "android",
"artifactType": "r8",
"size": 128394,
"sha256": "3b8f2a91c0de4b7a8f13d6c25e09a4b1f87c30d2416e5a9b8c7d0e1f2a3b4c1d",
"originalName": "mapping.txt"
}Responses
201 – Presigned
{
"uploadId": "665f1f77bcf86cd799439021",
"uploadUrl": "https://storage.newinstance.cloud/presigned/665f1f77bcf86cd799439021?sig=…",
"expiresAt": "2026-08-05T12:15:00.000Z"
}https://api.newinstance.cloud/api/v1/bugwatch/artifacts/presignHeaders
x-api-keyRequest body
Code samples
curl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/artifacts/presign' \
-H 'Content-Type: application/json' \
--data-raw '{
"release": "1.0.0",
"platform": "android",
"artifactType": "r8",
"size": 128394,
"sha256": "3b8f2a91c0de4b7a8f13d6c25e09a4b1f87c30d2416e5a9b8c7d0e1f2a3b4c1d",
"originalName": "mapping.txt"
}'/api/v1/bugwatch/artifacts/uploads/completeArtifact upload · step 2 – complete
Step 2 of 2 - confirm the artifact landed and swap it in.
Scope: symbols:upload
What is verified
completeconfirms the object actually landed in storage and that its size matches what was declared at presign.- The pending row is looked up scoped to the calling project, so one project can never finalise another's upload (a miss returns
404). - Only an
AWAITING_UPLOADrow can be completed, so a completion cannot be replayed to re-queue processing. - For debug symbols the worker re-hashes the real bytes before indexing and rejects a mismatch (
CHECKSUM_MISMATCH). A declared checksum is never trusted on its own.
Responses
201{ fileId, sha256 }- stored and now the active artifact for that(release, platform, artifactType).409- the object was never PUT touploadUrl.400- the stored size does not match what was declared at presign.404- no such upload for this project.
Headers
x-api-keyResponses
201 – Stored and active
{
"fileId": "f_9a2b1c3d4e5f",
"sha256": "3b8f2a91c0de4b7a8f13d6c25e09a4b1f87c30d2416e5a9b8c7d0e1f2a3b4c1d"
}https://api.newinstance.cloud/api/v1/bugwatch/artifacts/uploads/completeHeaders
x-api-keyRequest body
Code samples
curl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/artifacts/uploads/complete'/api/v1/bugwatch/debug-symbols/presignDebug symbols · step 1 – presign
Step 1 of 2 - request a presigned URL for a binary debug-symbol archive.
Scope: symbols:upload · Auth: project secret key (keyId:secret)
| Platform | Accepted |
|---|---|
| ios · macos · tvos · watchos · visionos · catalyst | .zip of .dSYM bundles, .xcarchive dSYMs, or a raw Mach-O |
| android | Raw ELF .so / .debug |
The file never passes through this API. You presign, PUT the bytes straight to storage, then confirm. That is why uploads are not bound by request-size limits - the previous raw-body routes were rejected by the edge proxy for any real symbol file.
1. POST …/presign → { uploadId, uploadUrl, expiresAt }
2. PUT <uploadUrl> → the raw bytes, streamed to storage
3. POST …/uploads/{uploadId}/complete → verified, then queued
The API key goes on steps 1 and 3 only - never to the storage host, which is authorised by the presigned URL's own signature.
Matching is by debug UUID / build-id, not by release name - the identifier embedded in
the binary. That is why an upload from any build machine resolves crashes from any device
running that exact binary, and why release here is metadata for search rather than the
match key. (Source maps and mappings are the opposite: those match on release + platform.)
Declaring a sha256 that already exists for this project short-circuits as a duplicate
and returns no uploadUrl - the transfer is skipped entirely.
Most integrators should use the CLI, which performs all three steps, streams the file (flat memory regardless of size) and retries transient failures:
# Binary debug-symbol archives - Apple dSYM, Android native .so
npx @newinstance/bugwatch-cli symbols upload MyApp.xcarchive \
--release "1.4.2" --build-number "318"
# Text artifacts - R8/ProGuard mapping, source map, Dart symbols
npx @newinstance/bugwatch-cli artifacts upload mapping.txt \
--release "1.4.2" --platform android --type r8The two commands are not interchangeable: symbols upload accepts binary archives
only and validates magic bytes, so a mapping.txt or .map is rejected.
Headers
x-api-keyRequest body
application/json{
"platform": "ios",
"release": "1.0.0",
"buildNumber": "318",
"size": 48210944,
"sha256": "9c1e5b7a3f28d4c6b0a1e9f2d5c8b7a64310f2e7d9c1b5a8e3f60412789bcf02",
"originalName": "MyApp.app.dSYM.zip"
}Responses
200 – Duplicate (already indexed)
{
"duplicate": true
}https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/presignHeaders
x-api-keyRequest body
Code samples
curl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/presign' \
-H 'Content-Type: application/json' \
--data-raw '{
"platform": "ios",
"release": "1.0.0",
"buildNumber": "318",
"size": 48210944,
"sha256": "9c1e5b7a3f28d4c6b0a1e9f2d5c8b7a64310f2e7d9c1b5a8e3f60412789bcf02",
"originalName": "MyApp.app.dSYM.zip"
}'/api/v1/bugwatch/debug-symbols/uploads/completeDebug symbols · step 2 – complete
Step 2 of 2 - confirm the archive landed and queue it for indexing.
Scope: symbols:upload
What is verified
completeconfirms the object actually landed in storage and that its size matches what was declared at presign.- The pending row is looked up scoped to the calling project, so one project can never finalise another's upload (a miss returns
404). - Only an
AWAITING_UPLOADrow can be completed, so a completion cannot be replayed to re-queue processing. - For debug symbols the worker re-hashes the real bytes before indexing and rejects a mismatch (
CHECKSUM_MISMATCH). A declared checksum is never trusted on its own.
Archive-type validation happens in the worker, not here, because this API never sees
the bytes. An archive that is not a zip / Mach-O / ELF lands as INVALID with
UNRECOGNIZED_ARCHIVE; an ELF declared under an Apple platform fails with
ARCHIVE_PLATFORM_MISMATCH.
Responses
200{ uploadId, status: "QUEUED" }- accepted; poll Get upload status for indexing progress.409- the archive was never PUT touploadUrl.400- declared size does not match the stored object.
Headers
x-api-keyResponses
200 – Queued for indexing
{
"uploadId": "665f1f77bcf86cd799439031",
"status": "QUEUED"
}https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/uploads/completeHeaders
x-api-keyRequest body
Code samples
curl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/uploads/complete'/api/v1/bugwatch/debug-symbols/uploadsList debug symbol uploads
List recent debug symbol uploads for this project, newest first.
Scope: symbols:read
Query params
limit(optional, int 1–200, default 50)status(optional, string) - filter byQUEUED|PROCESSING|DONE|FAILED
Success - 200 OK returns { uploads: [...] }
Headers
x-api-keyParameters
limitquerystringdefault: Number of results (default 50, max 200)Responses
200 – Uploads list
{
"uploads": [
{
"uploadId": "507f1f77bcf86cd799439011",
"status": "DONE",
"platform": "ios",
"release": "2.1.0",
"buildNumber": "1042",
"originalFilename": "MyApp.app.dSYM.zip",
"uploadedSize": 4823012,
"discoveredUuids": 3,
"validObjects": 3,
"invalidObjects": 0,
"createdAt": "2026-06-26T10:00:00.000Z",
"completedAt": "2026-06-26T10:00:15.000Z"
}
]
}https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/uploadsQuery parameters
limitHeaders
x-api-keyCode samples
curl -X GET 'https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/uploads'/api/v1/bugwatch/debug-symbols/uploads/reprocessReprocess – re-symbolicate waiting crashes
Trigger re-symbolication for crashes that arrived before symbols were uploaded.
Scope: symbols:reprocess
Previously-unsymbolicated crashes are then re-processed with the newly-uploaded symbols.
Body: empty
Success - 200 OK
{ "requeued": 12 }Headers
x-api-keyResponses
200 – Re-queued
{
"requeued": 12
}https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/uploads/reprocessHeaders
x-api-keyRequest body
Code samples
curl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/uploads/reprocess'Deployments (CI/CD)
3 endpoints/api/v1/bugwatch/deployStart deployment
Create a deployment record. Call at the start of your CI deploy job.
Scope: deploy:write
Body (all fields optional - an empty body is valid)
release- the release this deploy ships; should match what your SDK reports at runtime so errors correlatemeta- string map:commit,repo,branch,triggeredBy
Success - 201 Created
{ "id": "dep_64fa3c9e2b7d41c8a9f01234" }Keep the id: every later call needs it. Status starts as in_progress.
Common errors
401- invalid/missing key403- key lacksdeploy:write404- the project bound to this key no longer exists
Headers
x-api-keyRequest body
application/json{
"release": "1.0.0",
"meta": {
"commit": "9f2c1ab",
"repo": "acme/checkout",
"branch": "main",
"triggeredBy": "gitlab-ci"
}
}Responses
201 – Deployment created
{
"id": "dep_64fa3c9e2b7d41c8a9f01234"
}https://api.newinstance.cloud/api/v1/bugwatch/deployHeaders
x-api-keyRequest body
Code samples
curl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/deploy' \
-H 'Content-Type: application/json' \
--data-raw '{
"release": "1.0.0",
"meta": {
"commit": "9f2c1ab",
"repo": "acme/checkout",
"branch": "main",
"triggeredBy": "gitlab-ci"
}
}'/api/v1/bugwatch/deployReport stage · finish · fail
Update a deployment. The body must contain exactly one of stage, finish, fail.
Scope: deploy:write
1 - Stage transition
{ "stage": { "key": "migrate", "name": "Run DB migrations", "status": "running" } }status ∈ running | succeeded | failed; optional exitCode, errorSummary. Repeat per stage. A stage key is upserted - re-sending updates it.
2 - Finish
{ "finish": true }Final status is derived from stages: any failed stage ⇒ failed, else succeeded. A failed finish opens a DeploymentFailure issue in the project.
3 - Fail explicitly
{ "fail": { "summary": "migrate exited 1", "stageKey": "migrate" } }Success - 200 OK
{ "id": "dep_64fa3c9e2b7d41c8a9f01234", "status": "succeeded" }Once status is no longer in_progress, further PATCHes are no-ops that echo the final status - safe to retry.
Common errors
400- stage/log limits exceeded404- unknown deployment id (or wrong project's key)
Headers
x-api-keyRequest body
application/json{
"stage": {
"key": "migrate",
"name": "Run DB migrations",
"status": "succeeded",
"exitCode": 0
}
}Responses
200 – Stage recorded
{
"id": "dep_64fa3c9e2b7d41c8a9f01234",
"status": "in_progress"
}https://api.newinstance.cloud/api/v1/bugwatch/deployHeaders
x-api-keyRequest body
Code samples
curl -X PATCH 'https://api.newinstance.cloud/api/v1/bugwatch/deploy' \
-H 'Content-Type: application/json' \
--data-raw '{
"stage": {
"key": "migrate",
"name": "Run DB migrations",
"status": "succeeded",
"exitCode": 0
}
}'/api/v1/bugwatch/deploy/logsAppend stage logs (NDJSON)
Stream a batch of log lines for one stage. Call repeatedly while the stage runs - the CLI's deploy run does this for you from the wrapped command's stdout/stderr.
Scope: deploy:write · Max body: 2 MB per batch
Body: NDJSON (one JSON object per line) or a JSON array of { level, time, message }:
level- Pino-style number: 10 trace · 20 debug · 30 info · 40 warn · 50 error · 60 fataltime- epoch millisecondsmessage- the log line
Deploy tags (deployId, stage) are injected server-side - do not add them yourself. Lines land in the project's normal log search.
Success - 202 Accepted
{ "ingested": 2, "skipped": 0, "deduped": 0, "truncated": false }truncated: true means the per-deployment log cap was reached; further batches are dropped (finish/fail still work).
Headers
x-api-keyParameters
stagequerystringdefault: Stage key these log lines belong to (required)Request body
application/json{"level":30,"time":{{nowMs}},"message":"Applying migration 0042_add_billing_index"}
{"level":30,"time":{{nowMs}},"message":"Migration complete in 1.2s"}Responses
202 – Batch accepted
{
"ingested": 2,
"skipped": 0,
"deduped": 0,
"truncated": false
}https://api.newinstance.cloud/api/v1/bugwatch/deploy/logsQuery parameters
stageHeaders
x-api-keyRequest body
Code samples
curl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/deploy/logs' \
-H 'Content-Type: application/json' \
--data-raw '{"level":30,"time":{{nowMs}},"message":"Applying migration 0042_add_billing_index"}
{"level":30,"time":{{nowMs}},"message":"Migration complete in 1.2s"}'Support Tickets
4 endpoints/api/v1/support-tickets/auth/login-linksMint customer sign-in link
Mint a single-use support-portal sign-in link for a customer your backend has already authenticated.
Returns a URL that signs the customer into the support portal without a password. The link expires in 10 minutes and is single-use.
Scope: full-access or ticket-management
Request body
| Field | Type | Notes |
|---|---|---|
email | string (required) | Customer email - portal identity |
name | string (optional, max 100) | Customer display name |
externalId | string (optional, max 200) | Your internal customer ID |
returnTo | string (optional, max 500) | Portal path to land on (must start with /) |
Success - 200 OK
{ "url": "https://support.yourcompany.com/auth/redeem?token=abc123", "expiresAt": "2026-06-26T10:10:00.000Z" }Headers
x-api-keyRequest body
application/json{
"email": "alice@example.com",
"name": "Alice Smith",
"externalId": "cust_789",
"returnTo": "/tickets"
}Responses
200 – Link minted
{
"url": "https://support.yourcompany.com/auth/redeem?token=abc123",
"expiresAt": "2026-06-26T10:10:00.000Z"
}https://api.newinstance.cloud/api/v1/support-tickets/auth/login-linksHeaders
x-api-keyRequest body
Code samples
curl -X POST 'https://api.newinstance.cloud/api/v1/support-tickets/auth/login-links' \
-H 'Content-Type: application/json' \
--data-raw '{
"email": "alice@example.com",
"name": "Alice Smith",
"externalId": "cust_789",
"returnTo": "/tickets"
}'/api/v1/support-tickets/ticketsList customer tickets
List tickets for a customer, identified by email.
Scope: full-access or ticket-management or read-only
Query params
| Param | Type | Notes |
|---|---|---|
customerEmail | string (required, email) | Customer to fetch tickets for |
status | string (optional) | OPEN |
page | integer (optional, min 1, default 1) | Page number |
limit | integer (optional, 1–100, default 20) | Items per page |
Success - 200 OK returns paginated ticket list.
Headers
x-api-keyParameters
customerEmailquerystringdefault: Customer email (required)pagequerystringdefault: Page number (default 1)limitquerystringdefault: Items per page (1–100, default 20)Responses
200 – Tickets list
{
"tickets": [
{
"ticketId": "tkt_abc123",
"title": "Unable to export invoice PDF",
"status": "OPEN",
"priority": "HIGH",
"createdAt": "2026-06-26T10:00:00.000Z"
}
],
"total": 1,
"page": 1,
"limit": 20
}https://api.newinstance.cloud/api/v1/support-tickets/ticketsQuery parameters
customerEmailpagelimitHeaders
x-api-keyCode samples
curl -X GET 'https://api.newinstance.cloud/api/v1/support-tickets/tickets'/api/v1/support-tickets/ticketsCreate ticket
Create a support ticket on behalf of a customer. Auto-creates the customer record if not found.
Scope: full-access or ticket-management
Request body
| Field | Type | Notes |
|---|---|---|
title | string (required, max 200) | Ticket title |
description | string (required, max 5000) | Full description |
customerName | string (required, max 100) | Customer full name |
customerEmail | string (required, email) | Customer email |
priority | string (optional) | LOW |
category | string (optional, max 100) | Ticket category |
Success - 201 Created
{ "ticketId": "tkt_abc123", "status": "OPEN", "createdAt": "2026-06-26T10:00:00.000Z" }The returned ticketId is what the detail and comment endpoints take.
Headers
x-api-keyRequest body
application/json{
"title": "Unable to export invoice PDF",
"description": "When I click \"Export as PDF\" on any invoice page, I get a blank file. Tested on Chrome 125 and Firefox 128. The file is 0 bytes. My account ID is ACC-4471.",
"customerName": "Alice Smith",
"customerEmail": "alice@example.com",
"priority": "HIGH",
"category": "Billing"
}Responses
201 – Ticket created
{
"ticketId": "tkt_abc123",
"status": "OPEN",
"createdAt": "2026-06-26T10:00:00.000Z"
}https://api.newinstance.cloud/api/v1/support-tickets/ticketsHeaders
x-api-keyRequest body
Code samples
curl -X POST 'https://api.newinstance.cloud/api/v1/support-tickets/tickets' \
-H 'Content-Type: application/json' \
--data-raw '{
"title": "Unable to export invoice PDF",
"description": "When I click \"Export as PDF\" on any invoice page, I get a blank file. Tested on Chrome 125 and Firefox 128. The file is 0 bytes. My account ID is ACC-4471.",
"customerName": "Alice Smith",
"customerEmail": "alice@example.com",
"priority": "HIGH",
"category": "Billing"
}'/api/v1/support-tickets/tickets/commentsAdd customer comment
Add a public comment from the customer to a ticket.
Scope: full-access or ticket-management
Side effects:
- Auto-reopens
RESOLVEDtickets toIN_PROGRESS - Cannot comment on
CLOSEDtickets (returns 422)
Path param: ticketId (auto-filled from environment)
Request body
| Field | Type | Notes |
|---|---|---|
customerEmail | string (required, email) | Ownership verification |
content | string (required, max 5000) | Comment text |
attachments | array (optional, max 5) | Each item: URI string or {url, name} object. Host the file at a URL you control (your storage or CDN); the API stores the reference, it does not accept file uploads |
Success - 201 Created
{ "commentId": "cmt_abc123", "createdAt": "2026-06-26T10:05:00.000Z" }Headers
x-api-keyRequest body
application/json{
"customerEmail": "alice@example.com",
"content": "I tried on Edge as well and the PDF is still blank. I noticed it only happens for invoices older than 90 days. Recent invoices export fine.",
"attachments": [
{
"url": "https://storage.example.com/screenshots/blank-pdf.png",
"name": "blank-pdf.png"
}
]
}Responses
201 – Comment added
{
"commentId": "cmt_xyz789",
"createdAt": "2026-06-26T10:05:00.000Z"
}https://api.newinstance.cloud/api/v1/support-tickets/tickets/commentsHeaders
x-api-keyRequest body
Code samples
curl -X POST 'https://api.newinstance.cloud/api/v1/support-tickets/tickets/comments' \
-H 'Content-Type: application/json' \
--data-raw '{
"customerEmail": "alice@example.com",
"content": "I tried on Edge as well and the PDF is still blank. I noticed it only happens for invoices older than 90 days. Recent invoices export fine.",
"attachments": [
{
"url": "https://storage.example.com/screenshots/blank-pdf.png",
"name": "blank-pdf.png"
}
]
}'Secret Manager
2 endpoints/api/v1/secret-manager/apps/master-keyGet encrypted master key
Fetch the encrypted Master Encryption Key (MEK) for an app.
Scope: full-access or secret-read
Path param: appId - the App ID from the secret manager dashboard (set as {{appId}} in your environment)
Success - 200 OK
{
"encryptedMek": "<base64-encoded-encrypted-key>",
"algorithm": "AES-256-GCM",
"kdfAlgorithm": "HKDF-SHA256"
}Decrypt the MEK using your App Secret:
const mek = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv: decodeBase64(iv) },
await deriveKey(appSecret), // HKDF-SHA256
decodeBase64(encryptedMek)
);Common errors
401- invalid API key403- key lackssecret-readscope404- app not found429- rate limited (50 req/hour)
Headers
x-api-keyResponses
200 – Encrypted MEK
{
"encryptedMek": "base64encodedEncryptedKeyHere==",
"algorithm": "AES-256-GCM",
"kdfAlgorithm": "HKDF-SHA256"
}https://api.newinstance.cloud/api/v1/secret-manager/apps/master-keyHeaders
x-api-keyCode samples
curl -X GET 'https://api.newinstance.cloud/api/v1/secret-manager/apps/master-key'/api/v1/secret-manager/apps/variablesGet encrypted variables
Fetch encrypted environment variables for an app.
Scope: full-access or secret-read
Path param: appId - App ID from dashboard
Query param: environment (optional) - development | staging | production (default: development)
Success - 200 OK
{
"environment": "production",
"variables": [
{ "key": "DATABASE_URL", "encryptedValue": "<base64>", "iv": "<base64>" },
{ "key": "STRIPE_SECRET_KEY", "encryptedValue": "<base64>", "iv": "<base64>" }
]
}Decrypt each variable using the MEK from GET master-key:
const plaintext = await crypto.subtle.decrypt(
{ name: 'AES-GCM', iv: decodeBase64(variable.iv) },
mek,
decodeBase64(variable.encryptedValue)
);Common errors
401- invalid API key403- key lackssecret-readscope404- app not found or no variables for this environment
The nism CLI (recommended for local dev and CI)link
The published CLI/SDK nismsdk (Node 18+, binaries nism and nismsdk) wraps the fetch + local-decrypt flow; plaintext never transits the platform.
npm i -g nismsdk # or: npx nismsdk <command>
nism setup # store appId / orgId / apiKey in ~/.nism/config.json
nism verify # fetch, decrypt and confirm values are readable
nism load --environment production --create-env .env
eval "$(nism env --appSecret "$NISM_APP_SECRET" --format shell)" && node server.jsCommands: load (the default when no subcommand is given; flags include --environment, --create-env <file>, --env-set-name (default default) and --system-level), setup, config [--show-secrets], env (prints to stdout for piping; fish shells get set -gx), verify. nism does not wrap or spawn your process: to inject secrets into a command, eval the env output first as shown.
Credentials resolve in order: CLI flags, then NISM_* env vars, then legacy NEW_INSTANCE_* env vars, then ~/.nism/config.json, then an interactive prompt. The App Secret is never written to the config file: supply it per run or via NISM_APP_SECRET. Point at the live platform with NISM_GRAPHQL_URL=https://service.newinstance.cloud/service (the built-in default targets a local dev server, so production runs must set this).
Load flags (defaults): --environment development, --format dotenv|json|shell, --output <file>, --bridge true with --bridge-storage-dir ~/.nism, --shell-integration true (detects bash, zsh, fish, ksh, dash), --auto-add-to-profile, --ide-integration, --create-env [file] (a bare flag means .env), --silent, --debug.
Bridge files in ~/.nism: <environment>.json and ide.env at mode 0600, load-<environment>.sh at 0700, config.json at 0600. --auto-add-to-profile appends a marked source block to the matching shell profile. --create-env writes into the current directory with default permissions, unlike the 0600 bridge files, so gitignore the output.
CI: npx nismsdk load --silent --create-env .env with NISM_* repository secrets; add --no-bridge in Docker builds.
Programmatic use (same package): loadSecrets(), injectSecrets(), NismGraphQLClient, decryptMasterKey, decryptWithMasterKey.
Troubleshooting: an invalid App Secret fails the key check; "all variables failed to decrypt" means the right app but the wrong secret; authentication failures point at the API key or org; a missing inheritance link key surfaces when a linked app was unshared; ECONNREFUSED means the GraphQL URL is wrong or unreachable. Re-run with --debug.
Headers
x-api-keyParameters
environmentquerystringdefault: development | staging | production (default: development)Responses
200 – Encrypted variables
{
"environment": "production",
"variables": [
{
"key": "DATABASE_URL",
"encryptedValue": "base64ciphertext==",
"iv": "base64iv=="
},
{
"key": "STRIPE_SECRET_KEY",
"encryptedValue": "base64ciphertext2==",
"iv": "base64iv2=="
}
]
}https://api.newinstance.cloud/api/v1/secret-manager/apps/variablesQuery parameters
environmentHeaders
x-api-keyCode samples
curl -X GET 'https://api.newinstance.cloud/api/v1/secret-manager/apps/variables'Session tokens (REST API)
3 endpoints/api/v1/chat/sessionsGet a customer's live session
Does this customer already hold a live session?
Call it before minting to decide whether to reuse the token you have, wait, or revoke and re-mint. A customer can hold at most one session, so sessions is a list of zero or one.
Expired and revoked sessions are simply absent: the record exists only while the session is usable.
Headers
x-api-keyParameters
customerIdquerystringdefault: Responses
200 – Live session found
{
"sessions": [
{
"sessionId": "b3f1c2d4e5a67890b1c2d3e4f5a6b7c8",
"customerId": "usr_123",
"status": "active",
"expiresAt": "2026-08-26T16:00:00.000Z",
"expiresInSeconds": 21600
}
]
}https://api.newinstance.cloud/api/v1/chat/sessionsQuery parameters
customerIdHeaders
x-api-keyCode samples
curl -X GET 'https://api.newinstance.cloud/api/v1/chat/sessions'/api/v1/chat/sessionsCreate a chat session
Mint a chat session for one of your customers, from YOUR backend.
Call this wherever your app establishes a session - typically right after sign-in - and return the token to your frontend alongside your own session token. The frontend hands that one string to the chat SDK and passes nothing else.
Bodylink
| Field | Type | Required | Notes |
|---|---|---|---|
customerId | string | Yes | Your stable id for this customer. Becomes the verified customer id. Max 200 characters. |
customerName | string | Yes | Name on the conversation and on every message they send. Max 200. |
customerEmail | string | No | Also satisfies the merchant Require customer email setting, so the visitor is never asked. Max 320. |
customerPhone | string | No | Shown to agents. Max 40. |
metadata | object | No | Extra agent-visible context. At most 20 keys, 64-character keys, 500-character values, 4096 bytes total. Nested objects and arrays are rejected rather than flattened. |
expiresInSeconds | integer | No | You choose the lifetime: 60 to 86400 seconds (24 hours max). Defaults to 3600. The example asks for 6 hours. |
One live session per customerlink
A customer may hold one session at a time. While theirs is still valid this returns 409 with the session that is in the way. Once it expires, or you revoke it, a new one can be minted. That keeps a single identity from accumulating parallel credentials.
The 409 body carries the existing session, so you can decide whether to keep using the token you already have or revoke and re-mint.
What comes backlink
A signed JWT, and a session summary that is only an id, its owner and its expiry.
Everything about the customer is inside the token, signed. The platform keeps no copy: it would be duplicated state that could drift from the claims, and the JWT already answers the question. If you need the name, email, phone or metadata back, decode the token.
The token is never stored either. If you lose it, revoke the session and mint another.
Headers
x-api-keyRequest body
application/json{
"customerId": "usr_123",
"customerName": "Ada Lovelace",
"customerEmail": "ada@example.com",
"customerPhone": "+44 20 7946 0958",
"metadata": {
"plan": "enterprise",
"accountNumber": "AC-4471"
},
"expiresInSeconds": 21600
}Responses
201 – Session created
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJiM2YxYzJkNGU1YTY3ODkwIiwic3ViIjoidXNyXzEyMyJ9.7mK2_x1s0KQ",
"expiresAt": "2026-08-26T16:00:00.000Z",
"expiresInSeconds": 21600,
"session": {
"sessionId": "b3f1c2d4e5a67890b1c2d3e4f5a6b7c8",
"customerId": "usr_123",
"status": "active",
"expiresAt": "2026-08-26T16:00:00.000Z",
"expiresInSeconds": 21600
}
}https://api.newinstance.cloud/api/v1/chat/sessionsHeaders
x-api-keyRequest body
Code samples
curl -X POST 'https://api.newinstance.cloud/api/v1/chat/sessions' \
-H 'Content-Type: application/json' \
--data-raw '{
"customerId": "usr_123",
"customerName": "Ada Lovelace",
"customerEmail": "ada@example.com",
"customerPhone": "+44 20 7946 0958",
"metadata": {
"plan": "enterprise",
"accountNumber": "AC-4471"
},
"expiresInSeconds": 21600
}'/api/v1/chat/sessionsInvalidate a session
Kill a session before it expires.
The token stops being accepted on the next request: its signature is still valid and it has not expired, but the session is on the denylist. There is no propagation delay and no cache to wait out.
customerId is required so the customer's slot is freed as well as the token denied. Without it the token would die but the customer would stay blocked from a new session until natural expiry. You always have it: it is what you minted the session for.
Idempotent, so a retried sign-out is safe.
Headers
x-api-keyParameters
customerIdquerystringdefault: Responses
200 – Session invalidated
{
"sessionId": "b3f1c2d4e5a67890b1c2d3e4f5a6b7c8",
"session": {
"sessionId": "b3f1c2d4e5a67890b1c2d3e4f5a6b7c8",
"customerId": "usr_123",
"status": "active",
"expiresAt": "2026-08-26T16:00:00.000Z",
"expiresInSeconds": 21600
},
"revoked": true
}https://api.newinstance.cloud/api/v1/chat/sessionsQuery parameters
customerIdHeaders
x-api-keyCode samples
curl -X DELETE 'https://api.newinstance.cloud/api/v1/chat/sessions'AI Agent Access (MCP)
1 endpoint/mcpList available tools
List the tools this key can use. Keys scoped to BugWatch (or FULL_ACCESS) see the three BugWatch tools; a key with no matching product scopes sees an empty list.
Response - result.tools[] with name, description and a JSON-Schema inputSchema per tool.
Headers
x-api-keyAcceptRequest body
application/json{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}Responses
401 – Not a server key
{
"jsonrpc": "2.0",
"error": {
"code": -32001,
"message": "Unauthorized: this endpoint requires a server API key (keyId:secret) in the x-api-key header or as a Bearer token."
},
"id": null
}https://api.newinstance.cloud/mcpHeaders
x-api-keyAcceptRequest body
Code samples
curl -X POST 'https://api.newinstance.cloud/mcp' \
-H 'Content-Type: application/json' \
--data-raw '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}'