POST /poll/events
Drains pending invoice-state-change events for the authenticated owner. Each call returns events queued since the last successful drain — once consumed, events are removed from the queue. Suitable for serverless cron jobs and any client that can't hold a long-lived WebSocket open.
Request
POST /poll/events
x-session-nonce: <millisecond timestamp>
x-session-signature: <EIP-191 sig over sess:{nonce}:{sha256("")}>
The body is empty. The session signature alone identifies the owner — no
ownerAddress field is required.
Response
200 OK
{
"ownerAddress": "0xMerchantAddress…",
"events": [
{
"guid": "29f3b386-7c4a-4f6e-9d2b-1a8e3c5f7d09",
"createdAt": 1730393820,
"deadline": 1730480220,
"status": "paid",
"value": "100",
"token": "1:0xdac17f958d2ee523a2206206994597c13d831ec7",
"chainId": 1,
"data": { "description": "Hosting plan XLarge", "sku": "XL-2024" },
"description": "Hosting plan XLarge",
"address": "0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b",
"owner": "0xMerchantAddress…",
"paidAmount": "100",
"payments": [
{
"txHash": "0x4d5e6f…2d3e4f",
"logIndex": 0,
"chainId": "1",
"token": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"from": "0xfeedbeef…0000beef",
"amount": "100",
"vsValue": 100.0,
"timestamp": 1730393900
}
],
"acceptedTokens": ["1:0xdac17f958d2ee523a2206206994597c13d831ec7"]
}
]
}
Each event is the full Invoice record at the time of the change. There's
no separate "delta" payload: compare status against your locally stored copy
to decide what changed.
Examples
- JavaScript
- Python
- Bash
import { Wallet, sha256 } from 'ethers';
const SESSION_PRIVATE_KEY = '0x...';
const nonce = Date.now();
const hash = sha256('0x').slice(2); // sha256 of empty body
const sig = await new Wallet(SESSION_PRIVATE_KEY)
.signMessage(`sess:${nonce}:${hash}`);
const r = await fetch('https://api.feemaker.io/poll/events', {
method: 'POST',
headers: {
'x-session-nonce': String(nonce),
'x-session-signature': sig,
'x-encryption': 'none',
},
});
const { events } = await r.json();
from eth_account import Account
from eth_account.messages import encode_defunct
from hashlib import sha256
import time, requests
SESSION_PRIVATE_KEY = '0x...'
nonce = int(time.time() * 1000)
body_hash = sha256(b'').hexdigest()
sig = Account.sign_message(
encode_defunct(text=f'sess:{nonce}:{body_hash}'),
private_key=SESSION_PRIVATE_KEY,
).signature.hex()
resp = requests.post('https://api.feemaker.io/poll/events', headers={
'x-session-nonce': str(nonce),
'x-session-signature': sig,
'x-encryption': 'none',
})
for ev in resp.json()['events']:
print(ev['guid'], ev['status'])
SESSION_PRIVATE_KEY=0x...
NONCE=$(($(date +%s%N) / 1000000))
HASH=$(printf '' | openssl dgst -sha256 -hex | awk '{print $2}')
SIG=$(cast wallet sign --private-key $SESSION_PRIVATE_KEY "sess:$NONCE:$HASH")
curl -X POST https://api.feemaker.io/poll/events \
-H "x-session-nonce: $NONCE" \
-H "x-session-signature: $SIG" \
-H "x-encryption: none"
Polling cadence
The server holds events in-memory per session key — there's no long-poll window. Drive the loop yourself; once every 2–5 seconds is the recommended range for active payment flows. Idle merchants can poll less often without losing events: each event is stored until consumed, capped by the operator's configured retention.
Legacy bearer-token flow
A separate path exists for older Telegram-bot-style integrations that
registered an opaque token via POST /poll/register. Those clients omit
session headers and pass ownerAddress + token in the JSON body instead.
Modern integrations should use the session-key flow above.