Metadata-Version: 2.4
Name: relaybell
Version: 1.0.0
Summary: Official Python server-side SDK for RelayBell — multi-channel push notifications, events, and analytics.
Author: RelayBell
License: MIT
Project-URL: Homepage, https://relaybell.com
Project-URL: Documentation, https://relaybell.com/docs
Keywords: relaybell,push,notifications,web-push,sdk
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Description-Content-Type: text/markdown

# RelayBell Python SDK

Official server-side Python SDK for [RelayBell](https://relaybell.com) — send web-push notifications, track behavioral events, record conversions, and pull analytics.

- **Zero dependencies.** Pure standard library (`urllib`). Nothing to install beyond the package itself.
- **Python 3.8+.**
- Small, explicit surface that mirrors the RelayBell REST API 1:1.

## Installation

```bash
pip install relaybell
```

Or install from a local checkout:

```bash
pip install ./sdks/python
```

## Quick start

```python
from relaybell import RelayBell, RelayBellError

# Your secret API key looks like "rb_live_xxit".
rb = RelayBell("rb_live_xxit")

# Send a push notification.
res = rb.send(
    title="Flash sale!",
    body="50% off everything for the next hour.",
    url="https://shop.example/sale",
    icon="https://shop.example/icon.png",
)
print(res["notificationId"])

# Track a behavioral event.
rb.track("product_viewed", external_id="user-123", properties={"sku": "ABC-1"})

# Record a conversion with a value.
rb.track_conversion(49.99, external_id="user-123", order_id="ord_987")

# Pull analytics.
stats = rb.stats()
print(stats["totals"]["revenue"])

# Export subscribers as CSV.
csv_text = rb.export()
with open("subscribers.csv", "w", encoding="utf-8", newline="") as f:
    f.write(csv_text)
```

## Configuration

```python
RelayBell(api_key, base_url="https://relaybell.com", timeout=30.0)
```

| Argument   | Default                 | Description                                                             |
|------------|-------------------------|-------------------------------------------------------------------------|
| `api_key`  | *(required)*            | Secret key, e.g. `rb_live_xxit`. Sent as `Authorization: Bearer <key>`. |
| `base_url` | `https://relaybell.com` | Override for self-hosted or staging environments.                       |
| `timeout`  | `30.0`                  | Per-request socket timeout in seconds.                                  |

## Methods

### `send(notification=None, **fields) -> dict`

Send a push notification — `POST /api/v1/notifications`. Pass the payload either as keyword arguments **or** as a single dict (not both). `title` is required.

Supported fields (per the API contract): `title` (required), `body`, `url`, `icon`, `image`, `badge`, `actions`, `requireInteraction`, `tag`, `segmentId`, `filters`, `sendAt` (ISO 8601), `ttl` (seconds), `perTimezone` (bool), `localTime` (`"HH:MM"`), `ab` (`{"b": {"title", "body"}}`).

```python
rb.send(title="Hi", body="there")
rb.send({"title": "Hi", "body": "there", "url": "https://example.com"})

# Scheduled, per-timezone A/B test:
rb.send(
    title="Good morning",
    body="Your daily digest is ready.",
    perTimezone=True,
    localTime="08:00",
    ab={"b": {"title": "Rise and shine", "body": "Today's digest awaits."}},
)
```

Returns `202` → `{"ok": True, "notificationId": "...", "status": "..."}`.

### `track(event, external_id=None, properties=None) -> dict`

Track a behavioral event — `POST /api/v1/events`. `event` is required.

```python
rb.track("cart_updated", external_id="user-123", properties={"items": 3})
```

Returns `202` → `{"ok": True, "eventId": "...", "subscriberId": "...", "automationsTriggered": [...], "sequencesEnrolled": [...]}`.

### `track_conversion(value, **props) -> dict`

Convenience wrapper that emits a `"conversion"` event with `properties.value = value`. Extra keyword args are merged into the event properties. An optional `external_id` keyword associates the conversion with a subscriber (it is not stored as a property).

```python
rb.track_conversion(49.99, external_id="user-123", currency="USD", order_id="ord_987")
```

### `stats() -> dict`

Fetch analytics — `GET /api/v1/stats`.

```python
stats = rb.stats()
# {"subscribers": ..., "totals": {"sent", "clicks", "ctr", "conversions", "revenue"}, "notifications": [...]}
```

### `export() -> str`

Export subscribers as CSV — `GET /api/v1/subscribers/export`. Returns the raw CSV document as a string.

## Error handling

Every non-2xx response raises `RelayBellError`:

```python
try:
    rb.send(title="Hi")
except RelayBellError as e:
    print(e.status)  # HTTP status code (0 for transport-level failures)
    print(e.body)    # Parsed JSON error, e.g. {"error": "invalid_api_key"}
```

- `status` — the HTTP status code, or `0` when no HTTP response was received (DNS failure, connection refused, timeout).
- `body` — the parsed JSON error body (usually `{"error": "code"}`) when available, otherwise the raw response text.

## Browser SDK

This package is for **server-side** use. For the storefront, use the browser SDK instead:

```html
<script src="https://relaybell.com/relaybell-sdk.js"></script>
<script>
  RelayBell.init({ projectId: "YOUR_PROJECT_ID", apiBase: "https://relaybell.com" });
  // RelayBell.track(event, properties)
  // RelayBell.trackConversion(value, props)
  // RelayBell.unsubscribe()
  // RelayBell.isSupported()
</script>
```

## License

MIT
