Skip to content

API Reference

All public symbols exported from routeweiler.__all__.


Client

routeweiler.client.Routeweiler

Async HTTP client that transparently handles 402 Payment Required.

Mirrors the httpx.AsyncClient method surface (get/post/put/delete/ patch/head/options/request). Use as an async context manager to ensure the underlying connection pool is closed cleanly:

async with Routeweiler(funding=[Funding.base_usdc(wallet=signer)]) as c:
    resp = await c.get("https://api.vendor.com/data")

Parameters:

Name Type Description Default
funding list[FundingSource]

One or more funding sources (e.g. Funding.base_usdc(wallet=...)).

required
policy Policy | None

Optional Policy instance. When omitted, the built-in default is used (prefer x402, no rules).

None
budget_envelope str | BudgetEnvelope | None

Controls which spending envelope the client draws from. Three forms are accepted:

  • None (default) — no budget enforcement. Payments are made without any cap; trace events are still written when trace_sink is set, but envelope_id will be None.
  • str — ID of a pre-existing envelope. The envelope must already be present in the database; EnvelopeNotFoundError is raised at construction time if it is missing.
  • BudgetEnvelope — declarative spec. The envelope is created idempotently inside __aenter__ (i.e. the first async with Routeweiler(...) as client: call). If an envelope with the same id already exists it is reused unchanged.

Budget enforcement requires a trace_sink; if trace_sink is None no enforcement runs regardless of this argument.

None
trace_sink SqliteTraceSink | None

SQLite trace sink. Pass TraceSink.sqlite(path) to enable local tracing. Defaults to None (no tracing).

None
agent_id str | None

Optional identifier for the calling agent. Written into TraceEvent.agent_id and used as part of the sticky routing key.

None
session_id str | None

Optional session identifier. Used as part of the sticky routing key so multiple logical sessions sharing one client get independent sticky state.

None

envelopes property

envelopes: _EnvelopesNamespace

Namespace for creating and managing spending envelopes.

Requires trace_sink to be configured; methods raise RuntimeError when called without one.

get async

get(url: str | URL, **kwargs: Any) -> httpx.Response

Send a GET request, transparently handling any 402 Payment Required.

post async

post(url: str | URL, **kwargs: Any) -> httpx.Response

Send a POST request, transparently handling any 402 Payment Required.

put async

put(url: str | URL, **kwargs: Any) -> httpx.Response

Send a PUT request, transparently handling any 402 Payment Required.

delete async

delete(url: str | URL, **kwargs: Any) -> httpx.Response

Send a DELETE request, transparently handling any 402 Payment Required.

patch async

patch(url: str | URL, **kwargs: Any) -> httpx.Response

Send a PATCH request, transparently handling any 402 Payment Required.

head async

head(url: str | URL, **kwargs: Any) -> httpx.Response

Send a HEAD request, transparently handling any 402 Payment Required.

options async

options(url: str | URL, **kwargs: Any) -> httpx.Response

Send an OPTIONS request, transparently handling any 402 Payment Required.

request async

request(
    method: str, url: str | URL, **kwargs: Any
) -> httpx.Response

Send an arbitrary HTTP request, transparently handling any 402 Payment Required.

aclose async

aclose() -> None

Close the connection pool, budget store, credential store, and trace sink.

start async

start() -> None

Start background tasks (reaper). Called automatically by aenter.


Funding

routeweiler.funding.Funding

Namespace of factory helpers for funding sources passed to Routeweiler(funding=[...]).

Each static method returns a concrete funding source. This class is not meant to be instantiated; use the static methods directly (Funding.base_usdc(...), Funding.lightning_lnd(...), etc.).

base_usdc staticmethod

base_usdc(*, wallet: LocalAccount) -> EvmFundingSource

USDC on Base mainnet (chain ID 8453).

base_sepolia_usdc staticmethod

base_sepolia_usdc(
    *, wallet: LocalAccount
) -> EvmFundingSource

USDC on Base Sepolia testnet (chain ID 84532).

lightning_lnd staticmethod

lightning_lnd(
    *,
    client: LightningNodeClient,
    network: Literal[
        "bitcoin",
        "bitcoin-testnet",
        "bitcoin-regtest",
        "bitcoin-signet",
    ],
    node_pubkey: str,
    max_fee_msat: int = 1000,
) -> LightningFundingSource

Lightning on the specified network.

Use LightningFundingSource.create(client, network) when you want the node pubkey populated automatically via an async getinfo call. This synchronous factory requires it to be passed explicitly.

Parameters:

Name Type Description Default
client LightningNodeClient

A LightningNodeClient-conforming object (e.g. LndClient).

required
network Literal['bitcoin', 'bitcoin-testnet', 'bitcoin-regtest', 'bitcoin-signet']

The Bitcoin network the node operates on.

required
node_pubkey str

Hex-encoded 33-byte compressed pubkey of the node.

required
max_fee_msat int

Per-payment fee cap (default 1000 msat).

1000

tempo_pathusd_moderato staticmethod

tempo_pathusd_moderato(
    *, wallet: LocalAccount
) -> TempoFundingSource

PathUSD on Tempo Moderato testnet (chain ID 42431).

PathUSD is the primary faucet-funded stablecoin on Moderato. Use Funding.tempo_usdc() for Tempo mainnet USDC.

stripe staticmethod

stripe(
    *,
    api_key: str,
    customer: str,
    payment_method: str,
    currency: str = "usd",
    spt_creator: SptCreator | None = None,
) -> StripeFundingSource

Stripe fiat / card funding source for MPP-SPT payments.

Parameters:

Name Type Description Default
api_key str

Buyer's Stripe secret key (sk_live_... or sk_test_...).

required
customer str

Buyer's Stripe customer id (cus_<id>).

required
payment_method str

Buyer's saved Stripe payment method id (pm_<id>).

required
currency str

ISO-4217 lowercase currency this source covers (default "usd").

'usd'
spt_creator SptCreator | None

Optional injected SPT creator; defaults to StripeSptCreator(api_key). Pass a FakeSptCreator in tests to avoid hitting Stripe.

None

tempo_usdc staticmethod

tempo_usdc(*, wallet: LocalAccount) -> TempoFundingSource

USDC on Tempo mainnet (chain ID 42430).

routeweiler.funding.EvmFundingSource dataclass

An EVM wallet plus the (network, asset) pair it can pay on.

Use the Funding factory methods rather than constructing directly::

from routeweiler import Funding
source = Funding.base_usdc(wallet=signer)          # Base mainnet USDC
source = Funding.base_sepolia_usdc(wallet=signer)  # Base Sepolia testnet

Attributes:

Name Type Description
wallet LocalAccount

An eth_account.LocalAccount (from Account.from_key(...)). Signs EIP-3009 transferWithAuthorization messages in-process.

network str

x402 network identifier (e.g. "base", "base-sepolia"). Must match one of the network values in the server's accepts array.

asset str

Canonical token name ("usdc", "eurc") or lowercase ERC-20 address. The x402 adapter resolves canonical names to on-chain addresses.

routeweiler.funding.LightningFundingSource dataclass

A Lightning node client plus the network it operates on.

client must satisfy the LightningNodeClient protocol. Pass an LndClient for real usage, or a FakeLndClient in tests.

network must match the BOLT-11 HRP prefix of invoices this source can pay: "bitcoin" → lnbc... "bitcoin-testnet" → lntb... "bitcoin-regtest" → lnbcrt... "bitcoin-signet" → lntbs...

node_pubkey is populated at construction time (via get_node_pubkey) and used in trace events for audit. Pass it explicitly to avoid an extra async round-trip when constructing in sync context, or use the create() factory which awaits it automatically.

max_fee_msat is the per-payment fee cap passed to the node. Can be overridden per-call in LightningFundingSource.pay_invoice().

create async classmethod

create(
    client: LightningNodeClient,
    network: Literal[
        "bitcoin",
        "bitcoin-testnet",
        "bitcoin-regtest",
        "bitcoin-signet",
    ],
    *,
    max_fee_msat: int = 1000,
) -> LightningFundingSource

Async factory that populates node_pubkey from the client.

pay_invoice async

pay_invoice(
    bolt11: str, *, max_fee_msat: int | None = None
) -> bytes

Delegate to the underlying client, applying the per-source fee cap.

routeweiler.funding.TempoFundingSource dataclass

A Tempo signer plus the network and asset it operates on.

signer must satisfy the TempoSigner protocol. Pass an EthAccountTempoSigner for real usage, or a FakeTempoSigner in tests.

network must match the Tempo network identifier: "tempo-moderato" → chain ID 42431 (testnet) "tempo" → chain ID 42430 (mainnet, reserved)

asset is the canonical short name of the TIP-20 token: "pathusd" → Moderato testnet (faucet-funded) "usdc" → Tempo mainnet

rpc_url is the JSON-RPC endpoint used to fetch the on-chain nonce before signing. Leave empty to skip RPC nonce fetching (offline / test mode). The Funding factory methods set this to the canonical endpoint for each network.

routeweiler.funding.StripeFundingSource dataclass

A Stripe buyer profile used for MPP-SPT fiat payments.

Use Funding.stripe(...) rather than constructing directly.

spt_creator defaults to a StripeSptCreator built from api_key. Pass a FakeSptCreator (or any SptCreator-conforming object) to override in tests without monkey-patching.

Attributes:

Name Type Description
api_key str

Buyer's Stripe secret key (sk_live_... or sk_test_...). Excluded from repr to avoid accidental secret leakage.

customer str

Buyer's Stripe customer id (cus_<id>).

payment_method str

Buyer's saved Stripe payment method id (pm_<id>).

currency str

ISO-4217 lowercase currency this source covers ("usd", etc.).

spt_creator SptCreator

Injected SPT creator; defaults to StripeSptCreator(api_key).


Policy

routeweiler.policy.dsl.Policy

Bases: RouteweilerModel

Routing policy for a Routeweiler client.

Pass an instance as policy to Routeweiler(...)::

from routeweiler import Policy, PolicyRule, RuleMatch, Routeweiler

policy = Policy(
    default_rail="x402",
    currency="usd",
    rules=[
        PolicyRule(
            name="cap-per-call",
            when=RuleMatch(url_matches="*"),
            max_per_call_minor_units=500,
        ),
    ],
)

async with Routeweiler(funding=[...], policy=policy) as client:
    ...

Rules are evaluated first-match-wins, top to bottom. policy_hash is a stable SHA-256 fingerprint used in trace events.

currency declares the reference currency for max_per_call_minor_units rules when no budget_envelope is configured on the client. When a budget_envelope is set, its cap_currency takes precedence. If any rule declares max_per_call_minor_units and neither currency nor a budget_envelope is provided, Routeweiler raises ValueError at construction time.

policy_hash cached property

policy_hash: str

SHA-256 fingerprint of this policy. Format: 'sha256:<64hex>'.

routeweiler.policy.dsl.PolicyRule

Bases: RouteweilerModel

A single entry in a Policy.rules list (first-match wins).

When the when predicate matches a challenge, the rule's action fields (deny, prefer, max_per_call_minor_units) are applied. Only the first matching rule takes effect — later rules are not evaluated::

PolicyRule(
    name="cap-per-call",
    when=RuleMatch(url_matches="*"),
    max_per_call_minor_units=500,  # 5.00 USD in cents
)

Attributes:

Name Type Description
name str

Human-readable label; appears in trace events and error messages.

when RuleMatch

Condition that activates this rule.

prefer list[Rail] | None

Rails to prefer (score-boosted) when this rule fires.

deny bool

If True, raise PolicyDeniedError without paying.

max_per_call_minor_units int | None

Reject challenges whose amount exceeds this limit (in the reference currency's minor units).

reason str | None

Optional human-readable reason included in PolicyDeniedError.

routeweiler.policy.dsl.RuleMatch

Bases: RouteweilerModel

Condition predicate for a PolicyRule.

All non-None fields are combined with AND. Use any for an OR of sub-conditions::

# Match any x402 request on the "base" network:
RuleMatch(network="base")

# Match requests to *.example.com that use the "exact" scheme:
RuleMatch(url_matches="*.example.com", scheme="exact")

# OR: either of the above:
RuleMatch(any=[RuleMatch(network="base"), RuleMatch(url_matches="*.example.com")])

At least one condition must be set; ValueError is raised otherwise.

routeweiler.policy.engine.PolicyDecision dataclass

Outcome of evaluating a NormalizedChallenge against a Policy.

Attributes:

Name Type Description
rule_name str | None

Name of the rule that fired, or None when no rule matched and the policy default applies.

deny bool

Whether the challenge should be rejected outright.

prefer tuple[Rail, ...]

Rails to score-boost in the routing engine (empty tuple when the rule did not specify prefer).

max_per_call_minor_units int | None

Per-call spend cap from the matching rule, or None.

reason str | None

Optional human-readable reason for a denial.

routeweiler.policy.engine.PolicyEngine

Evaluates a NormalizedChallenge against a Policy (first-match wins).

Constructed automatically by Routeweiler; exposed in __all__ for callers that build a custom orchestration layer on top of the rail adapters.

default_rail property

default_rail: Rail

The default_rail from the policy — used as a routing tie-breaker.

evaluate

evaluate(challenge: NormalizedChallenge) -> PolicyDecision

Evaluate challenge against the policy and return a decision.

Iterates policy.rules in order and returns the PolicyDecision for the first rule whose when predicate matches. Falls back to a no-op default decision when no rule matches.

Parameters:

Name Type Description Default
challenge NormalizedChallenge

The parsed challenge to evaluate.

required

Returns:

Type Description
PolicyDecision

A PolicyDecision describing the matched rule's actions, or a default

PolicyDecision

decision with deny=False and empty prefer when no rule fires.


Budgets

routeweiler.budgets.schema.BudgetEnvelope

Bases: RouteweilerModel

Declarative spec for creating a spending envelope inside Routeweiler.__aenter__.

Pass an instance as budget_envelope to Routeweiler(...) to have the envelope created idempotently when the client enters its context — no separate client.envelopes.create(...) call or two-step construction required::

async with Routeweiler(
    funding=[Funding.base_usdc(wallet=signer)],
    trace_sink=TraceSink.sqlite("rw.db"),
    budget_envelope=BudgetEnvelope(
        id="session-abc",
        cap_minor_units=500,
        cap_currency="usd",
        allowed_rails=["x402", "l402"],
        ttl_seconds=3_600,
    ),
) as client:
    ...

If an envelope with the same id already exists in the database the spec is silently ignored and the existing envelope is used.

routeweiler.budgets.schema.BudgetEnvelopeRecord

Bases: RouteweilerModel

Persisted spending envelope — the DB-row shape returned by BudgetStore.

Caps are in minor units of cap_currency (USD cents, EUR cents, JPY whole yen, GBP pence). Budget enforcement always runs locally via SQLite at MVP.

routeweiler.budgets.schema.DrawReceipt

Bases: RouteweilerModel

Ed25519-signed token authorizing a single budget draw.

The signature covers all other fields. Issuance and verification logic live in budgets/receipts.py; this model is the wire/storage shape.

routeweiler.budgets.local.BudgetStore

Single-process SQLite budget counter.

Uses BEGIN IMMEDIATE transactions for atomic cap-check + insert. Budget enforcement is always local (no hosted counter at MVP). Initialises the envelopes and draws tables on construction (idempotent).

The reaper task is started by calling await store.start() once the event loop is running (e.g. from Routeweiler.__aenter__).

start async

start() -> None

Start background tasks. Idempotent.

get_envelope_currency_sync

get_envelope_currency_sync(
    envelope_id: str,
) -> EnvelopeCurrency | None

Return the cap_currency for an envelope, or None if not found.

Synchronous — safe to call from the Routeweiler constructor and start().

get_envelope_allowed_rails_sync

get_envelope_allowed_rails_sync(
    envelope_id: str,
) -> list[Rail]

Return the allowed_rails list for an envelope (empty list if not found).

Synchronous — safe to call from the Routeweiler constructor and start().

load_fmv_snapshot_sync

load_fmv_snapshot_sync(
    envelope_id: str,
) -> dict[str, Decimal] | None

Return the stored FMV snapshot rates for an envelope, or None if absent.

load_fmv_snapshot async

load_fmv_snapshot(
    envelope_id: str,
) -> dict[str, Decimal] | None

Async wrapper for load_fmv_snapshot_sync.

create_envelope async

create_envelope(
    envelope_id: str,
    *,
    cap_minor_units: int,
    cap_currency: EnvelopeCurrency,
    allowed_rails: list[Rail],
    allowed_origins_glob: list[str] | None = None,
    ttl_seconds: int,
    owner_agent_id: str | None = None,
) -> None

Insert a new envelope row and create the Ed25519 keypair.

Raises sqlite3.IntegrityError on duplicate id.

create_envelope_if_absent async

create_envelope_if_absent(spec: BudgetEnvelope) -> bool

Create an envelope from spec only when no row with that id exists.

Returns True when a new envelope was inserted, False when an existing row was found (which is left untouched). Delegates to :meth:create_envelope so keystore creation, FMV snapshot fetch, and the DB insert all follow the same code path.

draw async

draw(
    *,
    envelope_id: str,
    request_id: str,
    idempotency_key: str,
    amount_reserved_minor_units: int,
    rail_quoted: Rail,
    ttl_seconds: int = _DEFAULT_DRAW_TTL_SECONDS,
) -> DrawReceipt

Atomically reserve capacity from the envelope and return a signed receipt.

BEGIN IMMEDIATE → cap check → idempotency → INSERT → COMMIT. Raises BudgetExceededError, EnvelopeNotFoundError, EnvelopeFrozenError, or EnvelopeExpiredError on rejection.

confirm async

confirm(
    draw_id: str, amount_settled_minor_units: int
) -> None

Transition a reserved draw to settled with the actual settled amount.

rollback async

rollback(draw_id: str) -> None

Transition a reserved draw to rolled_back, freeing its reserved capacity.


Trace

routeweiler.trace.schema.TraceEvent

Bases: RouteweilerModel

One structured audit record per Routeweiler HTTP call.

Emitted by TraceEmitter and persisted by SqliteTraceSink to the trace_events table. Every call — paid, free, or failed — produces exactly one TraceEvent.

payment is None when the call did not reach the payment step (e.g. the request succeeded without a 402, or the budget draw was rejected before payment was attempted).

routeweiler.trace.sink_sqlite.TraceSink

Factory namespace for trace sink backends.

Use TraceSink.sqlite(path) to enable local tracing::

async with Routeweiler(
    funding=[...],
    trace_sink=TraceSink.sqlite("./routeweiler.db"),
) as client:
    ...

Passing trace_sink=None (the default) disables tracing and budget enforcement entirely.

sqlite classmethod

sqlite(
    path: str | Path = "./routeweiler-traces.db",
    *,
    url_mode: UrlEncoding = "raw",
) -> SqliteTraceSink

Create a local SQLite-backed sink.

Parameters:

Name Type Description Default
path str | Path

File path for the SQLite database (created if absent).

'./routeweiler-traces.db'
url_mode UrlEncoding

Controls URL storage. "raw" stores full URLs (local default). "drop" strips query strings (recommended when URLs carry PII or secrets in query params).

'raw'

routeweiler.trace.sink_sqlite.SqliteTraceSink

Append-only SQLite writer for TraceEvent records.

One sqlite3 connection per sink (WAL mode, check_same_thread=False). All blocking I/O is offloaded to a thread via asyncio.to_thread so the event loop is never blocked.

db_path property

db_path: Path

Filesystem path of the SQLite database file.

url_mode property

url_mode: UrlEncoding

URL storage mode: "raw" stores full URLs, "drop" strips query strings.

start async

start() -> None

No-op lifecycle hook — reserved for future background tasks.

emit async

emit(event: TraceEvent) -> None

Persist one TraceEvent row. Silently ignores duplicate request_ids.

aclose async

aclose() -> None

Close the underlying SQLite connection. Called automatically by Routeweiler.


Rails

routeweiler.rails.base.RailAdapter

Bases: Protocol

Protocol every rail adapter implements.

The adapter lifecycle per payment
  1. can_handle — detect a 402 as belonging to this rail.
  2. parse — decode the challenge into NormalizedChallenge.
  3. match_funding— confirm a funding source is available.
  4. pay — produce a PaymentResult (builds the signed credential and authorization header).
  5. confirm — read the server's settlement proof from the response.

The canonical implementation path: override pay and confirm. Adapters may use private helpers (e.g. _sign) but must not expose them through this Protocol.

rail instance-attribute

rail: Rail

Rail identity (e.g. "x402") — maps policy prefer lists to adapters.

proof_type instance-attribute

proof_type: ProofType

Proof category produced by this rail ("txid", "preimage", "spt_id").

can_handle

can_handle(response: Response) -> bool

Return True if this adapter recognizes the 402 challenge.

parse

parse(
    request: Request, response: Response
) -> NormalizedChallenge

Decode the 402 response into a NormalizedChallenge.

Raises ChallengeParseError on malformed or unsupported payloads.

match_funding

match_funding(
    challenge: NormalizedChallenge,
    funding: Sequence[FundingSource],
) -> FundingSource | None

Return the first funding source that can satisfy this challenge, or None.

Called by the router after parsing to check funding availability before committing to a payment attempt.

pay async

pay(challenge: NormalizedChallenge) -> PaymentResult

Execute the payment and return a PaymentResult.

Raises SigningError (or a rail-specific payment error) on failure.

confirm async

confirm(
    result: PaymentResult, response: Response
) -> SettlementInfo

Read settlement proof from the server's successful reply.

When no settlement header is present (mock/testnet), returns a SettlementInfo with tx_hash=None and success derived from the HTTP status code.

routeweiler.rails.base.PaymentResult dataclass

Output of RailAdapter.pay().

Attributes:

Name Type Description
header_name str | None

HTTP header to set on the retry request (e.g. "PAYMENT-SIGNATURE" for x402, "Authorization" for L402/MPP).

header_value str | None

The header string.

credential dict[str, Any] | None

Rail-specific persisted credential (e.g. {"macaroon": ..., "preimage": ...} for L402); None for x402.

proof_type ProofType

Category of payment proof produced by this rail.

proof_value str | None

Proof string (preimage hex for L402, SPT id for MPP-SPT, tx hash for MPP-Tempo). For x402, pay() sets this to None; the emitter falls back to settlement.tx_hash from the PAYMENT-RESPONSE header.

routeweiler.rails.base.SettlementInfo dataclass

Rail-agnostic payment proof from the server's response headers.

All fields except success are optional because the spec allows a facilitator to omit them (e.g. in testnet / mock scenarios).


Normalized challenge

routeweiler.normalized.NormalizedChallenge

Bases: RouteweilerModel

Rail-agnostic representation of a 402 Payment Required challenge.

Every rail adapter parses its wire format into this shape before the routing engine and budget counter see it. The rail field acts as a discriminator: raw is the corresponding *RailRaw subtype (X402RailRaw, L402RailRaw, MppTempoRailRaw, or MppSptRailRaw).


Errors

routeweiler.errors.RouteweilerError

Bases: Exception

Base for all Routeweiler exceptions.

routeweiler.errors.PaymentError

Bases: RouteweilerError

Raised when a 402 payment flow cannot be completed.

routeweiler.errors.BudgetError

Bases: PaymentError

Base for budget envelope failures.

routeweiler.errors.BudgetExceededError

Bases: BudgetError

Drawing this amount would breach the envelope's flat cap.

Attributes:

Name Type Description
envelope_id

The envelope that rejected the draw.

requested_minor_units

Amount that was requested.

available_minor_units

Remaining headroom at the time of the draw.

routeweiler.errors.EnvelopeNotFoundError

Bases: BudgetError

No envelope row matches the requested id.

routeweiler.errors.EnvelopeFrozenError

Bases: BudgetError

Envelope status is not 'active' (frozen or revoked).

routeweiler.errors.EnvelopeExpiredError

Bases: BudgetError

Envelope expires_at is in the past.

routeweiler.errors.PolicyError

Bases: PaymentError

Base for policy-engine failures.

routeweiler.errors.PolicyDeniedError

Bases: PolicyError

A policy rule with deny: true matched the challenge.

Attributes:

Name Type Description
reason

Human-readable reason string from the rule's reason field, or None.

rule_name

Name of the matching rule, or None.

routeweiler.errors.PolicyMaxPerCallExceededError

Bases: PolicyError

The challenge amount exceeds the policy's max_per_call_minor_units limit.

Attributes:

Name Type Description
rule_name

Name of the matching rule, or None.

requested

Challenge amount in the reference currency's minor units.

limit

Per-call cap from the matching rule.

routeweiler.errors.NoFeasibleRailError

Bases: PolicyError

No rail remains after policy, funding, and failover filters are applied.

routeweiler.errors.RailExecutionError

Bases: PaymentError

Base for errors that occur while executing a payment.

routeweiler.errors.SigningError

Bases: RailExecutionError

The rail adapter failed to produce a signed payment payload.

routeweiler.errors.InvoicePaymentError

Bases: RailExecutionError

Lightning node returned a terminal payment failure (no_route, channel offline, etc.).

routeweiler.errors.SptCreationError

Bases: RailExecutionError

Stripe rejected or failed to create the Shared Payment Token.

Raised when the Stripe API call in MppSptAdapter.pay() fails for any reason: network error, declined card, invalid customer or payment_method, expired payment method, Stripe API outage, etc.

routeweiler.errors.MppChargeFailedError

Bases: RailExecutionError

MPP server rejected the credential or payment did not settle.

Returned when the server responds 402 with a Problem-Details body (verification-failed, payment-insufficient, invalid-challenge) or with a non-2xx status that lacks a Payment-Receipt header.

routeweiler.errors.MppReceiptVerificationError

Bases: RailExecutionError

The Payment-Receipt header is malformed or mismatches our credential.

Raised when the receipt cannot be decoded, fails Pydantic validation, or the challengeId / method fields do not match what we sent.

routeweiler.errors.PostCommitPaymentError

Bases: RailExecutionError

A pay() exception raised AFTER funds committed on the wire.

Signals to the auth flow that the budget draw must be confirmed (not rolled back) and that failover must not proceed — the rail's payment cannot be re-issued without double-spending.

routeweiler.errors.PreimageMismatchError

Bases: PostCommitPaymentError

sha256(preimage) != invoice payment_hash; the node returned a corrupt preimage.

routeweiler.errors.RailParsingError

Bases: PaymentError

Base for errors that occur while parsing a 402 challenge.

routeweiler.errors.ChallengeParseError

Bases: RailParsingError

The PAYMENT-REQUIRED header could not be decoded or validated.

routeweiler.errors.ChallengeExpiredError

Bases: RailParsingError

Rail challenge expired before the client could pay.

Examples: BOLT-11 invoice expiry, L402 macaroon valid_until caveat, MPP challenge expires auth-param.

routeweiler.errors.RailNotSupportedError

Bases: PaymentError

No registered adapter can handle the 402 challenge.

routeweiler.errors.NoFundingForRailError

Bases: PaymentError

None of the available funding sources match the server's accepted payment options.

routeweiler.errors.CredentialError

Bases: PaymentError

Base for credential store failures.

routeweiler.errors.CredentialNotFoundError

Bases: CredentialError

No credential row matches the given id.

routeweiler.errors.InvalidCredentialTransitionError

Bases: CredentialError

Attempted state transition is not allowed by the credential state machine.

routeweiler.errors.ManifestParseError

Bases: CredentialError

A service-shape manifest YAML is malformed, fails schema validation, or contains an invalid id_extractor (unknown prefix, bad regex).

routeweiler.errors.KeystoreError

Bases: PaymentError

Base for keystore failures.

routeweiler.errors.KeystoreNotFoundError

Bases: KeystoreError

No key file exists for the given envelope id.

routeweiler.errors.KeystoreAlreadyExistsError

Bases: KeystoreError

A key file already exists for the given envelope id; will not overwrite.

routeweiler.errors.FmvUnavailableError

Bases: PaymentError

No cached FMV rate is available for the required currency pair.

routeweiler.errors.ReceiptVerificationError

Bases: PaymentError

Ed25519 signature on a DrawReceipt is invalid or the payload was tampered with.