npm package: @spaceinvoices/embed-sdk
Description: Embed Space Invoices UI in your application via iframes
Author: Space Invoices
Latest version: 1.0.5

# @spaceinvoices/embed-sdk

Embed Space Invoices UI in your application via iframes with customizable themes.

## Installation

```bash
npm install @spaceinvoices/embed-sdk
# or
yarn add @spaceinvoices/embed-sdk
# or
pnpm add @spaceinvoices/embed-sdk
```

## Quick Start

```typescript
import { SpaceInvoices } from '@spaceinvoices/embed-sdk';

const si = new SpaceInvoices({
  apiKey: 'ek_live_your_entity_api_key',
  entityId: 'ent_123',
  baseUrl: 'https://invoicing.example.com',
  theme: {
    primary: '#6366f1',
    radius: 8,
  },
  onAction: (action) => {
    console.log('Action:', action);
  },
});

// Open invoice list
si.open('invoices', { container: '#invoice-container' });

// Open invoice list sorted by newest invoice date first
si.open('invoices', {
  container: '#invoice-container',
  searchParams: { order_by: '-date' },
});
```

## Usage via Script Tag

```html
<div id="space-invoices-embed" style="height: 600px;"></div>

<script src="https://unpkg.com/@spaceinvoices/embed-sdk"></script>
<script>
  const si = new SpaceInvoices.SpaceInvoices({
    apiKey: 'ek_live_your_entity_api_key',
    entityId: 'ent_123',
    baseUrl: 'https://invoicing.example.com',
  });

  si.open('invoices');
</script>
```

## Configuration

```typescript
interface SpaceInvoicesConfig {
  // Required
  apiKey: string;      // Entity API key (ek_live_* or ek_test_*). Authenticates entity access only.
  entityId: string;    // Entity ID

  // Optional
  baseUrl?: string;    // White-label app origin. If omitted, loads https://app.spaceinvoices.com.
  locale?: EmbedLocale; // UI language ("en", "sl", "de", "it", "fr", "es", "pt", "nl", "pl", "hr")
  theme?: ThemeConfig; // Theme customization

  // Callbacks
  onAction?: (action: EmbedAction) => void;
  onReady?: () => void;
  onError?: (error: EmbedError) => void;
  onClose?: () => void;
}
```

White-label behavior is selected by the embed host, not by the entity API key. Use your managed or custom white-label domain as `baseUrl` if you want white-label branding, feature gating, billing UI, support contact, and default theme to apply.

If you omit `baseUrl`, the SDK loads `https://app.spaceinvoices.com`, which means the embed will authenticate with your entity API key but will use the default Space Invoices white-label.

## Available Views

| View | Description |
|------|-------------|
| `invoices` | Invoice list |
| `invoice/new` | Create new invoice |
| `invoice` | View invoice (requires `id`) |
| `estimates` | Estimate list |
| `estimate/new` | Create new estimate |
| `estimate` | View estimate (requires `id`) |
| `credit-notes` | Credit note list |
| `credit-note/new` | Create new credit note |
| `credit-note` | View credit note (requires `id`) |
| `advance-invoices` | Advance invoice list |
| `advance-invoice/new` | Create new advance invoice |
| `advance-invoice` | View advance invoice (requires `id`) |
| `delivery-notes` | Delivery note list |
| `delivery-note/new` | Create new delivery note |
| `delivery-note` | View delivery note (requires `id`) |
| `customers` | Customer list |
| `customer/new` | Create new customer |
| `customer` | View customer (requires `id`) |
| `settings/company` | Company settings |
| `settings/defaults` | Default settings |
| `settings/numbering` | Numbering settings |
| `settings/tax` | Tax settings |
| `settings/furs` | FURS fiscalization settings |
| `settings/fina` | FINA settings |
| `settings/e-invoicing` | E-invoicing settings |
| `settings/templates` | Document templates settings |
| `export` | Document export |

## Theme Customization

```typescript
const si = new SpaceInvoices({
  apiKey: 'ek_live_...',
  entityId: 'ent_123',
  baseUrl: 'https://invoicing.example.com',
  theme: {
    primary: '#6366f1',    // Primary brand color
    accent: '#8b5cf6',     // Accent color
    background: '#ffffff', // Background color
    foreground: '#1a1a1a', // Text color
    card: '#ffffff',       // Card background
    border: '#e5e5e5',     // Border color
    radius: 8,             // Border radius (px)
    darkMode: false,       // Enable dark mode
  },
});
```

You can update the theme dynamically:

```typescript
si.updateTheme({
  primary: '#10b981',
  darkMode: true,
});
```

## Locale

Set the UI language (defaults to browser language detection):

```typescript
const si = new SpaceInvoices({
  apiKey: 'ek_live_...',
  entityId: 'ent_123',
  locale: 'sl', // Slovenian
});
```

Supported locales: `en`, `sl`, `de`, `it`, `fr`, `es`, `pt`, `nl`, `pl`, `hr`

You can update the locale dynamically:

```typescript
si.setLocale('de'); // Switch to German
```

## Navigation

Navigate between views without recreating the embed:

```typescript
// Open invoice list
si.open('invoices');

// Open invoice list with custom initial sorting
si.open('invoices', {
  searchParams: { order_by: '-date' },
});

// Navigate to create form
si.navigate('invoice/new');

// Navigate to view a specific invoice
si.navigate('invoice', { id: 'inv_123' });
```

## Operator Prefill

For multi-user cashier/operator setups, pass operator identity through the existing `prefill` option. This override is request-scoped and is not persisted to user settings, entity defaults, or local storage.

The host should pass only operator identity. Premise, device, payment type, and skip/fiscalization controls stay inside the Space Invoices UI.

```typescript
si.open('invoice/new', {
  prefill: {
    operator: {
      label: 'Cashier 2',
      tax_number: '12345678',
    },
  },
});
```

The embed maps this generic shape to the active fiscalization provider automatically:

- FURS: `tax_number` -> `operator_tax_number`
- FINA: `tax_number` -> `operator_oib`

## Initial List State

Pass route search params through `searchParams` when you want to control the initial state of list views.
This is useful for sorting, search, and filters.

```typescript
si.open('invoices', {
  container: '#invoice-container',
  searchParams: {
    order_by: '-date',
    search: 'Acme',
  },
});
```

For multi-column sorting, pass an array:

```typescript
si.open('invoices', {
  searchParams: {
    order_by: ['-date', 'number'],
  },
});
```

## Troubleshooting

### White-label branding is not showing up

If the embed authenticates successfully but still looks like Space Invoices, check the `baseUrl` you passed to the SDK.

- `apiKey` selects the entity.
- `baseUrl` selects the white-label host.

This combination will authenticate correctly but will not activate white-label behavior:

```typescript
const si = new SpaceInvoices({
  apiKey: 'ek_live_...',
  entityId: 'ent_123',
  // baseUrl omitted -> defaults to https://app.spaceinvoices.com
});
```

To apply white-label branding, point the embed at your managed or custom white-label domain:

```typescript
const si = new SpaceInvoices({
  apiKey: 'ek_live_...',
  entityId: 'ent_123',
  baseUrl: 'https://invoicing.example.com',
});
```

## Actions

Listen for user actions within the embed:

```typescript
const si = new SpaceInvoices({
  // ...config
  onAction: (action) => {
    switch (action.type) {
      case 'document_created':
        console.log('Created document:', action.data?.id);
        break;
      case 'navigation':
        console.log('Navigated to:', action.data?.path);
        break;
      case 'close_requested':
        // User requested to close the embed
        si.close();
        break;
    }
  },
});
```

### Action Types

| Type | Description |
|------|-------------|
| `page_loaded` | Page finished loading |
| `document_created` | Document was created |
| `document_updated` | Document was updated |
| `document_deleted` | Document was deleted |
| `customer_created` | Customer was created |
| `customer_updated` | Customer was updated |
| `customer_deleted` | Customer was deleted |
| `navigation` | User navigated within embed |
| `close_requested` | User requested to close |
| `settings_updated` | Settings were updated |
| `export_started` | Document export started |
| `export_completed` | Document export completed |

## API Reference

### `SpaceInvoices`

Main SDK class.

#### Methods

- `open(view, options?)` - Open a view in a container
- `navigate(view, options?)` - Navigate to a different view
- `updateTheme(theme)` - Update theme colors dynamically
- `setLocale(locale)` - Change UI language dynamically
- `close()` - Close and unmount the embed
- `ready()` - Returns a promise that resolves when embed is ready
- `getInstance()` - Get current embed instance
- `destroy()` - Destroy SDK and clean up resources

### `EmbedInstance`

Returned by `open()`.

```typescript
interface EmbedInstance {
  id: string;
  container: HTMLElement;
  iframe: HTMLIFrameElement;
  view: EmbedView;
  unmount: () => void;
  navigate: (view, options?) => void;
  updateTheme: (theme) => void;
}
```

## Framework Examples

### React

```tsx
import { useEffect, useRef } from 'react';
import { SpaceInvoices } from '@spaceinvoices/embed-sdk';

function InvoiceEmbed() {
  const containerRef = useRef<HTMLDivElement>(null);
  const sdkRef = useRef<SpaceInvoices | null>(null);

  useEffect(() => {
    if (!containerRef.current) return;

    sdkRef.current = new SpaceInvoices({
      apiKey: 'ek_live_...',
      entityId: 'ent_123',
      onAction: (action) => console.log(action),
    });

    sdkRef.current.open('invoices', { container: containerRef.current });

    return () => sdkRef.current?.destroy();
  }, []);

  return <div ref={containerRef} style={{ height: '600px' }} />;
}
```

### Vue

```vue
<template>
  <div ref="container" style="height: 600px" />
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { SpaceInvoices } from '@spaceinvoices/embed-sdk';

const container = ref(null);
let sdk = null;

onMounted(() => {
  sdk = new SpaceInvoices({
    apiKey: 'ek_live_...',
    entityId: 'ent_123',
  });
  sdk.open('invoices', { container: container.value });
});

onUnmounted(() => sdk?.destroy());
</script>
```

## Security

- The SDK uses entity API keys (`ek_*`), which are scoped to a single entity
- All communication happens via postMessage with origin validation
- Iframes provide complete style isolation and security boundaries
- API keys should be kept secure and not exposed in client-side code for production use

## Browser Support

- Chrome/Edge 80+
- Firefox 75+
- Safari 13.1+

## License

MIT