Skip to main content

POST /invoice

Creates a new invoice and returns the deposit address the buyer should pay to, along with a hosted paymentUrl you can redirect them to.

Request

POST /invoice
x-session-nonce: <millisecond timestamp>
x-session-signature: <EIP-191 sig over sess:{nonce}:{sha256(body)}>
{
"value": "100",
"chainId": 1,
"description": "Hosting plan XLarge",
"acceptedTokens": [
"1:0xdac17f958d2ee523a2206206994597c13d831ec7"
],
"deadlineSecs": 86400
}
FieldTypeRequiredNotes
valuestringyesAmount in vsCurrency (USD by default). Use "0" for tip-jar mode — any deposit matching acceptedTokens marks the invoice paid.
chainIdnumberyesEVM/TVM chain id of the deposit network.
descriptionstringyesFree-form merchant copy displayed on checkout.
acceptedTokensstring[]yesWhitelist of "{chainId}:{tokenAddress}" entries (≥ 1). Native coin uses 0x0000000000000000000000000000000000000000.
deadlineSecsnumbernoValidity window from creation. Defaults to 90 hours.
dataobjectnoFree-form merchant metadata (line items, references). Defaults to {}.
addressstringnoPre-derived deposit address. When omitted, the server allocates a fresh smart-wallet index for the owner.
ownerstringnoThe recover-from-sig flow derives owner from the session key. Provide it to assert and double-check the recovered identity.
tokenstringnoPreferred display token. Defaults to acceptedTokens[0].

Response

201 Created

{
"guid": "29f3b386-7c4a-4f6e-9d2b-1a8e3c5f7d09",
"createdAt": 1730393820,
"deadline": 1730480220,
"status": "init",
"value": "100",
"token": "1:0xdac17f958d2ee523a2206206994597c13d831ec7",
"chainId": 1,
"data": {},
"description": "Hosting plan XLarge",
"address": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b",
"owner": "0xMerchantAddress…",
"paidAmount": "0",
"payments": [],
"acceptedTokens": ["1:0xdac17f958d2ee523a2206206994597c13d831ec7"],
"paymentUrl": "https://app.feemaker.io/?invoice=29f3b386-7c4a-4f6e-9d2b-1a8e3c5f7d09"
}

status lifecycle: init → partialPaid → paid on payment, or init → expired once deadline lapses without sufficient deposits.

Examples

import { Wallet, sha256, toUtf8Bytes } from 'ethers';

const SESSION_PRIVATE_KEY = '0x...';
const body = JSON.stringify({
value: '100',
chainId: 1,
description: 'Hosting plan XLarge',
acceptedTokens: ['1:0xdac17f958d2ee523a2206206994597c13d831ec7'],
deadlineSecs: 86400,
});
const nonce = Date.now();
const hash = sha256(toUtf8Bytes(body)).slice(2);
const sig = await new Wallet(SESSION_PRIVATE_KEY)
.signMessage(`sess:${nonce}:${hash}`);

const r = await fetch('https://api.feemaker.io/invoice', {
method: 'POST',
headers: {
'x-session-nonce': String(nonce),
'x-session-signature': sig,
'x-encryption': 'none',
},
body,
});
const { guid, paymentUrl } = await r.json();

PoW challenge

When the server is configured with pow_difficulty > 0, a successful create also requires a fresh challenge from POST /invoice/challenge plus a satisfying nonce, both attached to the body. Default deployments leave PoW disabled.

Proof-of-work challenge

POST /invoice/challenge
{ "address": "0xMerchantAddress…" }

Response:

{
"challenge": "<random hex>",
"difficulty": 18,
"expiresInSecs": 600,
"algorithm": "sha256(challenge || ':' || nonce) leading zero bits >= difficulty"
}

Find a nonce such that sha256(challenge || ':' || nonce) has at least difficulty leading zero bits, then include challenge and nonce alongside the rest of your POST /invoice body.