···11+MIT License
22+33+Copyright (c) 2026 Openstatus
44+55+Permission is hereby granted, free of charge, to any person obtaining a copy
66+of this software and associated documentation files (the "Software"), to deal
77+in the Software without restriction, including without limitation the rights
88+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
99+copies of the Software, and to permit persons to whom the Software is
1010+furnished to do so, subject to the following conditions:
1111+1212+The above copyright notice and this permission notice shall be included in all
1313+copies or substantial portions of the Software.
1414+1515+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1616+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1717+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1818+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1919+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2020+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121+SOFTWARE.
+412
README.md
···11+# OpenStatus Node.js SDK
22+33+[](https://jsr.io/@openstatus/sdk-node)
44+[](https://www.npmjs.com/package/@openstatushq/sdk-node)
55+[](https://opensource.org/licenses/MIT)
66+77+Official Node.js SDK for [OpenStatus](https://openstatus.dev) - the open-source
88+monitoring platform.
99+1010+## Features
1111+1212+- **HTTP Monitoring** - Monitor websites and APIs with customizable assertions
1313+- **TCP Monitoring** - Check database connections and other TCP services
1414+- **DNS Monitoring** - Verify DNS records and resolution
1515+- **Global Regions** - Monitor from 28 locations worldwide
1616+- **Type-safe** - Full TypeScript support with generated types
1717+1818+## Installation
1919+2020+### JSR
2121+2222+```bash
2323+npx jsr add @openstatus/node-sdk
2424+```
2525+2626+### npm
2727+2828+```bash
2929+npm install @openstatus/node-sdk
3030+```
3131+3232+### Deno
3333+3434+```typescript
3535+import { openstatus } from "jsr:@openstatus/node-sdk";
3636+```
3737+3838+## Quick Start
3939+4040+```typescript
4141+import { openstatus } from "@openstatus/node-sdk";
4242+4343+const headers = {
4444+ "x-openstatus-key": `Bearer ${process.env.OPENSTATUS_API_KEY}`,
4545+};
4646+4747+// Create a monitor
4848+const monitor = await openstatus.monitor.v1.MonitorService.createHTTPMonitor(
4949+ {
5050+ name: "My API",
5151+ url: "https://api.example.com/health",
5252+ periodicity: "1m",
5353+ method: "GET",
5454+ regions: ["ams", "iad", "syd"],
5555+ active: true,
5656+ statusCodeAssertions: [{ comparator: "EQUAL", target: 200 }],
5757+ },
5858+ { headers },
5959+);
6060+6161+console.log(`Monitor created: ${monitor.monitor?.id}`);
6262+6363+// List all monitors
6464+const { httpMonitors, tcpMonitors, dnsMonitors, totalSize } =
6565+ await openstatus.monitor.v1.MonitorService.listMonitors({}, { headers });
6666+6767+console.log(`Found ${totalSize} monitors`);
6868+```
6969+7070+## Authentication
7171+7272+All API requests require an API key. Get yours from the
7373+[OpenStatus dashboard](https://www.openstatus.dev/app).
7474+7575+```typescript
7676+const headers = {
7777+ "x-openstatus-key": `Bearer ${process.env.OPENSTATUS_API_KEY}`,
7878+};
7979+8080+// Pass headers to any service method
8181+await openstatus.monitor.v1.MonitorService.listMonitors({}, { headers });
8282+```
8383+8484+## Environment Variables
8585+8686+| Variable | Description | Default |
8787+| -------------------- | ----------------------- | -------------------------------- |
8888+| `OPENSTATUS_API_KEY` | Your OpenStatus API key | - |
8989+| `OPENSTATUS_API_URL` | Custom API endpoint | `https://api.openstatus.dev/rpc` |
9090+9191+## API Reference
9292+9393+### Monitor Service
9494+9595+#### `createHTTPMonitor(request, options)`
9696+9797+Create an HTTP/HTTPS monitor.
9898+9999+```typescript
100100+const { monitor } = await openstatus.monitor.v1.MonitorService
101101+ .createHTTPMonitor(
102102+ {
103103+ name: "My Website",
104104+ url: "https://example.com",
105105+ periodicity: "1m",
106106+ method: "GET",
107107+ regions: ["ams", "iad", "syd"],
108108+ active: true,
109109+ },
110110+ { headers },
111111+ );
112112+```
113113+114114+#### `createTCPMonitor(request, options)`
115115+116116+Create a TCP connection monitor.
117117+118118+```typescript
119119+const { monitor } = await openstatus.monitor.v1.MonitorService.createTCPMonitor(
120120+ {
121121+ name: "Database",
122122+ uri: "db.example.com:5432",
123123+ periodicity: "5m",
124124+ regions: ["ams", "iad"],
125125+ active: true,
126126+ },
127127+ { headers },
128128+);
129129+```
130130+131131+#### `createDNSMonitor(request, options)`
132132+133133+Create a DNS resolution monitor.
134134+135135+```typescript
136136+const { monitor } = await openstatus.monitor.v1.MonitorService.createDNSMonitor(
137137+ {
138138+ name: "DNS Check",
139139+ uri: "example.com",
140140+ periodicity: "10m",
141141+ regions: ["ams"],
142142+ active: true,
143143+ recordAssertions: [{
144144+ recordType: "A",
145145+ comparator: "EQUAL",
146146+ target: "93.184.216.34",
147147+ }],
148148+ },
149149+ { headers },
150150+);
151151+```
152152+153153+#### `listMonitors(request, options)`
154154+155155+List all monitors with pagination. Returns monitors grouped by type.
156156+157157+```typescript
158158+const { httpMonitors, tcpMonitors, dnsMonitors, nextPageToken, totalSize } =
159159+ await openstatus.monitor.v1.MonitorService.listMonitors(
160160+ { pageSize: 10, pageToken: "" },
161161+ { headers },
162162+ );
163163+```
164164+165165+#### `triggerMonitor(request, options)`
166166+167167+Trigger an immediate check.
168168+169169+```typescript
170170+await openstatus.monitor.v1.MonitorService.triggerMonitor(
171171+ { id: "mon_123" },
172172+ { headers },
173173+);
174174+```
175175+176176+#### `deleteMonitor(request, options)`
177177+178178+Delete a monitor.
179179+180180+```typescript
181181+await openstatus.monitor.v1.MonitorService.deleteMonitor(
182182+ { id: "mon_123" },
183183+ { headers },
184184+);
185185+```
186186+187187+#### `getMonitorStatus(request, options)`
188188+189189+Get the current status of a monitor across all configured regions.
190190+191191+```typescript
192192+const { id, regions } =
193193+ await openstatus.monitor.v1.MonitorService.getMonitorStatus(
194194+ { id: "mon_123" },
195195+ { headers },
196196+ );
197197+198198+// regions is an array of { region, status }
199199+// status: ACTIVE, DEGRADED, or ERROR
200200+for (const { region, status } of regions) {
201201+ console.log(`${region}: ${status}`);
202202+}
203203+```
204204+205205+#### `getMonitorSummary(request, options)`
206206+207207+Get aggregated metrics and latency percentiles for a monitor.
208208+209209+```typescript
210210+import { TimeRange } from "@openstatus/node-sdk";
211211+212212+const summary = await openstatus.monitor.v1.MonitorService.getMonitorSummary(
213213+ {
214214+ id: "mon_123",
215215+ timeRange: TimeRange.TIME_RANGE_7D, // 1D, 7D, or 14D
216216+ regions: [], // optional: filter by specific regions
217217+ },
218218+ { headers },
219219+);
220220+221221+console.log(`Last ping: ${summary.lastPingAt}`);
222222+console.log(`Success: ${summary.totalSuccessful}`);
223223+console.log(`Degraded: ${summary.totalDegraded}`);
224224+console.log(`Failed: ${summary.totalFailed}`);
225225+console.log(`P50 latency: ${summary.p50}ms`);
226226+console.log(`P95 latency: ${summary.p95}ms`);
227227+console.log(`P99 latency: ${summary.p99}ms`);
228228+```
229229+230230+### Health Service
231231+232232+Check API health status (no authentication required).
233233+234234+```typescript
235235+const { status } = await openstatus.health.v1.HealthService.check({});
236236+console.log(status); // "SERVING"
237237+```
238238+239239+## Monitor Options
240240+241241+### HTTP Monitor
242242+243243+| Option | Type | Required | Description |
244244+| ---------------------- | -------- | -------- | ------------------------------------------------- |
245245+| `name` | string | Yes | Monitor name (max 256 chars) |
246246+| `url` | string | Yes | URL to monitor |
247247+| `periodicity` | string | No | `30s`, `1m`, `5m`, `10m`, `30m`, `1h` |
248248+| `method` | string | No | `GET`, `POST`, `HEAD`, `PUT`, `PATCH`, `DELETE` |
249249+| `body` | string | No | Request body |
250250+| `headers` | object | No | Custom headers |
251251+| `timeout` | number | No | Timeout in ms (default: 45000, max: 120000) |
252252+| `retry` | number | No | Retry attempts (default: 3, max: 10) |
253253+| `followRedirects` | boolean | No | Follow redirects (default: true) |
254254+| `regions` | string[] | No | [Regions](#regions) for checks |
255255+| `active` | boolean | No | Enable monitoring (default: false) |
256256+| `public` | boolean | No | Public visibility (default: false) |
257257+| `degradedAt` | number | No | Latency threshold (ms) for degraded status |
258258+| `statusCodeAssertions` | array | No | [Status code assertions](#status-code-assertions) |
259259+| `bodyAssertions` | array | No | [Body assertions](#body-assertions) |
260260+| `headerAssertions` | array | No | [Header assertions](#header-assertions) |
261261+262262+### TCP Monitor
263263+264264+| Option | Type | Required | Description |
265265+| ------------- | -------- | -------- | ------------------------------ |
266266+| `name` | string | Yes | Monitor name |
267267+| `uri` | string | Yes | `host:port` to monitor |
268268+| `periodicity` | string | No | Check interval |
269269+| `timeout` | number | No | Timeout in ms (default: 45000) |
270270+| `retry` | number | No | Retry attempts (default: 3) |
271271+| `regions` | string[] | No | [Regions](#regions) for checks |
272272+| `active` | boolean | No | Enable monitoring |
273273+274274+### DNS Monitor
275275+276276+| Option | Type | Required | Description |
277277+| ------------------ | -------- | -------- | ----------------------------------------------- |
278278+| `name` | string | Yes | Monitor name |
279279+| `uri` | string | Yes | Domain to resolve |
280280+| `periodicity` | string | No | Check interval |
281281+| `timeout` | number | No | Timeout in ms (default: 45000) |
282282+| `retry` | number | No | Retry attempts (default: 3) |
283283+| `regions` | string[] | No | [Regions](#regions) for checks |
284284+| `recordAssertions` | array | No | [DNS record assertions](#dns-record-assertions) |
285285+286286+## Assertions
287287+288288+### Status Code Assertions
289289+290290+Validate HTTP response status codes.
291291+292292+```typescript
293293+{
294294+ statusCodeAssertions: [
295295+ { comparator: "EQUAL", target: 200 },
296296+ { comparator: "LESS_THAN", target: 400 },
297297+ ];
298298+}
299299+```
300300+301301+**Comparators:** `EQUAL`, `NOT_EQUAL`, `GREATER_THAN`, `GREATER_THAN_OR_EQUAL`,
302302+`LESS_THAN`, `LESS_THAN_OR_EQUAL`
303303+304304+### Body Assertions
305305+306306+Validate response body content.
307307+308308+```typescript
309309+{
310310+ bodyAssertions: [
311311+ { comparator: "CONTAINS", target: '"status":"ok"' },
312312+ { comparator: "NOT_EMPTY" },
313313+ ];
314314+}
315315+```
316316+317317+**Comparators:** `CONTAINS`, `NOT_CONTAINS`, `EQUAL`, `NOT_EQUAL`, `EMPTY`,
318318+`NOT_EMPTY`
319319+320320+### Header Assertions
321321+322322+Validate response headers.
323323+324324+```typescript
325325+{
326326+ headerAssertions: [
327327+ { key: "content-type", comparator: "CONTAINS", target: "application/json" },
328328+ ];
329329+}
330330+```
331331+332332+### DNS Record Assertions
333333+334334+Validate DNS records.
335335+336336+```typescript
337337+{
338338+ recordAssertions: [
339339+ { recordType: "A", comparator: "EQUAL", target: "93.184.216.34" },
340340+ { recordType: "CNAME", comparator: "CONTAINS", target: "cdn" },
341341+ ];
342342+}
343343+```
344344+345345+**Record types:** `A`, `AAAA`, `CNAME`, `MX`, `TXT`
346346+347347+## Regions
348348+349349+Monitor from 28 global locations across multiple providers:
350350+351351+### Fly.io Regions
352352+353353+| Code | Location | Code | Location |
354354+| ----- | --------------- | ----- | ------------ |
355355+| `ams` | Amsterdam | `lax` | Los Angeles |
356356+| `arn` | Stockholm | `lhr` | London |
357357+| `bom` | Mumbai | `nrt` | Tokyo |
358358+| `cdg` | Paris | `ord` | Chicago |
359359+| `dfw` | Dallas | `sjc` | San Jose |
360360+| `ewr` | Newark | `sin` | Singapore |
361361+| `fra` | Frankfurt | `syd` | Sydney |
362362+| `gru` | São Paulo | `yyz` | Toronto |
363363+| `iad` | Washington D.C. | `jnb` | Johannesburg |
364364+365365+### Koyeb Regions
366366+367367+| Code | Location |
368368+| ----------- | ------------- |
369369+| `koyeb_fra` | Frankfurt |
370370+| `koyeb_par` | Paris |
371371+| `koyeb_sfo` | San Francisco |
372372+| `koyeb_sin` | Singapore |
373373+| `koyeb_tyo` | Tokyo |
374374+| `koyeb_was` | Washington |
375375+376376+### Railway Regions
377377+378378+| Code | Location |
379379+| ----------------------- | -------------- |
380380+| `railway_us_west2` | US West |
381381+| `railway_us_east4` | US East |
382382+| `railway_europe_west4` | Europe West |
383383+| `railway_asia_southeast1` | Asia Southeast |
384384+385385+## Error Handling
386386+387387+The SDK uses Connect RPC. Errors include a `code` and `message`:
388388+389389+```typescript
390390+import { ConnectError } from "@connectrpc/connect";
391391+392392+try {
393393+ await openstatus.monitor.v1.MonitorService.deleteMonitor(
394394+ { id: "invalid" },
395395+ { headers },
396396+ );
397397+} catch (error) {
398398+ if (error instanceof ConnectError) {
399399+ console.error(`Error ${error.code}: ${error.message}`);
400400+ }
401401+}
402402+```
403403+404404+## Related
405405+406406+- [OpenStatus](https://openstatus.dev) - Open-source monitoring platform
407407+- [Documentation](https://docs.openstatus.dev) - Full API documentation
408408+- [Status Page](https://status.openstatus.dev) - OpenStatus service status
409409+410410+## License
411411+412412+MIT