nlpearl 2.1.1
A Python wrapper for the NLPearl API
License: BSD-3-Clause
Requires Python: <4,>=3.6
Keywords: nlpearl api wrapper client telephony automation python conversational-ai nlp call-handling

# NLPearl Python Wrapper

NLPearl is a Python wrapper for the NLPearl API, allowing developers to interact seamlessly with NLPearl's services. This package supports both API V1 and V2 with a unified interface.

## Table of Contents

- [Installation](#installation)
- [Getting Started](#getting-started)
- [Quick Start](#quick-start)
- [API Versions](#api-versions)
- [Usage Guide](#usage-guide)
  - [V2 API (Default)](#v2-api-default)
  - [V1 API](#v1-api)
  - [Shared Methods](#shared-methods)
- [Complete API Reference](#complete-api-reference)
- [Migration Guide](#migration-guide)
- [Changelog](#changelog)
- [License](#license)

## Installation

```bash
pip install nlpearl
```

## Getting Started

1. Get your API key from [NLPearl](mailto:samuel@nlpearl.ai)
2. Set your API version (V2 is default)
3. Start making API calls!

```python
import os
import nlpearl as pearl
from dotenv import load_dotenv

# Configuration
load_dotenv()
env = os.environ

pearl.api_key = env["PROD_API_KEY"]
pearl.api_version = "v2"  # Default, can be "v1" or "v2"
```

## Quick Start

### V2 Example (Default)

```python
import os
import nlpearl as pearl
from dotenv import load_dotenv

load_dotenv()
pearl.api_key = os.environ["PROD_API_KEY"]
pearl.api_version = "v2"  # Default

# Get all pearls
pearls = pearl.Pearl.get_all()

# Add a lead using pearl_id
pearl.Outbound.add_lead(
    pearl_id,
    phone_number="+1234567890",
    call_data={"firstName": "John"}
)

# Get analytics
analytics = pearl.Pearl.get_analytics(
    pearl_id,
    from_date="2024-01-01T00:00:00.000Z",
    to_date="2024-01-31T23:59:59.999Z"
)
```

### V1 Example

```python
import os
import nlpearl as pearl
from dotenv import load_dotenv

load_dotenv()
pearl.api_key = os.environ["PROD_API_KEY"]
pearl.api_version = "v1"

# Get all inbounds
inbounds = pearl.Inbound.get_all()

# Add a lead using outbound_id
pearl.Outbound.add_lead(
    outbound_id,
    phone_number="+1234567890",
    call_data={"firstName": "John"}
)

# Get analytics
analytics = pearl.Outbound.get_analytics(
    outbound_id,
    from_date="2024-01-01T00:00:00.000Z",
    to_date="2024-01-31T23:59:59.999Z"
)
```

## API Versions

**The wrapper uses the same class names for both V1 and V2**, automatically routing to the correct endpoints based on `pearl.api_version`.

### Version Differences

| Feature | V1 | V2 |
|---------|----|----|
| **Default** | No | **Yes** |
| **Structure** | Separate Inbound/Outbound | Unified Pearl |
| **ID Parameter** | `outbound_id`, `inbound_id` | `pearl_id` |
| **Classes** | `Inbound`, `Outbound` | `Pearl` |
| **Lead Operations** | `Outbound.add_lead(outbound_id, ...)` | `Outbound.add_lead(pearl_id, ...)` |
| **Total Endpoints** | 26 | 26 |

### Unified API Approach

- **Same class names** - Use `pearl.Outbound`, `pearl.Pearl`, `pearl.Inbound`  
- **Automatic routing** - Methods route to correct version based on `api_version`  
- **Clear errors** - Helpful messages when using wrong version  
- **Version switching** - Change `api_version` anytime  

**No separate V2 classes needed!** Just set the version once and use the same API.

## Usage Guide

### V2 API (Default)

#### Account Operations (V2)

```python
pearl.api_version = "v2"

# Get account info (works in both V1 and V2)
account = pearl.Account.get_account()

# V2-only account methods
users = pearl.Account.get_users()              # Get list of users
phones = pearl.Account.get_phone_numbers()     # Get active phone numbers
voices = pearl.Account.get_voices()            # Get available voices by language
```

#### Pearl Management (V2)

```python
pearl.api_version = "v2"

# Get all pearls
pearls = pearl.Pearl.get_all()

# Get specific pearl
pearl_details = pearl.Pearl.get(pearl_id)

# Create a new pearl
new_pearl = pearl.Pearl.create(
    name="My Pearl",
    pearl={"businessName": "My Company", "objective": "Sales"},
    variables=[{"name": "firstName", "description": "Customer first name"}],
    inbound={"isActive": True},
    outbound={"isActive": True, "callerIdNumber": "+1234567890"}
)

# Update pearl settings
pearl.Pearl.update(
    pearl_id,
    name="Updated Pearl Name",
    pearl={"businessName": "New Name"}
)

# Get pearl settings
settings = pearl.Pearl.get_settings(pearl_id)

# Update outbound/inbound settings
pearl.Pearl.update_outbound_settings(pearl_id, {"callerIdNumber": "+1234567890"})
pearl.Pearl.update_inbound_settings(pearl_id, {"isActive": True})

# Activate/Deactivate
pearl.Pearl.set_active(pearl_id, is_active=True)

# Get ongoing calls
ongoing = pearl.Pearl.get_ongoing_calls(pearl_id)

# Get calls with filters
calls = pearl.Pearl.get_calls(
    pearl_id,
    from_date=datetime(2024, 1, 1),
    to_date=datetime(2024, 1, 31),
    tags=["important"],
    statuses=[100, 130],
    search_input="John"
)

# Get analytics
analytics = pearl.Pearl.get_analytics(pearl_id, from_date, to_date)

# Reset customer memory
pearl.Pearl.reset_memory(pearl_id, phone_number="+1234567890")
```

#### Lead Management (V2)

```python
# Add lead (using pearl_id, uses POST in V2)
lead = pearl.Outbound.add_lead(
    pearl_id,
    phone_number="+1234567890",
    external_id="ext123",
    time_zone_id="Pacific Standard Time",
    call_data={"firstName": "John", "lastName": "Doe"}
)

# Get lead
lead = pearl.Outbound.get_lead_by_id(pearl_id, lead_id)
lead = pearl.Outbound.get_lead_by_external_id(pearl_id, "ext123")
lead = pearl.Outbound.get_lead_by_phone_number(pearl_id, "+1234567890")

# Update lead
pearl.Outbound.update_lead(
    pearl_id,
    lead_id,
    status=100,  # Mark as Success
    call_data={"notes": "Contact successful"}
)

# Search leads
leads = pearl.Outbound.get_leads(
    pearl_id,
    statuses=[1, 10],  # New and NeedRetry
    search_input="John",
    limit=50
)

# Delete leads
pearl.Outbound.delete_leads(pearl_id, [lead_id1, lead_id2])
pearl.Outbound.delete_leads_by_external_id(pearl_id, ["ext1", "ext2"])
```

### V1 API

#### Account Operations (V1)

```python
pearl.api_version = "v1"

# Get account info
account = pearl.Account.get_account()
```

#### Inbound Operations (V1 Only)

```python
pearl.api_version = "v1"

# Get all inbounds
inbounds = pearl.Inbound.get_all()

# Get specific inbound
inbound = pearl.Inbound.get(inbound_id)

# Activate/Deactivate
pearl.Inbound.set_active(inbound_id, is_active=True)

# Get ongoing calls
ongoing = pearl.Inbound.get_ongoing_calls(inbound_id)

# Get calls with filters
calls = pearl.Inbound.get_calls(
    inbound_id,
    from_date=datetime(2024, 1, 1),
    to_date=datetime(2024, 1, 31),
    tags=["important"],
    statuses=[100, 130],
    search_input="John"
)

# Get analytics
analytics = pearl.Inbound.get_analytics(inbound_id, from_date, to_date)
```

#### Outbound Operations (V1)

```python
# Get all outbounds
outbounds = pearl.Outbound.get_all()

# Get specific outbound
outbound = pearl.Outbound.get(outbound_id)

# Activate/Deactivate
pearl.Outbound.set_active(outbound_id, is_active=True)

# Get calls with filters
calls = pearl.Outbound.get_calls(
    outbound_id,
    from_date=datetime(2024, 1, 1),
    to_date=datetime(2024, 1, 31),
    statuses=[100, 130],
    search_input="John"
)

# Make a call
result = pearl.Outbound.make_call(
    outbound_id,
    to="+1234567890",
    call_data={"firstName": "Jane"}
)

# Get call requests
requests = pearl.Outbound.get_call_requests(
    outbound_id,
    from_date=datetime(2024, 1, 1),
    to_date=datetime(2024, 1, 31)
)

# Get specific call request
request = pearl.Outbound.get_call_request(request_id)

# Get analytics
analytics = pearl.Outbound.get_analytics(outbound_id, from_date, to_date)
```

#### Lead Management (V1)

```python
# Add lead (using outbound_id, uses PUT in V1)
lead = pearl.Outbound.add_lead(
    outbound_id,
    phone_number="+1234567890",
    external_id="ext123",
    call_data={"firstName": "John"}
)

# Update lead
pearl.Outbound.update_lead(outbound_id, lead_id, status=100)

# Search leads
leads = pearl.Outbound.get_leads(
    outbound_id,
    statuses=[1, 10],  # Array of status codes
    search_input="John"
)

# Other lead methods work the same
lead = pearl.Outbound.get_lead_by_id(outbound_id, lead_id)
pearl.Outbound.delete_leads(outbound_id, [lead_id1, lead_id2])
```

### Shared Methods

These methods work in **both V1 and V2**:

#### Account

```python
# Works in both versions
account = pearl.Account.get_account()
print(f"Balance: {account['creditBalance']}")
```

#### Call Operations

```python
# Get call info (both versions)
call = pearl.Call.get_call(call_id)

# Delete calls (both versions)
pearl.Call.delete_calls([call_id1, call_id2])
```

#### Memory Management

```python
# V1 and V2 (different implementations internally)
pearl.Pearl.reset_customer_memory(pearl_id, phone_number)

# V2 alias
pearl.Pearl.reset_memory(pearl_id, phone_number)
```

## Complete API Reference

### V1 Endpoints (26 total)

| Class | Method | Description |
|-------|--------|-------------|
| **Account** | `get_account()` | Get account information |
| **Call** | `get_call(call_id)` | Get call details |
| **Call** | `delete_calls(call_ids)` | Delete multiple calls |
| **Inbound** | `get_all()` | List all inbound pearls |
| **Inbound** | `get(inbound_id)` | Get specific inbound |
| **Inbound** | `set_active(inbound_id, is_active)` | Activate/deactivate |
| **Inbound** | `get_calls(inbound_id, ...)` | Get calls with filters |
| **Inbound** | `get_ongoing_calls(inbound_id)` | Get active calls |
| **Inbound** | `get_analytics(inbound_id, ...)` | Get analytics data |
| **Outbound** | `get_all()` | List all outbound pearls |
| **Outbound** | `get(outbound_id)` | Get specific outbound |
| **Outbound** | `set_active(outbound_id, is_active)` | Activate/deactivate |
| **Outbound** | `get_calls(outbound_id, ...)` | Get calls with filters |
| **Outbound** | `get_analytics(outbound_id, ...)` | Get analytics data |
| **Outbound** | `make_call(outbound_id, to, call_data)` | Initiate a call |
| **Outbound** | `get_call_request(request_id)` | Get call request status |
| **Outbound** | `get_call_requests(outbound_id, ...)` | List call requests |
| **Outbound** | `add_lead(outbound_id, ...)` | Add lead (uses PUT) |
| **Outbound** | `update_lead(outbound_id, lead_id, ...)` | Update lead |
| **Outbound** | `get_leads(outbound_id, ...)` | List leads with filters |
| **Outbound** | `get_lead_by_id(outbound_id, lead_id)` | Get lead by ID |
| **Outbound** | `get_lead_by_external_id(outbound_id, ext_id)` | Get lead by external ID |
| **Outbound** | `get_lead_by_phone_number(outbound_id, phone)` | Get lead by phone |
| **Outbound** | `delete_leads(outbound_id, lead_ids)` | Delete leads by ID |
| **Outbound** | `delete_leads_by_external_id(outbound_id, ext_ids)` | Delete by external ID |
| **Pearl** | `reset_customer_memory(pearl_id, phone)` | Reset memory (URL param) |

### V2 Endpoints (26 total)

| Class | Method | Description |
|-------|--------|-------------|
| **Account** | `get_account()` | Get account information |
| **Account** | `get_users()` | Get list of users [NEW] |
| **Account** | `get_phone_numbers()` | Get active phone numbers [NEW] |
| **Account** | `get_voices()` | Get available voices [NEW] |
| **Call** | `get_call(call_id)` | Get call details |
| **Call** | `delete_calls(call_ids)` | Delete multiple calls |
| **Pearl** | `get_all()` | List all pearls |
| **Pearl** | `get(pearl_id)` | Get specific pearl |
| **Pearl** | `create(name, pearl, ...)` | Create new pearl [NEW] |
| **Pearl** | `update(pearl_id, ...)` | Update pearl [NEW] |
| **Pearl** | `set_active(pearl_id, is_active)` | Activate/deactivate |
| **Pearl** | `get_calls(pearl_id, ...)` | Get calls with filters |
| **Pearl** | `get_ongoing_calls(pearl_id)` | Get active calls |
| **Pearl** | `get_analytics(pearl_id, ...)` | Get analytics data |
| **Pearl** | `get_settings(pearl_id)` | Get pearl settings [NEW] |
| **Pearl** | `update_outbound_settings(pearl_id, settings)` | Update outbound [NEW] |
| **Pearl** | `update_inbound_settings(pearl_id, settings)` | Update inbound [NEW] |
| **Pearl** | `reset_memory(pearl_id, phone)` | Reset memory (body) |
| **Outbound** | `add_lead(pearl_id, ...)` | Add lead (uses POST) |
| **Outbound** | `update_lead(pearl_id, lead_id, ...)` | Update lead |
| **Outbound** | `get_leads(pearl_id, ...)` | List leads with filters |
| **Outbound** | `get_lead_by_id(pearl_id, lead_id)` | Get lead by ID |
| **Outbound** | `get_lead_by_external_id(pearl_id, ext_id)` | Get lead by external ID |
| **Outbound** | `get_lead_by_phone_number(pearl_id, phone)` | Get lead by phone |
| **Outbound** | `delete_leads(pearl_id, lead_ids)` | Delete leads by ID |
| **Outbound** | `delete_leads_by_external_id(pearl_id, ext_ids)` | Delete by external ID |

### Method Availability Matrix

| Class | Method | V1 | V2 | Notes |
|-------|--------|:--:|:--:|-------|
| **Account** | `get_account()` | Y | Y | Shared |
| **Account** | `get_users()` | - | Y | V2 only |
| **Account** | `get_phone_numbers()` | - | Y | V2 only |
| **Account** | `get_voices()` | - | Y | V2 only |
| **Call** | `get_call(call_id)` | Y | Y | Shared |
| **Call** | `delete_calls(call_ids)` | Y | Y | Shared |
| **Inbound** | All methods | Y | - | V1 only |
| **Outbound** | `get_all()` | Y | - | V1 only |
| **Outbound** | `get(id)` | Y | - | V1 only |
| **Outbound** | `set_active(...)` | Y | - | V1 only |
| **Outbound** | `get_calls(...)` | Y | - | V1 only |
| **Outbound** | `get_analytics(...)` | Y | - | V1 only |
| **Outbound** | `make_call(...)` | Y | - | V1 only |
| **Outbound** | `get_call_request(...)` | Y | - | V1 only |
| **Outbound** | `get_call_requests(...)` | Y | - | V1 only |
| **Outbound** | Lead methods | Y | Y | Shared (different ID) |
| **Pearl** | `get_all()` | - | Y | V2 only |
| **Pearl** | `get(pearl_id)` | - | Y | V2 only |
| **Pearl** | `create(...)` | - | Y | V2 only |
| **Pearl** | `update(...)` | - | Y | V2 only |
| **Pearl** | `set_active(...)` | - | Y | V2 only |
| **Pearl** | `get_calls(...)` | - | Y | V2 only |
| **Pearl** | `get_ongoing_calls(...)` | - | Y | V2 only |
| **Pearl** | `get_analytics(...)` | - | Y | V2 only |
| **Pearl** | `get_settings(...)` | - | Y | V2 only |
| **Pearl** | `update_outbound_settings(...)` | - | Y | V2 only |
| **Pearl** | `update_inbound_settings(...)` | - | Y | V2 only |
| **Pearl** | `reset_customer_memory(...)` | Y | Y | Different implementation |

### Status Codes

#### Lead Status
- `1` - New
- `10` - NeedRetry
- `100` - Success
- `110` - NotSuccessful
- `130` - Completed
- `150` - Unreachable
- `220` - Blacklisted
- `500` - Error

#### Call Status
- `3` - InProgress
- `4` - Completed
- `5` - Busy
- `6` - Failed
- `7` - NoAnswer
- `8` - Canceled

#### Activity Status
- `1` - Running
- `2` - Paused
- `3` - Suspended
- `10` - TemporaryMaintenance

## Migration Guide

### From V1 to V2

**Before (V1):**
```python
pearl.api_version = "v1"

# Get outbounds
outbounds = pearl.Outbound.get_all()

# Add lead
pearl.Outbound.add_lead(outbound_id, phone_number="+123...")

# Get analytics
analytics = pearl.Outbound.get_analytics(outbound_id, from_date, to_date)
```

**After (V2):**
```python
pearl.api_version = "v2"

# Get pearls (replaces outbounds/inbounds)
pearls = pearl.Pearl.get_all()

# Add lead (same method, different ID)
pearl.Outbound.add_lead(pearl_id, phone_number="+123...")

# Get analytics (use Pearl class)
analytics = pearl.Pearl.get_analytics(pearl_id, from_date, to_date)
```

### Key Changes

1. **Replace `outbound_id`/`inbound_id` with `pearl_id`**
2. **Use `Pearl` class** for analytics, calls, and pearl management
3. **Keep using `Outbound`** for lead operations (just change the ID)
4. **`Inbound` class** is V1-only, use `Pearl` in V2

## Error Handling

The wrapper provides clear error messages when using methods in the wrong version:

```python
pearl.api_version = "v2"
pearl.Inbound.get_all()
# ValueError: Inbound.get_all() is only available in API v1.
# In v2, use Pearl.get_all() instead.
```

## Changelog

### Version 2.0.0 (February 2026)

#### API Versioning System
- Added `pearl.api_version` global variable (default: 'v2')
- Added `_get_api_url()` helper for dynamic URL generation
- Unified API: Same class names work for both V1 and V2

#### New V2 Account Endpoints
- `Account.get_users()` - Get list of users
- `Account.get_phone_numbers()` - Get active phone numbers
- `Account.get_voices()` - Get available voices by language

#### New V2 Pearl Endpoints
- `Pearl.create()` - Create a new Pearl with full configuration
- `Pearl.update()` - Update Pearl settings
- `Pearl.get_settings()` - Get Pearl configuration
- `Pearl.update_outbound_settings()` - Update outbound settings
- `Pearl.update_inbound_settings()` - Update inbound settings

#### V1 Fixes (from OpenAPI spec)
- `Outbound.get_calls()` - Added `statuses` (array), `search_input` parameters
- `Outbound.get_leads()` - Changed `status` to `statuses` (array), added `search_input`
- `Inbound.get_calls()` - Added `statuses` (array), `search_input` parameters

#### Version-Specific Behavior
- `Outbound.add_lead()`: V1 uses PUT, V2 uses POST
- `Pearl.reset_customer_memory()`: V1 uses URL param, V2 uses request body
- Methods raise `ValueError` if called with wrong API version

## License

BSD 3-Clause License - see [LICENSE](LICENSE) file for details.

## Contact

- **Support**: [support@nlpearl.ai](mailto:support@nlpearl.ai)
- **Documentation**: This README
- **API Key**: Contact [support@nlpearl.ai](mailto:samuel@nlpearl.ai)

---

**Version**: 2.0.0  
**Python**: 3.6+  
**API Versions**: V1 (26 endpoints) and V2 (26 endpoints) supported