Metadata-Version: 2.1
Name: messageblue
Version: 0.0.7
Summary: 
Requires-Python: >=3.10,<4.0
Classifier: Intended Audience :: Developers
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: OS Independent
Classifier: Operating System :: POSIX
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: 3.15
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Provides-Extra: aiohttp
Requires-Dist: aiohttp (>=3.14.1,<4) ; (python_version >= "3.10") and (extra == "aiohttp")
Requires-Dist: httpx (>=0.21.2)
Requires-Dist: httpx-aiohttp (>=0.1.8,<0.2.0) ; (python_version >= "3.10") and (extra == "aiohttp")
Requires-Dist: pydantic (>=1.9.2)
Requires-Dist: pydantic-core (>=2.18.2,<3.0.0)
Requires-Dist: typing_extensions (>=4.0.0)
Project-URL: Repository, https://github.com/MessageBlue/messageblue-python
Description-Content-Type: text/markdown

# MessageBlue Python SDK

The official Python SDK for the [MessageBlue](https://messageblue.ai) iMessage
Gateway. Generated with [Fern](https://buildwithfern.com) from the gateway's OpenAPI
spec, plus a hand-maintained HMAC request signer.

```bash
pip install messageblue
```

## Quickstart

```python
from messageblue import MessageBlue

client = MessageBlue(
    app_id="app_...",
    app_secret="sk_...",   # a signing key — never sent over the network
)

client.messages.send_individual(to="+16475137145", text="Hello from Python!")
```

### Async

```python
import asyncio
from messageblue import AsyncMessageBlue

client = AsyncMessageBlue(app_id="app_...", app_secret="sk_...")

async def main():
    await client.messages.send_individual(to="+16475137145", text="Hello!")

asyncio.run(main())
```

## Authentication

Every request is HMAC-signed. Your **App Secret is a signing key and is never
transmitted** — the SDK signs each request with it and sends only the signature. Each
request carries `X-App-Id`, `X-Timestamp`, `X-Nonce`, and `Authorization: HMAC <sig>`.
This is handled automatically by the client; you never build these headers yourself.

## Webhook verification

The gateway signs every outbound webhook. Verify against the **raw** request body
(re-serializing parsed JSON changes the bytes and breaks the signature):

```python
import os
from messageblue.webhook_verification import verify_webhook_signature, WebhookVerificationError

# Example: Flask
@app.post("/hooks/inbound")
def inbound():
    try:
        event = verify_webhook_signature(
            payload=request.get_data(),               # raw bytes
            headers=request.headers,
            secret=os.environ["MB_WEBHOOK_SECRET"],   # whsec_... from POST /api/app/webhooks/secret
        )
    except WebhookVerificationError:
        return "", 400
    # event is the parsed payload (inbound_message | inbound_reaction | status)
    return "", 200
```

## Custom HTTP configuration

Pass your own `httpx` client (its `auth` is set to the signer automatically):

```python
import httpx
from messageblue import MessageBlue

client = MessageBlue(
    app_id="app_...",
    app_secret="sk_...",
    httpx_client=httpx.Client(timeout=30.0),
)
```

## Development

The HMAC signer, signed client, and webhook verifier are hand-maintained (protected
by `.fernignore`); everything else is generated by Fern. Run the signer parity tests:

```bash
pip install -e . pytest
pytest tests/test_signing.py
```

