Payment Links
The payer’s view of one payment link, served without a key to the hosted checkout on payments.cosmospay.lat. The link id is the capability: anyone holding it can read the amount, the address to pay and the live status, and nothing about the merchant.
For a hosted checkout page: amount, asset, network, the address to pay (payTo), the link and its QR, and the live status, so the page can poll until the payment settles. Reachable without an API key or the gateway secret.
The id is the capability. Anyone who holds a payment link can read it here, which is what a payment link is for. Nothing about the owning merchant account, the payer’s own account, the envelope, the merchant’s reference or callback, or the status history is returned. uri is the link verbatim, so whatever the wallet needs from it is in it.
Answered with Cache-Control: no-store. Budgeted per client address (x-cosmos-rate-limit): poll at most every few seconds and honour Retry-After.
id*stringThe payment intent id (a cuid), as in the link https://payments.cosmospay.lat/<id>.
OK
The body, query or path failed validation. message is an array of the individual failures.
Routes that reject a valid request for a reason of their own (an amount, a memo, a state) document the code for it on their own 400.
No payment intent has this id: it never existed or it was deleted. The two are the same answer, and the same one GET /v1/payment-intents/{id} gives its owner.
The budget in x-cosmos-rate-limit is spent for this caller. Honour Retry-After.
The ratelimit-* triple rides along on successful responses too, so a client can pace itself instead of being refused. If the counter store itself is unreachable the guard fails closed and answers 503 provider_unavailable.
Unexpected server error. The detail is logged server-side and never returned: there is nothing in the body to branch on beyond the code.
curl -X GET 'https://api.cosmospay.lat/cosmos-api/v1/payment-links/cmh4x2k9a0001l70g8v5q3z2w'{ "id": "cmh4x2k9a0001l70g8v5q3z2w", "kind": "PAY", "chain": "stellar", "network": "public", "status": "PENDING", "payTo": "GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO", "amount": "25.5", "asset": "native", "assetIssuer": null, "memo": "4127759120438726917", "msg": "Order #24", "uri": "web+stellar:pay?destination=GCALNQQBXAPZ2WIRSDDBMSTAKCUH5SG6U76YBFLQLIXJTF7FE5AX7AOO&amount=25.5&memo=4127759120438726917&memo_type=MEMO_ID&msg=Order+%2324", "qr": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA…", "txHash": null, "networkFee": null, "merchantFee": { "bps": 150, "fixed": "0.25", "amount": "1.75", "asset": "XLM", "wallet": "GBDHAQXQQ75WVBUZ2IX3O7ME7JOKJUOUPUXRNDCORUJODWQCTVUEQ62R", "paidBy": "recipient" }, "payerAccountRequired": false, "expiresAt": "2026-10-24T13:34:56.000Z", "createdAt": "2026-10-24T12:34:56.000Z", "checkoutTheme": "classic"}A SEP-7 pay link pays one account, so a Stellar payment link with a merchant fee (payerAccountRequired on the link) asks for its payer: given their account, this builds the unsigned envelope with both payments (to the merchant, and the fee to its wallet) and the link’s MEMO_ID, and its web+stellar:tx link and QR. The link settles on any transaction that pays both with the memo, so nothing is stored and a lapsed envelope is simply built again.
Only for an open link (PENDING). Budgeted per client address.
id*stringThe payment intent id, as in the link.
application/json- body
The payer of a Stellar payment link with a merchant fee, for whom the service builds the envelope to sign.
account*stringThe Stellar account that will pay and sign: the envelope is built on its sequence number, so it must exist on the link’s network.
OK
validation_failed: the link pays its destination directly (open its uri), or account is not a Stellar account. invalid_state_transition: the link is no longer open. trustline_missing: the merchant no longer trusts the asset.
No payment intent has this id.
The budget in x-cosmos-rate-limit is spent for this caller. Honour Retry-After.
The ratelimit-* triple rides along on successful responses too, so a client can pace itself instead of being refused. If the counter store itself is unreachable the guard fails closed and answers 503 provider_unavailable.
Unexpected server error. The detail is logged server-side and never returned: there is nothing in the body to branch on beyond the code.
provider_unavailable: the provider in x-cosmos-upstream is down or unreachable; the message names it. Retry.
misconfigured: this deployment is missing configuration the route needs (a provider instance, a fee wallet, the plan header the gateway must forward). Retrying will not help; it is an operator problem.
curl -X POST 'https://api.cosmospay.lat/cosmos-api/v1/payment-links/cmh4x2k9a0001l70g8v5q3z2w/transaction' \ -H 'Content-Type: application/json' \ -d '{ "account": "GA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJVSGZ"}'{ "xdr": "AAAAAgAAAAA...", "uri": "web+stellar:tx?xdr=AAAAAgAAAAA...", "qr": "data:image/png;base64,iVBORw0KGgo..."}The hosted checkout tells what the payer did with a wallet while the link is pending: walletOpened (the wallet link, the web wallet, the extension or Freighter was opened), walletClosed (the wallet came back without paying) and sent (the page saw the wallet send the payment). The owner reads them as checkoutWalletAt, checkoutWalletClosedAt and checkoutSentAt on the intent, to follow a payment that is not on-chain yet.
Informational only: the status changes on what the chain says, never on these. An event for a link that is no longer pending is ignored. Budgeted per client address.
id*stringThe payment intent id, as in the link.
application/json- body
What the payer did with a wallet, as the hosted checkout page reports it. Informational: the intent status changes only on the chain.
event*stringwalletOpened: the payer opened a wallet from the page (the wallet link, the web wallet, the extension, Freighter). walletClosed: the wallet came back without paying (it declined, or the page was shown again first). sent: the page saw the wallet send the payment.
"walletOpened""walletClosed""sent"Noted (or ignored: the link is not pending).
event is not one of the three.
No payment intent has this id.
The budget in x-cosmos-rate-limit is spent for this caller. Honour Retry-After.
The ratelimit-* triple rides along on successful responses too, so a client can pace itself instead of being refused. If the counter store itself is unreachable the guard fails closed and answers 503 provider_unavailable.
Unexpected server error. The detail is logged server-side and never returned: there is nothing in the body to branch on beyond the code.
curl -X POST 'https://api.cosmospay.lat/cosmos-api/v1/payment-links/cmh4x2k9a0001l70g8v5q3z2w/events' \ -H 'Content-Type: application/json' \ -d '{ "event": "walletOpened"}'A Solana payment link with a merchant fee is a Solana Pay transaction request (solana:<this URL>), because a transfer request names one recipient. A wallet reads this first, then POSTs the payer’s account.
id*stringThe payment intent id.
OK
validation_failed: the link is not a Solana Pay transaction request (only a Solana link with a merchant fee is), or account is not a Solana address. invalid_state_transition: the link is no longer open.
No payment intent has this id.
The budget in x-cosmos-rate-limit is spent for this caller. Honour Retry-After.
The ratelimit-* triple rides along on successful responses too, so a client can pace itself instead of being refused. If the counter store itself is unreachable the guard fails closed and answers 503 provider_unavailable.
Unexpected server error. The detail is logged server-side and never returned: there is nothing in the body to branch on beyond the code.
curl -X GET 'https://api.cosmospay.lat/cosmos-api/v1/payment-links/cmh4x2k9a0001l70g8v5q3z2w/solana-pay'{ "label": "Cosmos Pay", "icon": "https://payments.cosmospay.lat/android-chrome-512x512.png"}Given the payer’s account, the unsigned transaction (base64) with the link’s memo, the transfer to the merchant carrying the link’s reference, and the merchant fee’s transfer to its wallet (creating their token accounts first when missing, paid by the payer). The link settles from the ledger, found by its reference; nothing is stored. Only for an open link.
id*stringThe payment intent id.
application/json- body
What a wallet POSTs to a Solana Pay transaction request: the account that will sign and pay.
account*stringThe payer’s account (base58), which pays the fee and signs.
OK
validation_failed: the link is not a Solana Pay transaction request (only a Solana link with a merchant fee is), or account is not a Solana address. invalid_state_transition: the link is no longer open.
No payment intent has this id.
The budget in x-cosmos-rate-limit is spent for this caller. Honour Retry-After.
The ratelimit-* triple rides along on successful responses too, so a client can pace itself instead of being refused. If the counter store itself is unreachable the guard fails closed and answers 503 provider_unavailable.
Unexpected server error. The detail is logged server-side and never returned: there is nothing in the body to branch on beyond the code.
The provider named in x-cosmos-upstream refused the request (provider_error) or could not be reached at all (provider_unavailable). Nothing was charged; retry is safe with the same Idempotency-Key.
provider_unavailable: the provider in x-cosmos-upstream is down or unreachable; the message names it. Retry.
misconfigured: this deployment is missing configuration the route needs (a provider instance, a fee wallet, the plan header the gateway must forward). Retrying will not help; it is an operator problem.
The provider in x-cosmos-upstream did not answer in time. Whether it acted on the request is unknown. Retry with the same Idempotency-Key, never with a fresh one.
curl -X POST 'https://api.cosmospay.lat/cosmos-api/v1/payment-links/cmh4x2k9a0001l70g8v5q3z2w/solana-pay' \ -H 'Content-Type: application/json' \ -d '{ "account": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU"}'{ "transaction": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...", "message": "Order #24"}Assets
The asset registry: which (code, issuer) pairs exist per network and who issues them. `verified` is a claim about the issuer’s identity, not about quality.
Public Key
The shared public API key, served without a key to wallets that have no account yet. Not a secret: what it may call is confined server-side to routes that return no per-consumer data.