Skip to main content

Embed the widget

Add RecoilPay intents to any web page with one script tag and one element. There's no build step, and no framework is required. Wallet connection is included.


1. Add the script and the element​

<!-- Once per page, anywhere. -->
<script type="module" src="https://cdn.jsdelivr.net/npm/@recoilpay/intent-widget@0.7/dist/recoilpay-intent-widget.js"></script>

<!-- Where you want it to appear. -->
<recoilpay-intent
hf-access-token="YOUR_HUGGING_FACE_TOKEN"
walletconnect-project-id="YOUR_REOWN_PROJECT_ID"
></recoilpay-intent>

That's a complete integration. Open the page, type swap 1 USDC on op sepolia for USDC on base sepolia, and connect a wallet.

The same field also cashes out to a bank account and buys crypto with a bank transfer: cash out 50 USDC on base sepolia to NGN, buy ₦20,000 of USDC on base sepolia. See Fiat ramps.

@0.7 follows the 0.7 line, so you get fixes without breaking changes. Pin an exact version, like @0.7.0, if you'd rather upgrade by hand.

Self-hosting

Prefer not to load from a CDN? Serve the package's whole dist/ folder from your own origin and point the script at dist/recoilpay-intent-widget.js. The widget loads its other chunks from next to that file, so keep the folder intact.

The element is 100% wide up to 640px, so place it in whatever container suits your layout.


2. Add your Hugging Face token​

The widget turns what users type into intents by calling a language model on Hugging Face from their browser, with your token in hf-access-token. The token is visible in your page, so create a fine-grained, inference-only token used for nothing else. See Bring your own Hugging Face token.

Without it, the widget still quotes and settles, but typed sentences aren't understood and users see an example sentence instead.


Browser-extension wallets work with no setup: MetaMask, Rabby, Coinbase Wallet, Brave, Phantom, and any other wallet that supports EIP-6963. Mobile wallets connect over WalletConnect, which needs a project id of your own:

  1. Create a free project at cloud.reown.com.
  2. Under the project's Domain settings, add every origin the widget runs on, such as https://yourapp.com. Add http://localhost:<port> while you're developing.
  3. Put the project id in walletconnect-project-id.

Without a project id, the WalletConnect option simply doesn't appear. The id is public by design, since it ships in your page's HTML, so it's safe to commit.

Why your own id

WalletConnect usage, rate limits and the domain allowlist belong to the project id. With your own, your traffic is yours to monitor, and nothing about another site affects it.

WalletConnect's code is about 450 KB gzipped, four times the rest of the widget. It's only downloaded when a visitor actually chooses WalletConnect.

Solana wallets need no setup either. Phantom, Solflare, Backpack and any other Wallet Standard wallet are listed in the picker under Solana wallets, for cashing out and buying on Solana devnet. A visitor can connect one alone or beside an EVM wallet.


4. Listen for results (optional)​

The widget reports what happens as DOM events. They bubble, so you can listen on document:

document.addEventListener('recoilpay-complete', (event) => {
const { orderId, status, explorerUrl } = event.detail;
// e.g. refresh balances, record a conversion, show your own confirmation
});
EventdetailFired when
recoilpay-wallet{ address }A wallet connects, switches account or disconnects (address: null)
recoilpay-solana-wallet{ address }A Solana wallet connects or disconnects (address: null). The address is base58
recoilpay-order-submitted{ orderId }The signed order was accepted by RecoilPay
recoilpay-complete{ orderId, status, explorerUrl }Every intent in the sentence reached a final status
recoilpay-error{ message }Anything failed. message is already written for the end user
recoilpay-cash-out-started{ tradeId }A cash-out opened
recoilpay-cash-out-ended{ tradeId, state }A cash-out settled, was refunded or failed
recoilpay-purchase-started{ tradeId }A purchase opened
recoilpay-purchase-ended{ tradeId, state }A purchase settled, was refunded or failed

orderId can be looked up at GET /api/v1/orders/{id}, and tradeId at GET /api/v1/fiat/trades/{id}. state is settled_to_fiat_payer, refunded_to_funder or failed.

A cash-out or purchase can wait hours on the user, so the widget remembers one that's under way in localStorage and shows it again on the next visit.


Configuration​

Everything is an attribute. Change one at any time and the widget updates in place.

AttributeDefault
hf-access-tokennoneYour Hugging Face token for plain-English parsing. Without it, parsing is off.
walletconnect-project-idnoneYour Reown project id. Without it, only browser wallets are offered.
modedarkdark or light.
accentRecoilPay violetButtons, focus ring and highlights, e.g. #0a7cff.
accent-textdepends on modeText colour on accent buttons.
radius12Corner radius in px.
font-familyyour page's fontAny CSS font-family value.
placeholdera sample intentText shown in the empty field.
examplestwo testnet swapsJSON array of { "label", "sentence" }, or false to hide the suggestions.
api-urlhttps://api.recoilpay.comOnly change this if we've given you a staging URL.
<recoilpay-intent
walletconnect-project-id="a1b2c3…"
mode="light"
accent="#0a7cff"
radius="8"
examples='[{"label":"Move 5 USDC to Base","sentence":"swap 5 USDC on op sepolia for USDC on base sepolia"}]'
></recoilpay-intent>

examples can also be set as a property, which saves writing JSON into HTML:

document.querySelector('recoilpay-intent').examples = [
{ label: 'Move 5 USDC to Base', sentence: 'swap 5 USDC on op sepolia for USDC on base sepolia' },
];

Using a wallet your page already connected​

If your site already has its own connect flow, hand the connected wallet to the widget. Any EIP-1193 provider works. The widget then uses that wallet and never shows its own picker:

const widget = document.querySelector('recoilpay-intent');
widget.provider = window.ethereum; // or your wagmi connector's provider, a WalletConnect session, etc.

Your site owns that connection, so the widget doesn't show a Disconnect link for it.


How the widget handles wallets​

  • Discovery. Wallets that announce themselves via EIP-6963 are listed by name and icon, so several installed wallets can coexist. Older wallets that only set window.ethereum are offered as a fallback.
  • Returning visitors are reconnected silently. The widget remembers which wallet was used and asks it for accounts without a prompt. If the wallet no longer authorises the site, the visitor just sees Connect wallet again.
  • Changes made in the wallet are followed: switching accounts updates the widget, and disconnecting resets it.
  • Missing testnets. Most wallets don't ship with testnets enabled. When a swap needs a chain the wallet doesn't know, the widget asks the wallet to add it, then switches.

Styling and isolation​

The widget renders inside Shadow DOM:

  • Your page's CSS can't reach in. Global resets, * { … !important } rules and button styles don't affect it.
  • Its CSS can't leak out. Nothing is added to your <head>.
  • The one thing it takes from your page is the font, so it blends in. Set font-family if you'd rather it didn't.

The wallet picker opens as a modal above your page at z-index: 2147483000. WalletConnect's QR modal is WalletConnect's own and renders over the page in the same way.


Content-Security-Policy​

If your site sends a CSP header, allow the following:

DirectiveAllowWhy
script-srchttps://cdn.jsdelivr.net (or your own origin, if self-hosting)The widget and its chunks
connect-srchttps://api.recoilpay.comQuotes, orders, supported assets
connect-srchttps://router.huggingface.coPlain-English parsing
connect-srchttps://*.publicnode.comPermit2 allowance reads, via the RPCs listed by /api/v1/chains
connect-srchttps://*.walletconnect.org wss://*.walletconnect.org https://*.walletconnect.com wss://*.walletconnect.com https://*.web3modal.orgOnly if you set a WalletConnect project id
img-srcdata:Wallet icons supplied by wallets as data URIs
script-srchttps://widget.dojah.ioThe identity check before a first purchase, loaded only then
frame-srchttps://identity.dojah.ioThe identity check's window

The widget's own styles are applied as constructed stylesheets, so it doesn't need style-src 'unsafe-inline'.


Size and compatibility​

  • First load: about 115 KB gzipped, which is React plus the intent flow. WalletConnect is a separate download, fetched only when a visitor picks it.
  • Browsers: needs module scripts, custom elements and Shadow DOM, which every current browser supports.
  • Loading twice is safe. Two embeds on one page, or the script included twice, won't conflict.

Troubleshooting​

You seeLikely cause
The element is emptyThe script didn't load. Check the network tab and your CSP script-src.
"Natural-language parsing isn't set up here"hf-access-token is missing or empty.
Every sentence gets the example hintThe Hugging Face token is invalid or out of credit, or router.huggingface.co is blocked by your CSP.
No WalletConnect optionwalletconnect-project-id is missing or empty.
WalletConnect QR never appearsThe project id is wrong, or your origin isn't on the project's domain allowlist at cloud.reown.com.
"No wallet found in this browser"No extension wallet is installed and WalletConnect isn't configured.
"Could not reach the solver network"api.recoilpay.com is blocked (CSP connect-src, an ad blocker) or briefly unavailable.
"X isn't supported yet"No active solver fills that token or chain today. See Supported networks.

Anything else: see Troubleshooting, or talk to us.