# SuperU - AI Voice Assistant Platform

[![PyPI version](https://img.shields.io/pypi/v/superu.svg)](https://pypi.org/project/superu/)
[![Python 3.7+](https://img.shields.io/badge/python-3.7+-blue.svg)](https://www.python.org/downloads/)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)

SuperU is a Python SDK for creating AI-powered voice assistants, running phone calls, and managing your assistants, tools, audiences, and knowledge bases from your SuperU account.

## Installation

```bash
pip install superu
```

## Quickstart

```python
import superu

client = superu.SuperU("your_api_key")

# Start an outbound call using an existing assistant ID
response = client.calls.create(
    from_="+15551234567",
    to_="+15557654321",
    assistant_id="assistant_id_from_dashboard",
    max_duration_seconds=120
)
response.raise_for_status()
call_uuid = response.json()["call_uuid"]

# Request call analysis
analysis_response = client.calls.analysis(call_uuid)
analysis_response.raise_for_status()
analysis = analysis_response.json()
print(analysis)
```

## Client Overview

The SDK exposes a single top-level client, `SuperU`, which validates your API key and provides typed wrappers for each API surface.

```python
import superu

client = superu.SuperU("your_api_key")

# Wrappers available on the client
client.assistants
client.calls
client.call_logs
client.tools
client.folders
client.pluto
client.phone_numbers
client.contacts
client.audience
client.knowledge_base
```

## API Reference

**SuperU**

High-level client that validates your API key and exposes service wrappers.

Constructor

- `SuperU(api_key: str)`

Attributes

- `assistants`: `AssistantWrapper`
- `calls`: `CallWrapper`
- `call_logs`: `CallLogsWrapper`
- `tools`: `ToolsWrapper`
- `folders`: `FolderWrapper`
- `pluto`: `PlutoWrapper`
- `phone_numbers`: `PhoneNumberWrapper`
- `contacts`: `ContactWrapper`
- `audience`: `AudienceWrapper`
- `knowledge_base`: `KnowledgeBaseWrapper`

Basic example

```python
import superu

client = superu.SuperU("your_api_key")
print(client.assistants.list_agents())
```

---

**AssistantWrapper**

Manage assistants and assistant versions.

Constructor

- `AssistantWrapper(api_key: str)`

Methods

- `create_version(agent_id, version, assistant_data, knowledge_base=None, tools=None, call_forwarding=None)`
  Creates a new assistant version. Returns a JSON dict.
- `update_version(agent_id, version_id, version=None, assistant_data=None, composio_app=None)`
  Updates an existing assistant version. Returns a JSON dict.
- `list_agents(page=1, limit=20, inbound_or_outbound=None, search_query=None)`
  Lists assistants with pagination and optional filtering. Returns a JSON dict.
- `get_version(agent_id, version)`
  Retrieves a specific assistant version. Returns a JSON dict.
- `list_versions(agent_id)`
  Lists all versions for an assistant. Returns a JSON dict.
- `deploy_version(agent_id, version_id)`
  Deploys a version. Returns a JSON dict.
- `create_agent(type=None, name=None, company_name=None, assistant_name=None, first_message=None, voice_id=None, voice_provider='11labs', speed='1.0', bg_noice=False, script=None, industry=None, useCase=None, form_model=None, assistant_data=None, knowledge_base=None, tools=None, call_forwarding=None)`
  Creates a new assistant. Returns a JSON dict.
- `update_name(agent_id, name)`
  Updates assistant name. Returns a JSON dict.
- `import_agents()`
  Imports assistants for the API key. Returns a JSON dict.
- `delete(agent_id)`
  Deletes an assistant. Returns a JSON dict.

Basic example

```python
assistant = client.assistants.create_agent(
    type="outbound",
    name="Demo Assistant",
    assistant_name="Demo Voice",
    first_message="Hello! This is a demo call.",
    script="You are a friendly assistant.",
    voice_id="90ipbRoKi4CpHXvKVtl0"
)
print(assistant)
```

---

**CallWrapper**

Create and analyze calls, including Twilio-backed calls.

Constructor

- `CallWrapper(api_key: str)`

Methods

- `create(from_, to_, first_message_url=None, assistant_id=None, max_duration_seconds=120, **kwargs)`
  Creates an outbound call using a SuperU number. Returns `requests.Response`.
- `analysis(call_uuid, custom_fields=None)`
  Requests analysis for a call. Returns `requests.Response`.
- `create_twilio_call(phoneNumberId, to_, assistant_id, additional_payload=None)`
  Creates a call using your Twilio number. Returns a JSON dict.
- `analysis_twilio_call(call_uuid, custom_fields=None)`
  Requests analysis for a Twilio call. Returns a JSON dict.
- `create_outbound_call(assistant_id, campaign_id, to, from_=None, customer_name='Unknown', customer_id='Unknown', variable_values=None)`
  Creates a campaign outbound call. Returns a JSON dict.

Custom analysis fields format

- Each item must be a dict with keys: `field`, `definition`, `outputs_options`.
- `field` and `definition` must be strings.
- `outputs_options` must be a list of strings.

Basic example

```python
response = client.calls.create(
    from_="+15551234567",
    to_="+15557654321",
    assistant_id="assistant_id_from_dashboard",
    max_duration_seconds=120
)
response.raise_for_status()
call_uuid = response.json()["call_uuid"]

analysis = client.calls.analysis(call_uuid)
analysis.raise_for_status()
print(analysis.json())
```

Outbound campaign example

```python
outbound = client.calls.create_outbound_call(
    assistant_id="assistant_id_from_dashboard",
    campaign_id="campaign_id_from_dashboard",
    to="+15557654321",
    customer_name="Ava",
    customer_id="cust_123",
    variable_values={"plan": "pro"}
)
print(outbound)
```

---

**PhoneNumberWrapper**

Retrieve owned phone numbers.

Constructor

- `PhoneNumberWrapper(api_key: str)`

Methods

- `get_owned()`
  Returns owned phone numbers as a JSON dict.

Basic example

```python
numbers = client.phone_numbers.get_owned()
print(numbers)
```

---

**CallLogsWrapper**

Retrieve call logs with filters.

Constructor

- `CallLogsWrapper(api_key: str)`

Methods

- `get_logs(assistant_id='all', limit=20, page=1, before=None, after=None, status=None, campaign_id=None, search_query=None)`
  Returns call logs as a JSON dict.

Basic example

```python
logs = client.call_logs.get_logs(assistant_id="all", limit=10, page=1)
print(logs)
```

---

**ToolsWrapper**

Create and manage tools that your assistants can call.

Constructor

- `ToolsWrapper(api_key: str)`

Methods

- `create(tool_data)`
  Creates a tool. `tool_data` must be a non-empty dict. Returns a JSON dict.
- `list(page=1, per_page=20, tool_type=None)`
  Lists tools. Returns a JSON dict.
- `get(tool_id)`
  Retrieves a tool. Returns a JSON dict.
- `update(tool_id, update_data)`
  Updates a tool. `update_data` must be a non-empty dict. Returns a JSON dict.
- `delete(tool_id)`
  Deletes a tool. Returns a JSON dict.

Basic example

```python
tool = client.tools.create({
    "name": "check-user-status",
    "description": "Check if a user exists in our database",
    "parameters": {
        "type": "object",
        "properties": {
            "email": {"type": "string", "description": "User email"}
        },
        "required": ["email"]
    },
    "tool_url": "/api/check-user",
    "tool_url_domain": "https://your-api.com",
    "async_": False
})
print(tool)
```

---

**FolderWrapper**

Organize assistants into folders.

Constructor

- `FolderWrapper(api_key: str)`

Methods

- `create(folder_name, description=None)`
  Creates a folder. Returns a JSON dict.
- `list(page=1, per_page=20)`
  Lists folders. Returns a JSON dict.
- `get(folder_id)`
  Retrieves a folder. Returns a JSON dict.
- `update(folder_id, folder_name=None, description=None)`
  Updates a folder. Returns a JSON dict.
- `delete(folder_id)`
  Deletes a folder. Returns a JSON dict.
- `assign_agent(agent_id, folder_id=None)`
  Assigns an assistant to a folder. Returns a JSON dict.
- `get_agents(folder_id, page=1, per_page=20)`
  Lists assistants in a folder. Returns a JSON dict.

Basic example

```python
folder = client.folders.create("Outbound Assistants", description="Sales and support")
folder_id = folder["folder_id"]

client.folders.assign_agent(agent_id="assistant_id_from_dashboard", folder_id=folder_id)
print(client.folders.get_agents(folder_id))
```

---

**PlutoWrapper**

Low-level call orchestration for Pluto WS calls.

Constructor

- `PlutoWrapper(api_key: str, user_id: str, assistants)`

Methods

- `register_call(call_uuid, assistant_id, Telephony)`
  Registers a call with Pluto. Returns `True` on success.
- `join_agent_advance(pluto_url, call_id, json_for_url)`
  Pre-warms an agent and returns a URL override.
- `create_call(voice_id, call_uuid=None, model=None, assistant_id=None, system_prompt=None, first_message=None, language_clue='en-US', background_audio=False, prewarm_agent=False, localhost=None, Telephony=False, custom_analysis_prompt=None, custom_summary_prompt=None)`
  Creates a Pluto WS call. Returns a dict with `streamId`, `ws_url`, and `call_id`.
- `create_call_plivo(voice_id, from_, to_, model=None, assistant_id=None, system_prompt=None, first_message=None, language_clue='en-US', background_audio=False, prewarm_agent=False, localhost=None, max_duration_seconds=120, custom_analysis_prompt=None, custom_summary_prompt=None, **kwargs)`
  Creates a Plivo telephony call plus a Pluto WS call. Returns `(plivo_call, call_create)`.
- `get_call(call_id, model=None)`
  Retrieves Pluto call logs. Returns a JSON dict.

Notes

- `assistant_id` usage expects an assistants mapping with `model` and `firstMessage` fields. If you do not have that mapping, pass `system_prompt` and `first_message` instead.

Basic example

```python
pluto_call = client.pluto.create_call(
    voice_id="90ipbRoKi4CpHXvKVtl0",
    system_prompt="You are a helpful assistant.",
    first_message="Hello! How can I help you today?"
)
print(pluto_call["ws_url"])
```

---

**ContactWrapper**

Create and list contacts with validation.

Constructor

- `ContactWrapper(api_key: str)`

Methods

- `create(first_name, last_name, email, country_code, phone_number, audience_id=None)`
  Creates a contact. Returns a JSON dict.
- `list(page=1, limit=10, search_query=None)`
  Lists contacts with pagination. Returns a JSON dict.

Validation

- Email, phone, and country code are validated before requests are sent.

Basic example

```python
contact = client.contacts.create(
    first_name="Ava",
    last_name="Patel",
    email="ava.patel@example.com",
    country_code="+1",
    phone_number="5551234567"
)
print(contact)
```

---

**AudienceWrapper**

Create, update, and manage audiences and their contacts.

Constructor

- `AudienceWrapper(api_key: str)`

Methods

- `create(audience_name, contacts, audience_description='')`
  Creates a new audience and contacts. Returns a JSON dict.
- `list()`
  Lists audiences. Returns a JSON dict.
- `get(audience_id)`
  Retrieves audience details and contacts. Returns a JSON dict.
- `get_contacts(audience_id, page=1, limit=10)`
  Lists contacts for an audience. Returns a JSON dict.
- `update(audience_id, audience_name=None, audience_description=None)`
  Updates audience metadata. Returns a JSON dict.
- `add_contacts(audience_id, contacts)`
  Adds contacts to an existing audience. Returns a JSON dict.
- `delete(audience_id)`
  Deletes an audience and its contacts. Returns a JSON dict.

Contact format

- Required: `first_name`, `country_code`, `phone_number`
- Optional: `last_name`, `email`, `variable_values`

Basic example

```python
audience = client.audience.create(
    audience_name="Demo Audience",
    contacts=[
        {"first_name": "Ava", "country_code": "+1", "phone_number": "5551234567"}
    ]
)
print(audience)
```

---

**KnowledgeBaseWrapper**

Create and manage knowledge bases with file uploads.

Constructor

- `KnowledgeBaseWrapper(api_key: str)`

Methods

- `create(name, description, files)`
  Creates a knowledge base with uploaded files. Returns a JSON dict.
- `list(page=1, limit=10)`
  Lists knowledge bases. Returns a JSON dict.
- `get(knowledge_base_id)`
  Retrieves a knowledge base. Returns a JSON dict.

Files format

- File-like objects: `open("file.pdf", "rb")`
- Tuples: `(filename, content, content_type)`

Basic example

```python
with open("faq.pdf", "rb") as f:
    kb = client.knowledge_base.create(
        name="FAQ",
        description="Frequently asked questions",
        files=[f]
    )
print(kb)
```

---

## Error Handling

Most methods validate inputs and raise `ValueError` on invalid data. API requests raise `Exception` when the server returns a non-success status. Methods that return `requests.Response` let you call `raise_for_status()` and `json()` directly.

## Support

- Documentation: [https://dev.superu.ai](https://dev.superu.ai)
- Issues: [https://github.com/superu/superu-python/issues](https://github.com/superu/superu-python/issues)
- Email: Contact support via your SuperU dashboard

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
