list_invoices
List invoices
List invoices. Newest first; pass `next_cursor` back as `cursor` for the next page.
limitcursorstatuscustomersubscription
io.perlyPublished by perly.iov0.3.0Updated Nov 3, 2026
Sell for crypto from your editor: products, payment links, invoices and test payments.
Perly Pay's local MCP server, published by perly.io; no sign-in required; STDIO; 38 tools.
No AI overview yet: these facts come from the registry entry and the server itself.
Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.
Follow the launch instructions in the repository.
Sell your work for crypto from your AI coding assistant. perly-pay-mcp is a
local MCP server for
Perly Pay: your assistant can create products, prices,
payment links and invoices, pay them in test mode, read payments and receipts,
and check a buyer's access. Bitcoin, USDC and USDT land straight in your own
wallet; Perly Pay never holds them. It costs 2.5¢ a payment, never more than
0.5% of it, and a month of up to 21 payments is free.
It runs on your machine over stdio and talks only to Perly Pay's public API with your key. There is no server of ours in between.
Get a test key in Perly Pay: Dashboard → Developers → API keys (sk_test_…).
Claude Code
Cursor: ~/.cursor/mcp.json, or .cursor/mcp.json in a project
Codex: ~/.codex/config.toml
Keep the key out of git: a config file in a project is often committed. Node 20 or newer.
Sell a one-hour consultation for $49 with a payment link, then pay it in test mode and show me the payment.
The assistant calls create_product, create_price, create_payment_link,
simulate_payment and list_payments.
The tools are generated from Perly Pay's OpenAPI document, so their arguments are the API's own.
confirm: true is written by the model; a client's approval prompt and an
MCP elicitation answer both come back through the client). So the server
never writes with a live key.pay_links gives the pages.confirm: true, which the assistant should
only send after asking you.key; they stay in the dashboard.Docs: https://pay.perly.io/docs/mcp. API reference: https://pay.perly.io/docs/api. MIT licence.
Source: README.md at commit 966d0f2
List invoices
List invoices. Newest first; pass `next_cursor` back as `cursor` for the next page.
limitcursorstatuscustomersubscriptionCreate an invoice
Create an invoice. Lines are prices (`{ price, quantity }`) or your own (`{ description, amount, quantity }`), in one currency. For a single amount, `amount` + `description` is short for one line. `customer` is an id or `{ email }`. A price line may carry its own `description` ("Seat #12"), and `discount` takes an amount off this invoice alone. The buyer pays at `url`, choosing bitcoin or a stablecoin there. Live mode: a new invoice is refused with 403 `bill_overdue` while a Perly Pay bill is unpaid past its 14 days, until it is paid in the dashboard's Billing; invoices already made are still paid. Test keys only: refused with a live key.
amountcouponlocalecurrencycustomerdiscountmetadataline_itemsreturn_urldescriptionsubscriptioncustomer_email+1 moreRetrieve an invoice
Retrieve an invoice.
idrequiredCancel an invoice
Cancel an invoice. Only while nothing has arrived. Money that arrives later is still seen (the address stays watched) and lands in review. Test keys only: refused with a live key. Cannot be undone — Cancels the invoice: its buyer can no longer pay it. Ask the person first, then call with `confirm: true`.
idrequiredconfirmrequiredSimulate a payment (test mode only)
Simulate a payment (test mode only). Test keys only. `full` pays the amount due: the invoice turns paid and is fulfilled — entitlements, license keys, a receipt, and the events your webhooks get. `short` sends half (underpaid), `over` a tenth more (overpaid); those two need the buyer's chain and asset chosen first. For `full`, an invoice with no chain yet gets one: `chain` and `asset` if given, else the first stablecoin offered (refund terms count as accepted). Pass the invoice's `id`, or a `payment_link` id: then a test invoice is made for the link's price (as a buyer opening the link would) and paid. Test keys only: refused with a live key.
idassetchainoutcomepayment_linkCreate a checkout session
Create a checkout session. A cart of 1–20 prices in one currency. Send the buyer to `url`, or open it in an overlay with pay.js (`data-session="cs_…"`). The buyer comes back to `return_url` after paying, `cancel_url` if they leave. Live mode: a new session is refused with 403 `bill_overdue` while a Perly Pay bill is unpaid past its 14 days. Test keys only: refused with a live key.
couponcustomermetadatacancel_urlline_itemsrequiredreturn_urlcustomer_emailexpires_in_minutesRetrieve a checkout session
Retrieve a checkout session.
idrequiredList products
List products. Newest first; pass `next_cursor` back as `cursor` for the next page.
limitactivecursorCreate a product
Create a product. Test keys only: refused with a live key.
namerequiredactivemetadataimage_urldescriptionissues_licenselicense_activation_limitUpdate a product
Update a product. Set `active: false` to stop selling it; products are never deleted (invoices point at them). Test keys only: refused with a live key.
idrequirednameactivemetadataimage_urldescriptionissues_licenselicense_activation_limitList prices
List prices. Newest first; pass `next_cursor` back as `cursor` for the next page.
limitmodelactivecursorproductCreate a price
Create a price. Every model: one_time, pay_what_you_want (with a minimum), fixed_term (N days of access), subscription (interval, trial, grace, prepay), lifetime (optional cap and cohort tiers), credit_pack. Amounts are immutable; make a new price to change one. Body is one of: model = one_time: needs product, model, unit_amount; model = pay_what_you_want: needs product, model, custom_amount; model = fixed_term: needs product, model, unit_amount, term_days; model = subscription: needs product, model, unit_amount, recurring; model = lifetime: needs product, model; model = credit_pack: needs product, model, unit_amount, credits. Test keys only: refused with a live key.
modelrequiredcreditsproductrequiredrefundscurrencylifetimemetadatanicknamerecurringterm_daysunit_amountcustom_amountUpdate a price
Update a price. Nickname, active, refund terms (`refunds` replaces them whole; what you leave out takes its default) and metadata. Amounts and the model never change. Test keys only: refused with a live key.
idrequiredactiverefundsmetadatanicknameList customers
List customers. Newest first; pass `next_cursor` back as `cursor` for the next page.
emaillimitcursorexternal_idCreate a customer
Create a customer. Test keys only: refused with a live key.
nameemaillocalecountrymetadataexternal_idRetrieve a customer
Retrieve a customer.
idrequiredList subscriptions
List subscriptions. Newest first; pass `next_cursor` back as `cursor` for the next page.
limitpricecursorstatuscustomerCreate a subscription
Create a subscription. Renewed by invoice: the customer pays each period themselves. Without a trial the first invoice opens now (`latest_invoice`); the subscription turns active when it is paid. Test keys only: refused with a live key.
pricerequiredcouponcustomerrequiredmetadataquantitytrial_daysRetrieve a subscription
Retrieve a subscription.
idrequiredList payment links
List payment links. Newest first; pass `next_cursor` back as `cursor` for the next page.
limitpriceactivecursorCreate a payment link
Create a payment link. A page at /l/<slug> that sells one price. Share it anywhere, or open it with pay.js. Test keys only: refused with a live key.
slugpricerequiredmetadataquantitysuccess_urlcollect_nameallow_couponsadjustable_quantityUpdate a payment link
Update a payment link. Test keys only: refused with a live key.
idrequiredactivemetadataquantitysuccess_urlcollect_nameallow_couponsadjustable_quantityCheck access
Check access. One call: may this customer use this product now? Name the customer by `customer`, `customer_email` or `customer_external_id`, and the product by `product` (or `price`).
priceproductcustomercustomer_emailcustomer_external_idList entitlements
List entitlements. Newest first; pass `next_cursor` back as `cursor` for the next page.
limitcursorproductcustomersubscriptionSpend credits
Spend credits. Spends credits from one credit pack, atomically. Refused when fewer are left. `action` labels the spend for your reports; with an `Idempotency-Key` a retry never spends twice. To spend from a customer's packs together, use POST /credits/consume. Test keys only: refused with a live key.
idrequiredactioncreditsrequiredList credit spends
List credit spends. Newest first; pass `next_cursor` back as `cursor` for the next page.
limitactioncursorproductcustomerList payments
List payments. Transfers seen on chain into addresses we handed out, newest first. Newest first; pass `next_cursor` back as `cursor` for the next page.
assetlimitcursorinvoiceRetrieve a payment
Retrieve a payment.
idrequiredList refunds
List refunds. Newest first; pass `next_cursor` back as `cursor` for the next page.
limitcursorstatusinvoiceOpen a refund
Open a refund. Starts a refund in the asset that was paid. We email the buyer a link to give an address; the link is never returned, since whoever holds it decides where the money goes. With no email on the invoice, the owner makes one in the dashboard. When the address is checked, the refund is `ready_to_send` with `amount_to_send`. Refused with `sanctioned_sender` when money on the invoice came from an address on a sanctions list, or one the coin's issuer froze: sending it back may itself be prohibited, so nothing refunds it — leave it and talk to a lawyer. Test keys only: refused with a live key. Cannot be undone — Opens a refund to the buyer. Ask the person first, then call with `confirm: true`.
amountreasonconfirmrequiredinvoicerequiredList receipts
List receipts. One for every fulfilled payment and every confirmed refund, newest first. Newest first; pass `next_cursor` back as `cursor` for the next page.
kindlimitcursorinvoiceRetrieve a receipt
Retrieve a receipt.
idrequiredList events
List events. What happened in the last 30 days, newest first. `type` filters (`invoice.paid`, or `invoice.*`). Newest first; pass `next_cursor` back as `cursor` for the next page.
typelimitcursorobjectList webhook endpoints
List webhook endpoints. Newest first; pass `next_cursor` back as `cursor` for the next page.
limitcursorCreate a webhook endpoint
Create a webhook endpoint. The response carries `secret` — the only time you see it. Keep it to verify `Perly-Signature`. Test keys only: refused with a live key.
urlrequireddescriptionenabled_eventsDelete a webhook endpoint
Delete a webhook endpoint. Test keys only: refused with a live key. Cannot be undone — Deletes the webhook endpoint: its events stop. Ask the person first, then call with `confirm: true`.
idrequiredconfirmrequiredVerify a webhook signature
Checks a Perly Pay webhook on this machine: the HMAC-SHA256 in its Perly-Signature header against the raw body and the endpoint's signing secret, and that it is under five minutes old. Nothing is sent anywhere. Use it to debug a webhook handler that rejects real events. Pass the body exactly as received (the raw text, before JSON parsing). `secret` defaults to PERLY_PAY_WEBHOOK_SECRET.
secretpayloadrequiredsignaturerequiredtolerance_secondsDashboard pages for what a person must do
The Perly Pay dashboard pages for steps no API key can take: pay a Perly Pay bill, connect a wallet (public key only), go live, set up two-factor sign-in, manage API keys and live webhooks, send live refunds, and the go-live checklist. Give the person the link and say what to do there; do not try these through other tools.
topic90% of the applicable checks met
How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.
Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.
claude mcp add perly-pay --env PERLY_PAY_SECRET_KEY=sk_test_… -- npx -y perly-pay-mcp{ "mcpServers": { "perly-pay": { "command": "npx", "args": ["-y", "perly-pay-mcp"], "env": { "PERLY_PAY_SECRET_KEY": "sk_test_…" } } }}[mcp_servers.perly-pay]command = "npx"args = ["-y", "perly-pay-mcp"]env = { PERLY_PAY_SECRET_KEY = "sk_test_…" }