Skip to main content

paymos — Python SDK

Non-custodial, headless control of a wallet vault: read balances, preview fee-transparent quotes, and move money with swap and withdraw — all over a co-signed 2-of-2 vault.

Your process holds one half of the signing key; the Paymos server holds the other. Neither side alone can move funds — every swap and withdrawal is co-signed, and your half never reaches the server.

  • Non-custodial — a leaked API key can't sign; your co-signing share stays in your process.
  • Fee-transparent — every quote is a dry preview with a full platform / network / route fee breakdown you see before signing anything.
  • Human amounts, no floats — you pass decimal strings like "100" / "0.5"; responses come back as raw integer strings.
  • No Rust toolchain — the native signing core ships compiled inside the wheel.
  • Async-first — built on httpx.AsyncClient, fully typed (py.typed).

The SDK talks in plain asset terms (USDC@base, ETH@arb) and hides the routing and the 2-of-2 signing behind a small, safe surface.

Install

pip install paymos

Requires Python ≥ 3.11. The only runtime dependency is httpx (installed automatically).

Prebuilt wheels cover Windows, macOS (Intel + Apple Silicon), and Linux (x86-64 and ARM, glibc and musl). Each cp311-abi3 wheel serves CPython 3.11 / 3.12 / 3.13+, so a single wheel per platform is all you need — and there's no compiler step on install.

Get a vault secret

The SDK is driven by one vs_live_… vault secret, minted in the wallet Mini App → Settings → Vault API. There are two kinds:

Key Can do Carries
read assets, balances, quote_swap, quote_withdraw, movement, movements API key only
full everything a read key does, plus swap and withdraw API key + your co-signing share

The share is decrypted on your device and never leaves your process. There is no CLI provisioner — the secret is minted in the Mini App. Treat a full secret like a private key.

Point the SDK at it with an environment variable:

export PAYMOS_VAULT_SECRET="vs_live_…"
# optional — defaults to https://wallet.paymos.io
# export PAYMOS_BASE_URL="https://wallet.paymos.io"

Quickstart

import asyncio
from paymos import Wallet

async def main():
    async with Wallet.from_env() as w:              # reads PAYMOS_VAULT_SECRET
        # Balances: per-asset, raw amount strings + a nullable USD value.
        for b in await w.balances():
            print(b.asset, b.amount_raw, b.usd)

        # Preview a withdraw — a dry, money-safe quote. Headline is the fee breakdown.
        q = await w.quote_withdraw(asset="USDC@base", amount="25", to="0xRecipient…")
        print(q.fees.platform, q.fees.network, q.fees.total)

        # Move money (full key only). Amounts are human decimal strings.
        mv = await w.swap(send="USDC@base", receive="ETH@arb", amount="100")
        settled = await w.wait(mv.id)               # poll to a terminal status
        print(settled.status)

asyncio.run(main())

Wallet is an async context manager (aclose() releases the HTTP client). You can also build it directly — Wallet("vs_live_…") — or from the environment with Wallet.from_env().

Core concepts

Amounts: human in, raw out

Amounts you pass are human decimal strings ("100", "0.5") — the server owns each asset's decimals and scales them to raw. Amounts in responses (balances, quote legs, fees) come back as raw integer strings. Never a float, in either direction.

Assets

Assets use white-label SYMBOL@chain ids (USDC@base, ETH@arb). assets() returns the catalog (fetched once and cached per Wallet); the SDK uses each asset's decimals to validate the amounts you pass — an unknown asset or an over-precise amount raises PaymosError locally, before anything hits the wire.

Quotes and fees

quote_swap / quote_withdraw always return a dry Quote — a pure preview that persists nothing and works for read and full keys alike. The headline is quote.fees:

q = await w.quote_swap(send="USDC@base", receive="ETH@arb", amount="100")
print(q.fees.platform)   # raw string
print(q.fees.network)    # raw string
print(q.fees.route)      # RouteFee(amount, estimate) — or None when there's no route leg
print(q.fees.total)      # raw string
print(q.receive.amount)  # expected out
print(q.receive.min)     # guaranteed out (your slippage floor)

Moving money

swap and withdraw (full key only) create a real movement and drive the 2-of-2 co-sign, then return a Movement. Before any signature the SDK re-checks that the server's quote echoes exactly the assets and amount you approved — a decimals bug, contract drift, or a lying server raises and signs nothing.

# Swap, delivered back to your own vault.
mv = await w.swap(send="USDC@base", receive="ETH@arb", amount="100",
                  min_receive="0.03")               # optional slippage floor

# Withdraw to an external chain address.
mv = await w.withdraw(asset="USDC@base", amount="25", to="0xRecipient…")

Withdraw modes

  • mode="exact_out" (default) — the recipient receives exactly amount; the vault debit (amount + fees) is server-computed.
  • mode="total_in" — amount is the total debited from the vault; the recipient gets that minus fees.

Safety caps

  • slippage_bps (swap / quote_swap, default 50 = 0.5%) — the slippage tolerance sent with the quote in basis points; the quote's guaranteed receive.min already reflects it.
  • min_receive (swap) — a human decimal in the receive asset. If the quote's guaranteed receive is below it, raises SlippageExceeded and signs nothing.
  • max_debit (swap / withdraw) — a human decimal ceiling on the total vault debit. Strongly recommended for unattended exact_out payouts, whose input side is server-computed. Exceeded → raises, signs nothing.

Idempotency

Pass your own idempotency_key (e.g. a durable payout id) so a retry after an ambiguous failure replays the same movement instead of paying twice; a retry that finds it already signed/relayed converges on it rather than double-signing. Omit it and a fresh key is generated per call.

mv = await w.withdraw(asset="USDC@base", amount="25", to="0xRecipient…",
                      idempotency_key="payout-8f21", max_debit="26")

Waiting and statuses

wait(id) polls a movement until it reaches a terminal status — completed | failed | refunded | expired | cancelled — and raises PaymosError on timeout.

mv = await w.wait(mv.id, timeout=120, poll=2.0)

API reference

All Wallet methods are async.

Method Returns Notes
Wallet(secret, base_url=…, *, timeout=None) Wallet secret is a vs_live_… string
Wallet.from_env(base_url=None, *, timeout=None) Wallet reads PAYMOS_VAULT_SECRET / PAYMOS_BASE_URL
assets() list[Asset] curated catalog, cached
balances() list[Balance] per-asset vault balances
quote_swap(send, receive, amount, slippage_bps=50) Quote dry preview
quote_withdraw(asset, amount, to, mode="exact_out") Quote dry preview
swap(send, receive, amount, slippage_bps=50, min_receive=None, *, max_debit=None, idempotency_key=None) Movement full key
withdraw(asset, amount, to, mode="exact_out", *, max_debit=None, idempotency_key=None) Movement full key
movement(id) Movement one movement, with fee breakdown
movements(limit=50, cursor=None) tuple[list[Movement], str | None] page + next cursor (newest first)
wait(id, timeout=120, poll=2.0) Movement poll to a terminal status
aclose() — release the underlying HTTP client

The default HTTP timeout is httpx.Timeout(60s, connect=10s) (the co-sign is multi-round); override it with Wallet(..., timeout=…).

Data models

Every amount field is a raw integer string. All models are frozen dataclasses.

  • Asset — asset, symbol, chain, decimals: int
  • Balance — asset, symbol, chain, decimals: int, amount_raw, usd: str | None
  • Amount — amount, asset
  • Receive — amount, min (guaranteed), asset
  • RouteFee — amount, estimate: bool (True = rate-derived cross-asset, False = exact same-asset spread)
  • Fees — asset, platform, network, route: RouteFee | None, total, usd: dict | None
  • Quote — movement_id: str | None (None on a dry preview), mode, send: Amount, debit: Amount (full vault debit = send + fees), receive: Receive, fees: Fees, expires_at, sources: int | None
  • Movement — id, type, status, send: Amount, receive: Receive, fees: Fees | None, dest_chain_tx_hash: str | None, dest_chain_explorer_url: str | None, created_at, completed_at: str | None

Timestamps stay raw ISO-8601 strings — the SDK does not parse them to datetime.

Errors

Every call raises a typed subclass of PaymosError, which carries message and status (the HTTP status, or None for a transport-level failure). Branch on the type instead of string-matching a message.

Exception When
AuthError 401 — API key missing, malformed, or unrecognized
Forbidden 403 — the key is valid but lacks the scope this call needs
InsufficientFunds 400 — the vault balance can't cover the amount (plus fees)
QuoteExpired 400 — the referenced quote expired; fetch a fresh one
SlippageExceeded 400 or local — delivered / guaranteed amount fell below your floor
CrossAssetWithdrawNotAllowed 400 — a withdraw changed the asset (that's a swap)
RouteUnavailable 400 — no route could be quoted right now; retry shortly
RateLimited 429 — carries .retry_after (seconds, or None)
Conflict 409 — idempotency key reused with a different request
PaymosError base — 404, any other 4xx/5xx, and transport errors (status=None)
import asyncio
from paymos import Wallet, InsufficientFunds, SlippageExceeded, RateLimited, PaymosError

try:
    mv = await w.swap(send="USDC@base", receive="ETH@arb", amount="100", min_receive="0.03")
except SlippageExceeded:
    ...                                   # guaranteed receive below your floor — nothing signed
except InsufficientFunds:
    ...                                   # not enough balance for amount + fees
except RateLimited as e:
    await asyncio.sleep(e.retry_after or 1)
except PaymosError as e:
    print(e.status, e.message)

Calling swap / withdraw with a read-only key raises PaymosError before any movement is created — a read key can never sign.

Security

  • A leaked API key alone cannot move funds — no share, no signature (and the key can be revoked).
  • A leaked share alone cannot move funds — no server co-sign.
  • Both are required, on purpose. The SDK additionally verifies every message it co-signs, and that the server's quote matches exactly what you approved — so a compromised server can't redirect funds or inflate an amount past your caps.

Treat a full vs_live_… secret like a private key, and back up the share — losing it means losing your ability to co-sign.

Examples

The source repository's examples/ folder has a clickable local web demo (FastAPI, prod-pointed) that loads balances, shows the fee breakdown, and — behind a confirm box — runs a real swap, plus a minimal demo.py CLI.

License

Proprietary. All rights reserved. Use of this SDK is subject to your agreement with Paymos.

Metadata

Release files for paymos 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for paymos 0.1.3
File Size Uploaded
paymos-0.1.3.tar.gz 40.7 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for paymos 0.1.3
File
paymos-0.1.3-cp311-abi3-win_amd64.whl CPython 3.11 abi3 Windows x86-64 Details
paymos-0.1.3-cp311-abi3-musllinux_1_2_x86_64.whl CPython 3.11 abi3 Linux musl 1.2+ x86-64 Details
paymos-0.1.3-cp311-abi3-musllinux_1_2_aarch64.whl CPython 3.11 abi3 Linux musl 1.2+ ARM64 Details
paymos-0.1.3-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.11 abi3 Linux glibc 2.17+ x86-64 Details
paymos-0.1.3-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.11 abi3 Linux glibc 2.17+ ARM64 Details
paymos-0.1.3-cp311-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl CPython 3.11 abi3 macOS 10.12+ x86-64, macOS 11.0+ ARM64, macOS 10.12+ universal2 (ARM64, x86-64) Details

Total release size: 3.7 MB

Release files / paymos-0.1.3.tar.gz

Download URL paymos-0.1.3.tar.gz
Size 40.7 kB
Tags Source
SHA-256 checksum
How to use checksums
ee23d88b93a1146f3c00ed1d8f21b5311c19a7d110fef76947aac0a9c3043f76
BLAKE2b-256 checksum
How to use checksums
b3687bc2a70f4a15870dad958290290cb616ff3913a6c3d36fafa992501a6e47
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 9, 2026.

Transparency log

Release files / paymos-0.1.3-cp311-abi3-win_amd64.whl

Download URL paymos-0.1.3-cp311-abi3-win_amd64.whl
Size 363.3 kB
Tags CPython 3.11 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
cfc67a7cdea6c032e8890db1efe9ae6edc9ff317db648bef57df6b23658466ca
BLAKE2b-256 checksum
How to use checksums
4e6943d0804ab94dbc2399074ef5a4b20e90e438ee1da0efa49d42036ce31f5b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 9, 2026.

Transparency log

Release files / paymos-0.1.3-cp311-abi3-musllinux_1_2_x86_64.whl

Download URL paymos-0.1.3-cp311-abi3-musllinux_1_2_x86_64.whl
Size 719.6 kB
Tags CPython 3.11 Linux musl 1.2+ x86-64 abi3
SHA-256 checksum
How to use checksums
84d211d7e74ad64666d810ad97d98c32459968946cbb2bffe7523b45c2dcbf6c
BLAKE2b-256 checksum
How to use checksums
ea96e41b86b62a729989753e9b9dfe081fcc8c9b6968c572630c04840b6c09bf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 9, 2026.

Transparency log

Release files / paymos-0.1.3-cp311-abi3-musllinux_1_2_aarch64.whl

Download URL paymos-0.1.3-cp311-abi3-musllinux_1_2_aarch64.whl
Size 669.9 kB
Tags CPython 3.11 Linux musl 1.2+ ARM64 abi3
SHA-256 checksum
How to use checksums
16680758b1afaefdd32130a30771f30bd8db8b7870b5ca143b6ba883d3837078
BLAKE2b-256 checksum
How to use checksums
c0ca16c3a420f6a2a7b89a35d31130251135aabba79ceffe4de4e2fe7c5efd64
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 9, 2026.

Transparency log

Release files / paymos-0.1.3-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL paymos-0.1.3-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 511.9 kB
Tags CPython 3.11 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
669fe586038adc1f9a2095ec017a2df3fb25a27cd08957cce1a26ea9d1c5f51f
BLAKE2b-256 checksum
How to use checksums
27cce69f1cecf981be3c83ade38f22fa47abf7035a2c7ebaaa82d6995e113f66
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 9, 2026.

Transparency log

Release files / paymos-0.1.3-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL paymos-0.1.3-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 494.6 kB
Tags CPython 3.11 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
f6686893af7210ab60c31b2536ed74e05335339bd1155b1cf18a81fd44b9d694
BLAKE2b-256 checksum
How to use checksums
0457d55f3391311933ae8feece496f795eee248b3c0b79527eea5ff0075ea23d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 9, 2026.

Transparency log

Release files / paymos-0.1.3-cp311-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl

Download URL paymos-0.1.3-cp311-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Size 884.5 kB
Tags CPython 3.11 abi3 macOS 10.12+ universal2 (ARM64, x86-64) macOS 10.12+ x86-64 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
50421c6f7dee012e37a9aab76b1d9dcdb9b0b42c1c1a1201c11377532066c172
BLAKE2b-256 checksum
How to use checksums
a3434a0bf2fc312dc25aa5214c59a3082867b3da4442e83dca77d90ed56d80b4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 9, 2026.

Transparency log

Release history Release notifications | RSS feed

0.1.4

7 release files

This release

0.1.3 This release

7 release files

0.1.2

3 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page