NewInstance
v1.0.0NewInstance gives you one account, one organization and one API key for a set of products you can adopt one at a time.
Productslink
| Product | What it does |
|---|---|
| BugWatch | Error monitoring, logs, traces, metrics and release health, with SDKs for servers, browsers and mobile apps. |
| Support Tickets | A hosted support portal for your customers, and an API to create and read their tickets. |
| Secret Manager | End-to-end encrypted configuration your apps fetch at runtime. |
| Chat | An AI chatbot and live chat for your website, with native mobile SDKs. |
| Docs Portal | Branded documentation for your own product, with an interactive API reference. |
| EHR | Electronic health records for behavioral health and human services agencies. |
| AI Agent Access (MCP) | An MCP endpoint that lets AI agents work with your data. |
Base URLslink
| Service | URL |
|---|---|
| REST API | https://api.newinstance.cloud |
| MCP endpoint | https://service.newinstance.cloud |
There is no separate sandbox host. Use a sk_test_ key on the same URLs and your calls only ever see test data.
Authenticationlink
Every request sends your API key in the x-api-key header. The Authentication page in this section shows the format, and Getting Started explains how to create a key and which scopes it needs. Browser and mobile SDKs never hold your secret key; their guides explain how they sign in.
Where to startlink
- Follow Getting Started to create an organization and an API key.
- Open the product you are integrating from the sidebar.
- When a call fails, Errors & Rate Limits explains every error and what to do about it.
Getting Started
1 endpointVerify API key
Checks that your API key is valid and returns the organization it belongs to and its environment.
Run this first after generating a key to confirm it is active and to see whether it is a test or a live key. A malformed or revoked key is refused the same way as a missing one.
Authorization
x-api-keyAPI key · headerrequiredResponses
200Key valid401Invalid or missing API key403Key inactivecurl -X GET 'https://api.newinstance.cloud/api/auth/verify' \
-H 'x-api-key: YOUR_API_KEY'{
"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"
}Browser Ingest
1 endpointBrowser ingest - error event (session token)
Sends a browser event authenticated with a session token minted by Server Ingest API → Mint browser session token (run that request first: it stores sessionToken). The payload contract is the same as server ingest. The browser's Origin header is checked against the project's allowed origins. When the token expires, the SDK mints a new one 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
202Error event ingested401Invalid or expired session token403Origin not allowedcurl -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 } ] }
}
}'{
"ingested": 1,
"skipped": 0,
"deduped": 0
}Server Ingest API
2 endpointsMint browser session token
Mints a short-lived browser session token. Call this from your backend with the secret key, then pass the token to your frontend. The secret never reaches the client.
Scope: ingest:write
Send the request with an empty body.
Authorization
x-api-keyAPI key · headerrequiredResponses
200Token minted401Invalid or missing API key403Missing permission scopecurl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/browser-session' \
-H 'x-api-key: YOUR_API_KEY'{
"token": "eyJhbGciOiJIUzI1NiJ9.example",
"expiresAt": "2026-06-26T12:15:00.000Z"
}Ingest – log event (JSON)
Ingests logs and error events from server-side or CI environments.
Scope: ingest:write
Send Content-Type: application/json with a single event or an array of events, or application/x-ndjson with one event per line. The body can be up to 2 MB.
Event fieldslink
| 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 are kept; 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 |
Send an error eventlink
Set level to 50 and add exception, breadcrumbs and trace ids. The eventId enables deduplication: re-sending the same event within 10 minutes increments deduped instead of ingested, as the duplicate example response shows.
{
"level": 50,
"time": {{nowMs}},
"message": "Uncaught TypeError: Cannot read properties of undefined",
"release": "{{release}}",
"environment": "{{environment}}",
"eventId": "evt_unique_abc123",
"traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
"spanId": "00f067aa0ba902b7",
"contexts": { "cart": { "id": "cart_42", "items": 3 } },
"tags": { "component": "checkout" },
"user": { "id": "usr_456", "username": "alice" },
"exception": {
"type": "TypeError",
"value": "Cannot read properties of undefined (reading 'price')",
"stacktrace": {
"frames": [
{ "filename": "checkout.js", "function": "calculateTotal", "lineno": 42, "colno": 15 },
{ "filename": "cart.js", "function": "applyDiscount", "lineno": 88, "colno": 7 }
]
}
},
"breadcrumbs": [
{ "timestamp": {{nowMs}}, "category": "navigation", "message": "Navigated to /checkout", "level": "info" },
{ "timestamp": {{nowMs}}, "category": "ui.click", "message": "Clicked \"Apply coupon\"", "level": "info" }
]
}Send a batch as NDJSONlink
Set Content-Type: application/x-ndjson and put one event on each line. Each line is an independent event. This is the preferred format for batching from server SDKs.
{"level":50,"time":{{nowMs}},"message":"Payment processing failed","release":"{{release}}","environment":"{{environment}}","tags":{"orderId":"ord_789"},"user":{"id":"usr_789"}}
{"level":30,"time":{{nowMs}},"message":"Retry attempt 1 of 3","release":"{{release}}","environment":"{{environment}}","tags":{"orderId":"ord_789"}}Authorization
x-api-keyAPI key · headerrequiredRequest 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
202Event ingested401Invalid or missing API key403Missing permission scope429Rate limitedcurl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/ingest' \
-H 'x-api-key: YOUR_API_KEY' \
-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" }
}'{
"ingested": 1,
"skipped": 0,
"deduped": 0
}OpenTelemetry, Prometheus & Tracing
11 endpointsOTLP/HTTP – ingest logs (JSON)
Sends OpenTelemetry logs to BugWatch over OTLP/HTTP.
Scope: ingest:write
Send application/json, as in this example, or application/x-protobuf with a protobuf ExportLogsServiceRequest, which is what the OTel Collector sends. A fully accepted batch answers with an empty {} object.
Authorization
x-api-keyAPI key · headerrequiredRequest 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
200All accepted401Invalid or missing API key429Rate limitedcurl -X POST 'https://api.newinstance.cloud/v1/logs' \
-H 'x-api-key: YOUR_API_KEY' \
-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"
}
}
]
}
]
}
]
}
]
}'nullOTLP/HTTP – ingest traces (JSON)
Sends OpenTelemetry traces to BugWatch over OTLP/HTTP.
Scope: ingest:write
Send application/json, as in this example, or application/x-protobuf with a protobuf ExportTraceServiceRequest, which is what the OTel Collector sends. A fully accepted batch answers with an empty {} object.
Authorization
x-api-keyAPI key · headerrequiredRequest 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
200All accepted401Invalid or missing API key429Rate limitedcurl -X POST 'https://api.newinstance.cloud/v1/traces' \
-H 'x-api-key: YOUR_API_KEY' \
-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
}
}
]
}
]
}
]
}
]
}'nullOTLP/HTTP – ingest metrics (JSON)
Sends OpenTelemetry metrics to BugWatch over OTLP/HTTP.
Scope: ingest:write
Send application/json, as in this example, or application/x-protobuf with a protobuf ExportMetricsServiceRequest, which is what the OTel Collector sends. A fully accepted batch answers with an empty {} object.
Authorization
x-api-keyAPI key · headerrequiredRequest 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
200All accepted401Invalid or missing API key429Rate limitedcurl -X POST 'https://api.newinstance.cloud/v1/metrics' \
-H 'x-api-key: YOUR_API_KEY' \
-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"
}
}
]
}
]
}
}
]
}
]
}
]
}'nullPrometheus remote-write (DOCUMENTATION ONLY)
Receives Prometheus remote-write samples.
Scope: ingest:write
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 the header
x-api-key: sk_live_KEYID:secret.
Authorization
x-api-keyAPI key · headerrequiredHeaders
X-Prometheus-Remote-Write-VersionRequest body
text/plain<<< BINARY SNAPPY-COMPRESSED PROTOBUF - sent by Prometheus/collector, not a manual JSON request >>>Responses
204Samples accepted400Malformed protobuf401Invalid or missing API key429Rate limitedcurl -X POST 'https://api.newinstance.cloud/api/v1/prom/write' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: text/plain' \
--data-raw '<<< BINARY SNAPPY-COMPRESSED PROTOBUF - sent by Prometheus/collector, not a manual JSON request >>>'nullPrometheus query (instant)
Returns the latest value of each matching metric series at one point in time, using a PromQL-compatible selector.
Scope: ingest:write (the key you write with is accepted for queries)
The same query also works as a POST with form-encoded parameters, which is what Grafana's Prometheus data source sends.
Authorization
x-api-keyAPI key · headerrequiredParameters
queryquery · stringdefault: Required. Metric name or label selector, such as http_requests_total or {service="api-gateway"}.
timequery · stringdefault: Optional. Evaluation time in Unix seconds (decimals allowed). Defaults to now.
Responses
200Instant vector401Invalid or missing API keycurl -X GET 'https://api.newinstance.cloud/api/v1/prom/query?query=http_requests_total&time=1767225600' \
-H 'x-api-key: YOUR_API_KEY'{
"status": "success",
"data": {
"resultType": "vector",
"result": [
{
"metric": {
"__name__": "http_requests_total",
"service": "api"
},
"value": [
1767225600,
"42"
]
}
]
}
}Prometheus query_range (over time)
Returns every stored point of each matching metric series over a time range.
Scope: ingest:write
The same query also works as a POST with form-encoded parameters, which is what Grafana's Prometheus data source sends.
Authorization
x-api-keyAPI key · headerrequiredParameters
queryquery · stringdefault: Required. Metric name or label selector, such as http_requests_total or {service="api-gateway"}.
startquery · stringdefault: Range start in Unix seconds (decimals allowed). Defaults to 0, the oldest stored point.
endquery · stringdefault: Range end in Unix seconds (decimals allowed). Defaults to now.
Responses
200Matrix result401Invalid or missing API keycurl -X GET 'https://api.newinstance.cloud/api/v1/prom/query_range?query=http_requests_total&start=1767222000&end=1767225600' \
-H 'x-api-key: YOUR_API_KEY'{
"status": "success",
"data": {
"resultType": "matrix",
"result": [
{
"metric": {
"__name__": "http_requests_total"
},
"values": [
[
1767222000,
"38"
],
[
1767225600,
"42"
]
]
}
]
}
}Prometheus label __name__ values (list metric names)
Lists every distinct metric name stored for the project and environment your key is scoped to.
Scope: ingest:write
Authorization
x-api-keyAPI key · headerrequiredResponses
200Metric names401Invalid or missing API keycurl -X GET 'https://api.newinstance.cloud/api/v1/prom/label/__name__/values' \
-H 'x-api-key: YOUR_API_KEY'{
"status": "success",
"data": [
"http_requests_total",
"rum.lcp",
"rum.cls",
"rum.inp"
]
}Jaeger – list service names
Lists the distinct service names that have sent traces to this BugWatch project.
Scope: ingest:write (the same API key)
Authorization
x-api-keyAPI key · headerrequiredResponses
200Services401Invalid or missing API keycurl -X GET 'https://api.newinstance.cloud/jaeger/api/services' \
-H 'x-api-key: YOUR_API_KEY'{
"data": [
"api-gateway",
"payment-service",
"user-service"
],
"total": 3,
"limit": 3,
"offset": 0,
"errors": null
}Jaeger – list traces
Lists recent distributed traces for the project and environment your key is scoped to, newest first, each with its spans in the Jaeger format.
Scope: ingest:write
Authorization
x-api-keyAPI key · headerrequiredParameters
limitquery · stringdefault: Optional. Maximum number of traces, up to 100. Defaults to 20.
lookbackquery · stringdefault: Optional. How far back to look: 1h, 24h, 7d or 30d. Defaults to 1h; any other value is treated as 1h.
Responses
200Traces list401Invalid or missing API keycurl -X GET 'https://api.newinstance.cloud/jaeger/api/traces?limit=20&lookback=1h' \
-H 'x-api-key: YOUR_API_KEY'{
"data": [],
"total": 0,
"limit": 20,
"offset": 0,
"errors": null
}Jaeger – get one trace
Returns every span of one trace in the Jaeger format.
Scope: ingest:write
traceId in the path is the 32-character hex trace identifier, as sent in traceparent or returned by Jaeger – list traces. A trace id with no stored spans answers 200 with an empty data list, not 404.
Authorization
x-api-keyAPI key · headerrequiredParameters
traceIdpath · stringrequireddefault: Responses
200Trace401Invalid or missing API keycurl -X GET 'https://api.newinstance.cloud/jaeger/api/traces/{traceId}' \
-H 'x-api-key: YOUR_API_KEY'{
"data": [
{
"traceID": "4bf92f3577b34da6a3ce929d0e0e4736",
"spans": []
}
],
"total": 1,
"limit": 1,
"offset": 0,
"errors": null
}RUM – ingest web-vitals
Ingests Real User Monitoring (RUM) web-vitals metrics (LCP, CLS, INP, FID, TTFB).
Authenticate with x-bugwatch-session from a browser, or with x-api-key from a server or while testing.
Scope (for x-api-key): ingest:write
Request body: three shapes are accepted.
- 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, needs-improvement or poor |
id | string (optional) | Client-side unique id |
Vitals are stored as rum.<name_lower> metric points (for example rum.lcp, rum.cls). A browser call from an origin outside the project's allowed origins is refused with 401, the same as a bad session token.
Authorization
x-api-keyAPI key · headerrequiredRequest 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
202Vitals ingested400Malformed JSON401Invalid or missing API key429Rate limitedcurl -X POST 'https://api.newinstance.cloud/api/v1/rum' \
-H 'x-api-key: YOUR_API_KEY' \
-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"
}
]
}'{
"ingested": 3
}Source Maps & Symbols
7 endpointsArtifact upload · step 1 – presign
Requests a presigned URL for a text symbolication artifact. This is step 1 of 2.
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 queuedThe API key goes on steps 1 and 3 only, never to the storage host, which is authorized 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.
Authorization
x-api-keyAPI key · headerrequiredRequest body
application/json{
"release": "1.0.0",
"platform": "android",
"artifactType": "r8",
"size": 128394,
"sha256": "3b8f2a91c0de4b7a8f13d6c25e09a4b1f87c30d2416e5a9b8c7d0e1f2a3b4c1d",
"originalName": "mapping.txt"
}Responses
201Presigned400Validation error401Invalid or missing API key403Missing symbols:uploadcurl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/artifacts/presign' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{
"release": "1.0.0",
"platform": "android",
"artifactType": "r8",
"size": 128394,
"sha256": "3b8f2a91c0de4b7a8f13d6c25e09a4b1f87c30d2416e5a9b8c7d0e1f2a3b4c1d",
"originalName": "mapping.txt"
}'{
"uploadId": "665f1f77bcf86cd799439021",
"uploadUrl": "https://storage.newinstance.cloud/presigned/665f1f77bcf86cd799439021?sig=…",
"expiresAt": "2026-08-05T12:15:00.000Z"
}Artifact upload · step 2 – complete
Confirms the artifact landed in storage and makes it the active artifact for its (release, platform, artifactType). This is step 2 of 2.
Scope: symbols:upload
uploadId in the path is the id returned by step 1, the presign request.
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 finalize another's upload (a miss returns
404). - Only an
AWAITING_UPLOADrow can be completed, so a completion cannot be replayed to re-queue processing. Completing it again answers200withalreadyCompleted: true. - 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.
Authorization
x-api-keyAPI key · headerrequiredParameters
uploadIdpath · stringrequireddefault: Responses
200Upload already completed201Stored and active400Size mismatch401Invalid or missing API key404Upload not found409File never uploadedcurl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/artifacts/uploads/{uploadId}/complete' \
-H 'x-api-key: YOUR_API_KEY'{
"fileId": "f_9a2b1c3d4e5f",
"sha256": "3b8f2a91c0de4b7a8f13d6c25e09a4b1f87c30d2416e5a9b8c7d0e1f2a3b4c1d",
"alreadyCompleted": true
}Debug symbols · step 1 – presign
Requests a presigned URL for a binary debug-symbol archive. This is step 1 of 2.
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 queuedThe API key goes on steps 1 and 3 only, never to the storage host, which is authorized by the presigned URL's own signature.
Matching is by debug UUID / build-id, the identifier embedded in the binary, not by
release name. 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, so 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.
Authorization
x-api-keyAPI key · headerrequiredRequest body
application/json{
"platform": "ios",
"release": "1.0.0",
"buildNumber": "318",
"size": 48210944,
"sha256": "9c1e5b7a3f28d4c6b0a1e9f2d5c8b7a64310f2e7d9c1b5a8e3f60412789bcf02",
"originalName": "MyApp.app.dSYM.zip"
}Responses
200Archive already uploaded, transfer skipped201Presigned400Validation error401Invalid or missing API key403Missing symbols:uploadcurl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/presign' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{
"platform": "ios",
"release": "1.0.0",
"buildNumber": "318",
"size": 48210944,
"sha256": "9c1e5b7a3f28d4c6b0a1e9f2d5c8b7a64310f2e7d9c1b5a8e3f60412789bcf02",
"originalName": "MyApp.app.dSYM.zip"
}'{
"uploadId": "665f1f77bcf86cd799439011",
"status": "AVAILABLE",
"duplicate": true
}Debug symbols · step 2 – complete
Confirms the archive landed in storage and queues it for indexing. This is step 2 of 2.
Scope: symbols:upload
uploadId in the path is the id returned by step 1, the presign request. After this call, poll Get upload status for indexing progress.
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 finalize another's upload (a miss returns
404). - Only an
AWAITING_UPLOADrow can be completed, so a completion cannot be replayed to re-queue processing. Completing it again answers200withalreadyCompleted: true. - 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.
Authorization
x-api-keyAPI key · headerrequiredParameters
uploadIdpath · stringrequireddefault: Responses
200Queued for indexing400Size mismatch401Invalid or missing API key404Upload not found409File never uploadedcurl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/uploads/{uploadId}/complete' \
-H 'x-api-key: YOUR_API_KEY'{
"uploadId": "665f1f77bcf86cd799439031",
"status": "QUEUED"
}List debug symbol uploads
Lists this project's recent debug symbol uploads, newest first.
Scope: symbols:read
Authorization
x-api-keyAPI key · headerrequiredParameters
limitquery · stringdefault: Optional. Number of uploads to return, 1 to 200. Defaults to 50.
Responses
200Uploads list401Invalid or missing API key403Missing symbols:readcurl -X GET 'https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/uploads?limit=20' \
-H 'x-api-key: YOUR_API_KEY'{
"uploads": [
{
"uploadId": "507f1f77bcf86cd799439011",
"status": "AVAILABLE",
"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"
}
]
}Get upload status
Returns the processing status and indexing summary of one debug symbol upload.
Scope: symbols:read
uploadId in the path comes from the presign response.
Status flow: QUEUED, then EXTRACTING, then one of AVAILABLE, PARTIALLY_AVAILABLE (some objects were invalid), INVALID or FAILED.
Poll this until status is AVAILABLE or PARTIALLY_AVAILABLE before relying on symbolication.
Authorization
x-api-keyAPI key · headerrequiredParameters
uploadIdpath · stringrequireddefault: Responses
200Upload detail400Upload id is not valid401Invalid or missing API key404Upload not foundcurl -X GET 'https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/uploads/{uploadId}' \
-H 'x-api-key: YOUR_API_KEY'{
"uploadId": "507f1f77bcf86cd799439011",
"status": "AVAILABLE",
"platform": "ios",
"release": "2.1.0",
"buildNumber": "1042",
"discoveredDsyms": 2,
"discoveredUuids": 3,
"validObjects": 3,
"invalidObjects": 0,
"failureCode": null,
"failureMessage": null,
"createdAt": "2026-06-26T10:00:00.000Z",
"completedAt": "2026-06-26T10:00:15.000Z"
}Reprocess – re-symbolicate waiting crashes
Re-symbolicates crashes that arrived before this upload's symbols did.
Scope: symbols:reprocess
uploadId in the path is the debug symbol upload the waiting crashes need. Crashes still waiting for its symbols, or only partly symbolicated, are queued again and processed with them. Send an empty body.
Authorization
x-api-keyAPI key · headerrequiredParameters
uploadIdpath · stringrequireddefault: Responses
200Crashes queued for re-symbolication400Upload id is not valid401Invalid or missing API key403Missing symbols:reprocess404Upload not foundcurl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/uploads/{uploadId}/reprocess' \
-H 'x-api-key: YOUR_API_KEY'{
"requeued": 12
}Deployments (CI/CD)
3 endpointsStart deployment
Creates a deployment record. Call it at the start of your CI deploy job.
Scope: deploy:write
Every field is optional, and an empty body is valid.
| Field | Type | Required | Description |
|---|---|---|---|
release | string | No | The release this deploy ships, up to 200 characters. Match what your SDK reports at runtime so errors correlate. |
meta | object | No | String map with commit, repo, branch and triggeredBy, each up to 300 characters. |
Keep the returned id: every later call needs it. The status starts as in_progress. A 404 means the project bound to this key no longer exists.
Authorization
x-api-keyAPI key · headerrequiredRequest body
application/json{
"release": "1.0.0",
"meta": {
"commit": "9f2c1ab",
"repo": "acme/checkout",
"branch": "main",
"triggeredBy": "gitlab-ci"
}
}Responses
201Deployment created401Invalid or missing API key403Missing deploy:write404Project not foundcurl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/deploy' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{
"release": "1.0.0",
"meta": {
"commit": "9f2c1ab",
"repo": "acme/checkout",
"branch": "main",
"triggeredBy": "gitlab-ci"
}
}'{
"id": "dep_64fa3c9e2b7d41c8a9f01234"
}Report stage · finish · fail
Updates a deployment with a stage transition, a finish or an explicit failure. The body carries one of stage, finish or fail.
Scope: deploy:write
deployId in the path is the id returned by Start deployment. Only a key for the same project can update it; any other key gets 404.
1. Stage transition
{ "stage": { "key": "migrate", "name": "Run DB migrations", "status": "running" } }status is running, succeeded or failed; exitCode and errorSummary are optional. Repeat per stage. A stage key is upserted, so 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" } }Once the status is no longer in_progress, further PATCHes are no-ops that echo the final status, so they are safe to retry.
| Status | When it happens |
|---|---|
400 | A per-deployment stage or log limit is exceeded |
Authorization
x-api-keyAPI key · headerrequiredParameters
deployIdpath · stringrequireddefault: Request body
application/json{
"stage": {
"key": "migrate",
"name": "Run DB migrations",
"status": "succeeded",
"exitCode": 0
}
}Responses
200Stage recorded401Invalid or missing API key403Missing deploy:write404Deployment not foundcurl -X PATCH 'https://api.newinstance.cloud/api/v1/bugwatch/deploy/{deployId}' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{
"stage": {
"key": "migrate",
"name": "Run DB migrations",
"status": "succeeded",
"exitCode": 0
}
}'{
"id": "dep_64fa3c9e2b7d41c8a9f01234",
"status": "in_progress"
}Append stage logs (NDJSON)
Streams a batch of log lines for one stage. Call it 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
deployId in the path is the id returned by Start deployment.
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, so do not add them yourself. Lines land in the project's normal log search.
truncated: true means the per-deployment log cap was reached; further batches are dropped, while finish and fail still work.
Authorization
x-api-keyAPI key · headerrequiredParameters
stagequery · stringdefault: Required. Key of the stage these log lines belong to, up to 100 characters, such as migrate.
deployIdpath · stringrequireddefault: 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
202Batch accepted401Invalid or missing API key403Missing deploy:write404Deployment not foundcurl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/deploy/{deployId}/logs?stage=migrate' \
-H 'x-api-key: YOUR_API_KEY' \
-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"}'{
"ingested": 2,
"skipped": 0,
"deduped": 0,
"truncated": false
}Support Tickets
5 endpointsMint customer sign-in link
Mints 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, used as the 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 /) |
Authorization
x-api-keyAPI key · headerrequiredRequest body
application/json{
"email": "alice@example.com",
"name": "Alice Smith",
"externalId": "cust_789",
"returnTo": "/tickets"
}Responses
200Link minted400Invalid email401Invalid or missing API key403Missing permission scopecurl -X POST 'https://api.newinstance.cloud/api/v1/support-tickets/auth/login-links' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{
"email": "alice@example.com",
"name": "Alice Smith",
"externalId": "cust_789",
"returnTo": "/tickets"
}'{
"url": "https://support.yourcompany.com/auth/redeem?token=abc123",
"expiresAt": "2026-06-26T10:10:00.000Z"
}List customer tickets
Lists one customer's tickets, page by page. The customer is identified by email.
Scope: full-access, ticket-management or read-only
Authorization
x-api-keyAPI key · headerrequiredParameters
customerEmailquery · stringdefault: Required. Email of the customer whose tickets to list.
pagequery · stringdefault: Optional. Page number, from 1. Defaults to 1.
limitquery · stringdefault: Optional. Tickets per page, 1 to 100. Defaults to 20.
Responses
200Tickets list400Missing customerEmail401Invalid or missing API key403Missing permission scopecurl -X GET 'https://api.newinstance.cloud/api/v1/support-tickets/tickets?customerEmail=alice%40example.com&page=1&limit=20' \
-H 'x-api-key: YOUR_API_KEY'{
"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
}Create ticket
Creates a support ticket on behalf of a customer, and creates the customer record if it does not exist yet.
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, NORMAL, HIGH or URGENT |
category | string (optional, max 100) | Ticket category |
The returned ticketId is what the detail and comment endpoints take.
Authorization
x-api-keyAPI key · headerrequiredRequest 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
201Ticket created400Validation error401Invalid or missing API key403Missing permission scopecurl -X POST 'https://api.newinstance.cloud/api/v1/support-tickets/tickets' \
-H 'x-api-key: YOUR_API_KEY' \
-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"
}'{
"ticketId": "tkt_abc123",
"status": "OPEN",
"createdAt": "2026-06-26T10:00:00.000Z"
}Get ticket detail
Returns one ticket with its public, customer-facing comments.
Scope: full-access, ticket-management or read-only
ticketId in the path is the ticketId returned by Create ticket; Postman fills it in after that request runs. customerEmail must belong to the customer who owns the ticket: any other email answers 404, so one customer can never read another's ticket.
Authorization
x-api-keyAPI key · headerrequiredParameters
customerEmailquery · stringdefault: Required. Email of the customer who owns the ticket, used as the ownership check.
ticketIdpath · stringrequireddefault: Responses
200Ticket detail400Missing customerEmail401Invalid or missing API key403Missing permission scope404Ticket not foundcurl -X GET 'https://api.newinstance.cloud/api/v1/support-tickets/tickets/{ticketId}?customerEmail=alice%40example.com' \
-H 'x-api-key: YOUR_API_KEY'{
"ticketId": "tkt_abc123",
"title": "Unable to export invoice PDF",
"status": "OPEN",
"priority": "HIGH",
"category": "Billing",
"description": "When I click \"Export as PDF\"...",
"createdAt": "2026-06-26T10:00:00.000Z",
"comments": []
}Add customer comment
Adds a public comment from the customer to a ticket.
Scope: full-access or ticket-management
ticketId in the path is the ticketId returned by Create ticket; Postman fills it in after that request runs.
A comment on a RESOLVED ticket reopens it to OPEN. A CLOSED ticket takes no more comments.
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 |
Authorization
x-api-keyAPI key · headerrequiredParameters
ticketIdpath · stringrequireddefault: Request 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
201Comment added400Ticket is closed401Invalid or missing API key403Missing permission scope404Ticket not foundcurl -X POST 'https://api.newinstance.cloud/api/v1/support-tickets/tickets/{ticketId}/comments' \
-H 'x-api-key: YOUR_API_KEY' \
-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"
}
]
}'{
"commentId": "cmt_xyz789",
"createdAt": "2026-06-26T10:05:00.000Z"
}Secret Manager
2 endpointsGet encrypted master key
Returns an app's Master Encryption Key (MEK), still encrypted under your App Secret.
Scope: full-access or secret-read
appId in the path is the App ID from the Secret Manager dashboard (set it as {{appId}} in your environment).
Unwrap encryptedMasterKey locally with your App Secret, using masterKeyIV and masterKeyTag (AES-256-GCM). The nismsdk package does this for you with decryptMasterKey. Each call counts toward your Secret Manager request allowance, and 429 means the allowance is used up under a hard limit.
Authorization
x-api-keyAPI key · headerrequiredParameters
appIdpath · stringrequireddefault: Responses
200Encrypted MEK400App secret not set up yet401Invalid or missing API key403Missing permission scope404App not found429Rate limitedcurl -X GET 'https://api.newinstance.cloud/api/v1/secret-manager/apps/{appId}/master-key' \
-H 'x-api-key: YOUR_API_KEY'{
"success": true,
"data": {
"appId": "66f1a2b3c4d5e6f7a8b9c0a1",
"appName": "checkout-api",
"encryptedMasterKey": "k3J9vQx0bW1lc3NhZ2UtY2lwaGVydGV4dA==",
"masterKeyIV": "q8Zx4Lm2Tn9Pw1Rs",
"masterKeyTag": "Vb7nC2xQ9mL4kP0sR6tY1w=="
}
}Get encrypted variables
Returns an app's environment variables for one environment, each value still encrypted under the app's MEK.
Scope: full-access or secret-read
appId in the path is the App ID from the Secret Manager dashboard.
Decrypt each value locally with the MEK from Get encrypted master key, using the variable's iv and tag (AES-256-GCM). The nismsdk package does this for you with decryptWithMasterKey. An environment with no variables answers 200 with an empty variables list.
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.
Authorization
x-api-keyAPI key · headerrequiredParameters
environmentquery · stringdefault: Optional. development, staging or production. Defaults to development.
appIdpath · stringrequireddefault: Responses
200Encrypted variables401Invalid or missing API key403Missing permission scope404App not foundcurl -X GET 'https://api.newinstance.cloud/api/v1/secret-manager/apps/{appId}/variables?environment=production' \
-H 'x-api-key: YOUR_API_KEY'{
"success": true,
"data": {
"appId": "66f1a2b3c4d5e6f7a8b9c0a1",
"environment": "production",
"variables": [
{
"id": "66f1a2b3c4d5e6f7a8b9c0b1",
"key": "DATABASE_URL",
"value": "Zm9vYmFyLWNpcGhlcnRleHQtMQ==",
"type": "String",
"description": "Primary Postgres connection string",
"environment": "production",
"isInherited": false,
"sourceAppId": null,
"version": 3,
"iv": "p2Lm8Qx1Zt7Rw4Ns",
"tag": "Hc5vB8nM2xK9qL1sT4wY7g=="
},
{
"id": "66f1a2b3c4d5e6f7a8b9c0b2",
"key": "STRIPE_SECRET_KEY",
"value": "Zm9vYmFyLWNpcGhlcnRleHQtMg==",
"type": "String",
"description": "Stripe live secret key",
"environment": "production",
"isInherited": false,
"sourceAppId": null,
"version": 1,
"iv": "Tn6Wq3Lx9Zp2Rm5K",
"tag": "Ld8sF3gH6jK1mN4pQ7rT0w=="
}
]
}
}Session tokens (REST API)
5 endpointsGet a customer's live session
Returns the customer's live session, if they hold one.
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.
Authorization
x-api-keyAPI key · headerrequiredParameters
customerIdquery · stringdefault: Required. Your id for the customer to look up, the same customerId you minted the session with.
Responses
200Live session found401Invalid or missing API keycurl -X GET 'https://api.newinstance.cloud/api/v1/chat/sessions?customerId=usr_123' \
-H 'x-api-key: YOUR_API_KEY'{
"sessions": [
{
"sessionId": "b3f1c2d4e5a67890b1c2d3e4f5a6b7c8",
"customerId": "usr_123",
"status": "active",
"expiresAt": "2026-08-26T16:00:00.000Z",
"expiresInSeconds": 21600
}
]
}Create a chat session
Mints 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.
Authorization
x-api-keyAPI key · headerrequiredRequest 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
201Session created400Validation error401Invalid or missing API key409Customer already has a live sessioncurl -X POST 'https://api.newinstance.cloud/api/v1/chat/sessions' \
-H 'x-api-key: YOUR_API_KEY' \
-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
}'{
"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
}
}Invalidate a customer's session
Invalidates whatever session a customer holds, for sign-out when you did not keep the session id.
Idempotent: when the customer holds no session it still returns 200 with revoked: false, because "this customer has no live chat session" is the desired end state either way. Wire it into your own sign-out and you never have to track session ids.
Authorization
x-api-keyAPI key · headerrequiredParameters
customerIdquery · stringdefault: Required. Your id for the customer to sign out of chat.
Responses
200Session invalidated401Invalid or missing API keycurl -X DELETE 'https://api.newinstance.cloud/api/v1/chat/sessions?customerId=usr_123' \
-H 'x-api-key: YOUR_API_KEY'{
"session": {
"sessionId": "b3f1c2d4e5a67890b1c2d3e4f5a6b7c8",
"customerId": "usr_123",
"status": "active",
"expiresAt": "2026-08-26T16:00:00.000Z",
"expiresInSeconds": 21600
},
"revoked": true
}Get one session
Reports whether this session has been invalidated.
chatSessionId in the path is the session.sessionId returned by Create a chat session; Postman stores it after that request runs.
Invalidation is the only thing a session id can answer, and the only thing worth asking. The token is a JWT: it carries its own exp and the customer's identity, so decode it for those. What the token cannot tell you is whether someone revoked it early, which is exactly what this reports.
revoked: false means no early invalidation was recorded. It does not mean the token is unexpired, because expiry is the token's business, not the platform's.
Authorization
x-api-keyAPI key · headerrequiredParameters
chatSessionIdpath · stringrequireddefault: Responses
200Not revoked401Invalid or missing API keycurl -X GET 'https://api.newinstance.cloud/api/v1/chat/sessions/{chatSessionId}' \
-H 'x-api-key: YOUR_API_KEY'{
"sessionId": "b3f1c2d4e5a67890b1c2d3e4f5a6b7c8",
"revoked": false
}Invalidate a session
Invalidates a session before it expires.
chatSessionId in the path is the session.sessionId returned by Create a chat session.
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.
Authorization
x-api-keyAPI key · headerrequiredParameters
customerIdquery · stringdefault: Required. The customer this session belongs to, so their one-session slot is freed too.
chatSessionIdpath · stringrequireddefault: Responses
200Session invalidated401Invalid or missing API keycurl -X DELETE 'https://api.newinstance.cloud/api/v1/chat/sessions/{chatSessionId}?customerId=usr_123' \
-H 'x-api-key: YOUR_API_KEY'{
"sessionId": "b3f1c2d4e5a67890b1c2d3e4f5a6b7c8",
"session": {
"sessionId": "b3f1c2d4e5a67890b1c2d3e4f5a6b7c8",
"customerId": "usr_123",
"status": "active",
"expiresAt": "2026-08-26T16:00:00.000Z",
"expiresInSeconds": 21600
},
"revoked": true
}EHR
14 endpointsList reference codes
Searches ICD-10-CM, HCPCS, place of service, modifier, LOINC, RxNorm and taxonomy reference codes. Matches code prefix or display text substring, case insensitive, sorted by code. Any ehr product scope on the key is enough, no specific group is required.
Authorization
x-api-keyAPI key · headerrequiredParameters
systemquery · stringdefault: Optional. Code system: ICD10CM, HCPCS, POS, MODIFIER, LOINC, RXNORM or TAXONOMY. Omit it to search every system.
qquery · stringdefault: Optional. Search text, 1 to 80 characters. Matches a code prefix or a display text substring, case insensitive.
limitquery · stringdefault: Optional. Number of codes to return, 1 to 100. Defaults to 25.
Responses
200Reference codes401Invalid or missing API key403Missing permission scopecurl -X GET 'https://api.newinstance.cloud/api/v1/ehr/reference-codes?system=ICD10CM&q=chol&limit=25' \
-H 'x-api-key: YOUR_API_KEY'{
"items": [
{
"system": "ICD10CM",
"code": "A00",
"display": "Cholera"
}
]
}List webhook subscriptions
Returns this organization's webhook subscriptions, newest first. Each item carries a masked secret preview, never the real secret.
Authorization
x-api-keyAPI key · headerrequiredResponses
200Webhook subscriptions401Invalid or missing API key403Missing permission scopecurl -X GET 'https://api.newinstance.cloud/api/v1/ehr/webhooks' \
-H 'x-api-key: YOUR_API_KEY'{
"items": [
{
"id": "66f1a2b3c4d5e6f7a8b9c0d1",
"name": "Order sync",
"url": "https://example.com/hooks/ehr",
"events": [
"ehr.program.created",
"ehr.program.updated"
],
"status": "active",
"includePhi": false,
"failureCount": 0,
"secretMasked": "whsec_••••ab12",
"createdAt": "2026-09-01T12:00:00.000Z",
"updatedAt": "2026-09-01T12:00:00.000Z"
}
]
}Create a webhook subscription
Creates a webhook subscription. The response includes the signing secret in clear text. Copy it now, it is never shown again. A key issued by a member whose role does not reach every client is refused, because a webhook is sent events about every client. The member who issued the key becomes the member the webhook is delivered under, and nothing more is sent to it once that member no longer reaches every client or leaves the organization, until a member who does saves it again.
Authorization
x-api-keyAPI key · headerrequiredRequest body
application/json{
"name": "Order sync",
"url": "https://example.com/hooks/ehr",
"events": [
"ehr.program.created",
"ehr.program.updated"
],
"includePhi": false
}Responses
201Webhook subscription created400Validation error401Invalid or missing API key403Missing permission scopecurl -X POST 'https://api.newinstance.cloud/api/v1/ehr/webhooks' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{
"name": "Order sync",
"url": "https://example.com/hooks/ehr",
"events": [
"ehr.program.created",
"ehr.program.updated"
],
"includePhi": false
}'{
"id": "66f1a2b3c4d5e6f7a8b9c0d1",
"name": "Order sync",
"url": "https://example.com/hooks/ehr",
"events": [
"ehr.program.created",
"ehr.program.updated"
],
"status": "active",
"includePhi": false,
"failureCount": 0,
"secretMasked": "whsec_••••ab12",
"secret": "whsec_5f2b9c1a7d3e4f6089ab12",
"createdAt": "2026-09-01T12:00:00.000Z",
"updatedAt": "2026-09-01T12:00:00.000Z"
}Send a test event
Queues an ehr.webhook.test event and a pending delivery for this subscription, so you can confirm your receiver handles a delivery correctly. ehrWebhookId in the path is the id returned by Create a webhook subscription, which Postman stores after that request runs.
Authorization
x-api-keyAPI key · headerrequiredParameters
ehrWebhookIdpath · stringrequireddefault: Responses
202Test event queued401Invalid or missing API key403Missing permission scope404Webhook subscription not foundcurl -X POST 'https://api.newinstance.cloud/api/v1/ehr/webhooks/{ehrWebhookId}/test' \
-H 'x-api-key: YOUR_API_KEY'{
"eventId": "66f1a2b3c4d5e6f7a8b9c0d2",
"deliveryId": "66f1a2b3c4d5e6f7a8b9c0d3"
}List events
Returns this organization's event log in sequence order. Pass the previous nextSeq back as sinceSeq to continue from where you left off. An event that names a client protected by 42 CFR Part 2 or a client with no record, or that is about an appointment marked protected or one that cannot be found, is left out, and nextSeq still moves past it. A key issued by a member whose role does not reach every client, such as a clinician who reads a caseload, is refused before a record is read; issue the key from the front desk or an administrator.
Authorization
x-api-keyAPI key · headerrequiredParameters
sinceSeqquery · stringdefault: Optional. Return only events with a sequence number greater than this value. Pass the previous nextSeq to continue.
limitquery · stringdefault: Optional. Number of events to return, 1 to 500. Defaults to 100.
Responses
200Event log page401Invalid or missing API key403Missing permission scopecurl -X GET 'https://api.newinstance.cloud/api/v1/ehr/events?sinceSeq=0&limit=100' \
-H 'x-api-key: YOUR_API_KEY'{
"items": [
{
"id": "66f1a2b3c4d5e6f7a8b9c0d2",
"seq": 1,
"type": "ehr.webhook.test",
"occurredAt": "2026-09-01T12:00:00.000Z",
"aggregate": {
"type": "webhook_subscription",
"id": "66f1a2b3c4d5e6f7a8b9c0d1"
},
"status": "pending"
}
],
"nextSeq": 1
}List appointments
Returns appointments that overlap the requested range, oldest first. Every instant is ISO 8601 in UTC and each appointment carries the IANA zone it was booked in, so a client in another zone can render the local wall time correctly. Needs the scheduling scope. A key issued by a member whose role does not reach every client, such as a clinician who reads a caseload, is refused before a record is read; issue the key from the front desk or an administrator.
Authorization
x-api-keyAPI key · headerrequiredParameters
fromquery · stringdefault: Required. Range start, an ISO 8601 instant.
toquery · stringdefault: Required. Range end, an ISO 8601 instant.
pagequery · stringdefault: Optional. Page number, from 1. Defaults to 1.
limitquery · stringdefault: Optional. Appointments per page, 1 to 100. Defaults to 25.
Responses
200Appointment page400Validation error401Invalid or missing API key403Missing permission scopecurl -X GET 'https://api.newinstance.cloud/api/v1/ehr/appointments?from=2026-10-25T00%3A00%3A00.000Z&to=2026-10-26T00%3A00%3A00.000Z&page=1&limit=25' \
-H 'x-api-key: YOUR_API_KEY'{
"items": [
{
"id": "66f1a2b3c4d5e6f7a8b9c0e1",
"seriesId": null,
"typeId": "66f1a2b3c4d5e6f7a8b9c0a1",
"status": "scheduled",
"clientIds": [
"66f1a2b3c4d5e6f7a8b9c0b1"
],
"primaryStaffId": "66f1a2b3c4d5e6f7a8b9c0c1",
"staffIds": [
"66f1a2b3c4d5e6f7a8b9c0c1"
],
"roomLocationId": "66f1a2b3c4d5e6f7a8b9c0d4",
"locationId": null,
"programId": null,
"serviceCodeId": null,
"start": "2026-10-25T11:00:00.000Z",
"end": "2026-10-25T12:00:00.000Z",
"timezone": "America/New_York",
"checkInAt": null,
"checkOutAt": null,
"encounterId": null
}
],
"total": 1,
"page": 1,
"limit": 25
}Create an appointment
Books an appointment. Send start and end in UTC and the zone the time was chosen in. A clash returns 409 naming the resource already booked, unless the organization double-booking policy allows it or you send allowConflicts. A client, staff member, room, program or appointment type that cannot take the booking is refused with 4xx and a message saying what to change; a 503 means the service could not confirm its own indexes and the request is worth retrying. A key issued by a member whose role does not reach every client, such as a clinician who reads a caseload, is refused before a record is read; issue the key from the front desk or an administrator.
Authorization
x-api-keyAPI key · headerrequiredRequest body
application/json{
"typeId": "66f1a2b3c4d5e6f7a8b9c0a1",
"clientIds": [
"66f1a2b3c4d5e6f7a8b9c0b1"
],
"primaryStaffId": "66f1a2b3c4d5e6f7a8b9c0c1",
"start": "2026-10-26T13:00:00.000Z",
"end": "2026-10-26T14:00:00.000Z",
"timezone": "America/New_York"
}Responses
201Appointment booked400Validation error401Invalid or missing API key403Missing permission scope409Booking clashcurl -X POST 'https://api.newinstance.cloud/api/v1/ehr/appointments' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{
"typeId": "66f1a2b3c4d5e6f7a8b9c0a1",
"clientIds": [
"66f1a2b3c4d5e6f7a8b9c0b1"
],
"primaryStaffId": "66f1a2b3c4d5e6f7a8b9c0c1",
"start": "2026-10-26T13:00:00.000Z",
"end": "2026-10-26T14:00:00.000Z",
"timezone": "America/New_York"
}'{
"id": "66f1a2b3c4d5e6f7a8b9c0e2",
"seriesId": null,
"typeId": "66f1a2b3c4d5e6f7a8b9c0a1",
"status": "scheduled",
"clientIds": [
"66f1a2b3c4d5e6f7a8b9c0b1"
],
"primaryStaffId": "66f1a2b3c4d5e6f7a8b9c0c1",
"staffIds": [
"66f1a2b3c4d5e6f7a8b9c0c1"
],
"roomLocationId": null,
"locationId": null,
"programId": null,
"serviceCodeId": null,
"start": "2026-10-26T13:00:00.000Z",
"end": "2026-10-26T14:00:00.000Z",
"timezone": "America/New_York",
"checkInAt": null,
"checkOutAt": null,
"encounterId": null
}List claims
Lists claims newest first with what each one is worth and whether the clearinghouse answered. Needs the billing-read group on the key and a creator who holds the permission to view billing amounts. Claims for a client protected by 42 CFR Part 2 are in neither the rows nor the total. Payer reason codes are on the single claim read, never here. A key issued by a member whose role does not reach every client, such as a clinician who reads a caseload, is refused before a record is read; issue the key from the front desk or an administrator.
Authorization
x-api-keyAPI key · headerrequiredParameters
pagequery · stringdefault: Optional. Page number, from 1. Defaults to 1.
limitquery · stringdefault: Optional. Claims per page, 1 to 100. Defaults to 25.
Responses
200Claims401Invalid or missing API key403Missing permission scopecurl -X GET 'https://api.newinstance.cloud/api/v1/ehr/billing/claims?page=1&limit=25' \
-H 'x-api-key: YOUR_API_KEY'{
"items": [
{
"id": "66f1a2b3c4d5e6f7a8b9c0e1",
"payerId": "66f1a2b3c4d5e6f7a8b9c0e2",
"controlNumber": "10001",
"status": "queued",
"chargeCount": 1,
"totalAmountMinor": 12500,
"currency": "USD",
"transmission": {
"status": "acknowledged",
"sentAt": "2026-09-02T11:00:00.000Z",
"acknowledgedAt": "2026-09-03T11:00:00.000Z"
},
"submittedAt": "2026-09-02T10:00:00.000Z"
}
],
"total": 1,
"page": 1,
"limit": 25
}Read one claim
Returns one claim with its service lines, the state of its transmission, and the payer reason and remark codes where the payer denied it, with deniedAt, the date the payer gave for the denial, as that calendar date in the form YYYY-MM-DD, such as 2026-09-11, with no time of day. The claim file itself, which carries the member number, the name, the date of birth and the diagnoses, is never returned. A claim for a Part 2 client answers 404. A key issued by a member whose role does not reach every client, such as a clinician who reads a caseload, is refused before a record is read; issue the key from the front desk or an administrator. ehrClaimId in the path is a claim id from List claims, which Postman stores after that request runs.
Authorization
x-api-keyAPI key · headerrequiredParameters
ehrClaimIdpath · stringrequireddefault: Responses
200Claim401Invalid or missing API key403Missing permission scope404Claim not foundcurl -X GET 'https://api.newinstance.cloud/api/v1/ehr/billing/claims/{ehrClaimId}' \
-H 'x-api-key: YOUR_API_KEY'{
"id": "66f1a2b3c4d5e6f7a8b9c0e1",
"payerId": "66f1a2b3c4d5e6f7a8b9c0e2",
"controlNumber": "10001",
"payerClaimControlNumber": null,
"status": "queued",
"format": "837P",
"frequencyCode": "1",
"totalAmountMinor": 12500,
"currency": "USD",
"transmission": {
"status": "acknowledged",
"sentAt": "2026-09-02T11:00:00.000Z",
"acknowledgedAt": "2026-09-03T11:00:00.000Z"
},
"lines": [
{
"lineNo": 1,
"chargeId": "66f1a2b3c4d5e6f7a8b9c0e3",
"serviceCode": "90834",
"modifiers": [],
"units": 1,
"amountMinor": 12500,
"dateOfService": "2026-09-01"
}
],
"denial": {
"status": "open",
"reasons": [
"CO-97"
],
"remarkCodes": [
"N19"
],
"deniedMinor": 2500,
"allowedMinor": 10000,
"deniedAt": "2026-09-11"
},
"submittedAt": "2026-09-02T10:00:00.000Z",
"updatedAt": "2026-09-02T10:00:00.000Z"
}Read one charge and its ledger
Returns one charge and every ledger entry posted against it, with what each party still owes on it: payerOwedMinor for the insurer, clientOwedMinor for the client, and transferredToClientMinor for what has moved from one to the other. There is no single party blind balance, because once responsibility has moved between the two a single figure names neither of them. The entry count is there because nothing owed is also what a charge nothing ever posted to looks like. Diagnoses are not returned. The figures are worked out from the entries returned beside them when the charge is read, so the two always agree. A charge for a Part 2 client answers 404. A key issued by a member whose role does not reach every client, such as a clinician who reads a caseload, is refused before a record is read; issue the key from the front desk or an administrator. ehrChargeId in the path is a chargeId from the lines of Read one claim.
Authorization
x-api-keyAPI key · headerrequiredParameters
ehrChargeIdpath · stringrequireddefault: Responses
200Charge and ledger401Invalid or missing API key403Missing permission scope404Charge not foundcurl -X GET 'https://api.newinstance.cloud/api/v1/ehr/billing/charges/{ehrChargeId}' \
-H 'x-api-key: YOUR_API_KEY'{
"id": "66f1a2b3c4d5e6f7a8b9c0e3",
"serviceCode": "90834",
"modifiers": [],
"units": 1,
"dateOfService": "2026-09-01",
"amountMinor": 12500,
"currency": "USD",
"status": "ready",
"payerOwedMinor": 0,
"clientOwedMinor": 2500,
"transferredToClientMinor": 2500,
"entryCount": 4,
"entries": [
{
"kind": "charge",
"sourceType": "charge",
"amountMinor": 12500,
"currency": "USD",
"payerId": "66f1a2b3c4d5e6f7a8b9c0e2",
"postedAt": "2026-09-01T12:00:00.000Z"
},
{
"kind": "payer_payment",
"sourceType": "remittance_line",
"amountMinor": 8000,
"currency": "USD",
"payerId": "66f1a2b3c4d5e6f7a8b9c0e2",
"postedAt": "2026-09-10T12:00:00.000Z"
},
{
"kind": "contractual_adjustment",
"sourceType": "remittance_line",
"amountMinor": 2000,
"currency": "USD",
"payerId": "66f1a2b3c4d5e6f7a8b9c0e2",
"postedAt": "2026-09-10T12:00:00.000Z"
},
{
"kind": "transfer_to_client",
"sourceType": "remittance_line",
"amountMinor": 2500,
"currency": "USD",
"payerId": null,
"postedAt": "2026-09-10T12:00:00.000Z"
}
],
"capturedAt": "2026-09-01T12:00:00.000Z"
}Look up a client by MRN
Returns the demographic summary for one client by medical record number. The number is sent in the request body, never in the path or the query string, because those reach an access log. Needs the clients-read scope, and a key issued by a member whose role reaches every client, such as the front desk or an administrator; any other key is refused before a record is read. Every call is written to the access log with the API key as the actor.
Authorization
x-api-keyAPI key · headerrequiredRequest body
application/json{
"mrn": ""
}Responses
200Client summary401Invalid or missing API key403Missing permission scope404No such clientcurl -X POST 'https://api.newinstance.cloud/api/v1/ehr/clients/lookup' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{
"mrn": ""
}'{
"id": "66f1a2b3c4d5e6f7a8b9c0b1",
"mrn": "MRN-1042",
"firstName": "Ada",
"lastName": "Okafor",
"dob": "1984-03-09",
"status": "active",
"part2": false,
"primaryProgramId": "66f1a2b3c4d5e6f7a8b9c0f4",
"primaryClinicianStaffId": "66f1a2b3c4d5e6f7a8b9c0c1"
}Inbound SMS webhook
Receives the message replies your SMS provider posts. The request carries the provider signature, so no API key is used. A signature that does not verify answers 401 with an empty body. A reply of C, CONFIRM, YES or Y confirms the next appointment within seven days, except from a number more than one client shares, where it confirms nothing and the reply is recorded as ignored. STOP, STOPALL, UNSUBSCRIBE, CANCEL, END or QUIT removes text message consent for that number immediately, so a reply of CANCEL opts the number out of text messages and cancels no appointment.
Headers
x-twilio-signatureRequest body
application/x-www-form-urlencoded{
"To": "+15550100000",
"From": "+15550101234",
"Body": "C",
"MessageSid": "SM00000000000000000000000000000001"
}Responses
204Reply handled400Validation error401Signature did not verify, empty body404No organization owns that numbercurl -X POST 'https://api.newinstance.cloud/api/v1/ehr/sms/inbound' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-raw '{
"To": "+15550100000",
"From": "+15550101234",
"Body": "C",
"MessageSid": "SM00000000000000000000000000000001"
}'nullDelete a webhook subscription
Soft deletes a webhook subscription. It stops receiving deliveries immediately and drops out of the list. ehrWebhookId in the path is the id returned by Create a webhook subscription. A key issued by a member whose role does not reach every client is refused, because a webhook is sent events about every client.
Authorization
x-api-keyAPI key · headerrequiredParameters
ehrWebhookIdpath · stringrequireddefault: Responses
204Subscription deleted401Invalid or missing API key403Missing permission scope404Webhook subscription not foundcurl -X DELETE 'https://api.newinstance.cloud/api/v1/ehr/webhooks/{ehrWebhookId}' \
-H 'x-api-key: YOUR_API_KEY'nullUpdate a webhook subscription
Updates one or more fields on a webhook subscription: name, url, events, or status. At least one field is required. ehrWebhookId in the path is the id returned by Create a webhook subscription. A key issued by a member whose role does not reach every client is refused, because a webhook is sent events about every client. The member who issued the key becomes the member the webhook is delivered under, and nothing more is sent to it once that member no longer reaches every client or leaves the organization, until a member who does saves it again.
Authorization
x-api-keyAPI key · headerrequiredParameters
ehrWebhookIdpath · stringrequireddefault: Request body
application/json{
"status": "paused"
}Responses
200Webhook subscription updated400Validation error401Invalid or missing API key403Missing permission scope404Webhook subscription not foundcurl -X PATCH 'https://api.newinstance.cloud/api/v1/ehr/webhooks/{ehrWebhookId}' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{
"status": "paused"
}'{
"id": "66f1a2b3c4d5e6f7a8b9c0d1",
"name": "Order sync",
"url": "https://example.com/hooks/ehr",
"events": [
"ehr.program.created",
"ehr.program.updated"
],
"status": "paused",
"includePhi": false,
"failureCount": 0,
"secretMasked": "whsec_••••ab12",
"createdAt": "2026-09-01T12:00:00.000Z",
"updatedAt": "2026-09-01T12:05:00.000Z"
}AI Agent Access (MCP)
1 endpointList available tools
Lists the tools this key can use, and documents the tools/call request that runs one. Keys scoped to BugWatch (or FULL_ACCESS) see the three BugWatch tools; a key with no matching product scopes sees an empty list.
The answer is result.tools[], each tool with name, description and a JSON Schema inputSchema, sent as a single SSE frame: parse the JSON after data:.
Call a toollink
Send tools/call to the same URL with the tool name and its arguments. Tool results come back MCP-style: result.content[0].text contains the JSON payload as a string, so parse it client-side. Tool-level failures set result.isError: true with the message in the text block.
bugwatch_list_projectslink
Lists the organization's BugWatch projects, newest first. Each project carries its id, name, slug, publicId, environments and settings, and the result adds totalCount and paging fields. search filters by name, case-insensitive; limit is 1 to 50 and defaults to 10.
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "bugwatch_list_projects",
"arguments": {
"search": "",
"page": 1,
"limit": 20
}
}
}bugwatch_list_issueslink
Lists the issues in one project, sorted by lastSeen unless sort is firstSeen or occurrences. Each issue carries its id, title, level, status, culprit, occurrence count, firstSeen and lastSeen. query (optional) matches title or culprit; status filters by unresolved, resolved or ignored; limit is 1 to 100 and defaults to 20.
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "bugwatch_list_issues",
"arguments": {
"projectId": "{{projectId}}",
"status": "unresolved",
"page": 1,
"limit": 20
}
}
}bugwatch_root_cause_analysislink
Runs the platform AI root-cause analysis for one issue. Use the same call shape with { "name": "bugwatch_root_cause_analysis", "arguments": { "projectId": "…", "issueId": "…" } }. The text block holds { analysis, contextSummary, error }; when error is set the analysis failed, but contextSummary is still real incident data.
Authorization
x-api-keyAPI key · headerrequiredHeaders
AcceptRequest body
application/json{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}Responses
200Tools listed401Not a server keycurl -X POST 'https://api.newinstance.cloud/mcp' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
--data-raw '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}'event: message
data: {"result":{"tools":[{"name":"bugwatch_list_projects","description":"List the BugWatch projects of the organization that owns the API key. Returns projects with id, name, platform, and counts, plus totalCount and pagination info. Results are paginated (default 10 per page); check hasNextPage and fetch further pages before concluding a project does not exist.","inputSchema":{"type":"object","properties":{"search":{"description":"Case-insensitive project name filter","type":"string"},"page":{"type":"integer","minimum":1,"maximum":9007199254740991},"limit":{"type":"integer","minimum":1,"maximum":50}},"$schema":"http://json-schema.org/draft-07/schema#"},"execution":{"taskSupport":"forbidden"}},{"name":"bugwatch_list_issues","description":"List error/issue groups in one BugWatch project (get projectId from bugwatch_list_projects). Each issue has id, title, level, status, culprit, occurrence counts, and firstSeen/lastSeen. Results are paginated (default 20 per page) and sorted by lastSeen unless sort is given; never claim an issue is absent from sampled or truncated results; page through or narrow with query.","inputSchema":{"type":"object","properties":{"projectId":{"description":"BugWatch project id","type":"string"},"status":{"description":"Filter: unresolved, resolved, or ignored (OPEN also means unresolved)","type":"string"},"query":{"description":"Case-insensitive match on issue title or culprit","type":"string"},"env":{"type":"string"},"sort":{"description":"lastSeen (default), firstSeen, or occurrences","type":"string"},"page":{"type":"integer","minimum":1,"maximum":9007199254740991},"limit":{"type":"integer","minimum":1,"maximum":100}},"required":["projectId"],"$schema":"http://json-schema.org/draft-07/schema#"},"execution":{"taskSupport":"forbidden"}},{"name":"bugwatch_root_cause_analysis","description":"Run the platform AI root-cause analysis for one BugWatch issue. Returns { analysis, contextSummary, error }: analysis is the AI-generated root cause and next steps, contextSummary is the deterministic incident context it was based on. If error is set, generation failed (e.g. no AI provider configured), so report that failure honestly; the contextSummary is still real incident data you may reason over yourself.","inputSchema":{"type":"object","properties":{"projectId":{"description":"BugWatch project id","type":"string"},"issueId":{"description":"Issue id from bugwatch_list_issues","type":"string"}},"required":["projectId","issueId"],"$schema":"http://json-schema.org/draft-07/schema#"},"execution":{"taskSupport":"forbidden"}}]},"jsonrpc":"2.0","id":1}