Metadata-Version: 2.4
Name: voicelayer
Version: 0.1.0
Summary: The VoiceLayer API client: messages, conversations, events, webhooks and channels for agents.
Author-email: VoiceLayer <customer@vlayers.ai>
License-Expression: Apache-2.0
Project-URL: Homepage, https://vlayers.ai
Project-URL: Documentation, https://docs.vlayers.ai/developers/sdks/python
Keywords: voicelayer,api,sms,whatsapp,messaging,agents,webhooks
Classifier: Programming Language :: Python :: 3
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: urllib3<3.0.0,>=2.2.0
Requires-Dist: python-dateutil>=2.8.2
Requires-Dist: pydantic>=2
Requires-Dist: typing-extensions>=4.7.1
Provides-Extra: test
Requires-Dist: pytest>=7.2.1; extra == "test"
Dynamic: license-file

# voicelayer

The VoiceLayer API client for Python: send and read messages on every channel, hand conversations between your code,
the agent and people, and receive events by webhook, stream or pull. Generated from the public OpenAPI document with
[OpenAPI Generator](https://openapi-generator.tech), plus a short `VoiceLayer` client, webhook verification and a
resuming event stream.

```bash
pip install voicelayer
```

## Send a message

```python
from voicelayer import VoiceLayer

vl = VoiceLayer()  # reads VOICELAYER_API_KEY (and VOICELAYER_API_URL)

sent = vl.messages.send(channel="sms", to="+15551234567", text="Your order shipped.")
vl.conversations.reply(str(sent.message.conversation_id), text="Thanks!")
```

Every operation of the [API reference](https://docs.vlayers.ai/reference) is a method under its resource (`messages`,
`conversations`, `events`, `webhooks`, `channels`, `test`, `logs`), named after it in snake case: `messages.list`,
`conversations.get`, `webhooks.create_subscription`. A path parameter is a positional argument; the query parameters and
the body's fields are keyword arguments. A method returns the answer's model. Pass `content=` instead of `text=` to send
media. Use `with VoiceLayer() as vl:` to close its connections when you are done.

Pass `idempotency_key="order-1042-shipped"` and a retry never sends twice.

## Handle errors

A refusal raises `VoiceLayerApiError` with the API's `code` (`output_refused`, `window_closed`, ...), `request_id`,
`retryable` and `docs`. It is an `ApiException`, so `except ApiException` still catches it.

```python
from voicelayer import VoiceLayer, VoiceLayerApiError

vl = VoiceLayer()

try:
    vl.messages.send(channel="sms", to="+15551234567", text="Your order shipped.")
except VoiceLayerApiError as err:
    if not err.retryable:
        raise
    # retry with the same idempotency_key
```

## Test mode

```python
inbound = vl.test.create_inbound(channel="sms", from_="+15550101", text="Are you open?")
# inbound.conversation_id: a test conversation; replies in it go through the loopback and cost nothing
```

A test key (`vl_test_…`) simulates a person writing from a reserved address (`+15550100`–`+15550199`, `test-<n>`,
`<anything>@test.vlayers.ai`). A workspace with no number or Binding on the channel gets a free test sender.

For agent frameworks (the Claude Agent SDK, OpenAI Agents, LangGraph) and direct mode (`serve_direct`), see
[`voicelayer-agents`](https://pypi.org/project/voicelayer-agents/).

## Verify webhooks

```python
from voicelayer.webhooks import verify_webhook, WebhookVerificationError

# the RAW request body, before any JSON parsing; the secret is VOICELAYER_WEBHOOK_SECRET
event = verify_webhook(raw_body, request.headers)
```

`verify_webhook` needs no client or API key, so a webhook receiver can import it alone. `vl.webhooks.verify` is the same
function.

## Stream events

```python
for item in vl.events.stream(types=["message.*"]):
    print(item.event["type"], item.event["data"])
    save_cursor(item.cursor)  # pass it back as after= to resume
```

The iterator reconnects after a drop with `Last-Event-ID` and dedupes by event id.

## Advanced: the generated classes

`VoiceLayer` is a thin layer over the package generated from the OpenAPI document, which stays intact underneath.
`vl.configuration` and `vl.api_client` are the generated `Configuration` and `ApiClient`, so any generated API class works
with them. Use those for the HTTP response (`send_message_with_http_info`) or the exact request model.

```python
import os
import voicelayer

config = voicelayer.Configuration(access_token=os.environ["VOICELAYER_API_KEY"])
with voicelayer.ApiClient(config) as client:
    voicelayer.MessagesApi(client).send_message(
        voicelayer.SendMessageRequest.from_dict(
            {"channel": "sms", "to": "+15551234567", "content": {"type": "text", "text": "Your order shipped."}}
        ),
        idempotency_key="order-1042-shipped",
    )
```

The generated API reference is in [docs/](docs/). Licence: Apache-2.0.
