Accept your first deposit
Go from a secret key to a settled payment in four steps.
Get a secret key
Every call below uses a secret key,
cnpy_sk_live_.... Keep it on your server. Never send it to a browser.Canopy has two other credential types. See credentials for where each one can be used.
Create a payment intent
Call
POST /api/v1/intentsfrom your server. Create one intent for each user who is about to send funds.curl -X POST https://www.canopypay.io/api/v1/intents \ -H "Authorization: Bearer cnpy_sk_live_a1b2c3_<64 hex chars>" \ -H "Canopy-Version: 2026-09-01" \ -H "Content-Type: application/json" \ -d '{ "merchantReference": "order_10024", "metadata": { "userId": "u_7781" } }'201 Createdjson { "intentId": "6f1b9c2e4d8a4f1b8c2e4d8a4f1b8c2e", "inboxAddress": "0x9f2a1c7b3e5d8a604c1f2b9e7d3a5c8b6e4f1d20", "created": true, "priceUnits": null, "oneTime": false, "widgetToken": "2f7a1d9c4b6e8035a1c7d3f6b8e04a2c5d7f9b1e3a6c8d0f2b4e6a8c0d2f4b6e" }This example has no destination field. The deposit settles to your account's own payout wallet.
To send the funds to your user's wallet instead, see payout destinations. Two things must be turned on first:
- the per-intent destinations setting on your account
- a route that Canopy has switched to Dynamic mode
Give your user a deposit address
The response includes an
inboxAddress. This is a deposit address for your user. They can send funds to it from any supported chain.For a checkout page instead, mount the widget with the response's
intentId.The browser only needs that id. Canopy looks up the price, destination, fee and supported chains on its server. You do not need to fetch or pass anything else.
If you use a bundler, install the package first:
npm install @canopypay/checkout-sdk@0.7.2Then mount it. Or use a script tag and skip bundling:
checkout.html<script src="https://canopypay.io/v1/canopy.js"></script> <script type="module"> const { intentId } = await ( await fetch("/my-server/create-canopy-intent") ).json(); const handle = Canopy.mount({ target: "#checkout", intentId, merchant: "cnpy_pk_live_abc123", onEvent: (e) => { if (e.type === "paid") showBanner("Payment sent"); }, }); payButton.addEventListener("click", () => { handle.openNow(window.open("", "canopy_checkout", "width=460,height=640")); }); </script>The npm build needs
canopyOrigin. The script tag reads it from its ownsrc. See the SDK reference for details.Open the popup synchronously, inside the click handler. If you
awaitanything before the call, the browser blocks the popup. See popup timing.To show checkout inside your own page instead of a popup, use
surface: "auto". You must register your page's origin first. See embed checkout inline.Watch for settlement
Canopy confirms settlement with a webhook to your server, not in the browser. See webhooks for the events and how to verify signatures.
Never wire fulfilment to the browser's paid eventThe checkout SDK's
paidevent is only for updating your UI. Release goods only after a settlement webhook or a status check from your server.
What's next
- Payment intents lists every field you can send when creating an intent. It also covers the rule that each wallet can have only one active intent.
- To set a different destination on each intent, see payout destinations. It lists the two settings that must be on first.
- The API reference has an example request and response for every operation.
- Credentials lists the three credential types and where each one can be used.