Skip to main content

Documentation Index

Fetch the complete documentation index at: https://pond.dflow.net/llms.txt

Use this file to discover all available pages before exploring further.

During development, you can use the developer endpoints without an API key. For production use, request an API key for higher rate limits.
1

Set up

Imports, env config, and the wallet/connection used across every step.
import "dotenv/config";

import { TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from "@solana/spl-token";
import { Connection, Keypair, VersionedTransaction } from "@solana/web3.js";
import bs58 from "bs58";

const DFLOW_METADATA_API_URL = process.env.DFLOW_METADATA_API_URL ?? "https://dev-prediction-markets-api.dflow.net";
const DFLOW_TRADE_API_URL = process.env.DFLOW_TRADE_API_URL ?? "https://dev-quote-api.dflow.net";
const DFLOW_PROOF_API_URL = process.env.DFLOW_PROOF_API_URL ?? "https://proof.dflow.net";
const DFLOW_API_KEY = process.env.DFLOW_API_KEY; // optional; not needed for dev endpoints
const DFLOW_SETTLEMENT_MINT = process.env.DFLOW_SETTLEMENT_MINT ?? "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"; // USDC (markets also support CASH)
const SOLANA_RPC_URL = process.env.SOLANA_RPC_URL ?? "https://api.mainnet-beta.solana.com";

const connection = new Connection(SOLANA_RPC_URL, "confirmed");
const keypair = Keypair.fromSecretKey(
  bs58.decode(process.env.SOLANA_PRIVATE_KEY ?? "")
);

const headers: HeadersInit = {};
if (DFLOW_API_KEY) headers["x-api-key"] = DFLOW_API_KEY;
2

Discover Kalshi markets

Use the Metadata API to fetch Kalshi events. Filter by series ticker, category, or tag to find relevant markets.
// Fetch active Kalshi BTC markets (series ticker prefix: KXBTC)
const response = await fetch(
  `${DFLOW_METADATA_API_URL}/api/v1/events?withNestedMarkets=true&seriesTickers=KXBTC&status=active`,
  { headers }
);

const data = await response.json();
const events = data.events;

// Pick a market to trade
const targetEvent = events[0];
const targetMarket = targetEvent?.markets?.[0];

const { yesMint, noMint } = targetMarket.accounts[DFLOW_SETTLEMENT_MINT];
Full filtering options.
3

Verify KYC

Kalshi requires KYC before a wallet can receive outcome tokens. Check verification status using the Proof API before submitting a trade.
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.
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.
5

Sign and send the transaction

if (!order.transaction) {
  throw new Error(`Order request failed: ${JSON.stringify(order)}`);
}

const txBuffer = Buffer.from(order.transaction, "base64");
const transaction = VersionedTransaction.deserialize(txBuffer);

transaction.sign([keypair]);
const signature = await connection.sendRawTransaction(transaction.serialize(), {
  skipPreflight: true,
  maxRetries: 2,
});
console.log(`  Tx: https://orbmarkets.io/tx/${signature}`);

await connection.confirmTransaction({
  signature,
  blockhash: transaction.message.recentBlockhash,
  lastValidBlockHeight: order.lastValidBlockHeight,
});
6

Track positions

After trading, the user holds YES or NO outcome tokens. Use filter_outcome_mints to identify which wallet mints are outcome tokens.
const [splAccounts, token2022Accounts] = await Promise.all([
  connection.getParsedTokenAccountsByOwner(keypair.publicKey, { programId: TOKEN_PROGRAM_ID }),
  connection.getParsedTokenAccountsByOwner(keypair.publicKey, { programId: TOKEN_2022_PROGRAM_ID }),
]);

const allMints = [...splAccounts.value, ...token2022Accounts.value]
  .map((a) => a.account.data.parsed.info)
  .filter((info) => Number(info.tokenAmount.amount) > 0)
  .map((info) => info.mint);

// Step 1: filter wallet mints down to outcome tokens
const filterRes = await fetch(`${DFLOW_METADATA_API_URL}/api/v1/filter_outcome_mints`, {
  method: "POST",
  headers: { ...headers, "Content-Type": "application/json" },
  body: JSON.stringify({ addresses: allMints }),
}).then((r) => r.json());

const outcomeMints: string[] = filterRes.outcomeMints ?? [];

if (outcomeMints.length === 0) {
  console.log("No outcome tokens in wallet.");
} else {
  // Step 2: fetch full market objects for each outcome mint
  const batchRes = await fetch(`${DFLOW_METADATA_API_URL}/api/v1/markets/batch`, {
    method: "POST",
    headers: { ...headers, "Content-Type": "application/json" },
    body: JSON.stringify({ mints: outcomeMints }),
  }).then((r) => r.json());

  batchRes.markets.forEach((market: any) => {
    const accounts = market.accounts[DFLOW_SETTLEMENT_MINT];
    const position = allMints.includes(accounts.yesMint) ? "YES"
      : allMints.includes(accounts.noMint) ? "NO"
      : "NONE";
    console.log(market.ticker, position);
  });
}
Full portfolio view with balances and market metadata.
7

Redeem after settlement

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.
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.

Find Markets

Advanced market discovery: filter by category, tag, and status.

Track User Positions

Full portfolio view with balances and market metadata.

Monitor Market Lifecycle

Track status changes and know when redemption opens.

Redeem Outcome Tokens

Complete redemption flow including scalar outcome payouts.

API Routes