npm package: @first-to-fly/crm-sdk
Description: CRM Embeddable App SDK — enables communication between third-party iframes and the CRM host
Author: First to Fly
Latest version: 1.0.1

# @first-to-fly/crm-sdk

CRM Embeddable App SDK — enables communication between third-party iframes and the CRM host.

## Installation

### npm / bun

```bash
npm install @first-to-fly/crm-sdk
# or
bun add @first-to-fly/crm-sdk
```

### Script tag

```html
<script src="https://your-crm-domain.com/sdk/v1/app.js"></script>
```

When loaded via script tag, `window.CrmApp` is available globally.

## Quick Start

### ESM / TypeScript

```typescript
import { CrmApp } from '@first-to-fly/crm-sdk';

CrmApp.on('ready', (context) => {
  console.log('Connected!', context.workspace_id);
  console.log('Conversation:', context.conversation_id);
  console.log('Contact:', context.contact_id);
});

CrmApp.on('context_update', (context) => {
  console.log('Context changed:', context);
});

// Signal readiness to the CRM host
CrmApp.init();
```

### Script tag

```html
<script src="https://your-crm-domain.com/sdk/v1/app.js"></script>
<script>
  CrmApp.on('ready', function (context) {
    document.getElementById('info').textContent =
      'Workspace: ' + context.workspace_id;
  });

  CrmApp.init();
</script>
```

## API Reference

### `CrmApp.init()`

Signals to the CRM host that your app is ready to receive context. Must be called once after setting up event listeners.

### `CrmApp.on(event, listener)`

Register an event listener.

| Event | Description |
|-------|-------------|
| `'ready'` | Fired once after `init()` when the host sends the initial context |
| `'context_update'` | Fired when the user switches conversations or the context changes |

The listener receives a `CrmContext` object:

```typescript
interface CrmContext {
  token: string | null;        // Short-lived JWT for authenticated API calls
  workspace_id: string;        // Current workspace ID
  conversation_id: string | null; // Current conversation (if in conversation sidebar)
  contact_id: string | null;      // Current contact (if available)
}
```

### `CrmApp.off(event, listener)`

Remove a previously registered event listener.

### `CrmApp.getContext()`

Returns the current `CrmContext` synchronously, or `null` if `init()` hasn't completed yet.

### `CrmApp.invoke(action, data?)`

Request an action from the CRM host. Returns a `Promise` that resolves with the result or rejects on error/timeout (30s).

```typescript
// Example: request iframe resize
const result = await CrmApp.invoke('resize', { height: 500 });
```

## PostMessage Protocol

For advanced use cases, the SDK communicates with the CRM host via `window.postMessage`.

### iframe → Host

Messages include `{ source: 'crm-app', type, ... }`:

| Type | Description |
|------|-------------|
| `app_ready` | Sent by `init()` to signal the app is ready |
| `invoke` | Sent by `invoke()` with `invoke_id`, `action`, `data` |

### Host → iframe

Messages include `{ source: 'crm-host', type, ... }`:

| Type | Description |
|------|-------------|
| `init` | Sent after `app_ready`, contains `context` |
| `context_update` | Sent when context changes, contains updated `context` |
| `response` | Response to an `invoke`, contains `invoke_id` and `result` or `error` |

## License

MIT