TypeScript Tips

Not yet available on FTStatus

The FTStatus SDKs aren't available yet. Talk to support if you need programmatic access for your account.

Working with bigint Fields

Protocol Buffers int64 fields map to bigint in TypeScript. This affects:

  • Monitor configuration: timeout, retry, degradedAt
  • Assertions: StatusCodeAssertion.target
  • Monitor summary: totalSuccessful, totalDegraded, totalFailed, p50, p75, p90, p95, p99

Use BigInt() to create values:

const { monitor } = await client.monitor.v1.MonitorService.createHTTPMonitor({
  monitor: {
    name: "My API",
    url: "https://example.com",
    periodicity: Periodicity.PERIODICITY_1M,
    active: true,
    timeout: BigInt(30000),       // 30 seconds
    retry: BigInt(5),             // 5 retries
    degradedAt: BigInt(3000),     // degraded after 3s
    statusCodeAssertions: [
      { comparator: NumberComparator.EQUAL, target: BigInt(200) },
    ],
  },
});

Reading bigint values:

const summary = await client.monitor.v1.MonitorService.getMonitorSummary({
  id: "mon_123",
  timeRange: TimeRange.TIME_RANGE_7D,
  regions: [],
});

// bigint values — use Number() for display if values are safe
console.log(`P95 latency: ${summary.p95}ms`);
console.log(`Total checks: ${summary.totalSuccessful + summary.totalDegraded + summary.totalFailed}`);

Handling oneof Types

Several responses use protobuf oneof fields, which map to discriminated unions in TypeScript.

MonitorConfig (getMonitor)

const { monitor } = await client.monitor.v1.MonitorService.getMonitor({
  id: "mon_123",
});

switch (monitor?.config.case) {
  case "http":
    // monitor.config.value is HTTPMonitor
    console.log(`URL: ${monitor.config.value.url}`);
    break;
  case "tcp":
    // monitor.config.value is TCPMonitor
    console.log(`URI: ${monitor.config.value.uri}`);
    break;
  case "dns":
    // monitor.config.value is DNSMonitor
    console.log(`Domain: ${monitor.config.value.uri}`);
    break;
}

NotificationData (createNotification)

The data.data field selects the provider-specific configuration:

// The case string matches the provider in camelCase
data: {
  data: { case: "slack", value: { webhookUrl: "..." } }
}
data: {
  data: { case: "googleChat", value: { webhookUrl: "..." } }
}
data: {
  data: { case: "grafanaOncall", value: { webhookUrl: "..." } }
}

Status Page Identifiers

getStatusPageContent and getOverallStatus accept a page identifier by ID or slug:

// By ID
{ identifier: { case: "id", value: "page_123" } }

// By slug
{ identifier: { case: "slug", value: "my-service" } }

Unsubscribe Identifier

unsubscribeFromPage accepts an email or subscriber ID:

// By email
{ identifier: { case: "email", value: "user@example.com" } }

// By subscriber ID
{ identifier: { case: "id", value: "sub_456" } }

Migrating from Default Client to createFTStatusClient

Before — manual headers on every call:

import { openstatus } from "@openstatus/sdk-node";

const headers = { "x-openstatus-key": process.env.OPENSTATUS_API_KEY };

const { httpMonitors } = await openstatus.monitor.v1.MonitorService
  .listMonitors({}, { headers });

const { monitor } = await openstatus.monitor.v1.MonitorService
  .createHTTPMonitor({
    monitor: { name: "API", url: "https://example.com", periodicity: 2, active: true },
  }, { headers });

After — configure once, use everywhere:

import { createFTStatusClient, Periodicity } from "@openstatus/sdk-node";

const client = createFTStatusClient({
  apiKey: process.env.OPENSTATUS_API_KEY,
});

const { httpMonitors } = await client.monitor.v1.MonitorService
  .listMonitors({});

const { monitor } = await client.monitor.v1.MonitorService
  .createHTTPMonitor({
    monitor: {
      name: "API",
      url: "https://example.com",
      periodicity: Periodicity.PERIODICITY_1M,
      active: true,
    },
  });