Metadata-Version: 2.4
Name: carrier-python
Version: 0.1.15
Summary: Python client for the Carrier API, MCP server, and CLI.
Author: Lifecycle Innovations Limited
License: MIT
Project-URL: Homepage, https://carrier.llc/sdks/
Project-URL: Documentation, https://carrier.llc/docs/sdks/
Keywords: carrier,esim,connectivity
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.11
Description-Content-Type: text/markdown

# carrier-python

Python client for the Carrier API, the hosted MCP server, and the `carrier` CLI. `intents.create` posts to `/v1/connectivity/intents`. `call` uses an OpenAPI operation id. `Mcp` speaks Streamable HTTP. `Cli` runs the binary with an argument list. `rest_tools`, `mcp_tools`, and `cli_tools` return plain dicts for LangGraph or an OpenAI-style tool loop. The price on a product is catalogue data. Neither an intent nor a product debits a wallet.

```bash
pip install carrier-python
```

```python
import os
from carrier import Carrier

carrier = Carrier(os.environ["CARRIER_API_KEY"])
intent = carrier.intents.create(
    plan="test_global_1gb",
    metadata={"order": "123"},
    idempotency_key="order_123",
)
```

`test_global_1gb` is a practice plan. The call succeeds, `livemode` is false, and the network is not used. The same idempotency key and body return the original intent.

A waiting intent takes a plan later:

```python
carrier.intents.confirm(intent.id, plan="test_global_1gb")
```

Webhook deliveries use `Carrier-Signature: t=<unix>,v1=<hex>` over `{timestamp}.{body}`. The endpoint secret (`whsec_…`) is returned once, on create.

```python
from carrier import verify_webhook_signature

ok = verify_webhook_signature(raw_body, header, secret)
```

`CarrierError.status`, `.code`, and `.type` match the API error envelope. A blank API key raises before a request is sent.

## Issuance safety (0.1.11)

For `intents.create` and `call("issue_esim_from_inventory", ...)`, supply a stable business key. Issuance keys must match `[A-Za-z0-9][A-Za-z0-9_.:-]{0,199}`. They are never trimmed, coerced, or replaced. Invalid keys fail before transport; nonissuance operations retain their existing key rules.

Transport loss, response-read failure, or response-decode failure after an issuance call raises `CarrierError` with `code == "outcome_unknown"`, the unchanged `.idempotency_key`, and `.retryable is False`. The SDK does not retry dispatch or mint a new key. Reconcile the original operation using that key; do not automatically reissue. A complete valid API error keeps its server status, code, message, and type. This is not a provider exactly-once guarantee. Custom transports must not retry issuance internally.

Every public operation:

```python
accounts = carrier.call("list_reseller_accounts")
issued = carrier.call(
    "create_connectivity_intent",
    body={"plan": "test_global_1gb"},
    idempotency_key="order_123",
)
```

`security` on each catalog entry is `bearer`, `public`, `internal`, or `storefront`. A customer API key is `bearer`. The two internal routes do not accept that key.

```python
from carrier import Mcp, Cli, rest_tools

session = Mcp(os.environ["ESIMVAULT_API_TOKEN"])  # ?TOKEN= on https://mcp.carrier.llc/mcp
tools = session.list_tools()
session.call_tool("list_subscribers", {"limit": 5})

cli = Cli()
cli.run(["subscribers", "list", "--limit", "5"])
agent_tools = rest_tools(carrier)
```

`Mcp(..., auth="bearer")` sends an OAuth access token or a Clerk organization API key. `Cli` never invokes a shell. Pass `strict=False` only when you intentionally run a command that is not in the catalog.

Python 3.11 or newer. The package uses the standard library only. `pip install carrier-python` installs it from PyPI.
