Nodela Poster

Nodela Developer Docs

The Nodela API enables you to create invoices, process payments in cryptocurrency and verify transactions programmatically. Whether you're building and e-commerce platform,subscription service, or any application that needs to accept crypto payments, this API provides the tools you need.

Getting Started

To get started, you'll first need to create a Nodela account and generate your API keys from the dashboard. Once your acount is set up, you can immeduately create test API keys (nk_test_) to begin building and testing your integration. However, live API keys (nk_live_) are only available after your KYC/KYB verification has been approved. Before you can process real payments in production,you'll need to complete the KYC/KYB process and await approval, which typically takes 24-48 hours.

Authentication

All API endpoints except /health require authentication. Nodela uses API keys to authenticate requests and determine whether to operate in test or live mode.

This is the credential for your account — invoice creation, transaction history, everything below. It should never appear in code that runs in a browser. If you're building your own checkout UI and need to call payment endpoints from the payer's browser, use a client secret instead — a separate, invoice-scoped credential covered in .

API Key Types

Key TypePrefixEnvironmentDescription
Testnk_test_SandboxFor development and testing. No real transactions occur.
Livenk_live_ProductionFor live applications. Real cryptocurrency transactions are processed.

Your API key carries significant privileges, so keep it secure. Do not share your API key in publicly accessible areas such as GitHub repositories, client-side code, or public forums.

Authentication Methods

Nodela supports two methods for providing your API key. Both methods are equally valid, so choose whichever fits best with your existing codebase or framework.

Method 1: X-API-Key Header

Pass your API key directly using the X-API-Key HTTP header. This is a straightforward approach commonly used in API integrations.

curl -X GET "https://api.nodela.co/v1/transactions" \
  -H "X-API-Key: nk_test_abc123..."

Method 2: Bearer Token

Pass your API key using the standard Authorization header with the Bearer scheme. This approach follows OAuth 2.0 conventions and may integrate more easily with frameworks that expect Bearer token authentication.

curl -X GET "https://api.nodela.co/v1/transactions" \
  -H "Authorization: Bearer nk_test_abc123..."

Which Method Should I Use?

Both authentication methods provide identical functionality. Here are some considerations:

  • X-API-Key is more explicit and makes it immediately clear that you're using an API key for authentication. It's a good choice for simple integrations or when you want clarity in your codebase.
  • Bearer Token follows the widely-adopted OAuth 2.0 convention. If your application already uses Bearer tokens for other services, or if your HTTP client or framework has built-in support for Bearer authentication, this method may fit more naturally into your existing code.

Authentication Errors

If authentication fails, the API returns one of the following errors:

Missing API Key (401)

json
{
  "success": false,
  "error": {
    "code": "missing_api_key",
    "message": "API key is required"
  }
}

This error occurs when no API key is provided in either the X-API-Key or Authorization header.

Invalid API Key (401)

json
{
  "success": false,
  "error": {
    "code": "invalid_api_key",
    "message": "API key not found, inactive, or malformed"
  }
}

This error occurs when the provided API key does not exist, has been deactivated, or is incorrectly formatted.

API Reference

Base URL

https://api.nodela.co

Health Check

Check the API and database health status. Use this endpoint to verify that the API is operational before making other requests.

GET /health

Authentication: Not required

Request

curl -X GET "https://api.nodela.co/health"

Response

Success (200 OK)

json
{
  "status": "healthy"
}

Service Unavailable (503)

json
{
  "status": "unhealthy"
}

Invoices

The Invoices API allows you to create payment requests and verify their status. When you create an invoice, your customer receives a checkout URL where they can complete their payment in cryptocurrency.

Create Invoice

Create a new invoice for payment processing. The response includes a checkout_url that you should redirect your customer to for payment.

POST /v1/invoices

Authentication: Required

Request Body Parameters
ParameterTypeRequiredDescription
amountnumberYesAmount to charge. Must be greater than 0.
currencystringYesCurrency code (e.g. USD, NGN, EUR, GBP, JPY). See Supported Currencies for the full list of 63 supported currencies.
success_urlstringNoURL to redirect the customer after successful payment.
cancel_urlstringNoURL to redirect the customer if payment is cancelled.
webhook_urlstringNoURL for payment notifications. Must be pre-registered in your dashboard.
referencestringNoYour unique reference for this invoice. Must be unique across all your invoices.
customerobjectNoCustomer information object.
customer.emailstringConditionalCustomer's email address. Required if customer object is provided.
customer.namestringNoCustomer's name.
titlestringNoInvoice title displayed to the customer.
descriptionstringNoInvoice description with additional details.
Request
curl -X POST "https://api.nodela.co/v1/invoices" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: nk_test_abc123..." \
  -d '{
    "amount": 50000,
    "currency": "NGN",
    "success_url": "https://example.com/success",
    "cancel_url": "https://example.com/cancel",
    "webhook_url": "https://example.com/webhook",
    "reference": "order-12345",
    "customer": {
      "email": "customer@example.com",
      "name": "John Doe"
    },
    "title": "Product Purchase",
    "description": "Order #12345"
  }'
Response

Success (201 Created)

json
{
  "success": true,
  "data": {
    "id": "6789abcd1234567890abcdef",
    "invoice_id": "NOD_INV_1234567890",
    "original_amount": 50000,
    "original_currency": "NGN",
    "amount": 33.33,
    "currency": "USD",
    "exchange_rate": 1500.0,
    "webhook_url": "https://example.com/webhook",
    "customer": {
      "email": "customer@example.com",
      "name": "John Doe"
    },
    "checkout_url": "https://checkout.nodela.co/checkout?id=6789abcd1234567890abcdef&invoice_id=NOD_INV_1234567890",
    "client_secret": "cs_live_9f2e1a4b8c3d...",
    "status": "pending",
    "created_at": "2026-01-30T12:00:00Z"
  }
}
Response Fields
FieldTypeDescription
idstringUnique identifier of the invoice.
invoice_idstringHuman-readable invoice ID. Format: NOD_INV_* for live mode, NOD_TEST_INV_* for test mode.
original_amountnumberAmount in the original currency you specified.
original_currencystringThe currency code you provided in the request.
amountnumberAmount in USD after conversion (if applicable).
currencystringAlways USD.
exchange_ratenumberExchange rate used for conversion. Only present if currency conversion occurred.
webhook_urlstringWebhook URL for notifications. Only present if provided in the request.
customerobjectCustomer information. Only present if provided in the request.
checkout_urlstringURL to redirect your customer to the hosted checkout or overlay.
client_secretstringAuthorizes the self-managed checkout API for this invoice only — safe to use in browser code, unlike your API key. Only ever returned here. See Self-Managed Checkout.
statusstringInvoice status. One of: pending, paid, cancelled, expired.
created_atstringISO 8601 timestamp of when the invoice was created.
Currency Conversion

When you create an invoice with a non-USD currency, the amount is automatically converted to USD using real-time exchange rates. Nodela supports 63 currencies across 6 regions.

  • The exchange_rate field in the response shows the conversion rate used
  • The final amount is rounded to 2 decimal places

Supported Currencies

RegionCurrencies
Americas (10)USD, CAD, MXN, BRL, ARS, CLP, COP, PEN, JMD, TTD
Europe (16)EUR, GBP, CHF, SEK, NOK, DKK, PLN, CZK, HUF, RON, BGN, HRK, ISK, TRY, RUB, UAH
Africa (11)NGN, ZAR, KES, GHS, EGP, MAD, TZS, UGX, XOF, XAF, ETB
Asia (15)JPY, CNY, INR, KRW, IDR, MYR, THB, PHP, VND, SGD, HKD, TWD, BDT, PKR, LKR
Middle East (8)AED, SAR, QAR, KWD, BHD, OMR, ILS, JOD
Oceania (3)AUD, NZD, FJD

Example Conversion:

InputExchange RateOutput
50,000 NGN1,500 NGN/USD33.33 USD
Error Responses

Validation Error (400)

json
{
  "success": false,
  "error": {
    "code": "validation_error",
    "message": "Amount is required; Currency must be a supported currency code (e.g. USD, NGN, EUR, GBP)"
  }
}

Invalid Webhook (400)

json
{
  "success": false,
  "error": {
    "code": "invalid_webhook",
    "message": "Webhook URL not registered for this user"
  }
}

Duplicate Reference (409)

json
{
  "success": false,
  "error": {
    "code": "duplicate_reference",
    "message": "duplicate reference: order-12345"
  }
}

Currency Service Unavailable (503)

json
{
  "success": false,
  "error": {
    "code": "currency_error",
    "message": "Exchange rate service unavailable"
  }
}

Verify Invoice

Verify the status of an invoice and retrieve payment details. Use this endpoint to confirm that a payment was successful before fulfilling an order.

GET /v1/invoices/{invoice_id}/verify

Authentication: Required

URL Parameters
ParameterTypeRequiredDescription
invoice_idstringYesThe invoice ID to verify. Format: NOD_INV_* or NOD_TEST_INV_*
Request
curl -X GET "https://api.nodela.co/v1/invoices/NOD_INV_1234567890/verify" \
  -H "X-API-Key: nk_test_abc123..."
Response

Success (200 OK)

json
{
  "success": true,
  "data": {
    "id": "6789abcd1234567890abcdef",
    "invoice_id": "NOD_INV_1234567890",
    "reference": "order-12345",
    "original_amount": 50000,
    "original_currency": "NGN",
    "amount": 33.33,
    "currency": "USD",
    "exchange_rate": 1500.0,
    "title": "Product Purchase",
    "description": "Order #12345",
    "status": "paid",
    "paid": true,
    "customer": {
      "email": "customer@example.com",
      "name": "John Doe"
    },
    "created_at": "2026-01-30T12:00:00Z",
    "payment": {
      "id": "abc123def456789012345678",
      "network": "ethereum",
      "token": "USDT",
      "address": "0x1234567890abcdef...",
      "amount": 33.33,
      "status": "completed",
      "tx_hash": ["0xabc123..."],
      "transaction_type": "credit",
      "payer_email": "customer@example.com",
      "created_at": "2026-01-30T12:05:00Z"
    }
  }
}
Response Fields
FieldTypeDescription
idstringUnique identifier of the invoice.
invoice_idstringHuman-readable invoice ID.
referencestringYour reference provided during creation. Only present if provided.
original_amountnumberAmount in the original currency.
original_currencystringThe original currency code.
amountnumberAmount in USD.
currencystringAlways USD.
exchange_ratenumberExchange rate used. Only present if conversion occurred.
titlestringInvoice title. Only present if provided.
descriptionstringInvoice description. Only present if provided.
statusstringInvoice status. One of: pending, paid, cancelled, expired.
paidbooleanWhether the invoice has been paid.
customerobjectCustomer information. Only present if provided.
created_atstringISO 8601 timestamp of invoice creation.
paymentobjectPayment details. Only present if a payment exists.
Payment Object Fields
FieldTypeDescription
idstringUnique payment ID.
networkstringBlockchain network used. Examples: ethereum, polygon.
tokenstringToken used for payment. Examples: USDT, USDC.
addressstringWallet address that received the payment.
amountnumberAmount received in USD.
statusstringPayment status.
tx_hasharrayArray of transaction hashes on the blockchain.
transaction_typestringAlways credit for deposits.
payer_emailstringPayer's email address. Only present if provided.
created_atstringISO 8601 timestamp of the payment.
Verifying Payment Success

To confirm a payment was successful, check that the invoice is paid and that the amount matches what you expected:

  1. paid equals true
  2. amount matches the expected payment amount
javascript
if (data.data.paid === true && data.data.amount === expectedAmount) {
  // Payment confirmed - fulfill the order
}
Important Notes
  • The payment object is only included when a payment has been made
  • Test API keys can only verify test invoices (NOD_TEST_INV_*)
  • Live API keys can only verify live invoices (NOD_INV_*)
  • The invoice must belong to the authenticated user
Error Responses

Invoice Not Found (404)

json
{
  "success": false,
  "error": {
    "code": "invoice_not_found",
    "message": "Invoice not found"
  }
}

Transactions

The Transactions API allows you to list all payments made through your API integration with pagination support.

List Transactions

Retrieve a paginated list of all transactions made through your API integration. This endpoint returns invoices created via the API, not payment links.

GET /v1/transactions

Authentication: Required

Query Parameters
ParameterTypeDefaultDescription
pageinteger1Page number (1-indexed).
limitinteger20Number of items per page. Maximum: 100.
Request
# Get first page with default limit
curl -X GET "https://api.nodela.co/v1/transactions" \
  -H "X-API-Key: nk_test_abc123..."

# Get page 2 with 50 items per page
curl -X GET "https://api.nodela.co/v1/transactions?page=2&limit=50" \
  -H "X-API-Key: nk_test_abc123..."
Response

Success (200 OK)

json
{
  "success": true,
  "data": {
    "transactions": [
      {
        "id": "6789abcd1234567890abcdef",
        "invoice_id": "NOD_INV_1234567890",
        "reference": "order-12345",
        "original_amount": 50000,
        "original_currency": "NGN",
        "amount": 33.33,
        "currency": "USD",
        "exchange_rate": 1500.0,
        "title": "Product Purchase",
        "description": "Order #12345",
        "status": "paid",
        "paid": true,
        "customer": {
          "email": "customer@example.com",
          "name": "John Doe"
        },
        "created_at": "2026-01-30T12:00:00Z",
        "payment": {
          "id": "abc123def456789012345678",
          "network": "ethereum",
          "token": "USDT",
          "address": "0x1234567890abcdef...",
          "amount": 33.33,
          "status": "completed",
          "tx_hash": ["0xabc123..."],
          "transaction_type": "credit",
          "payer_email": "customer@example.com",
          "created_at": "2026-01-30T12:05:00Z"
        }
      }
    ],
    "pagination": {
      "page": 1,
      "limit": 20,
      "total": 45,
      "total_pages": 3,
      "has_more": true
    }
  }
}
Pagination Object
FieldTypeDescription
pageintegerCurrent page number.
limitintegerItems per page.
totalintegerTotal number of transactions.
total_pagesintegerTotal number of pages.
has_morebooleanWhether more pages exist after the current page.
Pagination Example

Here's how to iterate through all transactions:

async function getAllTransactions() {
  const allTransactions = [];
  let page = 1;
  let hasMore = true;

  while (hasMore) {
    const response = await fetch(`https://api.nodela.co/v1/transactions?page=${page}&limit=100`, {
      method: "GET",
      headers: {
        "X-API-Key": "nk_test_abc123..."
      }
    });

    const data = await response.json();

    allTransactions.push(...data.data.transactions);
    hasMore = data.data.pagination.has_more;
    page++;
  }

  return allTransactions;
}
Important Notes
  • Only returns invoices created via API integration (not payment links)
  • Transactions are sorted by creation date, newest first
  • The payment object contains only the credit (deposit) payment
  • Test API keys return test transactions; live keys return live transactions
  • The maximum limit is 100; values above this are automatically capped

Errors

All API errors follow a consistent format to make error handling straightforward in your application.

Error Response Format

json
{
  "success": false,
  "error": {
    "code": "error_code",
    "message": "Human-readable error message"
  }
}

Error Codes Reference

HTTP StatusCodeDescription
400invalid_requestMalformed JSON or invalid request format.
400validation_errorRequired fields missing or invalid values provided.
400invalid_webhookWebhook URL has not been registered for this user.
401missing_api_keyNo API key was provided in the request headers.
401invalid_api_keyAPI key not found, inactive, or incorrectly formatted.
401missing_client_secretNo client secret was provided — see Self-Managed Checkout.
401invalid_client_secretClient secret malformed, for the wrong invoice, or the invoice is no longer pending.
404invoice_not_foundInvoice does not exist or does not belong to the authenticated user.
409duplicate_referenceThe reference has already been used for another invoice.
429rate_limitedToo many requests — see Rate Limits under Self-Managed Checkout.
500internal_errorAn unexpected server error occurred.
502chain_api_errorUpstream payment service unavailable. Safe to retry.
503currency_errorExchange rate service is temporarily unavailable.

Error Handling Example

const response = await fetch("https://api.nodela.co/v1/invoices", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-API-Key": "nk_test_abc123..."
  },
  body: JSON.stringify({
    amount: 50000,
    currency: "NGN"
  })
});

const data = await response.json();

if (!data.success) {
  switch (data.error.code) {
    case "validation_error":
      console.error("Invalid request:", data.error.message);
      break;
    case "duplicate_reference":
      console.error("Reference already exists:", data.error.message);
      break;
    case "invalid_api_key":
      console.error("Authentication failed:", data.error.message);
      break;
    case "currency_error":
      console.error("Try again later:", data.error.message);
      break;
    default:
      console.error("Error:", data.error.message);
  }
} else {
  // Success - process the invoice
  console.log("Invoice created:", data.data.invoice_id);
}

Embedded Checkout

Instead of redirecting your customer to checkout_url, you can open the exact same hosted checkout as an overlay on top of your own page — a single script tag, no redirect, no iframe wiring of your own.

Overview

nodela.js is a small script (under 10 KB) you load from Nodela's CDN. It renders the same checkout UI as the hosted redirect flow — network and currency selection, QR or wallet payment, live confirmation — inside a sandboxed iframe it manages for you, and reports back to your page via simple callbacks.

Use this when you want your customer to never leave your site, but don't want to build a payment UI yourself. If you want full control over the UI instead, see .

Hosted RedirectEmbedded CheckoutSelf-Managed
Customer leaves your site?YesNoNo
You build the UI?NoNoYes
Integration effortLowestLowHighest

Installation

Add the script tag anywhere on the page you want the checkout available. It attaches window.Nodela once loaded — no build step, no package to install.

html
<script src="https://cdn.nodela.co/nodela.js"></script>

That's it — there is no npm package. nodela.js is plain JavaScript that attaches window.Nodela; it works the same way whether your page is a static HTML file, or built with React, Vue, or anything else — just call Nodela.open() from your own click handler.

Nodela.open()

Opens the checkout overlay. Create the invoice from your backend first (see ) and pass its invoice_id here — never create the invoice from the browser, since that requires your secret API key. Everything else about the invoice — amount, currency, description, customer — is resolved server-side from that id, so there's nothing else to pass.

Options
OptionTypeRequiredDescription
baseUrlstringYesYour checkout domain, e.g. https://checkout.nodela.co.
invoiceIdstringYesThe invoice_id from Create Invoice.
titlestringNoOverrides the invoice's own title in the header.
themestringNo"light" | "dark" | "auto". Defaults to auto.
onSuccessfunctionNoCalled with the settlement result once payment is confirmed on-chain.
onClosefunctionNoCalled whenever the overlay closes, for any reason.
onCancelfunctionNoCalled when the payer explicitly cancels.
onErrorfunctionNoCalled if something goes wrong during checkout.
Example
html
<script src="https://cdn.nodela.co/nodela.js"></script>

<button id="pay-btn">Pay with Crypto</button>

<script>
  document.getElementById("pay-btn").addEventListener("click", () => {
    Nodela.open({
      baseUrl: "https://checkout.nodela.co",
      invoiceId: "NOD_INV_1234567890",
      theme: "auto",
      onSuccess: (result) => {
        console.log("Payment confirmed:", result.txHash);
        window.location.href = "/thank-you";
      },
      onClose: () => {
        console.log("Checkout closed");
      }
    });
  });
</script>

Handling Events

The callbacks passed to Nodela.open() are the whole integration surface — you don't need to listen for anything else. onSuccess receives:

typescript
interface PaymentSuccessResult {
  invoiceId: string;
  txHash?: string;
  amount: number;
  currency: string;
  network?: string;
  token?: string;
}

Treat onSuccess firing as "the payer finished" — for order fulfillment, still confirm with from your backend (or a webhook) before shipping anything, the same way you would after a hosted-redirect payment.

Call Nodela.close() at any time to dismiss the overlay programmatically.

Self-Managed Checkout

Build your own checkout UI end to end — network and currency selection, deposit address, status — calling this API directly. No hosted checkout, no overlay, no Nodela UI anywhere in your customer's flow.

Overview

Every endpoint below is plain JSON, authenticated the same way as the rest of this API but with a different credential (see , next). There is exactly one piece of this you can't do with a plain HTTP call: letting the payer connect and sign with a crypto wallet. That still needs something running in the browser that speaks to wallets — covers the one line of code that handles it for you.

Everything else — listing networks, listing currencies, generating a deposit address, polling status, rendering your own QR code — is a call you can make from anywhere: your backend, your frontend, a mobile app.

The Client Secret

Your API key authorizes your entire account and must never run in browser code. The endpoints below happen from the payer's browser, so they use a different credential instead: a client secret, scoped to exactly one invoice.

API Key (`nk_*`)Client Secret (`cs_*`)
Belongs inYour backend onlyBrowser code — your frontend, or the SDK
ScopeYour entire accountOne specific invoice
Obtained fromYour dashboardThe Create Invoice response, once
Stops workingWhen you revoke itAs soon as the invoice leaves pending

Get one by creating an invoice from your backend as usual — the response now includes client_secret alongside checkout_url. Hand that value to whatever runs in the payer's browser.

Every endpoint below accepts it the same two ways your API key works:

X-Client-Secret: cs_test_9f2e1a4b8c3d...

Test-mode invoices (created with an nk_test_ key) get a cs_test_ secret; live invoices get cs_live_. The two are not interchangeable, the same way test and live API keys aren't.

List Networks

GET /v1/invoices/{invoice_id}/networks

Authentication: Client secret

curl -X GET "https://api.nodela.co/v1/invoices/NOD_INV_1234567890/networks" \
  -H "X-Client-Secret: cs_test_abc123..."
Response
json
{
  "success": true,
  "data": [
    { "id": "base", "name": "Base", "active": true },
    { "id": "polygon", "name": "Polygon", "active": true },
    { "id": "bsc", "name": "BNB Chain", "active": false }
  ]
}

id is what you pass to every other endpoint below as network — a stable identifier, never an internal numeric chain id.

List Currencies

GET /v1/invoices/{invoice_id}/currencies?network=base

Authentication: Client secret

Query Parameters
ParameterTypeRequiredDescription
networkstringYesA network id from List Networks.
Response
json
{
  "success": true,
  "data": [
    { "symbol": "ETH", "name": "Ethereum", "address": "0x0", "decimals": 18, "icon_url": null, "is_native": true },
    { "symbol": "USDC", "name": "USD Coin", "address": "0x8335...", "decimals": 6, "icon_url": null, "is_native": false }
  ]
}

Only is_native: true currencies can be paid by sending to a plain deposit address. A non-native token means the payer needs a connected wallet — route them to instead of Deposit Address.

Generate a Deposit Address

POST /v1/invoices/{invoice_id}/deposit-address

Authentication: Client secret

Request Body
ParameterTypeRequiredDescription
networkstringYesA network id from List Networks.

No token field — a deposit address only ever exists for a network's native currency (see is_native: true in List Currencies), so it's resolved for you from network alone. Paying with any other token means signing a transaction, not sending to a static address — use (or ) instead. A native currency isn't limited to a deposit address either — it can go through Wallet Quote just as well, if that's what the payer prefers.

Amount and payer email are never accepted here either — they come from the invoice itself, so a payer can't change what they're being asked to pay.

curl -X POST "https://api.nodela.co/v1/invoices/NOD_INV_1234567890/deposit-address" \
  -H "Content-Type: application/json" \
  -H "X-Client-Secret: cs_test_abc123..." \
  -d '{ "network": "base" }'
Response
json
{
  "success": true,
  "data": {
    "payment_address": "0x71C8b3d4a1e...",
    "amount": "0.0161",
    "amount_smallest_unit": "16100000000000000",
    "token": "ETH",
    "barcode": "ethereum:0x71C8b3d4a1e..."
  }
}

token in the response is the network's native currency — it tells you what was resolved, not what you chose. barcode is ready to hand to any QR code library as-is.

Check Payment Status

GET /v1/invoices/{invoice_id}/status

Authentication: Client secret

const res = await fetch(
  `https://api.nodela.co/v1/invoices/${invoiceId}/status`,
  { headers: { "X-Client-Secret": clientSecret } }
);
const { data } = await res.json();

if (data.status === "paid" || data.status === "swept") {
  // Settled — safe to show a success state
}
Response
json
{
  "success": true,
  "data": {
    "status": "pending",
    "tx_hash": "0xabc123...",
    "expires_at": "2026-01-30T12:30:00Z",
    "payment": {
      "address": "0x71C8b3d4a1e...",
      "crypto_amount": "0.0161"
    }
  }
}

Two things worth building for explicitly:

  • A tx_hash appearing is not settlement — an on-chain transaction still needs confirmation. Only status reaching paid or swept means done. Keep polling past the first hash.
  • If payment.address ever changes between polls, the payer sent less than quoted — this is a fresh address/amount for the remaining balance. Show it in place of the old one rather than continuing to watch an address that will never fill.

A 2–3 second polling interval is reasonable — see .

Request a Wallet Payment Quote

POST /v1/invoices/{invoice_id}/wallet-quote

Authentication: Client secret

Give it the payer's connected wallet address and which currency they're paying with, and it returns a route: one or more transactions to sign, in order — a swap, then a bridge, then the final settlement transfer, for instance. If you're not driving the signing yourself, does this whole exchange for you in one call.

Request Body
ParameterTypeRequiredDescription
payer_wallet_addressstringYesThe connected wallet address that will sign.
networkstringYesA network id from List Networks.
tokenstringYesA currency symbol from List Currencies, on network — any of them, native included.

A native currency can be paid either way — , or a connected wallet, same as here. This prices the attempt itself before asking for a route — there's no separate call to make first.

Response
json
{
  "success": true,
  "data": {
    "payment_id": "abc123def456",
    "steps": [
      {
        "items": [
          { "to": "0x...", "data": "0x...", "value": "0", "chain_id": 8453 }
        ]
      }
    ]
  }
}

Sign every item in every step with the connected wallet, in order — switch to chain_id first if it doesn't match the wallet's current network. After each transaction is submitted, report it to Wallet Tx below.

Report a Signed Transaction

POST /v1/invoices/{invoice_id}/wallet-tx

Authentication: Client secret

Request Body
ParameterTypeRequiredDescription
payment_idstringYesFrom the wallet-quote response.
tx_hashstringYesHash of the transaction you just submitted.

Best-effort — settlement is still confirmed independently via , this just gives verification a head start. Call it once per signed item.

Headless Wallet Connect

Wallet payment is the one part of self-managed checkout that genuinely needs something running in the browser — a wallet has to be discovered, connected, and a transaction has to be signed. Doing that yourself means adding wagmi or an equivalent library to your bundle, and handling wallet discovery (EIP-6963) yourself so a payer with more than one wallet installed isn't left to whichever one happened to win window.ethereum.

The same nodela.js script from includes a headless helper that does all of it for you — discover wallets, connect, quote, sign every step, report each hash — behind one function call. It has no UI of its own; you keep your button, your loading state, your success screen.

It prefers the payer's injected browser wallet directly (EIP-1193, discovered via EIP-6963) — a desktop extension, or a mobile in-app browser. When none is found, which is the normal case for a payer on a plain mobile browser tab with a wallet app installed rather than an extension, it falls back to WalletConnect automatically, using Nodela's own Cloud project — you never register one of your own. That fallback loads as a separate script, on demand, only for a payer who actually needs it, so it never adds weight to nodela.js itself for anyone else.

Installation
html
<script src="https://cdn.nodela.co/nodela.js"></script>
The whole integration
javascript
document.getElementById("pay-with-wallet-btn").addEventListener("click", async () => {
  try {
    const result = await Nodela.headless.payWithWallet({
      invoiceId: "NOD_INV_1234567890",
      clientSecret: "cs_test_abc123...", // from your backend
      network: "base", // from List Networks
      token: "USDC", // from List Currencies — native currencies work here too
      onStatusChange: (status) => {
        // "connecting" -> "switching_network" -> "awaiting_signature" -> "confirming"
        updateYourOwnSpinner(status);
      },
    });

    console.log("Submitted:", result.txHash);
    // Now watch for settlement:
  } catch (err) {
    alert("Payment failed: " + err.message);
  }
});
Payers with more than one wallet

A payer with several wallet extensions installed (MetaMask and Rabby, say) has no single window.ethereum to fall back on — which extension holds that slot is a race between them on page load, entirely outside your control or the payer's intent. Calling payWithWallet with no providerId in that situation throws rather than guessing. Use discoverWallets to list what's installed (via EIP-6963) and let the payer pick:

javascript
const wallets = await Nodela.headless.discoverWallets();
// [{ rdns: "io.metamask", name: "MetaMask", icon: "data:..." },
//  { rdns: "io.rabby", name: "Rabby Wallet", icon: "data:..." },
//  { rdns: "walletconnect", name: "WalletConnect", icon: "" }]  <- always last, when configured

// Render your own picker from `wallets`, then:
const result = await Nodela.headless.payWithWallet({
  invoiceId: "NOD_INV_1234567890",
  clientSecret: "cs_test_abc123...",
  providerId: chosenWallet.rdns,
  onStatusChange: updateYourOwnSpinner,
});

Safe to skip entirely when there's exactly one EIP-6963 wallet installed — payWithWallet resolves it on its own. With zero, it falls back to the legacy window.ethereum global (a wallet that hasn't adopted EIP-6963 yet), then to WalletConnect if that's empty too — the normal case for a payer on a plain mobile browser tab.

Pass providerId: "walletconnect" directly to skip straight to it — useful for an explicit "Scan QR code" option in your own UI, alongside whatever injected wallets discoverWallets found.

Then watch for settlement

payWithWallet resolves once the transaction is submitted, not once it's settled — the same distinction as above. Use watchPayment to know when it's actually done:

javascript
const stopWatching = Nodela.headless.watchPayment({
  invoiceId: "NOD_INV_1234567890",
  clientSecret: "cs_test_abc123...",
  onConfirmed: ({ txHash }) => {
    console.log("Settled:", txHash);
    window.location.href = "/thank-you";
  },
});

// Call stopWatching() if the user navigates away
Options
FunctionOptionDescription
payWithWalletinvoiceIdThe invoice being paid.
payWithWalletclientSecretFrom your backend's Create Invoice response.
payWithWalletproviderIdWhich wallet to use, from discoverWallets() — required when more than one is available. "walletconnect" goes straight to WalletConnect.
payWithWallettestModeSimulates the whole flow with no real wallet or funds — useful in development.
payWithWalletonStatusChangeconnecting, switching_network, awaiting_signature, or confirming.
watchPaymentinvoiceId` / `clientSecretSame as payWithWallet — which invoice to poll.
watchPaymentpollingIntervalMsHow often to check. Defaults to 3000.
watchPaymenttestModeSimulates settlement after a short delay — pairs with payWithWallet's testMode.
watchPaymentonConfirmedFires once with the settled transaction hash.

Nodela.headless also exposes the pieces above as standalone functions, if you want to drive the sequence yourself instead of one payWithWallet call: discoverWallets(), isWalletAvailable(), connectWallet(providerId?), and generateQrData(options) for rendering your own QR from a deposit address.

That's the entire wallet-connect integration — no provider setup, no chain configuration, no WalletConnect project of your own. If you'd rather drive the wallet yourself (you already use wagmi for something else, say), call and directly — the SDK is a convenience, not a requirement.

Rate Limits

The endpoints on this page have their own budget, separate from your account-level API key limits — they're reachable from any payer's browser, not just your trusted backend.

ScopeLimitNotes
Per client secret20 requests / minuteCovers a normal integration comfortably — a status poll every few seconds plus occasional quote/address calls.
Per source IP60 requests / minuteA floor across all secrets from one place, independent of the per-secret limit.

Exceeding either returns 429 with error code rate_limited.

Webhooks

Set up webhooks to receive real-time notifications about events in your account.

Integrations

Connect Nodela with your favorite tools and platforms.

Security

Nodela implements several security measures to protect your account and transactions. This section covers the built-in security features and best practices for keeping your integration secure.

Built-in Security Features

API Key Isolation

Test and live environments are completely isolated from each other. Test API keys (nk_test_) can only access test data, and live API keys (nk_live_) can only access production data. This separation ensures that development and testing activities never interfere with real transactions or customer data.

Webhook URL Verification

To prevent unauthorized parties from receiving your payment notifications, webhook URLs must be pre-registered in your dashboard before they can be used. When you create an invoice with a webhook_url, the API verifies that the URL has been registered to your account. If the URL is not registered, the request is rejected with an invalid_webhook error.

User Isolation

All invoices and transactions are scoped to the authenticated user. You can only access, verify, or list invoices that belong to your account. Attempting to access another user's invoice returns an invoice_not_found error, preventing any data leakage between accounts.

Keeping Your API Keys Safe

Your API keys grant access to your Nodela account and should be treated like passwords. Follow these best practices to keep them secure:

Never expose keys in client-side code. API keys should only be used in server-side code. Never include them in JavaScript that runs in the browser, mobile app source code, or any code that end users can access.

Use environment variables. Store your API keys in environment variables rather than hardcoding them in your source code. This prevents accidental exposure through version control systems.

# .env file (never commit this to version control)
NODELA_API_KEY=nk_live_abc123...

Add sensitive files to .gitignore. Ensure that files containing API keys (like .env) are listed in your .gitignore file to prevent them from being committed to your repository.

gitignore
# .gitignore
.env
.env.local
.env.production

Use different keys for different environments. Use test keys for development and staging environments, and reserve live keys for production only. This limits exposure and makes it easier to rotate keys if needed.

Limit access within your team. Only share API keys with team members who need them. Use your dashboard's team management features to control access rather than sharing keys directly.

Monitor your API usage. Regularly review your transaction history and API logs in the dashboard. Unusual activity could indicate that your keys have been compromised.

Regenerating API Keys

If you suspect your API key has been compromised, or as part of routine security practices, you should regenerate your keys immediately.

To regenerate an API key:

  1. Log in to your Nodela dashboard
  2. Navigate to the API keys section
  3. Click Regenerate next to the key you want to replace
  4. Copy the new key immediately

Important: Your new API key is only displayed once at the time of generation. Make sure to copy it and store it securely before leaving the page. Once you navigate away, you will not be able to view the full key again.

After regenerating a key, the old key is immediately invalidated. Any requests using the old key will fail with an invalid_api_key error. Be prepared to update your applications with the new key to avoid service interruptions.

SDKs

Official SDKs and community libraries to help you integrate faster.

FAQ

Find answers to common questions about Nodela.

General

What is Nodela?

Nodela is a cryptocurrency payment processing API that allows you to accept crypto payments in your application. You create invoices, redirect your customers to a checkout page, and receive payments in cryptocurrency.

What currencies can I accept?

You can create invoices in any of 63 supported fiat currencies across the Americas, Europe, Africa, Asia, the Middle East, and Oceania — including USD, EUR, GBP, NGN, JPY, AED, AUD, and many more. All payments are ultimately processed in cryptocurrency (such as USDT or USDC) on supported blockchain networks. When you create an invoice in a non-USD currency, the amount is automatically converted to USD using real-time exchange rates.

Which blockchain networks are supported?

Nodela supports payments on multiple blockchain networks including Ethereum and Polygon. The available networks are presented to your customers at checkout.

Which tokens can customers pay with?

Customers can pay using stablecoins such as USDT and USDC. The available tokens are displayed on the checkout page.

Account & API Keys

How do I get my API keys?

After creating a Nodela account, you can generate API keys from your dashboard. Navigate to the API keys section and create a new key. Test keys are available immediately, while live keys require KYC approval.

What's the difference between test and live keys?

Test keys (nk_test_) are for development and testing. They operate in a sandbox environment where no real transactions occur. Live keys (nk_live_) are for production and process real cryptocurrency payments. Data between the two environments is completely isolated.

Why can't I create a live API key?

Live API keys are only available after your KYC verification has been approved. Complete the KYC process in your dashboard and wait for approval, which typically takes 24 to 48 hours.

Can I have multiple API keys?

Yes, you can create multiple API keys for different applications or environments. Each key can be independently regenerated or revoked without affecting the others.

What happens if I regenerate my API key?

When you regenerate an API key, the old key is immediately invalidated and a new key is generated. Any requests using the old key will fail. Make sure to copy your new key immediately as it is only displayed once.

Invoices & Payments

How do I create an invoice?

Send a POST request to /v1/invoices with the amount, currency, and any optional fields like customer information or redirect URLs. The response includes a checkout_url that you redirect your customer to for payment.

How long is an invoice valid?

Invoices have an expiration period after which they can no longer be paid. The status changes to expired once this period passes. Check your dashboard for the specific expiration settings.

Can I cancel an invoice?

Invoice cancellation is managed through your dashboard. Once cancelled, the invoice status changes to cancelled and the checkout URL will no longer accept payments.

How do I know when a payment is complete?

You have two options. First, you can use the verify endpoint (GET /v1/invoices/{invoice_id}/verify) to check the invoice status. A successful payment shows status: "paid" and paid: true. Second, you can register a webhook URL to receive real-time notifications when payment status changes.

What does the exchange rate field mean?

When you create an invoice in a non-USD currency, Nodela converts the amount to USD using real-time exchange rates. The exchange_rate field shows the conversion rate that was used. For example, if you create an invoice for 50,000 NGN and the exchange rate is 1,500 NGN/USD, the final amount is 33.33 USD.

Can customers make partial payments?

The payment must match the invoice amount. Partial payments are not supported.

What happens if a customer overpays?

The checkout system is designed to request the exact invoice amount. Overpayment handling depends on the specific token and network. Contact support if this situation occurs.

Webhooks

What are webhooks?

Webhooks are HTTP callbacks that notify your server when events occur, such as when a payment is completed. Instead of polling the API to check payment status, you receive a notification automatically.

How do I set up webhooks?

Register your webhook URL in your Nodela dashboard before using it. Once registered, you can include the URL in your invoice creation requests using the webhook_url parameter.

Why is my webhook URL being rejected?

Webhook URLs must be pre-registered in your dashboard before they can be used. If you receive an invalid_webhook error, check that the URL is correctly registered and matches exactly what you're sending in the request.

Are webhook deliveries retried if my server is down?

Webhook retry policies ensure that notifications are delivered even if your server is temporarily unavailable. Check the webhook documentation for specific retry intervals and limits.

Errors & Troubleshooting

Why am I getting a "missing_api_key" error?

This error means no API key was provided in your request. Include your API key in either the X-API-Key header or the Authorization: Bearer header.

Why am I getting an "invalid_api_key" error?

This error occurs when the API key is not found, has been deactivated, or is malformed. Verify that you're using the correct key and that it hasn't been regenerated. Also ensure you're using a test key for test endpoints and a live key for production.

Why am I getting a "duplicate_reference" error?

Each invoice reference must be unique within your account. If you're receiving this error, you've already created an invoice with the same reference. Use a different reference or check your existing invoices.

Why am I getting a "currency_error" error?

This error occurs when the exchange rate service is temporarily unavailable. This is usually a temporary issue. Wait a few moments and retry your request.

Why can't I find my invoice when verifying?

The invoice_not_found error can occur for several reasons. Ensure you're using the correct invoice_id (not the MongoDB id). Verify that you're using the right API key type—test keys can only access test invoices, and live keys can only access live invoices. Also confirm that the invoice belongs to your account.

My payment shows as completed but the invoice is still pending. What happened?

There may be a slight delay between payment confirmation on the blockchain and invoice status update. If the issue persists for more than a few minutes, contact support with your invoice ID and transaction hash.

Testing

How do I test my integration?

Use test API keys (nk_test_) to create invoices in the sandbox environment. Test invoices have IDs prefixed with NOD_TEST_INV_ and no real transactions occur.

Can I simulate a successful payment in test mode?

Check your dashboard for test mode simulation options, or contact support for information on how to trigger test payments.

How do I switch from test to live mode?

Once your KYC is approved, create a live API key from your dashboard. Update your application to use the live key (nk_live_) and the production base URL. Ensure you've thoroughly tested your integration before going live.

Support

How do I contact support?

You can reach the Nodela support team through your dashboard or by emailing the support address provided in your account settings.

Where can I report a bug or request a feature?

Use the support channels in your dashboard to report bugs or submit feature requests. Include as much detail as possible, including request IDs and timestamps when reporting issues.

Need Help?

Can't find what you're looking for? Our support team is here to help.