Skip to main content
Invoices vs orders: These guides walk through invoices (formal billing, line items, publish). The collect flow is the same for orders — use /v1/orders/* instead of /v1/invoices/* and set source_type: "ORDER" on the checkout session. See Orders and Parties & counterparties.Hosted checkout: To skip party, invoice, session, and execute steps entirely, see Hosted checkout.
End-to-end invoice collection with Node.js 18+ (fetch). For the full multi-language walkthrough, see Quick Start.

Prerequisites

  • Verified merchant account and API credentials
  • Server-side only — never embed the client secret in a browser or mobile app

Steps

1

Get an access token

const BASE_URL = "https://api.heydollr.app";

const { access_token } = await fetch(`${BASE_URL}/v1/jwt/client/obtain/token`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    client_id: process.env.DOLLR_CLIENT_ID,
    client_secret: process.env.DOLLR_CLIENT_SECRET,
  }),
}).then((r) => r.json());

const headers = {
  Authorization: `Bearer ${access_token}`,
  "Content-Type": "application/json",
};
2

Create party and counterparty

const party = await fetch(`${BASE_URL}/v1/parties/create`, {
  method: "POST", headers,
  body: JSON.stringify({
    fullname: "Amara Kamara",
    phone: "231771234567",
    email: "amara@example.com",
    country_code: "LR",
  }),
}).then((r) => r.json());

const cp = await fetch(`${BASE_URL}/v1/counterparties/create`, {
  method: "POST", headers,
  body: JSON.stringify({ relationship_type: "CUSTOMER", party_id: party.id }),
}).then((r) => r.json());
3

Create, itemize, and publish invoice

const invoice = await fetch(`${BASE_URL}/v1/invoices/create`, {
  method: "POST", headers,
  body: JSON.stringify({
    counterparty_id: cp.id,
    currency: "USD",
    note: "Consulting services",
    fee_bearer: "PAYER",
    as_payment_link: true,
  }),
}).then((r) => r.json());

await fetch(`${BASE_URL}/v1/invoices/${invoice.id}/items/add`, {
  method: "POST", headers,
  body: JSON.stringify({ name: "Strategy Session", currency: "USD", qty: 1, amount: 250 }),
});

await fetch(`${BASE_URL}/v1/invoices/publish/${invoice.id}`, { method: "PUT", headers });
4

Session, payment account, execute

import { randomUUID } from "crypto";

const session = await fetch(`${BASE_URL}/v1/sessions/checkout`, {
  method: "POST", headers,
  body: JSON.stringify({ source_id: invoice.id, source_type: "INVOICE" }),
}).then((r) => r.json());

const account = await fetch(
  `${BASE_URL}/v1/payment-accounts/create?operation_type=COLLECTION`,
  {
    method: "POST", headers,
    body: JSON.stringify({
      account_name: "Amara MTN Wallet",
      provider: "MTN_MOMO_LBR",
      method: "MTN_MOMO_LBR",
      party_id: party.id,
      country_code: "LR",
      insensitive_account_number: "231771234567",
    }),
  }
).then((r) => r.json());

const referenceId = randomUUID();
const execution = await fetch(`${BASE_URL}/v1/executions/collection`, {
  method: "POST", headers,
  body: JSON.stringify({
    session_id: String(session.id),
    payment_account_id: String(account.id),
    currency: "USD",
    reference_id: referenceId,
  }),
}).then((r) => r.json());
Store referenceId before the request. Use predictions to resolve method/provider from phone when possible.
5

Poll status

const status = await fetch(
  `${BASE_URL}/v1/status/collection/${referenceId}`,
  { headers }
).then((r) => r.json());
Expect PENDINGPROCESSINGCOMPLETED. Do not re-execute while PROCESSING.

Try it yourself

Hosted checkout

Mobile money and card on a Dollr-hosted page — fastest path.

Orders

Same collect flow with source_type: ORDER and /v1/orders/*.

Direct checkout

One API call to create source — API-embedded flow.
Last modified on May 22, 2026