Daejin Developers

Place Order

POST
/orders

Place a limit order signed by the market maker's own wallet. Requires write. The body must carry a fresh wallet signature for this exact payload; the API never creates signatures and does not take payment by itself — collateral must already be funded/deposited on chain by the trading flow. EVM orders are EIP-712 typed data signed against the ClobExchange contract for that chain; Solana orders are base58 ed25519 signatures over a canonical string.

FieldMeaning
signatureGenerated locally with wallet.signTypedData(domain, types, message) (EVM) or ed25519.sign (Solana). Do not copy a signature from an old GET /orders/{orderId} response.
marketAPI book label, for example evm:97:314 (EVM) or 373 (Solana).
message.marketNumeric on-chain market id inside the EIP-712 signed payload, for example 314.
priceTicks from 1 to 999. 660 means 66.0c.
sizeShare units with 6 decimals on every chain, whatever the collateral token's decimals: 18181818 means 18.181818 shares. The exchange converts to the token's own units; never scale by the token's decimals yourself.
saltUnique per order. A new salt means a new signature.
expiryUnix seconds, 0 for no expiry. Part of the signed message.
typeGTC (default), GTD, FOK, FAK or POST_ONLY. Not signed.

A successful post answers 201 { "accepted": true, "order": { "id": "o104", "status": "open", … }, "fills": [ … ] }; the id is what GET /orders/{orderId} and DELETE /orders/{orderId} take. 422 { "accepted": false, "reason": "…" } is a well-formed request the matching rules refused — invalid signature, a closed market, or a wallet that cannot cover the order.

Prerequisites for API-only trading

ValueMeaning
PRIVATE_KEYThe market maker's own wallet key. The API never creates signatures.
RPC_URLRPC for the chain where the CLOB market lives, for funding calls.
ENGINE_MARKETEngine label, for example evm:97:314.
CLOB_MARKET_IDNumeric on-chain market id, for example 314.
CLOB_EXCHANGEClobExchange contract address. This is the EIP-712 verifying contract.
PAYMENT_TOKENCollateral token used by this market, usually USDC for EVM markets.

Three raw API requests

This is the exact API flow a market maker bot performs after it has found the engine market label and generated a fresh order signature locally. The base URL is this environment's engine URL — the Server URL at the top of this page. X-API-Key is an API key with the write scope, from the portal's keys page.

GET https://app-staging.daejin.xyz/clob/v1/marketsPOST https://app-staging.daejin.xyz/clob/v1/orderscontent-type: application/json{  "user": "0x87c088C6F5550516b8Ee80a9608149e140779850",  "market": "evm:97:314",  "action": "buy",  "outcome": "YES",  "price": 660,  "size": "18181818",  "type": "GTC",  "salt": 1787155825206943,  "expiry": 0,  "signature": "0x<fresh_eip712_signature>"}GET https://app-staging.daejin.xyz/clob/v1/markets/evm%3A97%3A314/bookGET https://app-staging.daejin.xyz/clob/v1/markets/evm%3A97%3A314/orders

The playground on this page cannot invent signature. Use the example client (examples/market-maker/mm-client.mjs in prediction-backend) or your own bot to sign first, then send the POST body.

Signature generation — EVM

Market makers generate a new signature locally for each unique order payload. The private key stays in their bot or vault; the platform only receives the resulting signature. EVM orders are EIP-712 typed data signed against the ClobExchange contract for that chain.

import { Wallet } from "ethers";const wallet = new Wallet(process.env.PRIVATE_KEY);const chainId = 97;const clobMarketId = 314n;const clobExchange = process.env.CLOB_EXCHANGE;const domain = {  name: "tokenplay-clob-order-v1",  version: "1",  chainId,  verifyingContract: clobExchange,};const types = {  Order: [    { name: "user", type: "address" },    { name: "market", type: "uint256" },    { name: "action", type: "uint8" },    { name: "outcome", type: "uint8" },    { name: "price", type: "uint256" },    { name: "size", type: "uint256" },    { name: "salt", type: "uint256" },    { name: "expiry", type: "uint256" },  ],};const message = {  user: wallet.address,  market: clobMarketId,  action: 0,        // 0 = buy, 1 = sell  outcome: 0,       // 0 = YES, 1 = NO  price: 660n,      // 66.0c  size: 18181818n,  // 18.181818 shares  salt: BigInt(Date.now()) * 1000n,  expiry: 0n,};const signature = await wallet.signTypedData(domain, types, message);console.log(JSON.stringify({  user: wallet.address,  market: "evm:97:314",  action: "buy",  outcome: "YES",  price: Number(message.price),  size: message.size.toString(),  type: "GTC",  salt: Number(message.salt),  expiry: Number(message.expiry),  signature,}, null, 2));

Signature generation — Solana

Solana orders are ed25519 signatures over a canonical UTF-8 string, base58-encoded. Not EIP-712, and not a struct hash — the message is readable text because Phantom refuses to signMessage a binary payload. Four details that each surface as a bare invalid signature:

FieldRule
marketThe plain decimal on-chain market id, "373". Not evm:<chainId>:<id>, not sol:<pubkey> — a non-numeric label throws before the signature is read.
outcomeLowercase inside the signed message, UPPERCASE in the JSON body. The asymmetry is real.
signaturebase58. A base64 signature is rejected.
salt, expiryBoth must be sent. The settler rebuilds the exact signed bytes for settle_fill; an order placed without its salt can never settle.
import { ed25519 } from "@noble/curves/ed25519";import bs58 from "bs58";// 64-byte Solana secret key (Phantom export, or a solana-keygen JSON array).const secret = bs58.decode(process.env.SOLANA_PRIVATE_KEY);const seed = secret.slice(0, 32);   // ed25519 signs with the seedconst user = bs58.encode(ed25519.getPublicKey(seed));const order = {  user,  market: 373,      // numeric on-chain id — also the engine label  action: "buy",    // "buy" | "sell"  outcome: "yes",   // lowercase HERE, uppercase in the body  price: 660,       // ticks, 660 = 66.0c  size: 18181818,   // raw payment-mint units (USDC is 6dp)  salt: Date.now() * 1000,  expiry: 0,        // unix SECONDS, 0 = no expiry};// Byte-identical to the on-chain OrderData::message().const message = [  "tokenplay-clob-order-v1",  order.user, order.market, order.action, order.outcome,  order.price, order.size, order.salt, order.expiry,].join("|");const signature = bs58.encode(  ed25519.sign(new TextEncoder().encode(message), seed));console.log(JSON.stringify({  user,  market: "373",  action: order.action,  outcome: order.outcome.toUpperCase(),  price: order.price,  size: String(order.size),  type: "GTC",  salt: order.salt,  expiry: order.expiry,  signature,}, null, 2));

Or with the bundled clients: npm run mm -- place … (EVM) and npm run mm:solana -- place --action buy --outcome YES --price 400 --size 5000000 (Solana). Swap place for message to print the signed string and the signature without sending anything — the fastest way to see why a signature is being rejected. To prove the signature is really checked, post the same body with any other valid signature: the engine answers 422 {"accepted":false,"reason":"invalid signature"}.

Authorization

X-API-Key<token>

Developer-portal API key (tp_live_… / tp_test_…), also accepted as Authorization: Bearer tp_…. Public market data needs no key; user-scoped reads need read, placing and cancelling need write.

In: header

Request Body

application/json

Response Body

curl -X POST "https://example.com/orders" \  -H "Content-Type: application/json" \  -d '{    "user": "string",    "market": "evm:56:123",    "action": "buy",    "outcome": "YES",    "price": 0,    "size": "string",    "signature": "string"  }'
Empty