npm package: @numbervault/recovery-flow-sdk
Description: Number Vault Recovery Flow SDK — verify virtual numbers and receive number change webhooks
Latest version: 0.1.1

# @numbervault/recovery-flow-sdk

The official Number Vault SDK for B2B platforms. Verify virtual numbers during user account recovery and receive real-time number change webhooks.

## What is Number Vault?

Number Vault is a virtual phone number layer that protects users from losing account access when their real phone number changes. Instead of registering their real number with your platform, users register a Number Vault virtual number. When their real number changes, they update it in one place — and all linked services stay connected automatically.

This SDK gives your platform two things:

1. **Recovery Flow** — verify a virtual number is active before granting account access
2. **Number Change Webhooks** — get notified in real time when a user's number mapping changes

---

## Installation

```bash
npm install @numbervault/recovery-flow-sdk
```

Requires **Node.js 18+** (uses native `fetch`). Works in the browser too.

---

## Quick Start

```ts
import { NumberVaultClient } from '@numbervault/recovery-flow-sdk';

const client = new NumberVaultClient({
  apiKey: process.env.NUMBERVAULT_API_KEY, // starts with nvk_
});
```

---

## Recovery Flow

Call `verify()` during your account recovery or login flow to confirm the user's virtual number is still active and mapped to a real number.

```ts
const result = await client.verify('+12125550100');

if (result.verified) {
  // Number is active — proceed with recovery
} else {
  // result.status will be one of:
  // 'retired'          — number permanently deactivated
  // 'suspended'        — number temporarily suspended
  // 'no_active_mapping' — number exists but has no verified real number linked
  // 'not_found'        — number not recognised
  console.log(result.status);
}
```

Requires the `recovery.sdk` scope on your API key.

---

## Number Change Subscriptions

Subscribe to be notified when a user's virtual number changes or is recycled.

```ts
// Subscribe
const subscription = await client.subscribe('+12125550100', [
  'number.changed',
  'number.recycled',
]);

console.log(subscription.id); // save this to unsubscribe later

// List all subscriptions
const all = await client.listSubscriptions();

// Unsubscribe
await client.unsubscribe(subscription.id);
```

Requires the `number.subscribe` scope on your API key.

---

## Receiving Webhooks

Number Vault signs every webhook POST with HMAC-SHA256. Always verify the signature before processing the payload.

```ts
import { verifyWebhookSignature } from '@numbervault/recovery-flow-sdk';
import express from 'express';

const app = express();

// IMPORTANT: use raw body parser — signature verification requires the raw bytes
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const result = verifyWebhookSignature(
    req.body.toString(),
    req.headers['x-numbervault-signature'] as string,
    process.env.NUMBERVAULT_WEBHOOK_SECRET
  );

  if (!result.valid) {
    console.error('Invalid webhook signature:', result.error);
    return res.sendStatus(400);
  }

  const { eventType, payload } = result.event;

  switch (eventType) {
    case 'number.changed':
      // User's virtual number is now mapped to a new real number
      // payload.virtualNumber — the virtual number that changed
      break;
    case 'number.recycled':
      // Virtual number has been retired — unlink it from this user's account
      break;
  }

  res.sendStatus(200);
});
```

Respond with `2xx` within 10 seconds. Number Vault retries failed deliveries up to 3 times with exponential backoff (5s → 30s → 5m).

---

## API Reference

### `new NumberVaultClient(options)`

| Option | Type | Required | Default |
|---|---|---|---|
| `apiKey` | `string` | Yes | — |
| `baseUrl` | `string` | No | `https://api.numbervault.io` |
| `timeoutMs` | `number` | No | `10000` |

### `client.verify(virtualNumber)`

Verify a virtual number is active. Returns a `VerifyResult`.

| Field | Type | Description |
|---|---|---|
| `verified` | `boolean` | `true` only when status is `active` |
| `status` | `VirtualNumberStatus` | `active` \| `retired` \| `suspended` \| `no_active_mapping` \| `not_found` |
| `virtualNumber` | `string` | The number that was checked |

### `client.subscribe(virtualNumber, eventTypes)`

Subscribe to events for a virtual number. Returns a `Subscription`.

### `client.listSubscriptions()`

List all active subscriptions for this platform. Returns `Subscription[]`.

### `client.unsubscribe(subscriptionId)`

Remove a subscription by ID.

### `verifyWebhookSignature(rawBody, signature, secret)`

Verify an incoming webhook. Returns `{ valid: true, event }` or `{ valid: false, error }`.

---

## Error Handling

All client methods throw `NumberVaultError` on failure.

```ts
import { NumberVaultClient, NumberVaultError } from '@numbervault/recovery-flow-sdk';

try {
  await client.verify('+12125550100');
} catch (err) {
  if (err instanceof NumberVaultError) {
    console.error(err.message);   // human-readable message
    console.error(err.statusCode); // HTTP status code (401, 403, 404, etc.)
  }
}
```

Common status codes:

| Code | Meaning |
|---|---|
| `401` | Invalid, revoked, or expired API key |
| `403` | Platform suspended or missing required scope |
| `404` | Resource not found |
| `408` | Request timed out |

---

## API Keys & Scopes

API keys are issued by Number Vault and scoped to specific capabilities:

| Scope | Required for |
|---|---|
| `recovery.sdk` | `client.verify()` |
| `number.subscribe` | `client.subscribe()`, `client.listSubscriptions()`, `client.unsubscribe()` |
| `webhook.receive` | Receiving webhook deliveries |

Contact your Number Vault account manager to obtain API keys.

---

## License

MIT