{"id":50518260,"url":"https://github.com/nikoSchoinas/routeweiler-python-sdk","last_synced_at":"2026-06-19T19:01:10.520Z","repository":{"id":358463891,"uuid":"1222893298","full_name":"nikoSchoinas/routeweiler-python-sdk","owner":"nikoSchoinas","description":"The neutral micropayment router for autonomous agents. A single HTTP client that intercepts '402 Payment Required', routes across x402, L402, and MPP, persists credentials as recoverable assets, enforces per-envelope budgets, and emits a structured trace.","archived":false,"fork":false,"pushed_at":"2026-05-25T10:40:26.000Z","size":1186,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-25T12:25:18.350Z","etag":null,"topics":["agent-payments","agentic","agentic-commerce","bitcoin","commerce","ethereum","http-402","l402","lightning","micropayments","mpp","payments","stripe","stripe-spt","tempo","x402"],"latest_commit_sha":null,"homepage":"https://www.routeweiler.com/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nikoSchoinas.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-27T20:12:05.000Z","updated_at":"2026-05-25T10:36:34.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/nikoSchoinas/routeweiler-python-sdk","commit_stats":null,"previous_names":["nikoschoinas/routeweiler-python-sdk"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/nikoSchoinas/routeweiler-python-sdk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nikoSchoinas%2Frouteweiler-python-sdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nikoSchoinas%2Frouteweiler-python-sdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nikoSchoinas%2Frouteweiler-python-sdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nikoSchoinas%2Frouteweiler-python-sdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nikoSchoinas","download_url":"https://codeload.github.com/nikoSchoinas/routeweiler-python-sdk/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nikoSchoinas%2Frouteweiler-python-sdk/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34544413,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-19T02:00:06.005Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent-payments","agentic","agentic-commerce","bitcoin","commerce","ethereum","http-402","l402","lightning","micropayments","mpp","payments","stripe","stripe-spt","tempo","x402"],"created_at":"2026-06-03T02:00:18.705Z","updated_at":"2026-06-19T19:01:10.514Z","avatar_url":"https://github.com/nikoSchoinas.png","language":"Python","funding_links":[],"categories":["🔧 Server Frameworks \u0026 Middleware","Libraries"],"sub_categories":["Python"],"readme":"# routeweiler\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/nikoSchoinas/routeweiler-python-sdk/main/assets/routeweiler.png\" alt=\"Routeweiler\" width=\"720\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/nikoSchoinas/routeweiler-python-sdk/actions/workflows/ci.yml?query=branch%3Amain\"\u003e\u003cimg src=\"https://img.shields.io/github/actions/workflow/status/nikoSchoinas/routeweiler-python-sdk/ci.yml?branch=main\u0026style=for-the-badge\u0026label=build\" alt=\"Build status\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://pypi.org/project/routeweiler/\"\u003e\u003cimg src=\"https://img.shields.io/pypi/v/routeweiler?style=for-the-badge\" alt=\"PyPI version\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://pypi.org/project/routeweiler/\"\u003e\u003cimg src=\"https://img.shields.io/pypi/dm/routeweiler?style=for-the-badge\" alt=\"PyPI downloads\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://pypi.org/project/routeweiler/\"\u003e\u003cimg src=\"https://img.shields.io/pypi/pyversions/routeweiler?style=for-the-badge\u0026logo=python\u0026logoColor=white\" alt=\"Python versions\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://codecov.io/gh/nikoSchoinas/routeweiler-python-sdk\"\u003e\u003cimg src=\"https://img.shields.io/codecov/c/github/nikoSchoinas/routeweiler-python-sdk?style=for-the-badge\" alt=\"Coverage\"\u003e\u003c/a\u003e\n  \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/license-Apache_2.0-blue?style=for-the-badge\" alt=\"License: Apache-2.0\"\u003e\u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/badge/status-beta-blue?style=for-the-badge\" alt=\"Status: Beta\"\u003e\n  \u003ca href=\"https://github.com/astral-sh/ruff\"\u003e\u003cimg src=\"https://img.shields.io/badge/code%20style-ruff-261230?style=for-the-badge\" alt=\"Code style: Ruff\"\u003e\u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/badge/type%20checked-mypy-1f5082?style=for-the-badge\" alt=\"Type-checked with mypy\"\u003e\n\u003c/p\u003e\n\nThe neutral micropayment router for autonomous agents. A single async HTTP client —\n`await routeweiler.get(url)` — that transparently handles `402 Payment Required` across\nx402 (EVM), L402 (Lightning), MPP-Tempo (stablecoin), and MPP-SPT (Stripe).\n\n**Documentation:** [docs.routeweiler.com](https://docs.routeweiler.com)\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/nikoSchoinas/routeweiler-python-sdk/main/assets/diagram.png\" alt=\"Routeweiler flow diagram\" width=\"900\"\u003e\n\u003c/p\u003e\n\n## Install\n\n```bash\npip install routeweiler\n```\n\nPython 3.11+ required.\n\n## Quick start\n\n```python\nimport asyncio\nimport os\nfrom eth_account import Account\nfrom routeweiler import Routeweiler, Funding\n\nsigner = Account.from_key(os.environ[\"PRIVATE_KEY\"])\n\nasync def main():\n    async with Routeweiler(funding=[Funding.base_usdc(wallet=signer)]) as client:\n        response = await client.get(\"https://api.example.com/data\")\n        print(response.json())\n\nasyncio.run(main())\n```\n\n## Supported rails\n\n| Rail | Method | Funding source | Networks |\n|------|--------|---------------|----------|\n| [x402](https://x402.org) | EVM signed transaction | `EvmFundingSource` | Base, Base-Sepolia |\n| [L402](https://docs.lightning.engineering/the-lightning-network/l402) | BOLT-11 Lightning invoice | `LightningFundingSource` | Bitcoin, Regtest |\n| [MPP-Tempo](https://paymentauth.org) | Tempo 0x76 stablecoin tx | `TempoFundingSource` | Moderato testnet |\n| [MPP-SPT](https://docs.stripe.com/agentic-commerce) | Stripe Shared Payment Token | `StripeFundingSource` | USD, EUR, GBP |\n\n## Funding sources\n\nEach rail accepts a typed `FundingSource` holding the credentials it uses to sign or authorise payments. Key material stays in your process; Routeweiler never transmits private keys.\n\n### x402 — `EvmFundingSource`\n\nAn `eth_account.LocalAccount` that signs EIP-3009 `transferWithAuthorization` for USDC on Base.\n\n```python\nfrom eth_account import Account\nfrom routeweiler import Funding\n\nwallet = Account.from_key(os.environ[\"PRIVATE_KEY\"])  # 64-char hex secp256k1 key, 0x prefix optional\nfunding = Funding.base_usdc(wallet=wallet)            # mainnet (chain 8453)\n# funding = Funding.base_sepolia_usdc(wallet=wallet)  # testnet (chain 84532)\n```\n\n### L402 — `LightningFundingSource`\n\nWraps an LND gRPC client. Bring a running LND node and pass its admin macaroon + TLS cert; Routeweiler pays BOLT-11 invoices through it.\n\n```python\nfrom routeweiler.funding.lightning import LightningFundingSource, LndClient\n\nlnd = LndClient(\n    grpc_host=\"localhost\",\n    grpc_port=10009,\n    macaroon_path=\"/path/to/admin.macaroon\",\n    tls_cert_path=\"/path/to/tls.cert\",\n)\nfunding = await LightningFundingSource.create(lnd, network=\"bitcoin\")\n# network: \"bitcoin\" | \"bitcoin-testnet\" | \"bitcoin-regtest\" | \"bitcoin-signet\"\n```\n\n### MPP-Tempo — `TempoFundingSource`\n\nSigns Tempo's type-0x76 charge transactions with an `eth_account.LocalAccount` — same key shape as x402, different chain. The same wallet can fund both rails.\n\n```python\nfrom eth_account import Account\nfrom routeweiler import Funding\n\nwallet = Account.from_key(os.environ[\"PRIVATE_KEY\"])  # 64-char hex secp256k1 key, 0x prefix optional\nfunding = Funding.tempo_usdc(wallet=wallet)              # mainnet, USDC\n# funding = Funding.tempo_pathusd_moderato(wallet=wallet)  # testnet, pathUSD\n```\n\n### MPP-SPT — `StripeFundingSource`\n\nNo on-device signing. Stripe holds the card; you supply a secret API key, a customer id, and a saved payment method id. Routeweiler asks Stripe to mint a Shared Payment Token at pay-time.\n\n```python\nfrom routeweiler import Funding\n\nfunding = Funding.stripe(\n    api_key=os.environ[\"STRIPE_API_KEY\"],   # sk_live_... / sk_test_...\n    customer=\"cus_ABC123\",                  # buyer's Stripe customer id\n    payment_method=\"pm_XYZ789\",             # saved card / bank\n    currency=\"usd\",                         # ISO-4217: usd | eur | gbp | ...\n)\n```\n\nPass any combination to `Routeweiler(funding=[...])`. The router picks the best rail per challenge based on [Policy](#policy) and what the server accepts.\n\n## SQLite trace recorder\n\nEnable local tracing with `TraceSink.sqlite`. Every call (paid or free) produces\nexactly one `TraceEvent` row, including the on-chain tx hash and the payment outcome:\n\n```python\nfrom routeweiler import Routeweiler, Funding, TraceSink\n\nasync with Routeweiler(\n    funding=[Funding.base_usdc(wallet=signer)],\n    trace_sink=TraceSink.sqlite(\"./routeweiler.db\"),\n) as client:\n    response = await client.get(\"https://api.example.com/data\")\n\n# Inspect with the sqlite3 CLI:\n# sqlite3 ./routeweiler.db \\\n#   'SELECT request_id, selected_rail, http_status FROM trace_events;'\n```\n\n## Budget envelopes\n\nEnforce per-session or per-agent spend caps with local SQLite budget envelopes.\nWithout a `budget_envelope`, tracing still works but **no cap is enforced**.\n\n```python\nfrom routeweiler import BudgetEnvelope, Funding, Routeweiler, TraceSink\n\nasync with Routeweiler(\n    funding=[Funding.base_usdc(wallet=signer)],\n    trace_sink=TraceSink.sqlite(\"routeweiler.db\"),\n    budget_envelope=BudgetEnvelope(\n        id=\"session-abc\",\n        cap_minor_units=500,           # 5.00 USD (in cents)\n        cap_currency=\"usd\",\n        allowed_rails=[\"x402\", \"l402\"],\n        ttl_seconds=3_600,             # 1 hour\n    ),\n) as client:\n    response = await client.get(\"https://api.example.com/data\")\n```\n\n`budget_envelope` accepts three forms:\n\n- **`None`** (default) — no cap enforcement.\n- **`str`** — ID of a pre-existing envelope; raises `EnvelopeNotFoundError` at\n  construction time if the row is missing.\n- **`BudgetEnvelope`** — declarative spec; If an envelope with the same `id` already exists it is reused unchanged.\n\nEnvelopes track reserved and settled amounts with Ed25519-signed draw receipts.\n`BudgetExceededError` is raised if a payment would breach the cap.\n\n## Policy\n\nControl which rails are used, set per-call spend limits, or deny specific URLs:\n\n```python\nfrom routeweiler import Policy, PolicyRule, RuleMatch, Routeweiler\n\nasync with Routeweiler(\n    funding=[...],\n    policy=Policy(\n        currency=\"usd\",          # reference currency for max_per_call_minor_units\n        rules=[\n            PolicyRule(\n                name=\"deny analytics\",\n                when=RuleMatch(url_matches=\"*.tracking.io\"),\n                deny=True,\n            ),\n            PolicyRule(\n                name=\"cap per call\",\n                when=RuleMatch(url_matches=\"*\"),\n                max_per_call_minor_units=500,  # 5 USD cents\n            ),\n        ]\n    ),\n) as client:\n    ...\n```\n\n`max_per_call_minor_units` requires a reference currency to compare rail-native quotes\nagainst. Set `Policy(currency=\"usd\")` (or any supported currency) when no\n`budget_envelope` is configured. The envelope's `cap_currency` takes precedence when\nboth are present. If neither is provided and a rule uses `max_per_call_minor_units`,\n`Routeweiler` raises `ValueError` at construction time.\n\n## Releases\n\nReleases follow [SemVer](https://semver.org). Pre-1.0 minors (`0.1.0 → 0.2.0`) may include breaking changes.\n\n| Tag format | Channel | Install |\n|---|---|---|\n| `v0.2.0` | Stable | `pip install routeweiler` |\n| `v0.2.0b1` | Beta | `pip install --pre routeweiler` |\n\nA release is a git tag, not a merge. Merges to `main` run CI but don't publish. Pushing `python/vX.Y.Z` (or `python/vX.Y.ZbN`) to the monorepo triggers the release workflow, which builds the wheel + sdist, mirrors the tag as `vX.Y.Z` to this repo, attaches artefacts to the GitHub Release here, and — once the package is public — publishes to PyPI.\n\n## License\n\nApache 2.0 — see [LICENSE](../../LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FnikoSchoinas%2Frouteweiler-python-sdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FnikoSchoinas%2Frouteweiler-python-sdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FnikoSchoinas%2Frouteweiler-python-sdk/lists"}