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

# Integration Guide

> End-to-end guide to provisioning a fully trained, live AI support agent for your customers.

This guide walks through the complete lifecycle of setting up a customer org — from creation to a fully trained AI agent handling live conversations.

## Overview

```mermaid theme={"dark"}
graph LR
    A["1. Create org"] --> B["2. Create org API key"]
    B --> C["3. Crawl website"]
    C --> D["4. Add training"]
    D --> E["5. Grant dashboard access"]
    E --> G["6. Embed widget"]
    G --> F["Live"]
```

***

## Step 1: Create the org

Use your **Partner API key** to create an org for your customer.

```bash theme={"dark"}
curl -X POST https://api.open.cx/partner/v1/orgs \
  -H "Authorization: Bearer YOUR_PARTNER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Tours",
    "external_id": "acme-123",
    "website": "https://acmetours.com",
    "language": "en",
    "ai_instructions": "You are a helpful support assistant for Acme Tours. Help customers with booking questions, cancellations, and tour information. Be friendly and concise."
  }'
```

Save the `id` and `widget_token` from the response — you'll need both.

<Note>
  The `ai_instructions` field is the AI profile — the system prompt that defines the agent's personality, knowledge scope, and behavior. Write it as if you're briefing a new support agent — who they work for, what they help with, and how they should behave.
</Note>

<Tip>
  See the full request/response schema in the [Create Org API Reference](/partners/api-reference#create-org).
</Tip>

***

## Step 2: Create an org API key

The Partner API creates the org, but to **train** the AI and **manage** the org, you need an **org-level API key**. Create one with your partner key:

```bash theme={"dark"}
curl -X POST https://api.open.cx/partner/v1/orgs/ORG_ID/api-keys \
  -H "Authorization: Bearer YOUR_PARTNER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Production" }'
```

Save the `api_key` from the response — this is the org-level API key you'll use for all subsequent calls.

<Warning>
  The org API key is different from your partner API key. The partner key manages orgs. The org key manages a specific org's data (training, crawling, contacts, etc). The key is only returned once — store it securely.
</Warning>

All subsequent API calls use the org key:

```bash theme={"dark"}
Authorization: Bearer ORG_API_KEY
```

<Tip>
  See the full request/response schema in the [Create Org API Key Reference](/partners/api-reference#create-org-api-key).
</Tip>

***

## Step 3: Crawl the customer's website

The fastest way to train the AI is to crawl the customer's website. The crawler indexes every page into the knowledge base automatically.

```bash theme={"dark"}
curl -X POST https://api.open.cx/crawl \
  -H "Authorization: Bearer ORG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://acmetours.com",
    "page_limit": 500,
    "auto_start_crawl": true,
    "crawl_interval_hours": 168
  }'
```

You can check crawl progress at any time:

```bash theme={"dark"}
curl https://api.open.cx/crawl/DATASOURCE_ID \
  -H "Authorization: Bearer ORG_API_KEY"
```

Or sync a single page without a full crawl:

```bash theme={"dark"}
curl -X POST https://api.open.cx/crawl/DATASOURCE_ID/pages/sync \
  -H "Authorization: Bearer ORG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://acmetours.com/faq" }'
```

<Tip>
  See all available parameters and endpoints in the [Crawl API Reference](/api-reference/crawl/create-datasource).
</Tip>

***

## Step 4: Add custom training

Website content gives the AI factual knowledge. **Training scenarios** teach it *how to behave* — tone, policies, edge cases, and workflows.

```bash theme={"dark"}
curl -X POST https://api.open.cx/training \
  -H "Authorization: Bearer ORG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Cancellation policy",
    "content": "Customers can cancel up to 24 hours before their tour for a full refund. Cancellations within 24 hours receive a 50% refund. No-shows are non-refundable."
  }'
```

Use `"type": "BEHAVIORAL"` for always-active instructions (tone, guardrails) that apply to every conversation:

```bash theme={"dark"}
curl -X POST https://api.open.cx/training \
  -H "Authorization: Bearer ORG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Brand voice",
    "content": "Always be warm and friendly. Use the customer'\''s first name. Never use corporate jargon.",
    "type": "BEHAVIORAL"
  }'
```

<Tip>
  See all training types, directory management, and search in the [Training API Reference](/api-reference/training/create).
</Tip>

***

## Step 5: Grant dashboard access

Your customers need dashboard access to manage their inbox, handle human handoffs, and review conversations. Use **Login Links** to give them frictionless access — no emails, no forms, no org picker.

```bash theme={"dark"}
curl -X POST https://api.open.cx/partner/v1/orgs/ORG_ID/login-links \
  -H "Authorization: Bearer YOUR_PARTNER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "email": "owner@acmetours.com", "name": "Jane Smith" }'
```

The response contains a short-lived URL:

```json theme={"dark"}
{
  "url": "https://platform.open.cx/partner-login?token=a1b2c3...",
  "expires_at": "2026-04-12T12:15:00.000Z"
}
```

Redirect the user's browser to the returned `url`. If no account exists, one is created automatically. The user lands directly in the dashboard with **your partner branding** (logo, app name) — not the OpenCX defaults.

<Note>
  Login links expire after **15 minutes** and are **single-use**. Generate a new link each time a user needs to access the dashboard from your platform.
</Note>

<Tip>
  Login links are ideal for embedding a "Manage support" button in your own platform. See the [Login Links API Reference](/partners/api-reference#create-login-link) for full details.
</Tip>

***

## Step 6: Embed the widget

Use the `widget_token` from step 1 to embed the chat widget on your customer's site.

```html theme={"dark"}
<script
  src="https://cloud.opencopilot.so/widget.js"
  data-token="WIDGET_TOKEN"
></script>
```

Or with React:

```bash theme={"dark"}
npm install @opencx/widget-react
```

```tsx theme={"dark"}
import { Widget } from '@opencx/widget-react';

function App() {
  return <Widget token="WIDGET_TOKEN" />;
}
```

If your customers have logged-in users, you can authenticate them so the AI has context:

```bash theme={"dark"}
curl -X POST https://api.open.cx/widget/authenticate-user \
  -H "Authorization: Bearer ORG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "jane@example.com",
    "name": "Jane Smith"
  }'
```

<Tip>
  See the full widget setup and authentication options in the [Widget docs](/widget/introduction) and [Widget Authentication API](/api-reference/widget/authenticate-contact).
</Tip>

***

## Putting it all together

Here's a complete example that provisions a customer from scratch:

<CodeGroup>
  ```javascript Node.js theme={"dark"}
  const PARTNER_KEY = 'YOUR_PARTNER_API_KEY';
  const BASE = 'https://api.open.cx';

  async function provisionCustomer({ name, externalId, website, instructions }) {
    // 1. Create the org
    const orgRes = await fetch(`${BASE}/partner/v1/orgs`, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${PARTNER_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        name,
        external_id: externalId,
        website,
        ai_instructions: instructions,
      }),
    });
    const org = await orgRes.json();
    console.log(`Created org: ${org.id}`);

    // 2. Create an org API key
    const keyRes = await fetch(`${BASE}/partner/v1/orgs/${org.id}/api-keys`, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${PARTNER_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ name: 'Production' }),
    });
    const { api_key: orgApiKey } = await keyRes.json();

    // 3. Crawl the website
    await fetch(`${BASE}/crawl`, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${orgApiKey}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        url: website,
        page_limit: 500,
        auto_start_crawl: true,
      }),
    });
    console.log(`Started crawling: ${website}`);

    // 4. Add behavioral training
    await fetch(`${BASE}/training`, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${orgApiKey}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        title: 'Brand voice',
        content: `You are a support agent for ${name}. Be helpful, friendly, and concise.`,
        type: 'BEHAVIORAL',
      }),
    });
    console.log('Added behavioral training');

    // 5. Generate a login link for the customer
    const loginRes = await fetch(`${BASE}/partner/v1/orgs/${org.id}/login-links`, {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${PARTNER_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        email: `admin@${name.toLowerCase().replace(/\s+/g, '')}.com`,
        name: 'Admin',
      }),
    });
    const { url: loginUrl } = await loginRes.json();
    console.log(`Login link: ${loginUrl}`);

    // 6. Return the widget token and login URL
    return { orgId: org.id, widgetToken: org.widget_token, loginUrl };
  }

  // Usage
  const { widgetToken } = await provisionCustomer({
    name: 'Acme Tours',
    externalId: 'acme-123',
    website: 'https://acmetours.com',
    instructions: 'Help customers with tour bookings and questions.',
  });
  ```

  ```python Python theme={"dark"}
  import requests

  PARTNER_KEY = 'YOUR_PARTNER_API_KEY'
  BASE = 'https://api.open.cx'

  def provision_customer(name, external_id, website, instructions):
      # 1. Create the org
      org = requests.post(
          f'{BASE}/partner/v1/orgs',
          headers={'Authorization': f'Bearer {PARTNER_KEY}'},
          json={
              'name': name,
              'external_id': external_id,
              'website': website,
              'ai_instructions': instructions,
          },
      ).json()
      print(f"Created org: {org['id']}")

      # 2. Create an org API key
      key_res = requests.post(
          f'{BASE}/partner/v1/orgs/{org["id"]}/api-keys',
          headers={'Authorization': f'Bearer {PARTNER_KEY}'},
          json={'name': 'Production'},
      ).json()
      org_api_key = key_res['api_key']
      headers = {'Authorization': f'Bearer {org_api_key}'}

      # 3. Crawl the website
      requests.post(
          f'{BASE}/crawl',
          headers=headers,
          json={'url': website, 'page_limit': 500, 'auto_start_crawl': True},
      )
      print(f'Started crawling: {website}')

      # 4. Add behavioral training
      requests.post(
          f'{BASE}/training',
          headers=headers,
          json={
              'title': 'Brand voice',
              'content': f'You are a support agent for {name}. Be helpful and concise.',
              'type': 'BEHAVIORAL',
          },
      )
      print('Added behavioral training')

      # 5. Generate a login link for the customer
      login_res = requests.post(
          f'{BASE}/partner/v1/orgs/{org["id"]}/login-links',
          headers={'Authorization': f'Bearer {PARTNER_KEY}'},
          json={
              'email': f'admin@{name.lower().replace(" ", "")}.com',
              'name': 'Admin',
          },
      ).json()
      print(f'Login link: {login_res["url"]}')

      return {'org_id': org['id'], 'widget_token': org['widget_token'], 'login_url': login_res['url']}

  result = provision_customer(
      name='Acme Tours',
      external_id='acme-123',
      website='https://acmetours.com',
      instructions='Help customers with tour bookings and questions.',
  )
  ```
</CodeGroup>

***

## What's next

After provisioning, the org is fully operational. The AI will:

1. **Answer questions** using crawled website content and training scenarios
2. **Hand off to humans** when it can't resolve an issue (configurable via [Handoff settings](/handoff/introduction))
3. **Re-crawl** the website on the configured interval to stay up to date

For advanced configuration:

<CardGroup cols={2}>
  <Card title="Autopilot Settings" icon="robot" href="/api-reference/autopilot/get">
    Configure which channels the AI operates on and its behavior mode.
  </Card>

  <Card title="Office Hours" icon="clock" href="/api-reference/office-hours/get">
    Set business hours — the AI behaves differently outside office hours.
  </Card>

  <Card title="Tags" icon="tag" href="/api-reference/tags/list">
    Auto-tag conversations for categorization and reporting.
  </Card>

  <Card title="Contacts" icon="users" href="/api-reference/contacts/list">
    Manage customer contacts and their metadata.
  </Card>
</CardGroup>
