Settley Send is rail-agnostic payment and claim infrastructure for cross-border support money. A sender creates a transfer in Telegram, pays through the live funding rail for that payment, and receives a claim code/link that the recipient can use to choose a payout method.
Base USDC is the live wallet-funding rail today. Solana and additional rails are planned next, but they are not live payment options yet.
- Telegram sender intake with natural-language parsing and a button-based transfer form.
- FX estimates for supported destination currencies before the sender pays.
PAY-...payment references for sender funding.- Deterministic deposit-address support for the live funding rail. In production today, that means Base USDC.
- Strict payment detection with exact amount matching, expiry checks, tx-hash reuse protection, underpayment handling, and manual review for unsafe cases.
- Over-$200 identity review with age validation, restricted-jurisdiction controls, uploaded ID evidence, local OCR name/DOB matching, and OpenSanctions screening.
STL-...claim codes and claim links for receivers.- Live receiver payout methods:
- Supported African payout corridors, loaded dynamically from the active payout provider's coverage API.
- Bank payout where the corridor supports
BANK. - EVM wallet payout.
- Visible but not live in this deployment:
- Mobile money.
- Airtime.
- Virtual card.
PAY-...: sender payment reference. This identifies the pending payment and the deposit address for the live rail selected by the product. Today that rail is Base USDC.STL-...: receiver claim code. This is generated only after payment is detected and can be shared by Telegram, email, or copy/paste.- Receiver PIN: optional. A PIN only blocks payout when claim metadata explicitly has
requireClaimPin === "true".
-
Under-$200 transfer:
- Sender opens
@settleysendbot. - Sender enters amount, destination, recipient name, and sender display name.
- Bot shows FX estimate.
- Sender confirms.
- Bot creates a
PAY-...reference and payment link. - Sender sends the exact amount on the payment page's live rail. Today that is USDC on Base.
- Bot detects payment and generates an
STL-...claim code/link.
- Sender opens
-
Over-$200 transfer:
- Same quote flow as above.
- Bot pauses before payment link creation.
- Sender provides legal name, DOB, country of residence, government ID number, and an uploaded ID image.
- Bot validates age and jurisdiction.
- Bot runs OCR matching against name/DOB and screens through OpenSanctions.
- If clear and matched, bot unlocks the payment link. Today that link asks for Base USDC.
- If unclear, the transfer remains pending manual review.
-
Funded payment recovery:
- Sender has a funded
PAY-...reference but no delivered claim code. - Bot can recover/generate the
STL-...code from the funded payment.
- Sender has a funded
-
Bank payout:
- Receiver opens the claim link or types
/claim STL-.... - Receiver chooses bank payout when the destination corridor supports
BANK. - Receiver enters bank name and account number.
- Payout-provider account lookup verifies or presents the account details for confirmation.
- Bot locks the claim, initiates the provider payout, sends the partner deposit, and waits for webhook status.
- The payout webhook marks the claim
settledon success and the bot notifies sender and receiver.
- Receiver opens the claim link or types
-
Wallet payout:
- Receiver claims
STL-.... - Receiver chooses crypto wallet.
- Receiver enters a valid EVM
0x...address. - Bot transfers the held funds onchain and marks the claim
settled.
- Receiver claims
- Wrong network or token: not treated as a normal payment. Funds are ledgered where detected and routed to reconciliation/recovery instead of blindly crediting or refunding.
- Underpayment: bot reports the partial amount seen and the remaining amount needed.
- Overpayment: accepted only in attended scans when the payer matches the expected wallet; excess remains recoverable through the recorded transfer plan.
- Expired unpaid payment:
PAY-...is marked expired and the sender must start again. - Late payment after expiry: recent expired payments can be revived to funded if the matching onchain payment arrives.
- Duplicate transaction hash: one source transaction cannot fund two claims.
- Sweep failure from deterministic deposit vault: payment is held for operator review before claim-code issuance.
- Missing payer wallet or missing refund transfer breakdown: refund is queued for manual reconciliation.
- Sender refund before claim-code generation: funded
PAY-...payments can be refunded to the recorded payer wallet when the transfer plan is available. - Payment already generated a claim code: automatic sender refund is blocked; operator review is required.
- Provider bank payout failure: claim returns to
funded, so the receiver can retry bank details or choose another live payout method. - Provider webhook success: claim moves to
settledand notifications are sent. - Compliance underage/restricted jurisdiction/blocked watchlist: transfer is blocked before payment link creation.
- Compliance OCR mismatch, unreadable document, OpenSanctions error, or ambiguous result: transfer stays in manual review.
Telegram sender
-> transfer intake + FX estimate
-> compliance gate when required
-> PAY- reference + live-rail payment link
-> deterministic deposit address
-> scanner confirms exact payment
-> STL- claim code/link
-> receiver chooses bank or wallet payout
-> payout webhook or onchain payout settles the claim
- Node.js and TypeScript.
- Telegraf for Telegram.
- Supabase for claims, sessions, compliance profiles, deposits, and document metadata.
- Supabase Storage for private ID document uploads.
tesseract.jsfor local OCR.- OpenSanctions for sanctions/watchlist screening.
- Viem for EVM reads/transfers.
- Active payout provider for live African payout coverage, payout initiation, and webhook settlement.
| Var | Purpose |
|---|---|
TELEGRAM_BOT_TOKEN |
Telegram bot token from BotFather. |
OPENAI_BASE_URL / OPENAI_API_KEY / OPENAI_MODEL |
Optional LLM intent parsing. |
MAX_USD |
Per-transfer safety cap. |
DAPP_URL |
Public frontend origin for payment links. |
PAY_API_BASE_URL |
Backend API origin used by payment pages. |
DIRECT_PAYMENT_ASSET |
Live sender funding asset. Use base:usdc for today's production rail. |
DEPOSIT_FACTORY_BASE_ADDRESS |
Base deterministic deposit factory. Required for today's Base USDC copy-address payments. |
EVM_ESCROW_WALLET |
Settlement wallet used by scanners and payouts. |
AUTO_REFUND_UNMATCHED_DEPOSITS |
Keep false unless recovery policy and gas are ready. |
SWITCH_SERVICE_KEY |
Switch service key for bank payouts. |
SWITCH_WEBHOOK_SECRET |
HMAC secret for Switch webhook verification. |
SWITCH_ASSET |
Partner payout deposit asset, currently base:usdc. |
OPENSANCTIONS_API_KEY |
OpenSanctions API key. |
OPENSANCTIONS_DATASET |
OpenSanctions dataset, default default. |
OPENSANCTIONS_MIN_SCORE |
Screening threshold, default 0.7. |
COMPLIANCE_DOCUMENT_BUCKET |
Private Supabase Storage bucket for ID uploads. |
COMPLIANCE_MAX_UPLOAD_MB |
Max ID image size. |
COMPLIANCE_DOCUMENT_RETENTION_DAYS |
Retention window for uploaded ID files. |
COMPLIANCE_OCR_LANG |
Tesseract OCR language, default eng. |
SUPABASE_URL / SUPABASE_SERVICE_ROLE_KEY |
Supabase database and storage access. |
ADMIN_USERNAME / ADMIN_PASSWORD |
Basic auth for admin routes. |
POST /api/admin/compliance/review: approve, reject, or block a pending compliance profile.GET /api/admin/claims: list recent claims.POST /api/admin/refund: refund a claim to the recorded payer or an admin-provided recipient.GET /api/admin/deposits: list unmatched deposits.POST /api/admin/refund-deposit: refund an unmatched deposit.POST /api/admin/deposits/issue-claim: issue a claim code from an unmatched deposit.POST /api/admin/recover-stuck: reset stale paying/refunding claims.
npm install
cp .env.example .env
npm run typecheck
npm test
npm startStatic marketing/payment pages live in public/. The backend serves API routes plus Telegram and payout-provider webhooks.
- Backend bot runtime deploys to Fly.
- Public pages deploy to Cloudflare Pages.
- Apply Supabase migrations before enabling document upload in production.
- Create the private
compliance-documentsstorage bucket through the migration. - Do not expose
SUPABASE_SERVICE_ROLE_KEYto any public frontend.
Settley Send runs risk-based identity review and sanctions screening. For over-threshold transfers it collects identity details, asks for uploaded ID evidence, uses local OCR to compare document text with the provided name and DOB, checks age and jurisdiction, and screens through OpenSanctions.
This is not full vendor KYC. It does not yet include liveness, selfie matching, document authenticity checks, proof of address, or a third-party identity verification vendor.