Perly Pay

io.perlyPublished by perly.iov0.3.0Updated Nov 3, 2026

Sell for crypto from your editor: products, payment links, invoices and test payments.

VerifiedSTDIODesktop onlyFinanceBusiness & Commerce

Overview

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.

Publisher
perly.io

Installation

In SourceWeft

  1. Open Perly Pay in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.

Other MCP clients

Follow the launch instructions in the repository.

README

perly-pay-mcp

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.

Set up

Get a test key in Perly Pay: Dashboard → Developers → API keys (sk_test_…).

Claude Code

bash
claude mcp add perly-pay --env PERLY_PAY_SECRET_KEY=sk_test_… -- npx -y perly-pay-mcp

Cursor: ~/.cursor/mcp.json, or .cursor/mcp.json in a project

json
{  "mcpServers": {    "perly-pay": {      "command": "npx",      "args": ["-y", "perly-pay-mcp"],      "env": { "PERLY_PAY_SECRET_KEY": "sk_test_…" }    }  }}

Codex: ~/.codex/config.toml

toml
[mcp_servers.perly-pay]command = "npx"args = ["-y", "perly-pay-mcp"]env = { PERLY_PAY_SECRET_KEY = "sk_test_…" }

Keep the key out of git: a config file in a project is often committed. Node 20 or newer.

Variable
PERLY_PAY_SECRET_KEYsk_test_… while you build. sk_live_… reads only (see below).
PERLY_PAY_APIDefault https://pay.perly.io. A perly.io host or this machine; plain http only for localhost.
PERLY_PAY_ALLOW_ANY_API1 sends your key to any other https host in PERLY_PAY_API, for a copy you run yourself.
PERLY_PAY_WEBHOOK_SECRETOptional whsec_…, the default for verify_webhook_signature.

Try it

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.

Tools

AreaTools
Products and pricescreate_product, list_products, update_product, create_price, list_prices, update_price
Payment linkscreate_payment_link, list_payment_links, update_payment_link
Invoices and checkoutcreate_invoice, list_invoices, retrieve_invoice, cancel_invoice, create_checkout_session, retrieve_checkout_session
Customerscreate_customer, list_customers, retrieve_customer
Subscriptions (renewed by invoice)create_subscription, list_subscriptions, retrieve_subscription
Access and creditscheck_entitlement, list_entitlements, consume_entitlement_credits, list_credit_usage
Money in, read onlylist_payments, retrieve_payment, list_receipts, retrieve_receipt, list_events
Refundscreate_refund, list_refunds
Webhookscreate_webhook_endpoint, list_webhook_endpoints, delete_webhook_endpoint
Test modesimulate_payment: pays an invoice, or buys from a payment link and pays that
On your machineverify_webhook_signature checks a Perly-Signature; pay_links gives the dashboard pages only you can use: plan, wallet, go live, two-factor, keys

The tools are generated from Perly Pay's OpenAPI document, so their arguments are the API's own.

What it will not do

  • With a live key, it only reads. Every tool that writes is refused: creating invoices, checkout sessions, products, prices, customers, payment links and subscriptions, cancelling, editing, refunds, credit spending, simulated payments and webhook endpoints. It says why, and you do it in the dashboard or in your app's own code. Lists and lookups work as in test. Why: text a buyer typed can reach the assistant, and nothing the server receives proves that you, not the model, approved a live change (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.
  • Wallets, API keys, billing, live mode and two-factor have no tool at all. You set them in the dashboard; pay_links gives the pages.
  • What cannot be undone (cancelling an invoice, opening a refund, deleting a webhook endpoint) waits for confirm: true, which the assistant should only send after asking you.
  • Your secret key is never repeated in an answer or a log.
  • Buyers' license keys never reach the assistant: an event that carries one comes back without its key; they stay in the dashboard.
  • Text others wrote is marked as data. Customer names, emails, descriptions and metadata can come from buyers, or from anyone who opens a payment link; every API answer ends with a note telling the assistant not to follow them. Still read what a tool is about to change before you approve it.

Docs: https://pay.perly.io/docs/mcp. API reference: https://pay.perly.io/docs/api. MIT licence.

Source: README.md at commit 966d0f2

Tools

38

list_invoices

List invoices

List invoices. Newest first; pass `next_cursor` back as `cursor` for the next page.

Parameterslimitcursorstatuscustomersubscription

create_invoice

Create 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.

Parametersamountcouponlocalecurrencycustomerdiscountmetadataline_itemsreturn_urldescriptionsubscriptioncustomer_email+1 more

retrieve_invoice

Retrieve an invoice

Retrieve an invoice.

Parametersidrequired

cancel_invoice

Cancel 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`.

Parametersidrequiredconfirmrequired

simulate_payment

Simulate 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.

Parametersidassetchainoutcomepayment_link

create_checkout_session

Create 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.

Parameterscouponcustomermetadatacancel_urlline_itemsrequiredreturn_urlcustomer_emailexpires_in_minutes

retrieve_checkout_session

Retrieve a checkout session

Retrieve a checkout session.

Parametersidrequired

list_products

List products

List products. Newest first; pass `next_cursor` back as `cursor` for the next page.

Parameterslimitactivecursor

create_product

Create a product

Create a product. Test keys only: refused with a live key.

Parametersnamerequiredactivemetadataimage_urldescriptionissues_licenselicense_activation_limit

update_product

Update 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.

Parametersidrequirednameactivemetadataimage_urldescriptionissues_licenselicense_activation_limit

list_prices

List prices

List prices. Newest first; pass `next_cursor` back as `cursor` for the next page.

Parameterslimitmodelactivecursorproduct

create_price

Create 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.

Parametersmodelrequiredcreditsproductrequiredrefundscurrencylifetimemetadatanicknamerecurringterm_daysunit_amountcustom_amount

update_price

Update 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.

Parametersidrequiredactiverefundsmetadatanickname

list_customers

List customers

List customers. Newest first; pass `next_cursor` back as `cursor` for the next page.

Parametersemaillimitcursorexternal_id

create_customer

Create a customer

Create a customer. Test keys only: refused with a live key.

Parametersnameemaillocalecountrymetadataexternal_id

retrieve_customer

Retrieve a customer

Retrieve a customer.

Parametersidrequired

list_subscriptions

List subscriptions

List subscriptions. Newest first; pass `next_cursor` back as `cursor` for the next page.

Parameterslimitpricecursorstatuscustomer

create_subscription

Create 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.

Parameterspricerequiredcouponcustomerrequiredmetadataquantitytrial_days

retrieve_subscription

Retrieve a subscription

Retrieve a subscription.

Parametersidrequired

check_entitlement

Check 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`).

Parameterspriceproductcustomercustomer_emailcustomer_external_id

list_entitlements

List entitlements

List entitlements. Newest first; pass `next_cursor` back as `cursor` for the next page.

Parameterslimitcursorproductcustomersubscription

consume_entitlement_credits

Spend 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.

Parametersidrequiredactioncreditsrequired

list_credit_usage

List credit spends

List credit spends. Newest first; pass `next_cursor` back as `cursor` for the next page.

Parameterslimitactioncursorproductcustomer

list_payments

List 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.

Parametersassetlimitcursorinvoice

retrieve_payment

Retrieve a payment

Retrieve a payment.

Parametersidrequired

list_refunds

List refunds

List refunds. Newest first; pass `next_cursor` back as `cursor` for the next page.

Parameterslimitcursorstatusinvoice

create_refund

Open 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`.

Parametersamountreasonconfirmrequiredinvoicerequired

list_receipts

List 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.

Parameterskindlimitcursorinvoice

retrieve_receipt

Retrieve a receipt

Retrieve a receipt.

Parametersidrequired

list_events

List 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.

Parameterstypelimitcursorobject

list_webhook_endpoints

List webhook endpoints

List webhook endpoints. Newest first; pass `next_cursor` back as `cursor` for the next page.

Parameterslimitcursor

create_webhook_endpoint

Create 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.

Parametersurlrequireddescriptionenabled_events

delete_webhook_endpoint

Delete 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`.

Parametersidrequiredconfirmrequired

verify_webhook_signature

Verify 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.

Parameterssecretpayloadrequiredsignaturerequiredtolerance_seconds

Version history

1
  1. v0.3.0LatestNov 3, 2026

Score

90% 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.

  • Reached in the last 30 days25 pts
  • Auth declared10 pts

    Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.

  • Tools listedRequired15 pts
  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts