Partner Integration Guide

Build an agent or MCP client on Bitrefill's MCP server: authentication, guest checkout, affiliate attribution and how to test.

Bitrefill sells gift cards, phone top-ups and eSIMs for crypto, cards and account balance. Our MCP server lets an AI agent do the whole job: find a product, check the price, buy it and read the result. This guide explains how to connect your agent or MCP client, how guest checkout works when the buyer has no Bitrefill account, how affiliate attribution pays you, and how to test without spending real money.

It is written for developers at partner companies. You need to know what MCP (Model Context Protocol) is and how HTTP and OAuth work. Everything else is explained here. If you only want to connect a desktop client to your own account, the eCommerce MCP Server page and the Setup Guides are the shorter read.

At a glance

TopicValue
Server URLhttps://api.bitrefill.com/mcp
ProtocolMCP over Streamable HTTP, stateless, JSON-RPC 2.0
AuthenticationOAuth 2.1 (user login or client credentials) or a Bitrefill API key
Guest checkoutYes. A client-credentials token can buy without a Bitrefill account
Affiliate attributionAdd ?ref=<your code> to the server URL
Tools12 in total, 7 visible to guests
Rate limit30 requests per minute per token
Response formatTOON text for read tools, JSON for buy-products

Quick start

📘

In short

Point an MCP client at https://api.bitrefill.com/mcp, log in when the client asks, and call search-products. Add ?ref=<code> to the URL if you have an affiliate code.

Connect a client

Most MCP clients only need the URL. Claude, ChatGPT, Cursor and Claude Code discover our OAuth server automatically and open the Bitrefill login and consent screen in a browser.

claude mcp add --transport http bitrefill "https://api.bitrefill.com/mcp?ref=YOURCODE"

If your product has no browser, or your users have no Bitrefill account, use a client-credentials token instead. If your own company is the buyer, an API key is the simplest option. Both are described in The three modes in detail.

First request with curl

curl -s https://api.bitrefill.com/mcp \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

The Accept header must contain both application/json and text/event-stream. Without both you get HTTP 400.

The purchase flow

Every purchase follows the same four tool calls.

StepToolWhat you get
1search-productsProduct ids (slugs) such as amazon_com-usa
2get-product-detailsDenominations (package_value), prices, accepted payment methods and whether a recipient is needed
3buy-productsAn invoice with payment instructions
4get-invoice-by-idPayment and delivery status, then the codes

A guest purchase of a 10 USD Amazon gift card paid in Bitcoin:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "buy-products",
    "arguments": {
      "cart_items": [{ "product_id": "amazon_com-usa", "package_value": "10" }],
      "payment_method": "bitcoin",
      "email": "[email protected]"
    }
  }
}

The email argument only exists for guests. Logged-in buyers get the receipt on their account email.

How the server behaves

📘

In short

One authenticated HTTP POST per JSON-RPC message, no sessions, compact text responses, 30 requests per minute.

Stateless transport. Each POST is handled on its own. There is no Mcp-Session-Id header. GET /mcp and DELETE /mcp answer HTTP 405, because there are no server-initiated streams and no sessions to close. JSON-RPC notifications receive HTTP 202 with an empty body.

Every request is authenticated. The bearer token decides who the buyer is and which tools appear in tools/list. Do not cache the tool list across different tokens.

Response format. Read tools return TOON (Token-Oriented Object Notation), a compact text format built for language models. It reads like indented key-value lines and uses about 40 percent fewer tokens than JSON. buy-products returns plain JSON so payment details can be parsed exactly. Errors are always JSON with isError: true (see Errors).

Rate limit. 30 requests per minute per bearer token, or per IP address when there is no token. Over the limit you get HTTP 429 with the body below. Back off for the rest of the minute.

{ "status": "rate_limit_reached", "message": "You have reached your request quota, if you need a higher quota please contact us" }

Country. The server reads the country from the IP address of the caller. When an AI vendor proxies the traffic, that is the vendor's IP. The country is used for fiat payment rules on some product categories. Crypto payments are not affected.

Attribution data. Every invoice created through MCP records the channel, your OAuth client and, when present, your affiliate id. That is what Bitrefill uses to report on your integration.

Authentication

📘

In short

Three ways in. OAuth login gives you the user's own account. OAuth client credentials give you a guest. An API key gives you your own account.

OAuth user login

The buyer is the person who logged in. All 12 tools. For consumer assistants where each user has, or creates, a Bitrefill account.

OAuth client credentials

The buyer is a guest. 7 tools. For agents that buy for people without an account, or before they log in.

API key

The buyer is the account that owns the key. All 12 tools. For server-side agents buying on behalf of your company.

OAuth discovery

Our authorization server follows the MCP authorization specification (OAuth 2.1). Start from the protected resource metadata and let it lead you to the rest:

GET https://api.bitrefill.com/.well-known/oauth-protected-resource

It names the authorization server, https://api.bitrefill.com/oauth/mcp. The authorization server metadata (RFC 8414) lists the endpoints. Today they are:

PurposeEndpoint
Authorizationhttps://api.bitrefill.com/oauth/mcp/authorize
Tokenhttps://api.bitrefill.com/oauth/mcp/token
Dynamic client registrationhttps://api.bitrefill.com/oauth/mcp/register
Revocationhttps://api.bitrefill.com/oauth/mcp/revoke

Read them from the metadata instead of hard-coding them.

Facts that matter when you build your own client:

  • Scope. Request mcp.
  • Resource indicator. Send resource=https://api.bitrefill.com/mcp (RFC 8707) on authorize and token requests.
  • PKCE with S256 is mandatory.
  • Client identity. Two options. Client ID Metadata Documents (CIMD), where your client_id is an HTTPS URL to a JSON document you host, is the recommended one and the one Claude uses. Dynamic Client Registration (RFC 7591) at the register endpoint also works. A CIMD document must be public, served over HTTPS on a real hostname, and smaller than 10 KB. token_endpoint_auth_method is none for CIMD clients, and none or client_secret_post for registered clients.
  • Redirect URIs must match exactly. Loopback URIs (http://127.0.0.1:<any port>/callback) are accepted for native apps.
  • Token lifetime. Access tokens live 6 hours, refresh tokens 7 days. A refresh token is single use and the refresh response contains a new one. Revoking a refresh token also invalidates the access tokens from the same grant.
  • Rate limits. The OAuth endpoints are rate limited per IP address. If many of your users share one egress IP, register your client once and reuse it, and spread token refreshes over time.

The three modes in detail

The browser flow (authorization code). Your client redirects the user to the authorize endpoint, Bitrefill shows a login screen followed by a consent screen that names your client, and the user comes back with a code that you exchange for tokens.

The buyer is that Bitrefill account. All tools are available, including account balance, saved cards, invoice history and bill payments.

Authentication failures

A missing, invalid, expired or revoked credential gets HTTP 401 with a WWW-Authenticate: Bearer error="invalid_token" header. So does a disabled account. Compliant clients start the OAuth flow again when they see 401.

Guest accounts

📘

In short

A guest is a client-credentials token. Guests see 7 tools, must pass email to buy-products, pay with crypto or a payment link, and read their invoice with the invoice_access_token they received when buying. Keep that token safe.

What a guest is

A guest has no Bitrefill account. Bitrefill cannot tell one guest from another between requests. That has two consequences you must design for:

  • A guest has no stable identity. The checkout email and the invoice access token are the only handles on a purchase. Store them on your side if you want to show the purchase again later.
  • Ownership of an invoice is proven by its access token, not by who is calling. Anyone with invoice_id and invoice_access_token can read the invoice, including the codes. Store both like a password.

Build on email plus access token and do not assume anything about how guests are represented internally.

What guests can and cannot do

Available to guestsLogin required
search-products, get-product-detailslist-invoices (history)
buy-products with crypto, fiat and third-party payment linksbuy-products with payment_method balance or cashback
get-invoice-by-id with invoice_access_tokensaved_fiat_card_id and list-saved-fiat-cards
search_help_articles, get_help_article, list_help_articlessubmit-prepayment-step and every product that needs a bill_payment_id (bill payments, prepaid cards)
Gift delivery by email (cart_items[].gift)update-order, get-balances, balance top-ups (deposit products)

Login-only tools are not listed in tools/list for a guest token, so a well-behaved agent never sees them. Calling one anyway returns PERMISSION_DENIED. Asking for a balance payment as a guest returns INVALID_INPUT with the message "Paying with account balance requires you to be logged in".

A guest purchase, step by step

  1. Search and check the product. get-product-details returns the accepted payment_methods for that product in three groups: address_based (crypto with an on-chain address), link_only (cards, Apple Pay, Google Pay, iDEAL, EPS, Przelewy24, Bancontact, Binance Pay, Kraken Pay) and balance.
  2. Buy. Call buy-products with cart_items, payment_method and email. Fiat methods also need billing_address with at least country. fiat_card also needs city, postal_code and address_line1.
  3. Store the response. The result is a JSON object with two keys. response holds the invoice: invoice_id, invoice_access_token, expiration_minutes, cart_items, and for crypto a payment_info block (address and amount), a payment_link to the web checkout and an x402_payment_url (see Paying an MCP invoice with x402). For link-only methods payment_link is the only way to pay. agent_instructions tells the agent what to do next in plain language.
  4. Let the user pay. Crypto: send the exact amount to the address before the invoice expires. Cards and other links: open payment_link in a browser. The link contains the access token, so treat it as private.
  5. Poll. Call get-invoice-by-id with invoice_id and invoice_access_token until invoice_status is complete. Poll every 10 to 30 seconds, not faster. Statuses move through unpaid, payment_detected, payment_confirmed and complete. blocked means a compliance review that the agent cannot resolve.
  6. Deliver. Each order in the invoice reports status: delivered and redemption_info with the code, plus an access_link to its page on bitrefill.com. eSIM orders add esim_install_link. Do not rely on email for delivery. Read the codes from the tool response or send the user to the link.

To change the payment method, call buy-products again. The unpaid invoice simply expires.

Limits and checks that apply to guests

  • Purchase limits apply to guest purchases. Exceeding them returns purchase_limit_reached.
  • Fraud and compliance checks run on every invoice. A blocked invoice reports invoice_status: blocked. The errors verification_required and access_denied mean the buyer has to continue on the website. Do not retry.
  • Product availability can differ between guests and logged-in users. Balance top-up products refuse guests with user_not_registered.

From guest to account

Guests often become customers. Two paths exist:

  • Log in later through OAuth. New purchases land on the user's account. Guest invoices stay reachable with their id and access token, but they do not appear in list-invoices.
  • Create an account from the receipt. The checkout and receipt pages on bitrefill.com (reached through payment_link and the order access_link) offer to create an account. Within 30 days of completion, the purchase and its cashback can be claimed by that account.

If your product cannot open a browser at all, talk to your Bitrefill contact about login options before you build a workaround.

Affiliate attribution

📘

In short

Get an affiliate code from Bitrefill, append ?ref=<code> to the server URL, and every purchase made through that connection is attributed to you. Commission accrues in BTC per delivered order and is paid out from your Bitrefill account.

Getting a code

Affiliate status is a Bitrefill account type. Apply at bitrefill.com/integrate or ask your Bitrefill contact. After a short business verification, your account becomes an affiliate account and receives an affiliate code, shown in your account.

Passing the code

MCP. Configure the server URL as:

https://api.bitrefill.com/mcp?ref=YOURCODE

The code is read from the URL on every request, with every authentication mode (OAuth login, client credentials, API key). It must therefore be part of the URL your client stores, not something you send once.

x402 REST API. Send the header X-Bitrefill-Affiliate-Id: YOURCODE on POST /x402/invoice/create (see The x402 REST API).

Rules:

  • Codes are 6 to 16 letters and digits. Anything else is ignored.
  • Attribution fails open. An unknown or malformed code never fails the purchase. It is dropped silently and the purchase goes through without attribution. Test your attribution explicitly (see Test plan).
  • A coupon_code is a different thing. Coupons change the price for the buyer. Affiliate codes change who receives commission. Both can be present on one purchase.

What is recorded and how commission works

  • The invoice and each of its orders store your affiliate id.
  • Commission is calculated per order at the rate agreed with Bitrefill, which can differ per product. Some products are excluded from the program and earn no commission.
  • Commission counts once the order is paid and delivered. Refunded or failed orders earn nothing.
  • Attribution does not depend on who pays or how: guest or logged-in buyer, crypto, card or balance.
  • The commission report and payouts (withdrawals) are available from your Bitrefill affiliate account.

Testing

📘

In short

Read tools need no setup. For purchases, ask us to enable test products on your account: their invoices confirm automatically and nothing is charged. Test guest flows on staging or with one small real purchase. Verify affiliate attribution with a separate, non-affiliate account.

Environments

EnvironmentMCP URLNotes
Productionhttps://api.bitrefill.com/mcpReal products, real money, real delivery
StagingProvided by your Bitrefill contactTest accounts included. x402 payments settle on a test network

Discovery tools (search-products, get-product-details, help articles) are free to call on production with any token, including a guest token. Start there.

Test products

Bitrefill has a family of test products from a test provider. Their slugs end in -syldavia. Examples:

SlugBehaviour
delos-syldaviaDelivers a test code
syl-tel-syldaviaTest phone refill, needs refill_input
test-esim-data-syldaviaDelivers a test eSIM with an install link
simple-bill-syldaviaBill payment. Exercises submit-prepayment-step (login required)
slowcorp-syldaviaSlow delivery, for testing polling
failcorp-syldaviaDelivery fails on purpose

All of them belong to the test country KN, so search-products with country: "KN" lists them.

Two rules make them useful:

  1. They are hidden until Bitrefill enables them for your account. Ask your Bitrefill contact to enable test products on the account behind your API key or OAuth login. Until then, get-product-details and buy-products answer product_not_found, even if search listed the product.
  2. Invoices made only of test products confirm automatically when paid with a crypto or link-only method. No payment is needed and nothing is charged. Delivery then runs normally, so you can test the loop all the way to redemption_info. Test products that exist to simulate failures, such as failcorp-syldavia, are never confirmed automatically. Your Bitrefill contact can tell you how to exercise them.
🚧

Do not pay test products from balance

A balance payment debits your real balance, also for a test product. Use a crypto or link-only method, which is confirmed automatically for test products.

Test products are enabled per account, so a guest token cannot use them. To test a guest flow end to end, use staging, or make one small real purchase on production (for example the cheapest denomination of a gift card you can use yourself).

Interactive testing

Run npx @modelcontextprotocol/inspector, choose the Streamable HTTP transport and enter https://api.bitrefill.com/mcp. The Inspector runs the OAuth flow in the browser and lets you call tools by hand.

Test plan

CheckHowExpected
Discoverytools/list with a guest token, then with a user token7 tools, then 12
Accept headerCall without text/event-stream in AcceptHTTP 400
Search and detailssearch-products with intent, then get-product-detailsSlugs, then the package_value list and payment_methods
Guest purchasebuy-products with email and a crypto methodinvoice_id, invoice_access_token, payment_info, payment_link
Guest pollingget-invoice-by-id without the token, then with itRESOURCE_NOT_FOUND, then the invoice
Logged-in purchaseThe same purchase with an OAuth user token or API keyNo email field in the schema, receipt to the account
Balance paymentpayment_method: balance with a user tokenpayment_info.status and an invoice that confirms by itself
Missing recipientA refill product without refill_inputINVALID_INPUT with details.status: number_missing
Wrong denominationpackage_value: "7" for a product with fixed packagesinvalid_package and the list of valid values
Expired tokenWait, or revoke, then callHTTP 401, the client re-authenticates
Rate limit31 requests in one minuteHTTP 429 on the 31st
Affiliate attributionPurchase through ?ref= with a separate non-affiliate account or a guest token, then ask your Bitrefill contact to confirm the affiliate id on the invoiceAttributed. Do not use your own affiliate account as the buyer for this check, the result would be ambiguous
Payment uncertainA balance payment that times out (staging)PAYMENT_UNCERTAIN, and the agent polls instead of retrying

When something fails

Send us the invoice_id, the tool name, the UTC timestamp, your OAuth client_id or API key name (never the key itself) and the full error object. That is enough to find the request in our logs.

Errors

📘

In short

Tool errors come back as isError: true with a JSON body { error, code, details }. Retry only SERVICE_UNAVAILABLE. Never retry PAYMENT_UNCERTAIN.

{
  "error": "Invalid denomination '7' for product 'amazon_com-usa'. Available denominations: 5, 10, 25, 50, 100",
  "code": "INVALID_INPUT",
  "details": { "status": "invalid_package" }
}
codeMeaningWhat to do
VALIDATION_ERRORArguments do not match the tool schema. details lists the fieldsFix the arguments
INVALID_INPUTArguments are well formed but wrong for this purchase. details.status says whyFix and retry once
RESOURCE_NOT_FOUNDUnknown product, invoice or order, or a guest without the right access tokenCheck the ids. details.suggestions may list similar slugs
PERMISSION_DENIEDThe tool needs a logged-in userLog in, or hide the tool
SERVICE_UNAVAILABLESearch, a quote or a payment session failed temporarilyRetry with back-off
PAYMENT_UNCERTAINA balance payment may have startedDo not retry. Poll get-invoice-by-id
INTERNAL_ERRORUnexpected failure on our sideReport it with the details from When something fails
❗️

Never retry PAYMENT_UNCERTAIN

A second buy-products call after PAYMENT_UNCERTAIN can charge the balance twice. Poll get-invoice-by-id instead and let the invoice settle.

The details.status values you will meet most often: product_not_found, product_not_available, invalid_package, number_missing, payment_method_not_accepted, invalid_payment_method, purchase_limit_reached, balance_too_low, bill_payment_id_missing, coupon_invalid, verification_required, access_denied, user_not_registered, missing_fiat_payment_info, invalid_billing_country.

HTTP-level errors are separate: 400 for a bad Accept header, 401 for authentication, 405 for GET or DELETE, 429 for the rate limit.

Tool reference

ToolLoginPurposeKey arguments
search-productsNoFind products by brand, country, category or typequery, intent (required, the user's goal), country (default US), product_type, category, page, per_page (max 250)
get-product-detailsNoPrices, denominations, payment methods, recipient type, prepayment formproduct_id, currency (default BTC), language
buy-productsNoCreate an invoice and, for balance, pay itcart_items[] (product_id, package_value, refill_input, bill_payment_id, gift), payment_method, email (guests only), billing_address, saved_fiat_card_id, balance_currency, coupon_code, return_payment_link
get-invoice-by-idNoStatus, payment info, orders and codesinvoice_id, invoice_access_token (guests)
search_help_articles, get_help_article, list_help_articlesNoBitrefill help center content for support questionsquery, article id
list-invoicesYesPaid invoices of the logged-in userlimit (max 50), start, after, before, include_orders
get-balancesYesStore credit per sub-account (EUR, USD, BTC, cashback) and top-up optionscurrency
list-saved-fiat-cardsYesCards saved on the account, for saved_fiat_card_idnone
submit-prepayment-stepYesMulti-step forms for bill payments and prepaid cards, ending in a bill_payment_idproduct_id, step_number, form_data, bill_payment_id
update-orderYesTrack the remaining balance of a gift card, archive an orderorder_id, remaining_amount, is_archived

Payment methods accepted by buy-products:

GroupMethodsWhat the response contains
Address based (crypto)bitcoin, lightning, ethereum, usdc_base, usdt_erc20, usdc_solana and the other chains listed by get-product-detailspayment_info with address and amount, payment_link, x402_payment_url
Link onlyfiat_card, apple_pay, google_pay, ideal, eps, p24, bancontact, binance_pay, kraken_paypayment_link only. The buyer finishes on bitrefill.com
Balancebalance (with balance_currency EUR, USD or BTC) and cashbackPaid at once. Poll for confirmation

Fiat methods are charged in the method's standard currency, for example USD for cards. The tool description lists the currency per method. A cart holds up to 15 items. Add the same product twice for two units. Any item can be sent as a gift by email with cart_items[].gift (recipient_name, recipient_email, sender_name, optional message, send_date between 12 hours and 6 months ahead, and theme).

Paying an MCP invoice with x402

buy-products returns x402_payment_url (https://api.bitrefill.com/x402/invoice/pay) for crypto invoices. A wallet that speaks x402 can settle a USDC invoice with two HTTP calls instead of a manual transfer to the address:

  1. POST /x402/invoice/pay with the body { "invoice_id": "<id>" }. The answer is HTTP 402 with a PAYMENT-REQUIRED header (base64 JSON describing amount, asset and networks).
  2. Sign the payment with your x402 client and repeat the POST with a PAYMENT-SIGNATURE header. HTTP 200 means the payment is accepted and being settled. Poll get-invoice-by-id, or GET /x402/invoice/status?invoice_id=<id>, for delivery.

Conditions: the invoice was created with a USDC method (usdc_base, usdc_arbitrum, usdc_polygon or usdc_solana) and is younger than 15 minutes, which is the price lock. The @x402/core and @x402/mcp npm packages implement the client side.

The x402 REST API

For agents that hold a wallet but no Bitrefill account, Bitrefill also exposes a wallet-native REST API on https://api.bitrefill.com. Identity is the wallet. Calls are paid per request in USDC (0.001 to 0.002 USD), or made free by signing in with the wallet (Sign In With X). It covers the same catalog as MCP with fewer options: crypto only, no gifts, no coupons.

x402 routes
Method and pathPurpose
GET /x402/gift-cards/search, /x402/esims/search, /x402/topups/searchDiscovery with q and country
GET /x402/products/detailProduct details
GET /x402/checkout/infoStorefront information and the list of routes
POST /x402/invoice/createPrice-locked invoice for items[] (product_id, package_value, refill_input). The affiliate header goes here
POST /x402/invoice/paySettle the invoice
GET /x402/invoice/statusStatus, plus codes when signed in with the paying wallet
POST /x402/connectSign once and receive a session token to send as X-Access-Token
GET /x402/my/orders, /x402/my/esimsOrders and eSIMs of the signed-in wallet

Rate limits: 30 requests per minute per IP and 10 invoice creations per minute per IP. The OpenAPI description is at https://api.bitrefill.com/openapi.json.

Glossary

Terms used in this guide
TermMeaning
InvoiceOne payment intent. It has a UUID with dashes, a status, an access token and one or more orders
OrderOne product unit inside an invoice. It has a 24-character hex id, a delivery status and, once delivered, the code
Product id (slug)The text id of a product, for example amazon_com-usa or esim-france
package_valueThe denomination, for example "10" or "1GB, 7 Days". Ranged products accept any value between range.min and range.max
refill_inputThe recipient for refills: phone number, account id, email or username, as recipient_type says
GuestA buyer without a Bitrefill account, identified only by checkout email and invoice access token
Affiliate code6 to 16 letters and digits identifying a partner for commission
TOONToken-Oriented Object Notation, the compact text format used by read tools
x402An HTTP 402 based payment protocol for wallets, and the name of Bitrefill's wallet-native REST API

Did this page help you?