Metadata-Version: 2.4
Name: naulix
Version: 0.1.0
Summary: Official Python SDK for the NAULIX API — XRPL-native staged-escrow trade settlement.
Author: NAULIX
License: Apache-2.0
Project-URL: Homepage, https://naulix.io
Project-URL: Documentation, https://docs.naulix.io
Project-URL: Repository, https://github.com/DSR-WET/naulix
Project-URL: Issues, https://github.com/DSR-WET/naulix/issues
Keywords: naulix,xrpl,trade-finance,escrow,ripple
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
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: Topic :: Software Development :: Libraries
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: httpx<1.0,>=0.25

# naulix — official Python SDK

```bash
pip install naulix
```

```python
from naulix import NaulixClient

naulix = NaulixClient(api_key="sk_test_...")

voyage = naulix.voyages.create(
    origin_port_locode      = "IEDUB",
    destination_port_locode = "DEHAM",
    mmsi                    = "636019825",
    amount_rlusd            = 10_000,
    seller_xrpl_address     = "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
    buyer_xrpl_address      = "rUgw8e2x8wMFeB8N3W2mJj6935wB5wimsm",
)

print(voyage["frid"], voyage["stage_1_create_tx_hash"])
```

## Features

- **One client, both environments.** The key prefix decides — a
  `sk_test_…` key mints testnet escrows, a `sk_live_…` key mints
  mainnet. No separate base URL to remember.
- **Idempotency by default.** Every write call gets an
  `Idempotency-Key` header so SDK-level retries can't double-charge.
  Pass `idempotency_key=` explicitly when you want a customer-level
  identity (e.g. one key per logical business operation, persisted in
  your DB).
- **Retry on 5xx and transport errors.** Three retries with
  exponential backoff (0.5s, 1.5s, 4s). 4xx never retries — `code` on
  the raised `NaulixError` tells you why, so you can fix the request
  and re-issue.
- **Structured errors.** Every API failure raises `NaulixError` with
  `.code`, `.type`, `.message`, `.status_code`, `.param`, `.doc_url`,
  `.context`. Branch on `.code` (stable), never on `.message` (human
  text, may change).
- **Webhook signature verifier.** Standalone helper — use it in your
  webhook handler regardless of whether you use the SDK for outbound
  calls.

## Error handling

```python
from naulix import NaulixClient, NaulixError

naulix = NaulixClient(api_key="sk_test_...")

try:
    naulix.voyages.create(
        origin_port_locode      = "XXAAA",   # not a real LOCODE
        destination_port_locode = "DEHAM",
        mmsi                    = "636019825",
        amount_rlusd            = 10_000,
        seller_xrpl_address     = "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
        buyer_xrpl_address      = "rUgw8e2x8wMFeB8N3W2mJj6935wB5wimsm",
    )
except NaulixError as e:
    if e.code == "voyage_invalid_port_locode":
        # Don't retry — fix the LOCODE and try again
        print(f"Invalid {e.param}: {e.message}")
        print(f"See: {e.doc_url}")
    elif e.code == "escrow_kyb_required":
        # Send the user to KYB
        ...
    else:
        raise
```

See the [error-codes catalogue](https://docs.naulix.io/errors) for
every code the API can emit.

## Verifying webhooks

```python
from naulix import verify_webhook_signature

@app.post("/webhooks/naulix")
def handle():
    raw = request.get_data(as_text=True)
    sig = request.headers.get("Naulix-Signature", "")
    if not verify_webhook_signature(SECRET, signature_header=sig, body=raw):
        abort(401)

    event = json.loads(raw)
    if seen_recently(event["id"]):     # dedupe on your side
        return ("", 200)
    process(event)
    return ("", 200)
```

The verifier is intentionally a free function — you can use it without
ever constructing a `NaulixClient`. It uses constant-time compare to
defeat timing attacks and rejects timestamps older than 5 minutes by
default (configurable via `tolerance_s=`).

## Sub-clients

| Sub-client                  | Operations                                          |
| --------------------------- | --------------------------------------------------- |
| `naulix.voyages`            | `create`, `list`, `retrieve(id)`, `events(id)`      |
| `naulix.api_keys`           | `list`, `create`, `rotate(id)`, `revoke(id)`        |
| `naulix.webhook_endpoints`  | `create`, `list`, `retrieve(id)`, `disable(id)`, `rotate_secret(id)`, `deliveries(id)`, `test(id)` |

## Testing against a local NAULIX

```python
client = NaulixClient(
    api_key="sk_test_local",
    base_url="http://localhost:8000",
)
```

Or pass a `httpx.Client` (or any duck-typed equivalent) via
`http_client=` to dispatch through a FastAPI `TestClient`. The
in-repo SDK tests use this pattern — see
`Backend/tests/test_xrpl_python_sdk.py`.

## Versioning

This SDK targets the NAULIX `/v1` API surface. Breaking changes to
the API ship under `/v2` and require a new major SDK version. We
never retroactively change the response shape of an endpoint or the
meaning of an error code on a stable surface.

## License

Apache-2.0.
