npm package: @siymo/otp-sdk-core
Description: TypeScript SDK for the siymo-otp-service REST and WebSocket APIs
Latest version: 1.0.0

# siymo-otp-sdk

TypeScript SDK for the REST and WebSocket contract exposed by `siymo-otp-service`.

## Features

- Typed wrappers for OTP voice, SMS, inbound call, inbound SMS, verification, direct SMS, dongles, and health endpoints
- WebSocket subscription helper for `/ws/otp`
- Live attempt and lockout events during OTP verification
- `waitForConfirmation()` helper for inbound OTP confirmation flows
- `waitForConfirmationLongPoll()` helper for one-request HTTP long polling
- Base URL can be passed directly or resolved from environment variables

## Install

```bash
npm install siymo-otp-sdk
```

## Configuration

The client resolves the service base URL in this order:

1. `baseUrl` passed to the constructor
2. `SIYMO_OTP_BASE_URL`
3. `OTP_BASE_URL`

```ts
import { SiymoOtpClient } from 'siymo-otp-sdk';

const client = new SiymoOtpClient({
  baseUrl: 'http://localhost:3000',
});
```

Or with environment variables:

```bash
export SIYMO_OTP_BASE_URL=http://localhost:3000
```

```ts
import { SiymoOtpClient } from 'siymo-otp-sdk';

const client = new SiymoOtpClient();
```

## Usage

### Outbound OTP

```ts
import { SiymoOtpClient } from 'siymo-otp-sdk';

const client = new SiymoOtpClient();

const session = await client.initiateVoice({
  phone: '+998901234567',
  languages: ['uz', 'ru', 'en'],
  repeat: true,
});

const verification = await client.verify({
  sessionId: session.sessionId,
  otp: '123456',
});
```

### Inbound OTP with WebSocket confirmation

```ts
import { SiymoOtpClient } from 'siymo-otp-sdk';

const client = new SiymoOtpClient({
  baseUrl: 'http://localhost:3000',
});

const session = await client.initiateInboundSms({
  phone: '+998990649000',
});

const confirmed = await client.waitForConfirmation(session.sessionId, {
  timeoutMs: session.expiresIn * 1000,
  onAttempt(event) {
    console.log('Invalid attempt', event.data.attempts, 'tries left', event.data.triesLeft);
  },
});

console.log(confirmed.data.verifiedAt);
```

### Inbound OTP with HTTP long polling

```ts
import { SiymoOtpClient } from 'siymo-otp-sdk';

const client = new SiymoOtpClient({
  baseUrl: 'http://localhost:3000',
});

const session = await client.initiateInboundSms({
  phone: '+998990649000',
  expiration: 60,
});

const confirmed = await client.waitForConfirmationLongPoll(session.sessionId);

console.log(confirmed.status, confirmed.verifiedAt);
```

### Low-level WebSocket subscription

```ts
const subscription = client.subscribeToSession(session.sessionId, {
  onSubscribed(event) {
    console.log('Subscribed until', event.data.expiresAt);
  },
  onAttempt(event) {
    console.log('Attempts used', event.data.attempts, 'tries left', event.data.triesLeft);
  },
  onLocked(event) {
    console.log('Locked after', event.data.attempts, 'attempts');
  },
  onVerified(event) {
    console.log('Verified', event.data.sessionId);
  },
  onExpired(event) {
    console.log('Expired', event.data.sessionId);
  },
});

// Later, if needed:
subscription.close();
await subscription.closed;
```

## Notes

- The SDK uses the service's existing WebSocket contract at `/ws/otp?sessionId=<uuid>`.
- The SDK also supports the long-poll endpoint at `GET /otp/wait?sessionId=<uuid>`.
- Inbound call and SMS initiation requests accept an optional `qrCode: true` flag. When requested, the response includes `qrCodeImage` as a data URL.
- The WebSocket stream can emit `otp.subscribed`, `otp.attempt`, `otp.locked`, `otp.verified`, and `otp.expired`.
- In modern Node runtimes such as Node `22.x`, `WebSocket` is available globally. If you need to override it, pass `webSocketFactory`.
- `waitForConfirmation()` keeps waiting through `otp.attempt` events, forwards WebSocket callbacks like `onAttempt`, and rejects on `otp.locked` or `otp.expired`.
- `waitForConfirmationLongPoll()` resolves on verification and rejects with the service error response if the session locks or expires.
- Verification endpoints intentionally return `200 OK` for both success and logical verification failures. Inspect the typed response fields like `verified`, `message`, and `triesLeft`.