New Instance Platform

v1.0.0

New 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, nism CLI
  • 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
GET/api/auth/verify

Verify 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 revoked
  • 403 - key found but inactive

Headers

x-api-key

Responses

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"
}
boltTry it
env
GEThttps://api.newinstance.cloud/api/auth/verify

Headers

x-api-key

Code samples

curl -X GET 'https://api.newinstance.cloud/api/auth/verify'

Browser Ingest

1 endpoint
POST/api/v1/bugwatch/ingest/browser

Browser 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-session

Request 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
}
boltTry it
env
POSThttps://api.newinstance.cloud/api/v1/bugwatch/ingest/browser

Headers

x-bugwatch-session

Request 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
POST/api/v1/bugwatch/browser-session

Mint 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/missing x-api-key
  • 403 - key lacks ingest:write scope

Headers

x-api-key

Responses

200 – Token minted

{
  "token": "eyJhbGciOiJIUzI1NiJ9.example",
  "expiresAt": "2026-06-26T12:15:00.000Z"
}
boltTry it
env
POSThttps://api.newinstance.cloud/api/v1/bugwatch/browser-session

Headers

x-api-key

Request body

Code samples

curl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/browser-session'
POST/api/v1/bugwatch/ingest

Ingest – 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) or application/x-ndjson (one event per line)
  • Max body: 2 MB

Event fields

FieldTypeNotes
levelnumber10/20/30/40/50/60
timenumberUnix ms timestamp
messagestringHuman-readable text
releasestringApp build version
environmentstringproduction / staging / development
eventIdstringOptional; enables deduplication within 10-min window
tagsobjectFlat key-value labels (≤50 keys)
userobject{id, email, username, ip} - only these four keys; others are silently dropped
traceIdstringHex distributed trace id (≤32 chars)
spanIdstringSpan id
exceptionobject{type, value, stacktrace: {frames}}
breadcrumbsarrayLeading events

Success - 202 Accepted

{ "ingested": 1, "skipped": 0, "deduped": 0 }

Headers

x-api-key

Request 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
}
boltTry it
env
POSThttps://api.newinstance.cloud/api/v1/bugwatch/ingest

Headers

x-api-key

Request 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
POST/v1/logs

OTLP/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 - protobuf ExportLogsServiceRequest (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-key

Request 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

null
boltTry it
env
POSThttps://api.newinstance.cloud/v1/logs

Headers

x-api-key

Request 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"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}'
POST/v1/traces

OTLP/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 - protobuf ExportTraceServiceRequest (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-key

Request 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

null
boltTry it
env
POSThttps://api.newinstance.cloud/v1/traces

Headers

x-api-key

Request 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
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}'
POST/v1/metrics

OTLP/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 - protobuf ExportMetricsServiceRequest (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-key

Request 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

null
boltTry it
env
POSThttps://api.newinstance.cloud/v1/metrics

Headers

x-api-key

Request 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"
                        }
                      }
                    ]
                  }
                ]
              }
            }
          ]
        }
      ]
    }
  ]
}'
POST/api/v1/prom/write

Prometheus 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 protobuf WriteRequest - you cannot send a readable JSON body here.

To use Prometheus remote-write with BugWatch:

  1. Deploy the bugwatch-otel-collector (or configure Prometheus remote_write)
  2. Set the remote-write URL to: {{baseUrl}}/api/v1/prom/write
  3. Add header: x-api-key: sk_live_KEYID:secret

Success - 204 No Content

Common errors

  • 400 - malformed Snappy/protobuf body
  • 401 - invalid API key
  • 429 - rate limited

Headers

x-api-key
X-Prometheus-Remote-Write-Version

Request body

text/plain
<<< BINARY SNAPPY-COMPRESSED PROTOBUF - sent by Prometheus/collector, not a manual JSON request >>>

Responses

204 – Accepted

null
boltTry it
env
POSThttps://api.newinstance.cloud/api/v1/prom/write

Headers

x-api-key
X-Prometheus-Remote-Write-Version

Request 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 >>>'
GET/api/v1/prom/query

Prometheus 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_total or {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-key

Parameters

queryquerystringdefault: Metric name or label selector
timequerystringdefault: 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"
        ]
      }
    ]
  }
}
boltTry it
env
GEThttps://api.newinstance.cloud/api/v1/prom/query

Query parameters

query
time

Headers

x-api-key

Code samples

curl -X GET 'https://api.newinstance.cloud/api/v1/prom/query'
GET/api/v1/prom/query_range

Prometheus query_range (over time)

Query stored Prometheus metrics over a time range.

Scope: ingest:write

Query params

  • query (required) - metric selector
  • start (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-key

Parameters

queryquerystringdefault: Metric name or label selector
startquerystringdefault: Range start Unix seconds
endquerystringdefault: Range end Unix seconds

Responses

200 – Matrix result

{
  "status": "success",
  "data": {
    "resultType": "matrix",
    "result": [
      {
        "metric": {
          "__name__": "http_requests_total"
        },
        "values": [
          [
            1788738382,
            "38"
          ],
          [
            1788741982,
            "42"
          ]
        ]
      }
    ]
  }
}
boltTry it
env
GEThttps://api.newinstance.cloud/api/v1/prom/query_range

Query parameters

query
start
end

Headers

x-api-key

Code samples

curl -X GET 'https://api.newinstance.cloud/api/v1/prom/query_range'
GET/api/v1/prom/label/__name__/values

Prometheus 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-key

Responses

200 – Metric names

{
  "status": "success",
  "data": [
    "http_requests_total",
    "rum.lcp",
    "rum.cls",
    "rum.inp"
  ]
}
boltTry it
env
GEThttps://api.newinstance.cloud/api/v1/prom/label/__name__/values

Headers

x-api-key

Code samples

curl -X GET 'https://api.newinstance.cloud/api/v1/prom/label/__name__/values'
GET/jaeger/api/services

Jaeger – 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-key

Responses

200 – Services

{
  "data": [
    "api-gateway",
    "payment-service",
    "user-service"
  ],
  "total": 3,
  "limit": 3,
  "offset": 0,
  "errors": null
}
boltTry it
env
GEThttps://api.newinstance.cloud/jaeger/api/services

Headers

x-api-key

Code samples

curl -X GET 'https://api.newinstance.cloud/jaeger/api/services'
GET/jaeger/api/traces

Jaeger – list traces

List recent distributed traces for this project+environment.

Scope: ingest:write

Query params

  • service (optional) - filter by service name
  • limit (optional, default 20, max 100)
  • lookback (optional) - 1h | 24h | 7d | 30d (default 1h)

Success - 200 OK - returns Jaeger-compatible trace list with spans.

Headers

x-api-key

Parameters

limitquerystringdefault: Max traces (default 20, max 100)
lookbackquerystringdefault: 1h | 24h | 7d | 30d

Responses

200 – Traces list

{
  "data": [],
  "total": 0,
  "limit": 20,
  "offset": 0,
  "errors": null
}
boltTry it
env
GEThttps://api.newinstance.cloud/jaeger/api/traces

Query parameters

limit
lookback

Headers

x-api-key

Code samples

curl -X GET 'https://api.newinstance.cloud/jaeger/api/traces'
POST/api/v1/rum

RUM – 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:

  1. Wrapped array:
{ "vitals": [{ "name": "LCP", "value": 1250.5, "url": "https://example.com/", "rating": "good" }] }
  1. Bare array:
[{ "name": "CLS", "value": 0.05, "rating": "good" }, { "name": "INP", "value": 180, "rating": "needs-improvement" }]
  1. Single vital object:
{ "name": "TTFB", "value": 320, "url": "https://example.com/checkout", "rating": "needs-improvement" }

Vital field schema:

FieldTypeNotes
namestring (required)LCP, CLS, INP, FID, TTFB
valuenumber (required)Metric value in ms (or unitless for CLS)
urlstring (optional)Page URL
ratingstring (optional)good
idstring (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 JSON
  • 401 - invalid session token or API key
  • 403 - origin not in allowedOrigins (when using session token)
  • 429 - rate limited

Headers

x-api-keyOr use x-bugwatch-session for browser calls

Request 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
}
boltTry it
env
POSThttps://api.newinstance.cloud/api/v1/rum

Headers

x-api-key

Request 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
POST/api/v1/bugwatch/artifacts/presign

Artifact 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)

artifactTypeFilePlatform
r8 / proguardmapping.txtandroid
sourcemap.mapios · react-native · android
dart-symbols--split-debug-info outputflutter
dsymApple dSYM referenced as an artifactios

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 r8

The 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-key

Request 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"
}
boltTry it
env
POSThttps://api.newinstance.cloud/api/v1/bugwatch/artifacts/presign

Headers

x-api-key

Request 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"
}'
POST/api/v1/bugwatch/artifacts/uploads/complete

Artifact upload · step 2 – complete

Step 2 of 2 - confirm the artifact landed and swap it in.

Scope: symbols:upload

What is verified

  • complete confirms 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_UPLOAD row 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 to uploadUrl.
  • 400 - the stored size does not match what was declared at presign.
  • 404 - no such upload for this project.

Headers

x-api-key

Responses

201 – Stored and active

{
  "fileId": "f_9a2b1c3d4e5f",
  "sha256": "3b8f2a91c0de4b7a8f13d6c25e09a4b1f87c30d2416e5a9b8c7d0e1f2a3b4c1d"
}
boltTry it
env
POSThttps://api.newinstance.cloud/api/v1/bugwatch/artifacts/uploads/complete

Headers

x-api-key

Request body

Code samples

curl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/artifacts/uploads/complete'
POST/api/v1/bugwatch/debug-symbols/presign

Debug 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)

PlatformAccepted
ios · macos · tvos · watchos · visionos · catalyst.zip of .dSYM bundles, .xcarchive dSYMs, or a raw Mach-O
androidRaw 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 r8

The 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-key

Request 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
}
boltTry it
env
POSThttps://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/presign

Headers

x-api-key

Request 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"
}'
POST/api/v1/bugwatch/debug-symbols/uploads/complete

Debug symbols · step 2 – complete

Step 2 of 2 - confirm the archive landed and queue it for indexing.

Scope: symbols:upload

What is verified

  • complete confirms 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_UPLOAD row 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 to uploadUrl.
  • 400 - declared size does not match the stored object.

Headers

x-api-key

Responses

200 – Queued for indexing

{
  "uploadId": "665f1f77bcf86cd799439031",
  "status": "QUEUED"
}
boltTry it
env
POSThttps://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/uploads/complete

Headers

x-api-key

Request body

Code samples

curl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/uploads/complete'
GET/api/v1/bugwatch/debug-symbols/uploads

List 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 by QUEUED | PROCESSING | DONE | FAILED

Success - 200 OK returns { uploads: [...] }

Headers

x-api-key

Parameters

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"
    }
  ]
}
boltTry it
env
GEThttps://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/uploads

Query parameters

limit

Headers

x-api-key

Code samples

curl -X GET 'https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/uploads'
POST/api/v1/bugwatch/debug-symbols/uploads/reprocess

Reprocess – 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-key

Responses

200 – Re-queued

{
  "requeued": 12
}
boltTry it
env
POSThttps://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/uploads/reprocess

Headers

x-api-key

Request body

Code samples

curl -X POST 'https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/uploads/reprocess'

Deployments (CI/CD)

3 endpoints
POST/api/v1/bugwatch/deploy

Start 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 correlate
  • meta - 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 key
  • 403 - key lacks deploy:write
  • 404 - the project bound to this key no longer exists

Headers

x-api-key

Request 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"
}
boltTry it
env
POSThttps://api.newinstance.cloud/api/v1/bugwatch/deploy

Headers

x-api-key

Request 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"
  }
}'
PATCH/api/v1/bugwatch/deploy

Report 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" } }

statusrunning | 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 exceeded
  • 404 - unknown deployment id (or wrong project's key)

Headers

x-api-key

Request body

application/json
{
  "stage": {
    "key": "migrate",
    "name": "Run DB migrations",
    "status": "succeeded",
    "exitCode": 0
  }
}

Responses

200 – Stage recorded

{
  "id": "dep_64fa3c9e2b7d41c8a9f01234",
  "status": "in_progress"
}
boltTry it
env
PATCHhttps://api.newinstance.cloud/api/v1/bugwatch/deploy

Headers

x-api-key

Request 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
  }
}'
POST/api/v1/bugwatch/deploy/logs

Append 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 fatal
  • time - epoch milliseconds
  • message - 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-key

Parameters

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
}
boltTry it
env
POSThttps://api.newinstance.cloud/api/v1/bugwatch/deploy/logs

Query parameters

stage

Headers

x-api-key

Request 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
GET/api/v1/support-tickets/tickets

List customer tickets

List tickets for a customer, identified by email.

Scope: full-access or ticket-management or read-only

Query params

ParamTypeNotes
customerEmailstring (required, email)Customer to fetch tickets for
statusstring (optional)OPEN
pageinteger (optional, min 1, default 1)Page number
limitinteger (optional, 1–100, default 20)Items per page

Success - 200 OK returns paginated ticket list.

Headers

x-api-key

Parameters

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
}
boltTry it
env
GEThttps://api.newinstance.cloud/api/v1/support-tickets/tickets

Query parameters

customerEmail
page
limit

Headers

x-api-key

Code samples

curl -X GET 'https://api.newinstance.cloud/api/v1/support-tickets/tickets'
POST/api/v1/support-tickets/tickets

Create 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

FieldTypeNotes
titlestring (required, max 200)Ticket title
descriptionstring (required, max 5000)Full description
customerNamestring (required, max 100)Customer full name
customerEmailstring (required, email)Customer email
prioritystring (optional)LOW
categorystring (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-key

Request 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"
}
boltTry it
env
POSThttps://api.newinstance.cloud/api/v1/support-tickets/tickets

Headers

x-api-key

Request 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"
}'
POST/api/v1/support-tickets/tickets/comments

Add customer comment

Add a public comment from the customer to a ticket.

Scope: full-access or ticket-management

Side effects:

  • Auto-reopens RESOLVED tickets to IN_PROGRESS
  • Cannot comment on CLOSED tickets (returns 422)

Path param: ticketId (auto-filled from environment)

Request body

FieldTypeNotes
customerEmailstring (required, email)Ownership verification
contentstring (required, max 5000)Comment text
attachmentsarray (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-key

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

201 – Comment added

{
  "commentId": "cmt_xyz789",
  "createdAt": "2026-06-26T10:05:00.000Z"
}
boltTry it
env
POSThttps://api.newinstance.cloud/api/v1/support-tickets/tickets/comments

Headers

x-api-key

Request 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
GET/api/v1/secret-manager/apps/master-key

Get 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 key
  • 403 - key lacks secret-read scope
  • 404 - app not found
  • 429 - rate limited (50 req/hour)

Headers

x-api-key

Responses

200 – Encrypted MEK

{
  "encryptedMek": "base64encodedEncryptedKeyHere==",
  "algorithm": "AES-256-GCM",
  "kdfAlgorithm": "HKDF-SHA256"
}
boltTry it
env
GEThttps://api.newinstance.cloud/api/v1/secret-manager/apps/master-key

Headers

x-api-key

Code samples

curl -X GET 'https://api.newinstance.cloud/api/v1/secret-manager/apps/master-key'
GET/api/v1/secret-manager/apps/variables

Get 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 key
  • 403 - key lacks secret-read scope
  • 404 - app not found or no variables for this environment

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.js

Commands: 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-key

Parameters

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=="
    }
  ]
}
boltTry it
env
GEThttps://api.newinstance.cloud/api/v1/secret-manager/apps/variables

Query parameters

environment

Headers

x-api-key

Code samples

curl -X GET 'https://api.newinstance.cloud/api/v1/secret-manager/apps/variables'

Session tokens (REST API)

3 endpoints
GET/api/v1/chat/sessions

Get 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-key

Parameters

customerIdquerystringdefault:

Responses

200 – Live session found

{
  "sessions": [
    {
      "sessionId": "b3f1c2d4e5a67890b1c2d3e4f5a6b7c8",
      "customerId": "usr_123",
      "status": "active",
      "expiresAt": "2026-08-26T16:00:00.000Z",
      "expiresInSeconds": 21600
    }
  ]
}
boltTry it
env
GEThttps://api.newinstance.cloud/api/v1/chat/sessions

Query parameters

customerId

Headers

x-api-key

Code samples

curl -X GET 'https://api.newinstance.cloud/api/v1/chat/sessions'
POST/api/v1/chat/sessions

Create 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

FieldTypeRequiredNotes
customerIdstringYesYour stable id for this customer. Becomes the verified customer id. Max 200 characters.
customerNamestringYesName on the conversation and on every message they send. Max 200.
customerEmailstringNoAlso satisfies the merchant Require customer email setting, so the visitor is never asked. Max 320.
customerPhonestringNoShown to agents. Max 40.
metadataobjectNoExtra 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.
expiresInSecondsintegerNoYou 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-key

Request 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
  }
}
boltTry it
env
POSThttps://api.newinstance.cloud/api/v1/chat/sessions

Headers

x-api-key

Request 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
}'
DELETE/api/v1/chat/sessions

Invalidate 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-key

Parameters

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
}
boltTry it
env
DELETEhttps://api.newinstance.cloud/api/v1/chat/sessions

Query parameters

customerId

Headers

x-api-key

Code samples

curl -X DELETE 'https://api.newinstance.cloud/api/v1/chat/sessions'

AI Agent Access (MCP)

1 endpoint
POST/mcp

List 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-key
Accept

Request 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
}
boltTry it
env
POSThttps://api.newinstance.cloud/mcp

Headers

x-api-key
Accept

Request 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"
}'