Kalshi requires KYC before a wallet can receive outcome tokens. Check verification status using the Proof API before submitting a trade.
Verify KYC
const { verified } = await fetch( `${DFLOW_PROOF_API_URL}/verify/${keypair.publicKey.toBase58()}`).then((r) => r.json());if (!verified) { // Redirect the user to complete KYC at https://dflow.net/proof // then send them back to your app via a redirect_uri throw new Error("KYC verification required");}
Unverified users can still get quotes. Omit userPublicKey from the /order request to return pricing without requiring verification. This lets users browse and preview markets before completing KYC.
KYC alone is not sufficient. You must also implement geoblocking to restrict access in jurisdictions where prediction markets are not permitted. Prediction Market Compliance.
Cache the /verify response on your backend with a short TTL rather than calling it on every trade. Full Proof integration guide.
4
Buy YES or NO outcome tokens
Trading is a single /order call. Set inputMint to USDC (or CASH) and outputMint to either yesMint or noMint.
Buy YES or NO outcome tokens
const params = new URLSearchParams({ userPublicKey: keypair.publicKey.toBase58(), inputMint: DFLOW_SETTLEMENT_MINT, // USDC or CASH depending on the market outputMint: yesMint, // or noMint for NO amount: "100000", // 0.1 units (6 decimals)});const order = await fetch(`${DFLOW_TRADE_API_URL}/order?${params}`, { headers }).then((r) => r.json());
You can trade from any token, not just USDC. The /order endpoint automatically routes through the settlement mint. Using USDC directly is the lowest latency trade path.
Once the market is determined, check market.result and redeem by selling the outcome token back to USDC. For Kalshi markets, also verify redemptionStatus === "open" before redeeming.
Redeem after settlement
const market = await fetch( `${DFLOW_METADATA_API_URL}/api/v1/market/by-mint/${yesMint}`, { headers }).then((r) => r.json());const isDetermined = market.status === "determined" || market.status === "finalized";const settlementAccount = market.accounts?.[DFLOW_SETTLEMENT_MINT];const isRedemptionOpen = settlementAccount?.redemptionStatus === "open";if (!isDetermined) { console.log("Not yet determined:", market.status);} else if (!isRedemptionOpen) { console.log("Redemption not yet funded");} else { const result = market.result; // "yes" or "no" const redeemMint = result === "yes" ? settlementAccount.yesMint : settlementAccount.noMint; // Sell outcome token → USDC (same /order pattern as buying) const redeemParams = new URLSearchParams({ userPublicKey: keypair.publicKey.toBase58(), inputMint: redeemMint, outputMint: DFLOW_SETTLEMENT_MINT, amount: userOutcomeTokenBalance, }); const redeemOrder = await fetch(`${DFLOW_TRADE_API_URL}/order?${redeemParams}`, { headers }).then((r) => r.json()); // Sign and send using the same pattern as Step 4}
Some Kalshi markets have scalar (non-binary) outcomes where both YES and NO tokens are partially redeemable. Full redemption flow.