> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dolfinai.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Client Integration

> End-to-end flow for provisioning organisations, users, and generating session tokens

This guide walks through the complete integration flow for a client platform that wants to onboard SMB users into Dolfin. By the end, your users will have session tokens they can use to interact with the Dolfin API directly from the browser.

## Prerequisites

* A Dolfin **API key** (distributed during onboarding)
* Your **Client ID** (associated with your API key)

## Overview

The integration follows three phases:

<Steps>
  <Step title="Create an organisation and users">
    Your backend creates an organisation and provisions users into it using your API key.
  </Step>

  <Step title="Generate auth codes">
    When a user needs to access Dolfin, your backend generates a short-lived auth code for that user.
  </Step>

  <Step title="Exchange for a session token">
    The user's browser exchanges the auth code for a session JWT, which is used for all subsequent API calls.
  </Step>
</Steps>

***

## Step 1: Create an Organisation

First, create an organisation for the SMB business. You'll need an `industryId` - retrieve the list of available industries from `GET /industries`.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.dolfinai.co/organisations \
    -H "x-dolfin-api-key: dol_live_abc123" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Acme Plumbing Ltd",
      "industryId": "b2d4f6a8-1234-5678-9abc-def012345678"
    }'
  ```

  ```javascript Node.js theme={null}
  const response = await fetch('https://api.dolfinai.co/organisations', {
    method: 'POST',
    headers: {
      'x-dolfin-api-key': 'dol_live_abc123',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      name: 'Acme Plumbing Ltd',
      industryId: 'b2d4f6a8-1234-5678-9abc-def012345678',
    }),
  });

  const organisation = await response.json();
  console.log(organisation.id); // "9a658587-fe02-402e-b1ac-bfaf53274ef8"
  ```
</CodeGroup>

**Response:**

```json theme={null}
{
  "id": "9a658587-fe02-402e-b1ac-bfaf53274ef8",
  "name": "Acme Plumbing Ltd",
  "clientId": "c1234567-abcd-ef01-2345-6789abcdef01",
  "industry": {
    "id": "b2d4f6a8-1234-5678-9abc-def012345678",
    "name": "Plumbing & HVAC"
  },
  "createdAt": "2025-01-15T10:30:00Z"
}
```

<Note>
  Store the `id` from the response - this is the `organisationId` you'll use in all subsequent calls.
</Note>

## Step 2: Create Users

Now create users within the organisation. Each user is assigned the **Member** role.

### User 1: Alice

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.dolfinai.co/organisations/9a658587-fe02-402e-b1ac-bfaf53274ef8/users \
    -H "x-dolfin-api-key: dol_live_abc123" \
    -H "Content-Type: application/json" \
    -d '{
      "email": "alice@acmeplumbing.co.uk",
      "name": "Alice Johnson"
    }'
  ```

  ```javascript Node.js theme={null}
  const user1 = await fetch(
    'https://api.dolfinai.co/organisations/9a658587-fe02-402e-b1ac-bfaf53274ef8/users',
    {
      method: 'POST',
      headers: {
        'x-dolfin-api-key': 'dol_live_abc123',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        email: 'alice@acmeplumbing.co.uk',
        name: 'Alice Johnson',
      }),
    }
  ).then(r => r.json());
  ```
</CodeGroup>

**Response:**

```json theme={null}
{
  "userId": "a1111111-1111-1111-1111-111111111111",
  "email": "alice@acmeplumbing.co.uk",
  "name": "Alice Johnson",
  "organisationId": "9a658587-fe02-402e-b1ac-bfaf53274ef8",
  "role": "Member"
}
```

### User 2: Bob

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.dolfinai.co/organisations/9a658587-fe02-402e-b1ac-bfaf53274ef8/users \
    -H "x-dolfin-api-key: dol_live_abc123" \
    -H "Content-Type: application/json" \
    -d '{
      "email": "bob@acmeplumbing.co.uk",
      "name": "Bob Smith"
    }'
  ```

  ```javascript Node.js theme={null}
  const user2 = await fetch(
    'https://api.dolfinai.co/organisations/9a658587-fe02-402e-b1ac-bfaf53274ef8/users',
    {
      method: 'POST',
      headers: {
        'x-dolfin-api-key': 'dol_live_abc123',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        email: 'bob@acmeplumbing.co.uk',
        name: 'Bob Smith',
      }),
    }
  ).then(r => r.json());
  ```
</CodeGroup>

**Response:**

```json theme={null}
{
  "userId": "b2222222-2222-2222-2222-222222222222",
  "email": "bob@acmeplumbing.co.uk",
  "name": "Bob Smith",
  "organisationId": "9a658587-fe02-402e-b1ac-bfaf53274ef8",
  "role": "Member"
}
```

<Note>
  If a user with the same email already exists, they will be added to the organisation rather than duplicated. A `409 Conflict` is returned if the user is already a member of that organisation.
</Note>

## Step 3: Generate an Auth Code

When a user needs to access Dolfin (e.g. they click "Open Invoicing" in your app), generate a short-lived auth code from your backend.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.dolfinai.co/auth/codes \
    -H "x-dolfin-api-key: dol_live_abc123" \
    -H "Content-Type: application/json" \
    -d '{
      "userId": "a1111111-1111-1111-1111-111111111111",
      "organisationId": "9a658587-fe02-402e-b1ac-bfaf53274ef8"
    }'
  ```

  ```javascript Node.js theme={null}
  const authCode = await fetch('https://api.dolfinai.co/auth/codes', {
    method: 'POST',
    headers: {
      'x-dolfin-api-key': 'dol_live_abc123',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      userId: 'a1111111-1111-1111-1111-111111111111',
      organisationId: '9a658587-fe02-402e-b1ac-bfaf53274ef8',
    }),
  }).then(r => r.json());

  // Pass authCode.code to the user's browser
  console.log(authCode.code);      // "eyJhbGciOiJIUzI1NiIs..."
  console.log(authCode.expiresAt); // "2025-01-15T10:40:00Z"
  ```
</CodeGroup>

**Response:**

```json theme={null}
{
  "code": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expiresAt": "2025-01-15T10:40:00Z"
}
```

<Warning>
  Auth codes expire after **10 minutes** and are **single-use**. Generate a new code each time the user needs to authenticate.
</Warning>

## Step 4: Exchange the Auth Code for a Session Token

Pass the auth code to the user's browser (e.g. as a URL parameter in a redirect). The browser then exchanges it for a session JWT.

This call is made **from the browser** and does not require an API key.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.dolfinai.co/auth/exchange \
    -H "Content-Type: application/json" \
    -d '{
      "code": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
    }'
  ```

  ```javascript Browser theme={null}
  const session = await fetch('https://api.dolfinai.co/auth/exchange', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      code: authCode, // the code from the URL parameter
    }),
  }).then(r => r.json());

  // Store the token for subsequent API calls
  localStorage.setItem('dolfin_token', session.token);
  ```
</CodeGroup>

**Response:**

```json theme={null}
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "userId": "a1111111-1111-1111-1111-111111111111",
  "email": "alice@acmeplumbing.co.uk",
  "organisations": [
    {
      "organisationId": "9a658587-fe02-402e-b1ac-bfaf53274ef8",
      "organisationName": "Acme Plumbing Ltd",
      "role": "Member"
    }
  ]
}
```

The session `token` is a JWT valid for **8 hours**.

## Step 5: Make Authenticated API Calls

The user can now call any Dolfin API endpoint using the session token:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET https://api.dolfinai.co/invoices \
    -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
    -H "x-dolfin-organisation-id: 9a658587-fe02-402e-b1ac-bfaf53274ef8"
  ```

  ```javascript Browser theme={null}
  const invoices = await fetch('https://api.dolfinai.co/invoices', {
    headers: {
      'Authorization': `Bearer ${localStorage.getItem('dolfin_token')}`,
      'x-dolfin-organisation-id': '9a658587-fe02-402e-b1ac-bfaf53274ef8',
    },
  }).then(r => r.json());
  ```
</CodeGroup>

***

## Sequence Diagram

```mermaid theme={null}
sequenceDiagram
    participant Client as Your Backend
    participant Dolfin as Dolfin API
    participant Browser as User's Browser

    Note over Client,Browser: Setup (one-time)

    Client->>+Dolfin: POST /organisations
    Dolfin-->>-Client: 201 { id: "org-id", ... }

    Client->>+Dolfin: POST /organisations/{orgId}/users
    Dolfin-->>-Client: 201 { userId: "alice-id", role: "Member" }

    Client->>+Dolfin: POST /organisations/{orgId}/users
    Dolfin-->>-Client: 201 { userId: "bob-id", role: "Member" }

    Note over Client,Browser: Per-session (each time a user logs in)

    Client->>+Dolfin: POST /auth/codes { userId, organisationId }
    Dolfin-->>-Client: 201 { code: "eyJ...", expiresAt }

    Client-->>Browser: Redirect with auth code

    Browser->>+Dolfin: POST /auth/exchange { code }
    Dolfin-->>-Browser: 200 { token, userId, organisations[] }

    Browser->>+Dolfin: GET /invoices (Bearer token)
    Dolfin-->>-Browser: 200 { data: [...] }
```

## Summary

| Phase         | Endpoint                            | Called by      | Auth          |
| ------------- | ----------------------------------- | -------------- | ------------- |
| Create org    | `POST /organisations`               | Your backend   | API key       |
| Create users  | `POST /organisations/{orgId}/users` | Your backend   | API key       |
| Generate code | `POST /auth/codes`                  | Your backend   | API key       |
| Exchange code | `POST /auth/exchange`               | User's browser | None (public) |
| Use API       | Any endpoint                        | User's browser | Bearer token  |
