# Watchtower Node.js SDK Documentation

## Introduction

The Watchtower Node.js SDK provides a convenient way to interact with the Watchtower API for logging, metrics, analysis, and more. This SDK is designed to be used in Node.js environments.

## Installation

To install the SDK, run the following command in your project directory:

```bash
npm install sjursen-digital-watchtower
```

## Initialization

Import and initialize the SDK with your API base URL:

```typescript
import { WatchtowerSDK } from 'sjursen-digital-watchtower';

const sdk = new WatchtowerSDK('YOUR_API_BASE_URL');
```

Replace `YOUR_API_BASE_URL` with the actual base URL of the Watchtower API.

## Endpoints

The SDK organizes API endpoints into logical categories:

### 1. Health Endpoint

Check the health of the API.

#### `health.check()`

`GET /api/health`

Checks if the API is reachable and operational.

```typescript
async check(): Promise<any>
```

**Example:**

```typescript
try {
  const healthStatus = await sdk.health.check();
  console.log('API Health Status:', healthStatus);
} catch (error) {
  console.error('Failed to check API health:', error);
}
```

### 2. API Key Endpoints

Manage API keys for organizations, apps, and tenants.

#### `apikey.createOrganization(data)`

`POST /api/apikey/org`

Creates a new organization and returns the API key.

```typescript
async createOrganization(data: CreateOrganizationRequest): Promise<APIKey>
```

**Request Interface (`CreateOrganizationRequest`):**

```typescript
interface CreateOrganizationRequest {
  // ... organization details ...
}
```

**Response Interface (`APIKey`):**

```typescript
interface APIKey {
  organization_apikey: string;
}
```

**Example:**

```typescript
try {
  const apiKey = await sdk.apikey.createOrganization({
    // organization details
  });
  console.log('New Organization API Key:', apiKey.organization_apikey);
} catch (error) {
  console.error('Failed to create organization:', error);
}
```

#### `apikey.createApp(data)`

`POST /api/apikey/app`

Creates a new app and returns the app API key.

```typescript
async createApp(data: CreateAppRequest): Promise<AppAPIKey>
```

**Request Interface (`CreateAppRequest`):**

```typescript
interface CreateAppRequest {
  organization_apikey: string;
  // ... app details ...
}
```

**Response Interface (`AppAPIKey`):**

```typescript
interface AppAPIKey {
  app_apikey: string;
}
```

**Example:**

```typescript
try {
  const appApiKey = await sdk.apikey.createApp({
    organization_apikey: 'YOUR_ORG_API_KEY',
    // app details
  });
  console.log('New App API Key:', appApiKey.app_apikey);
} catch (error) {
  console.error('Failed to create app:', error);
}
```

#### `apikey.createTenant(data)`

`POST /api/apikey/tenant`

Creates a new tenant and returns the tenant API key.

```typescript
async createTenant(data: CreateTenantRequest): Promise<TenantAPIKey>
```

**Request Interface (`CreateTenantRequest`):**

```typescript
interface CreateTenantRequest {
  organization_apikey: string;
  app_apikey: string;
  // ... tenant details ...
}
```

**Response Interface (`TenantAPIKey`):**

```typescript
interface TenantAPIKey {
  tenant_apikey: string;
}
```

**Example:**

```typescript
try {
  const tenantApiKey = await sdk.apikey.createTenant({
    organization_apikey: 'YOUR_ORG_API_KEY',
    app_apikey: 'YOUR_APP_API_KEY',
    // tenant details
  });
  console.log('New Tenant API Key:', tenantApiKey.tenant_apikey);
} catch (error) {
  console.error('Failed to create tenant:', error);
}
```

#### `apikey.rollAPIKey(data)`

`POST /api/apikey/roll`

Rolls (rotates) an existing API key.

```typescript
async rollAPIKey(data: RollAPIKeyRequest): Promise<RolledAPIKey>
```

**Request Interface (`RollAPIKeyRequest`):**

```typescript
interface RollAPIKeyRequest {
  organization_apikey: string;
  // ... key details to roll ...
}
```

**Response Interface (`RolledAPIKey`):**

```typescript
interface RolledAPIKey {
  // ... new key details ...
}
```

**Example:**

```typescript
try {
  const rolledKey = await sdk.apikey.rollAPIKey({
    organization_apikey: 'YOUR_ORG_API_KEY',
    // key details to roll
  });
  console.log('Rolled API Key:', rolledKey);
} catch (error) {
  console.error('Failed to roll API key:', error);
}
```

### JWT-based API Key Endpoints

These endpoints allow you to create API keys using a Supabase JWT token for authentication. Use these if you want to manage API keys with user-level permissions and JWT-based auth.

#### `apikey.createOrgKey(organizationId, jwtToken)`

`POST /api/apikey/org/jwt`

Creates a new API key for an existing organization using a Supabase JWT token.

```typescript
async createOrgKey(organizationId: string, jwtToken: string): Promise<APIKeyResponse>
```

**Parameters:**
- `organizationId` (string): The ID of the existing organization
- `jwtToken` (string): A valid Supabase JWT token (must be included in the Authorization header)

**Returns:**
- `Promise<APIKeyResponse>`

**Example:**
```typescript
const apiKey = await sdk.apikey.createOrgKey('org_123', 'your_jwt_token');
console.log('New Org API Key:', apiKey.key);
```

#### `apikey.createAppKey(appName, tenancyModel, jwtToken)`

`POST /api/apikey/app/jwt`

Creates a new API key for an app in the user's organization using a Supabase JWT token.

```typescript
async createAppKey(appName: string, tenancyModel: 'single' | 'multi', jwtToken: string): Promise<APIKeyResponse>
```

**Parameters:**
- `appName` (string): Name of the app
- `tenancyModel` ('single' | 'multi'): App's tenancy model
- `jwtToken` (string): A valid Supabase JWT token (must be included in the Authorization header)

**Returns:**
- `Promise<APIKeyResponse>`

**Example:**
```typescript
const apiKey = await sdk.apikey.createAppKey('My App', 'multi', 'your_jwt_token');
console.log('New App API Key:', apiKey.key);
```

**Notes:**
- JWT tokens must be obtained from Supabase Auth and included as `Bearer <token>` in the Authorization header.
- These endpoints require the user to have appropriate permissions (e.g., 'admin' or 'owner' role).
- API keys are only returned once upon creation. Store them securely.
- All timestamps are in UTC (ISO 8601 format).

**Error Handling:**
- `400 Bad Request`: Missing required fields or invalid request
- `401 Unauthorized`: Missing or invalid JWT token
- `403 Forbidden`: Insufficient permissions
- `404 Not Found`: Organization not found
- `500 Internal Server Error`: Server/database error

### 3. Logs Endpoints

Manage log entries.

#### `logs.createLog(data)`

`POST /api/logs`

Creates a new log entry.

```typescript
async createLog(data: CreateLogRequest): Promise<void>
```

**Request Interface (`CreateLogRequest`):**

```typescript
interface CreateLogRequest {
  organization_apikey: string;
  app_apikey: string;
  tenant_apikey?: string;
  item_id: string;
  timestamp: string; // ISO 8601
  level: string;
  message: string;
  context?: Record<string, any>;
  metadata?: Record<string, any>;
}
```

**Example:**

```typescript
try {
  await sdk.logs.createLog({
    organization_apikey: 'YOUR_ORG_API_KEY',
    app_apikey: 'YOUR_APP_API_KEY',
    item_id: 'item123',
    timestamp: new Date().toISOString(),
    level: 'info',
    message: 'User logged in',
    context: { userId: 'user456' },
  });
  console.log('Log created successfully');
} catch (error) {
  console.error('Failed to create log:', error);
}
```

#### `logs.getLogs(data)`

`GET /api/logs`

Retrieves log entries with filtering options.

```typescript
async getLogs(data: GetLogsRequest): Promise<GetLogsResponse>
```

**Request Interface (`GetLogsRequest`):**

```typescript
interface GetLogsRequest {
  organization_apikey: string;
  app_apikey: string;
  tenant_apikey?: string;
  item_id?: string;
  level?: string;
  after?: string; // ISO 8601
  before?: string; // ISO 8601
  limit?: number;
  offset?: number;
}
```

**Response Interface (`GetLogsResponse`):**

```typescript
interface GetLogsResponse {
  logs: Array<{
    // log entry details
  }>;
}
```

**Example:**

```typescript
try {
  const logs = await sdk.logs.getLogs({
    organization_apikey: 'YOUR_ORG_API_KEY',
    app_apikey: 'YOUR_APP_API_KEY',
    item_id: 'item123',
    level: 'error',
    after: '2023-10-26T10:00:00Z',
  });
  console.log('Retrieved Logs:', logs);
} catch (error) {
  console.error('Failed to get logs:', error);
}
```

### 4. Metrics Endpoints

Retrieve metadata for apps and tenants.

#### `metrics.getAppMetadata(data)`

`GET /api/metrics/app`

Gets metadata for a specific app.

```typescript
async getAppMetadata(data: GetAppMetadataRequest): Promise<GetAppMetadataResponse>
```

**Request Interface (`GetAppMetadataRequest`):**

```typescript
interface GetAppMetadataRequest {
  organization_apikey: string;
  app_apikey: string;
}
```

**Response Interface (`GetAppMetadataResponse`):**

```typescript
interface GetAppMetadataResponse {
  id: string;
  app_id: string;
  tenant_count: number;
  created_at: string;
  updated_at: string;
}
```

**Example:**

```typescript
try {
  const appMetadata = await sdk.metrics.getAppMetadata({
    organization_apikey: 'YOUR_ORG_API_KEY',
    app_apikey: 'YOUR_APP_API_KEY',
  });
  console.log('App Metadata:', appMetadata);
} catch (error) {
  console.error('Failed to get app metadata:', error);
}
```

#### `metrics.getTenantMetadata(data)`

`GET /api/metrics/tenant`

Gets metadata for a specific tenant.

```typescript
async getTenantMetadata(data: GetTenantMetadataRequest): Promise<GetTenantMetadataResponse>
```

**Request Interface (`GetTenantMetadataRequest`):**

```typescript
interface GetTenantMetadataRequest {
  organization_apikey: string;
  app_apikey: string;
  tenant_apikey: string;
}
```

**Response Interface (`GetTenantMetadataResponse`):**

```typescript
interface GetTenantMetadataResponse {
  id: string;
  app_id: string;
  tenant_id: string;
  log_count: number;
  created_at: string;
  updated_at: string;
}
```

**Example:**

```typescript
try {
  const tenantMetadata = await sdk.metrics.getTenantMetadata({
    organization_apikey: 'YOUR_ORG_API_KEY',
    app_apikey: 'YOUR_APP_API_KEY',
    tenant_apikey: 'YOUR_TENANT_API_KEY',
  });
  console.log('Tenant Metadata:', tenantMetadata);
} catch (error) {
  console.error('Failed to get tenant metadata:', error);
}
```

### 5. Items Endpoints

List items associated with an app or tenant.

#### `items.listItems(data)`

`GET /api/items`

Lists items for a given app or tenant.

```typescript
async listItems(data: ListItemsRequest): Promise<ListItemsResponse>
```

**Request Interface (`ListItemsRequest`):**

```typescript
interface ListItemsRequest {
  organization_apikey: string;
  app_apikey: string;
  tenant_apikey?: string;
}
```

**Response Interface (`ListItemsResponse`):**

```typescript
interface ListItemsResponse {
  items: Array<{
    item_id: string;
    friendly_name?: string;
    last_seen: string; // ISO 8601
  }>;
}
```

**Example:**

```typescript
try {
  const items = await sdk.items.listItems({
    organization_apikey: 'YOUR_ORG_API_KEY',
    app_apikey: 'YOUR_APP_API_KEY',
    tenant_apikey: 'YOUR_TENANT_API_KEY', // Optional
  });
  console.log('Items:', items.items);
} catch (error) {
  console.error('Failed to list items:', error);
}
```

### 6. Intelligence Endpoints

Get predictions and decisions based on data.

#### `intelligence.getPredictConfig()`

`GET /api/intelligence/predict/config`

Gets the configuration for predictions, including available types and timeframes.

```typescript
async getPredictConfig(): Promise<PredictConfigResponse>
```

**Response Interface (`PredictConfigResponse`):**

```typescript
interface PredictConfigResponse {
  prediction_types: Array<{ type: string; description: string; use_cases: string[]; example: string }>;
  timeframes: Array<{ value: string; description: string; use_cases: string }>;
  example_request: PredictRequest;
  example_response: PredictResponse;
}
```

**Example:**

```typescript
try {
  const config = await sdk.intelligence.getPredictConfig();
  console.log('Prediction Config:', config);
} catch (error) {
  console.error('Failed to get prediction config:', error);
}
```

#### `intelligence.predict(data)`

`POST /api/intelligence/predict`

Gets a prediction for a specific item based on the provided parameters.

```typescript
async predict(data: PredictRequest): Promise<PredictResponse>
```

**Request Interface (`PredictRequest`):**

```typescript
interface PredictRequest {
  organization_apikey: string;
  app_apikey: string;
  tenant_apikey?: string;
  item_id: string;
  prediction_type: 'trend' | 'anomaly' | 'failure';
  timeframe: '1h' | '24h' | '7d' | '30d';
}
```

**Response Interface (`PredictResponse`):**

```typescript
interface PredictResponse {
  timestamp: string; // ISO 8601
  item_id: string;
  prediction: string;
  confidence: number;
  explanation: string;
  timeframe: string;
  recommendations: string[];
}
```

**Example:**

```typescript
try {
  const prediction = await sdk.intelligence.predict({
    organization_apikey: 'YOUR_ORG_API_KEY',
    app_apikey: 'YOUR_APP_API_KEY',
    item_id: 'item123',
    prediction_type: 'anomaly',
    timeframe: '24h',
  });
  console.log('Prediction Result:', prediction);
} catch (error) {
  console.error('Failed to get prediction:', error);
}
```

#### `intelligence.decide(data)`

`POST /api/intelligence/decide`

Gets a decision based on log data.

```typescript
async decide(data: DecideRequest): Promise<DecideResponse>
```

**Request Interface (`DecideRequest`):**

```typescript
interface DecideRequest {
  organization_apikey: string;
  app_apikey: string;
  tenant_apikey?: string;
  log_data: {
    metrics: Array<{ name: string; friendly_name?: string; value: any; unit?: string }>;
    context: string;
    item_name: string;
  };
  allowed_actions: Record<string, string>;
}
```

**Response Interface (`DecideResponse`):**

```typescript
interface DecideResponse {
  timestamp: string; // ISO 8601
  decision: string;
  reason: string;
  confidence: number;
  message?: string;
}
```

**Example:**

```typescript
try {
  const decision = await sdk.intelligence.decide({
    organization_apikey: 'YOUR_ORG_API_KEY',
    app_apikey: 'YOUR_APP_API_KEY',
    log_data: {
      metrics: [{ name: 'cpu_usage', value: 90 }],
      context: 'high_load',
      item_name: 'server1',
    },
    allowed_actions: { restart: 'Restart the server' },
  });
  console.log('Decision Result:', decision);
} catch (error) {
  console.error('Failed to get decision:', error);
}
```

### 7. Analyze Endpoints

Perform analysis on logs and get current status.

#### `analyze.getLatest(data)`

`GET /api/analyze/latest`

Gets the latest analysis for a specific item.

```typescript
async getLatest(data: GetLatestRequest): Promise<AnalysisResult>
```

**Request Interface (`GetLatestRequest`):**

```typescript
interface GetLatestRequest {
  organization_apikey: string;
  app_apikey: string;
  tenant_apikey?: string;
  item_id: string;
}
```

**Response Interface (`AnalysisResult`):**

```typescript
interface AnalysisResult {
  summary: { /* ... */ };
  metrics: Record<string, { /* ... */ }>;
  time_series: Array<{ /* ... */ }>;
  ai_analysis?: { /* ... */ };
}
```
*(See `src/endpoints/analyze/types.ts` for full `AnalysisResult` interface details)*

**Example:**

```typescript
try {
  const latestAnalysis = await sdk.analyze.getLatest({
    organization_apikey: 'YOUR_ORG_API_KEY',
    app_apikey: 'YOUR_APP_API_KEY',
    item_id: 'item123',
  });
  console.log('Latest Analysis:', latestAnalysis);
} catch (error) {
  console.error('Failed to get latest analysis:', error);
}
```

#### `analyze.analyzeLogs(data)`

`GET /api/analyze`

Analyzes logs for an item within a specified time range.

```typescript
async analyzeLogs(data: AnalyzeLogsRequest): Promise<AnalysisResult>
```

**Request Interface (`AnalyzeLogsRequest`):**

```typescript
interface AnalyzeLogsRequest {
  organization_apikey: string;
  app_apikey: string;
  tenant_apikey?: string;
  item_id: string;
  after?: string; // ISO 8601
  before?: string; // ISO 8601
}
```

**Response Interface (`AnalysisResult`):**

```typescript
interface AnalysisResult {
  summary: { /* ... */ };
  metrics: Record<string, { /* ... */ }>;
  time_series: Array<{ /* ... */ }>;
  ai_analysis?: { /* ... */ };
}
```
*(See `src/endpoints/analyze/types.ts` for full `AnalysisResult` interface details)*

**Example:**

```typescript
try {
  const analysis = await sdk.analyze.analyzeLogs({
    organization_apikey: 'YOUR_ORG_API_KEY',
    app_apikey: 'YOUR_APP_API_KEY',
    item_id: 'item123',
    after: '2023-10-01T00:00:00Z',
    before: '2023-10-31T23:59:59Z',
  });
  console.log('Log Analysis:', analysis);
} catch (error) {
  console.error('Failed to analyze logs:', error);
}
```

#### `analyze.analyzeCurrent(data)`

`GET /api/analyze/current`

Gets the current analysis status for a specific item.

```typescript
async analyzeCurrent(data: AnalyzeCurrentRequest): Promise<AnalyzeCurrentResponse>
```

**Request Interface (`AnalyzeCurrentRequest`):**

```typescript
interface AnalyzeCurrentRequest {
  organization_apikey: string;
  app_apikey: string;
  tenant_apikey?: string;
  item_id: string;
}
```

**Response Interface (`AnalyzeCurrentResponse`):**

```typescript
interface AnalyzeCurrentResponse {
  timestamp: string; // ISO 8601
  overview: string;
  status: 'normal' | 'warning' | 'critical';
  description: string;
  metrics: Record<string, { /* ... */ }>;
  recommendations: Array<{ /* ... */ }>;
}
```

**Example:**

```typescript
try {
  const currentAnalysis = await sdk.analyze.analyzeCurrent({
    organization_apikey: 'YOUR_ORG_API_KEY',
    app_apikey: 'YOUR_APP_API_KEY',
    item_id: 'item123',
  });
  console.log('Current Analysis:', currentAnalysis);
} catch (error) {
  console.error('Failed to get current analysis:', error);
}
```

### 8. App Endpoints

Manage and retrieve the status of an app.

#### `app.updateStatus(data)`

`POST /api/app/status`

Updates the status of an app.

```typescript
async updateStatus(data: AppStatusRequest): Promise<AppStatusOverview>
```

**Request Interface (`AppStatusRequest`):**

```typescript
interface AppStatusRequest {
  organization_apikey: string;
  app_apikey: string;
  tenant_apikey?: string;
  // ... status details ...
}
```

**Response Interface (`AppStatusOverview`):**

```typescript
interface AppStatusOverview {
  timestamp: string; // ISO 8601
  overview: string;
  status: 'normal' | 'warning' | 'critical';
  item_statuses: Array<{ /* ... */ }>;
  recommendations: Array<{ /* ... */ }>;
}
```

**Example:**

```typescript
try {
  const updatedStatus = await sdk.app.updateStatus({
    organization_apikey: 'YOUR_ORG_API_KEY',
    app_apikey: 'YOUR_APP_API_KEY',
    // status details
  });
  console.log('Updated App Status:', updatedStatus);
} catch (error) {
  console.error('Failed to update app status:', error);
}
```

#### `app.getLatestStatus(data)`

`GET /api/app/status/latest`

Gets the latest status overview for an app.

```typescript
async getLatestStatus(data: GetLatestAppStatusRequest): Promise<AppStatusOverview>
```

**Request Interface (`GetLatestAppStatusRequest`):**

```typescript
interface GetLatestAppStatusRequest {
  organization_apikey: string;
  app_apikey: string;
  tenant_apikey?: string;
}
```

**Response Interface (`AppStatusOverview`):**

```typescript
interface AppStatusOverview {
  timestamp: string; // ISO 8601
  overview: string;
  status: 'normal' | 'warning' | 'critical';
  item_statuses: Array<{ /* ... */ }>;
  recommendations: Array<{ /* ... */ }>;
}
```

**Example:**

```typescript
try {
  const latestStatus = await sdk.app.getLatestStatus({
    organization_apikey: 'YOUR_ORG_API_KEY',
    app_apikey: 'YOUR_APP_API_KEY',
  });
  console.log('Latest App Status:', latestStatus);
} catch (error) {
  console.error('Failed to get latest app status:', error);
}
```

## Error Handling

The SDK throws custom error types for different API responses:

- `InvalidRequestError`: For missing or invalid request parameters (status 400).
- `AuthenticationError`: For invalid API keys (status 401).
- `ServerError`: For internal server errors (status 500).

You can catch these errors to handle specific API responses:

```typescript
import { InvalidRequestError, AuthenticationError, ServerError } from 'sjursen-digital-watchtower/errors';

try {
  // SDK call
} catch (error) {
  if (error instanceof InvalidRequestError) {
    console.error('Invalid Request:', error.message);
  } else if (error instanceof AuthenticationError) {
    console.error('Authentication Failed:', error.message);
  } else if (error instanceof ServerError) {
    console.error('Server Error:', error.message);
  } else {
    console.error('An unexpected error occurred:', error);
  }
}
```

## Contributing

[Add contributing guidelines here]

## License

[Add license information here] 