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

# Onboarding Clients via API

> Programmatically create partner invitations to let clients connect their WhatsApp Business account or Facebook Page

This guide shows you how to automate client onboarding using the Zavu API to create partner invitations programmatically.

## Overview

The Partner Invitations API allows you to:

* Create invitation links programmatically
* Onboard a client on WhatsApp (`whatsapp_waba`) or Messenger (`messenger`)
* Integrate client onboarding into your own application
* Optionally pre-assign phone numbers to WhatsApp invitations
* Track invitation status and completion

### Choosing a channel

`connectionType` decides what the client connects. It defaults to `whatsapp_waba`, so existing integrations keep working unchanged.

| `connectionType` | Client authorizes                           | Result                                                                    |
| ---------------- | ------------------------------------------- | ------------------------------------------------------------------------- |
| `whatsapp_waba`  | Meta's embedded signup                      | A sender with the WhatsApp channel                                        |
| `messenger`      | Facebook, then picks a Page they administer | A sender with the Messenger channel, receiving Page and Marketplace chats |

One invitation connects one channel. To onboard a client on both, create two invitations; each completes into its own sender.

## Creating an Invitation

### Basic Invitation

Create an invitation where the client provides their own phone number:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import Zavudev from '@zavudev/sdk';

  const zavu = new Zavudev({
    apiKey: process.env["ZAVUDEV_API_KEY"],
  });

  const invitation = await zavu.invitations.create({
    clientName: 'Acme Corp',
    clientEmail: 'contact@acme.com',
    expiresInDays: 7,
  });

  console.log('Invitation URL:', invitation.url);
  console.log('Token:', invitation.token);
  console.log('Expires:', invitation.expiresAt);
  ```

  ```ruby Ruby theme={null}
  require "zavudev"

  client = Zavudev::Client.new(api_key: ENV["ZAVUDEV_API_KEY"])

  invitation = client.invitations.create(
    client_name: "Acme Corp",
    client_email: "contact@acme.com",
    expires_in_days: 7
  )

  puts "Invitation URL: #{invitation.url}"
  puts "Token: #{invitation.token}"
  puts "Expires: #{invitation.expires_at}"
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.zavu.dev/v1/invitations \
    -H "Authorization: Bearer $ZAVUDEV_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "clientName": "Acme Corp",
      "clientEmail": "contact@acme.com",
      "expiresInDays": 7
    }'
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "invitation": {
    "id": "jh7am5bng9p3v2x1k4r8",
    "url": "https://dashboard.zavu.dev/invite/abc123xyz",
    "token": "abc123xyz",
    "clientName": "Acme Corp",
    "clientEmail": "contact@acme.com",
    "phoneNumberId": null,
    "status": "pending",
    "senderId": null,
    "expiresAt": "2025-01-22T00:00:00.000Z",
    "createdAt": "2025-01-15T12:00:00.000Z"
  }
}
```

### With Pre-assigned Phone Number

Pre-assign a Zavu phone number that the client will register with WhatsApp:

<CodeGroup>
  ```typescript TypeScript theme={null}
  // First, purchase a phone number
  const phoneNumber = await zavu.phoneNumbers.purchase({
    phoneNumber: '+14155551234',
    name: 'Client: Acme Corp',
  });

  // Then create the invitation with the phone number
  const invitation = await zavu.invitations.create({
    clientName: 'Acme Corp',
    clientEmail: 'contact@acme.com',
    phoneNumberId: phoneNumber.id,
    expiresInDays: 14,
  });

  console.log('Invitation URL:', invitation.url);
  ```

  ```ruby Ruby theme={null}
  # First, purchase a phone number
  phone_number = client.phone_numbers.purchase(
    phone_number: "+14155551234",
    name: "Client: Acme Corp"
  )

  # Then create the invitation with the phone number
  invitation = client.invitations.create(
    client_name: "Acme Corp",
    client_email: "contact@acme.com",
    phone_number_id: phone_number.id,
    expires_in_days: 14
  )

  puts "Invitation URL: #{invitation.url}"
  ```

  ```bash cURL theme={null}
  # First, purchase a phone number
  curl -X POST https://api.zavu.dev/v1/phone-numbers \
    -H "Authorization: Bearer $ZAVUDEV_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "phoneNumber": "+14155551234",
      "name": "Client: Acme Corp"
    }'

  # Then create the invitation with the phone number ID
  curl -X POST https://api.zavu.dev/v1/invitations \
    -H "Authorization: Bearer $ZAVUDEV_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "clientName": "Acme Corp",
      "clientEmail": "contact@acme.com",
      "phoneNumberId": "pn_abc123",
      "expiresInDays": 14
    }'
  ```
</CodeGroup>

<Info>
  When you pre-assign a phone number, the invitation page will show the number and automatically display the verification code when it's received via phone call.
</Info>

### Messenger Invitation

Create an invitation for a Facebook Page. There is no phone number involved, so the WhatsApp-only options are rejected with `400`.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const { invitation } = await zavu.invitations.create({
    clientName: 'Acme Corp',
    clientEmail: 'contact@acme.com',
    connectionType: 'messenger',
  });

  console.log(invitation.url);
  ```

  ```bash cURL theme={null}
  curl -X POST https://api.zavu.dev/v1/invitations \
    -H "Authorization: Bearer $ZAVUDEV_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "clientName": "Acme Corp",
      "clientEmail": "contact@acme.com",
      "connectionType": "messenger"
    }'
  ```
</CodeGroup>

<Note>
  A Facebook Page can only be connected to one Zavu project at a time. If the client picks a Page that another project already connected, the newer connection wins and the older one is disconnected.
</Note>

### Create Parameters

| Parameter               | Type      | Required | Description                                                 |
| ----------------------- | --------- | -------- | ----------------------------------------------------------- |
| `clientName`            | string    | No       | Name to identify the client (max 100 chars)                 |
| `clientEmail`           | string    | No       | Client's email address                                      |
| `clientPhone`           | string    | No       | Client's phone number (E.164 format)                        |
| `connectionType`        | string    | No       | `whatsapp_waba` (default) or `messenger`                    |
| `phoneNumberId`         | string    | No       | Pre-assign a Zavu phone number. `whatsapp_waba` only        |
| `allowedPhoneCountries` | string\[] | No       | ISO country codes for allowed numbers. `whatsapp_waba` only |
| `expiresInDays`         | integer   | No       | Link validity in days (1-30, default: 7)                    |

## Listing Invitations

Retrieve all invitations for your project:

<CodeGroup>
  ```typescript TypeScript theme={null}
  // List all invitations
  const { items } = await zavu.invitations.list({});

  // Filter by status
  const pending = await zavu.invitations.list({
    status: 'pending',
  });

  for (const inv of pending.items) {
    console.log(inv.clientName, inv.status, inv.url);
  }
  ```

  ```ruby Ruby theme={null}
  # List all invitations
  result = client.invitations.list

  # Filter by status
  pending = client.invitations.list(status: "pending")

  pending.items.each do |inv|
    puts "#{inv.client_name} #{inv.status} #{inv.url}"
  end
  ```

  ```bash cURL theme={null}
  # List all invitations
  curl "https://api.zavu.dev/v1/invitations" \
    -H "Authorization: Bearer $ZAVUDEV_API_KEY"

  # Filter by status
  curl "https://api.zavu.dev/v1/invitations?status=pending" \
    -H "Authorization: Bearer $ZAVUDEV_API_KEY"
  ```
</CodeGroup>

### List Parameters

| Parameter | Type    | Description                                                                     |
| --------- | ------- | ------------------------------------------------------------------------------- |
| `status`  | string  | Filter by status: `pending`, `in_progress`, `completed`, `expired`, `cancelled` |
| `limit`   | integer | Results per page (default: 50, max: 100)                                        |
| `cursor`  | string  | Pagination cursor                                                               |

## Getting Invitation Details

Retrieve details for a specific invitation:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const invitation = await zavu.invitations.retrieve({
    invitationId: 'jh7am5bng9p3v2x1k4r8',
  });

  console.log('Status:', invitation.status);
  console.log('Client:', invitation.clientName);

  if (invitation.status === 'completed') {
    console.log('Sender ID:', invitation.senderId);
  }
  ```

  ```ruby Ruby theme={null}
  invitation = client.invitations.retrieve(
    invitation_id: "jh7am5bng9p3v2x1k4r8"
  )

  puts "Status: #{invitation.status}"
  puts "Client: #{invitation.client_name}"

  if invitation.status == "completed"
    puts "Sender ID: #{invitation.sender_id}"
  end
  ```

  ```bash cURL theme={null}
  curl "https://api.zavu.dev/v1/invitations/jh7am5bng9p3v2x1k4r8" \
    -H "Authorization: Bearer $ZAVUDEV_API_KEY"
  ```
</CodeGroup>

### Response Fields

| Field              | Type   | Description                                                                                                                   |
| ------------------ | ------ | ----------------------------------------------------------------------------------------------------------------------------- |
| `id`               | string | Unique invitation ID                                                                                                          |
| `url`              | string | Full invitation URL to share with client                                                                                      |
| `token`            | string | URL token (part of the URL)                                                                                                   |
| `clientName`       | string | Client name                                                                                                                   |
| `clientEmail`      | string | Client email                                                                                                                  |
| `phoneNumberId`    | string | Pre-assigned phone number ID (if any)                                                                                         |
| `connectionType`   | string | `whatsapp_waba` or `messenger`                                                                                                |
| `status`           | string | Current status                                                                                                                |
| `senderId`         | string | Created sender ID (when completed)                                                                                            |
| `connectedAccount` | object | What was linked, once completed: `{ channel, id, name }`. The WhatsApp number, or the Facebook Page. `null` before completion |
| `failureReason`    | string | Why the last attempt failed, when `status` is `failed`                                                                        |
| `failedAt`         | string | When the last attempt failed                                                                                                  |
| `expiresAt`        | string | Expiration timestamp                                                                                                          |
| `viewedAt`         | string | When client first viewed the invitation                                                                                       |
| `startedAt`        | string | When client started the setup                                                                                                 |
| `completedAt`      | string | When setup was completed                                                                                                      |

## Cancelling an Invitation

Cancel an invitation to prevent it from being used:

<CodeGroup>
  ```typescript TypeScript theme={null}
  await zavu.invitations.cancel({
    invitationId: 'jh7am5bng9p3v2x1k4r8',
  });
  ```

  ```ruby Ruby theme={null}
  client.invitations.cancel(
    invitation_id: "jh7am5bng9p3v2x1k4r8"
  )
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.zavu.dev/v1/invitations/jh7am5bng9p3v2x1k4r8/cancel" \
    -H "Authorization: Bearer $ZAVUDEV_API_KEY"
  ```
</CodeGroup>

<Note>
  You cannot cancel a completed invitation. Once a sender is created, manage it through the Senders API instead.
</Note>

## Invitation Lifecycle

```mermaid theme={null}
stateDiagram-v2
    [*] --> pending: Create invitation
    pending --> in_progress: Client starts setup
    pending --> expired: Time expires
    pending --> cancelled: Cancel invitation
    in_progress --> completed: Setup successful
    in_progress --> failed: Client cancelled or abandoned
    in_progress --> expired: Time expires
    in_progress --> cancelled: Cancel invitation
    failed --> in_progress: Client retries the same link
    completed --> [*]: Sender created
    expired --> [*]
    cancelled --> [*]
```

A `failed` invitation is not dead: the link stays usable and moves back to `in_progress` when the client tries again. `failureReason` carries a stable code — `fb_cancelled`, `fb_not_authorized`, `signup_abandoned`, `meta_no_pages` (the client administers no Facebook Page), and others. Treat unknown codes as a generic failure.

## Integration Examples

### SaaS Onboarding Flow

Integrate WhatsApp setup into your SaaS onboarding:

```typescript theme={null}
async function onboardNewCustomer(customer: Customer) {
  // 1. Create the customer in your system
  await createCustomer(customer);

  // 2. Create a Zavu invitation
  const invitation = await zavu.invitations.create({
    clientName: customer.companyName,
    clientEmail: customer.email,
    expiresInDays: 14,
  });

  // 3. Send onboarding email with the invitation link
  await sendEmail({
    to: customer.email,
    subject: 'Connect Your WhatsApp Business',
    body: `
      Welcome to our platform!

      To enable WhatsApp messaging, please complete the setup:
      ${invitation.url}

      This link expires in 14 days.
    `,
  });

  // 4. Store the invitation ID for tracking
  await saveInvitationId(customer.id, invitation.id);
}
```

### Polling for Completion

Check if a client has completed the setup:

```typescript theme={null}
async function checkInvitationStatus(invitationId: string) {
  const invitation = await zavu.invitations.retrieve({ invitationId });

  switch (invitation.status) {
    case 'completed':
      console.log('Client connected! Sender ID:', invitation.senderId);
      // Start sending messages through this sender
      break;
    case 'pending':
    case 'in_progress':
      console.log('Still waiting for client to complete setup');
      break;
    case 'expired':
      console.log('Invitation expired, creating new one...');
      // Create a new invitation
      break;
    case 'cancelled':
      console.log('Invitation was cancelled');
      break;
  }

  return invitation;
}
```

### Webhook Integration

Set up a webhook to be notified when invitations are completed:

```typescript theme={null}
// In your webhook handler
app.post('/webhooks/zavu', async (req, res) => {
  const event = req.body;

  if (event.type === 'sender.created') {
    // A new sender was created (possibly from an invitation)
    const sender = event.data;
    console.log('New sender:', sender.id, sender.phoneNumber);

    // Update your system
    await updateCustomerWhatsAppStatus(sender.phoneNumber, 'connected');
  }

  res.status(200).send('OK');
});
```

## Error Handling

<CodeGroup>
  ```typescript TypeScript theme={null}
  try {
    const invitation = await zavu.invitations.create({
      clientName: 'Acme Corp',
      phoneNumberId: 'pn_invalid',
    });
  } catch (error) {
    if (error.code === 'not_found') {
      console.error('Phone number not found');
    } else if (error.code === 'invalid_request') {
      console.error('Invalid request:', error.message);
    } else {
      console.error('Unexpected error:', error);
    }
  }
  ```

  ```ruby Ruby theme={null}
  begin
    invitation = client.invitations.create(
      client_name: "Acme Corp",
      phone_number_id: "pn_invalid"
    )
  rescue Zavudev::Error => e
    case e.code
    when "not_found"
      puts "Phone number not found"
    when "invalid_request"
      puts "Invalid request: #{e.message}"
    else
      puts "Unexpected error: #{e}"
    end
  end
  ```
</CodeGroup>

### Common Errors

| Code                      | Description                              |
| ------------------------- | ---------------------------------------- |
| `not_found`               | Phone number or invitation not found     |
| `invalid_request`         | Invalid parameters                       |
| `phone_number_wrong_team` | Phone number belongs to a different team |
| `already_completed`       | Cannot cancel a completed invitation     |

## Best Practices

<CardGroup cols={2}>
  <Card title="Store Invitation IDs" icon="database">
    Save the invitation ID in your database to track status and link to your customer records.
  </Card>

  <Card title="Set Reminders" icon="bell">
    If an invitation is pending for several days, send a reminder to your client.
  </Card>

  <Card title="Handle Expiration" icon="clock">
    Monitor for expired invitations and automatically create new ones if needed.
  </Card>

  <Card title="Use Webhooks" icon="webhook">
    Set up webhooks to get real-time notifications when senders are created.
  </Card>
</CardGroup>

## Rate Limits

The Invitations API has the following rate limits:

| Endpoint          | Limit               |
| ----------------- | ------------------- |
| Create invitation | 100 requests/minute |
| List invitations  | 200 requests/minute |
| Get invitation    | 200 requests/minute |
| Cancel invitation | 100 requests/minute |

## Next Steps

<CardGroup cols={2}>
  <Card title="Dashboard Guide" icon="browser" href="/guides/partner-invitations/dashboard">
    Learn to create invitations from the dashboard
  </Card>

  <Card title="Senders API" icon="paper-plane" href="/concepts/senders">
    Work with senders created from invitations
  </Card>

  <Card title="Send WhatsApp Messages" icon="message" href="/guides/whatsapp/overview">
    Start sending messages through your new senders
  </Card>

  <Card title="Phone Numbers" icon="phone" href="/guides/phone-numbers/purchasing">
    Purchase phone numbers to pre-assign to invitations
  </Card>
</CardGroup>
