Для разработчиков / SENDIT

HTTP API SENDIT

Два сервиса за origin SENDIT. dex-api - индекс сети только для чтения; social-api хранит сессии, подписки, кланы и тезисы. Ни один из них не является сетью: когда от ответа зависят деньги, читайте контракты.

Базовые URL

  • dex-api: https://sendit.picklechain.xyz/api/...
  • social-api: https://sendit.picklechain.xyz/api/social/...
  • Манифест: https://sendit.picklechain.xyz/op-deployment/l2-perps.json

Прокси не передаёт dex-api никаких cookie. dex-api отвечает только на GET и не ставит заголовок CORS для других origin: вызывайте его с сервера или из бота, а не со страницы на другом сайте.

Общие правила всех маршрутов

ПравилоЗначение
MethodsGET onlyA trailing slash is ignored.
Errors400 {"error":"bad parameter"}A malformed parameter. An unknown path is 404 {"error":"not found"}. An index that is not ready is 503.
Busy503 {"error":"busy"}Feed, traders and copy routes run at most four reads at once and give up after a few seconds. Retry with a backoff.
Cachingno-storeFeed-family answers are memoised for 3 seconds on the server, per argument set.
Moneydecimal stringspUSD amounts have six decimals ("1040.000000"); base-unit amounts on /bank are integers as strings. Unknown is null, never 0.
Addresses40 hex digits0x optional. Feed and market ids are 64 hex digits.
Feed paginglimit 1-50, before = nextFeed cursors look like <block>.<txIndex>.<logIndex>.<tie>. Each query key may appear once, values at most 100 characters.

Бессрочные контракты

Params
none
Returns
{ engine, orders, vault, previous: [{ engine, orders, vault, version, bank }], bank, spotRouter, pusdExchange }

The manifest's perps addresses, lower-cased and renamed (perps -> engine, perpOrders -> orders, perpVault -> vault). bank and spotRouter appear only when a bank is deployed, pusdExchange only when one is named. Without a usable manifest every address is null.

Example
{"engine":"0x57c6...e971","orders":"0xcb62...2c84","vault":"0x20f4...f459","previous":[{"engine":"0x8452...0ef2","orders":"0xe728...dae8","vault":"0xc1bd...942d","version":1,"bank":null}],"bank":"0x3366...298f","spotRouter":"0xd651...a04b","pusdExchange":"0x07b5...385f"}
Params
none
Returns
{ markets: [{ feedId, symbol, maxLeverage, spreadBps, enabled, inverse, oiLong, oiShort, fundingRatePerHour, fundingSide, closedSpreadBps, engine, legacy, engines: [...] }] }

Every market in listing order, open interest summed over the engines. fundingSide is the side that pays (long, short or null). A market listed only on an old engine shows enabled false and legacy true.

Example
{"markets":[{"feedId":"0x...","symbol":"ETH/USD","maxLeverage":25,"spreadBps":2,"enabled":true,"inverse":false,"oiLong":"3000.000000","oiShort":"1000.000000","fundingRatePerHour":"0.00625%","fundingSide":"long","closedSpreadBps":10,"engine":"0x57c6...","legacy":false,"engines":[...]}]}
Params
trader (address, required); status open (default) or all; limit 1-500, default 50
Returns
{ positions: [{ positionId, trader, feedId, isLong, leverage, margin, size, notional, entryPrice, openFee, status, tp, sl, limitPrice, openedAt, closePrice, pnl, funding, closeFee, penalty, payout, badDebt, closedAt, engine, legacy }] }

A trader's positions, newest first. tp, sl and limitPrice are null when unset.

Example
{"positions":[{"positionId":"10000042","status":"liquidated","leverage":25,"margin":"49.375000","openFee":"0.625000","pnl":"-24.673911","penalty":"12.253260","payout":"11.981817","badDebt":"0.000000", "...": "..."}]}
Params
id (1 to 19 digits, required)
Returns
{ position: { ...position, closeReason, trades: [...] } }

One position with its last 10 trades. closeReason is closed, take-profit, stop-loss, limit, liquidated or null. An unknown id answers 404 {"error":"There is no such position."}.

Example
{"position":{"positionId":"10000042","closeReason":"liquidated","trades":[{"kind":"liquidation","netPnl":"-38.018183","penalty":"12.253260"}]}}
Params
trader (address, optional); feed (32-byte feed id, optional); limit 1-500, default 50
Returns
{ trades: [{ txHash, block, at, positionId, trader, feedId, kind, isLong, price, notional, fee, pricePnl, netPnl, penalty, engine, legacy }] }

Opens, closes and liquidations. kind is open, close or liquidation; pricePnl and penalty are null on opens. Without trader, copy accounts' trades are left out.

Example
{"trades":[{"kind":"close","fee":"0.500000","pricePnl":"0.097399","netPnl":"-0.902401","penalty":"0.000000"}]}
Params
trader (address, required); limit 1-500, default 50
Returns
{ requests: [{ requestId, trader, feedId, kind, isLong, leverage, margin, acceptablePrice, positionId, state, cancelReason, fillPrice, createdAt, engine, legacy }] }

Two-step requests. state is pending, executed or cancelled; cancelReason is cancelled, expired, price moved past your limit, market closed, position gone, rejected, liquidatable or null.

Example
{"requests":[{"requestId":"10000007","kind":"open","state":"executed","cancelReason":null}]}
Params
offset (default 0)
Returns
{ positions: [...], next }

Every open position with the market state a keeper needs, 500 a page, integers as strings. next is null on the last page.

Example
{"positions":[{"position_id":"10000042","leverage":"10","funding_index":"-4","m_funding_at":"0","engine":"0x57c6...","legacy":false}],"next":null}
Params
none
Returns
{ basis, bookAssets, plpSupply, plpBookPrice, apr7d, sharePrice, nav, asOf, vault, engine, legacy, previous: [...] }

The vault's ledger (deposits minus withdrawals plus settlements, no unrealised PnL) and its live sharePrice and nav read from the chain. apr7d is from the last 7 days' settlements.

Example
{"bookAssets":"1100.000000","plpBookPrice":"1.100000","apr7d":33.18,"sharePrice":"1.083412","nav":"1083.412345","previous":[]}
Params
account (address, required)
Returns
{ account, deposited, withdrawn, sharesIn, sharesOut, deposits, withdrawals, vault, engine, legacy, previous: [...] }

One LP's flows. previous lists old vaults where the account still holds pLP.

Example
{"account":"0x0a0a...","deposited":"1500.000000","withdrawn":"400.250000","sharesIn":"1490.000000000000000000","sharesOut":"397.000000000000000000","deposits":2,"withdrawals":1}
Params
feed (required); resolution 60, 300, 900, 3600 (default), 14400 or 86400; from; to
Returns
{ t: [...], v: [...] }

Traded notional per bucket, at most 5000 buckets.

Example
{"t":[1792537200,1792540800],"v":["12500.000000","8400.000000"]}

Банк

Params
account (address, required)
Returns
{ account, key, keys, keyEpoch, globalEpoch, paused, caps, balances: { pusd, eth, tokens }, indexedBlock }

An account's live keys (newest first, at most 16), epochs, caps and balances in base units. caps comes from keyCapsOf on the chain; when the node does not answer, spent and windowEnds are null.

Example
{"account":"0x0a0a...","key":"0x6b6b...","keys":["0x6b6b...","0x6c6c..."],"keyEpoch":2,"globalEpoch":1,"paused":false,"caps":{"pusdPerDay":"10000000000","ethPerDay":"5000000000000000000","pusdSpent":"0","ethSpent":"0","windowEnds":1790086400},"balances":{"pusd":"900000000","eth":"1000000000000000000","tokens":[{"token":"0xa1a1...","symbol":"CRN","name":"Cornichon","amount":"5000000000000000000"}]},"indexedBlock":40}
Params
account (required); kind deposit, withdraw, send, gas, key, venue or buy (default: deposit, withdraw, send, gas, buy); limit 1-100, default 30; before (the previous page's next)
Returns
{ account, flows: [{ id, kind, direction, asset, assetKind, symbol, amount, counterparty, toWallet, venue, by, via, txHash, block, at }], next }

Money in and out of an account. via is key or wallet. A pUSD exchange buy adds ethIn, pusdOut, price and paidFrom (balance or wallet).

Example
{"flows":[{"id":"20.3.2","kind":"buy","direction":"in","assetKind":"pusd","symbol":"pUSD","amount":"4000000000","via":"key","ethIn":"1000000000000000000","pusdOut":"4000000000","price":"400000000000","paidFrom":"balance"}],"next":null}
Params
key (address, required)
Returns
{ key, account, since }

The account a key serves, and the block it was registered in; both null when the key is not live.

Example
{"key":"0x6b6b...","account":"0x0a0a...","since":10}
Params
none
Returns
{ bank, indexedBlock, assets: [{ asset, indexed, onChain, negativeBalances, ok }], ok }

The index's balances against totalHeld on the chain, per asset.

Example
{"bank":"0x3366...","indexedBlock":9440000,"assets":[{"asset":"0x4709...","indexed":"...","onChain":"...","negativeBalances":0,"ok":true}],"ok":true}

Лента, трейдеры и лидеры копирования

Params
token or market (one, optional); limit 1-50, default 30; before
Returns
{ items: [...], next }

Everything traders did: launches, buys, sells, opens, closes, liquidations.

Example
{"items":[{"id":"...","kind":"close","trader":"0x...","side":"short","leverage":10,"usd":"1040.000000","pnlUsd":"49.500000","pnlPct":49.25,"pnl":"known","market":{"feedId":"0x..."},"token":null}],"next":"9440012.3.1."}
Params
viewer (required); token or market; limit; before
Returns
{ items, next, follows }

The accounts a viewer follows (up to 2000). follows is false before social-api has created its tables.

Example
{"items":[],"next":null,"follows":true}
Params
clan (clan id, 1 to 16 digits, required); token or market; limit; before
Returns
{ items, next, clans }

What a clan's members did, each counted from when they joined. Members are capped at 201.

Example
{"items":[],"next":null,"clans":true}
Params
token (required); limit; before
Returns
{ items, next }

One launched token's activity.

Example
{"items":[{"kind":"buy","usd":"400","pnl":null}],"next":null}
Params
address (required); limit; before
Returns
{ items, next }

One trader's activity.

Example
{"items":[],"next":null}
Params
tokens and/or markets (comma-separated, 100 at most together); limit; before
Returns
{ items, next }

A watchlist's activity.

Example
{"items":[],"next":null}
Params
token or market (exactly one); from, to (unix, at most 400 days apart); min 0 or 1000; who all or friends; viewer
Returns
{ markers: [{ at, trader, side, usd, mine, friend, price, priceEth, eth }], capped }

Trades to draw on a chart, at most 300.

Example
{"markers":[{"at":1792537260,"side":"long","usd":"250.000000","mine":false,"friend":true}],"capped":false}
Params
sort active (default), pnl, volume or new; limit 1-50, default 50; offset (offset + limit at most 1000); addresses (up to 10) or prefix (1-40 hex)
Returns
{ sort, offset, limit, rows: [{ address, pnlUsd, volumeUsd, trades, firstAt, lastAt }], more, catchingUp, recounting }

The trader directory.

Example
{"sort":"active","offset":0,"limit":50,"rows":[{"address":"0x...","pnlUsd":"49.000000","volumeUsd":"5000.000000","trades":4,"firstAt":10,"lastAt":20}],"more":false,"catchingUp":false,"recounting":[]}
Params
none
Returns
{ asOf, trending, trendingBy, copied, copiedBy, returnWindow, rules, stale, catchingUp, recounting, pulse }

The traders page's podium: trending traders (at most 10), the most copied (at most 30) and a 24-hour pulse.

Example
{"pulse":{"window":"24h","traders":2,"volumeUsd":"6250.000000","trades":13,"copies":5},"returnWindow":"30d","trending":[],"copied":[]}
Params
window 7d, 30d (default) or all; sort copied (default), return, risk or new; addresses or prefix
Returns
{ asOf, window, sort, rows: [{ address, copiers, newCopiers, capitalUsd, copiersEver, copiersPnlUsd, firstAt, lastAt, trades7d, risk, window }], more, rules, catchingUp, recounting }

Traders to copy, at most 50 rows, with their copiers and a risk score.

Example
{"rows":[{"copiers":2,"newCopiers":1,"capitalUsd":"300.000000","copiersEver":4,"copiersPnlUsd":"28.000000","risk":{"days":7,"trades":3,"avgLeverage":"5.33","score":5},"window":{"closed":2,"wins":1,"liquidated":0,"pnlUsd":"49.000000","baseUsd":"202.000000","returnPct":24.26,"maxDrawdownUsd":"5.000000"}}]}

Запущенные токены

Params
none
Returns
{ trending, new, gainers, count, countCapped, ethUsd }

Up to 12 tokens per list. gainers holds only tokens that are up.

Example
{"trending":[...],"new":[...],"gainers":[],"count":1,"countCapped":false,"ethUsd":{"pair":"ETH/USD","price":"...","stale":false}}
Params
sort trending (default), new, gainers or mcap; limit 1-500, default 50
Returns
{ tokens: [summary], ethUsd }

Token summaries: identity, price, change, 24-hour activity, liquidity, holders and fees. Dollar fields are null without a fresh ETH/USD price.

Example
{"tokens":[{"address":"0x...","symbol":"CRN","priceUsd":"0.0000123","marketCapUsd":"12300","change24h":12.5,"fees":{"creatorWethWei":"...","creatorTokens":"..."}}]}
Params
address in the path
Returns
{ token: summary + creatorBasis, ethUsd }

One token. 404 {"error":"not a SENDIT launch"} for any other address.

Example
{"token":{"address":"0x...","symbol":"CRN","creatorBasis":{"basis":"known"}}}
Params
creator (required)
Returns
{ tokens, ethUsd }

Tokens an account launched, newest first.

Example
{"tokens":[]}
Params
holder (required)
Returns
{ holder, positions: [...], capped, ethUsd }

What an account holds, with cost basis and PnL, at most 500.

Example
{"holder":"0x...","positions":[{"symbol":"CRN","balance":"5000000000000000000","valueUsd":"61.5","pnlUsd":"11.5","basis":"known"}],"capped":false}
Params
holder (required)
Returns
{ holder, tokens: [{ address, launcher, balance, symbol, name, where }], capped, limit, indexedBlock }

Balances only; where is bank or wallet.

Example
{"tokens":[{"symbol":"CRN","balance":"5000000000000000000","where":"bank"}],"capped":false,"limit":500}
Params
holder, token (required); limit 1-50, default 20; before
Returns
{ holder, token, position, activity, next, transfersFrom, transfersFromAt, ethUsd }

One holding with its buys, sells and transfers.

Example
{"activity":[{"kind":"buy","usd":"50","tokens":"..."}],"next":null}
Params
token (required); limit 1-500, default 50
Returns
{ holders: [{ address, balance, pct, valueUsd, kind }], count }

Holders; kind is pool, burn, creator or holder.

Example
{"holders":[{"kind":"pool","pct":92.1}],"count":14}
Params
token (required); limit 1-500, default 50
Returns
{ trades: [{ txHash, logIndex, block, at, trader, isBuy, ethWei, tokens, usd, priceUsd, marketCapUsd, priceEth, marketCapEth }] }

A token's trades, newest first.

Example
{"trades":[{"isBuy":true,"usd":"400"}]}
Params
token (required); resolution 1, 5, 15 (default), 60, 240 or 1D; from; to; countback (at most 2000); unit usd (default) or eth
Returns
{ s: "ok", t, o, h, l, c, v, through } or { s: "no_data", nextTime }

TradingView UDF bars.

Example
{"s":"ok","t":[1792537200],"o":["0.0000120"],"h":["0.0000125"],"l":["0.0000119"],"c":["0.0000123"],"v":["400"]}

social-api: сессии

ПравилоЗначение
База/api/socialпрефикс отрезается до того, как social-api видит путь
ЗаписьPOST, Content-Type: application/jsonне больше 4 KB JSON
ЗаголовокX-Pepper-Social: 1обязателен при каждой записи, иначе 403
Сессияcookie __Host-pepper_socialHttpOnly, Secure, SameSite=Strict, 30 дней
Originсобственный origin SENDIT, если он переданстраница на другом сайте не может писать
Запись от имени аккаунта{ "as": "0x..." } в теле409, если сессия принадлежит другому аккаунту

Вход работает по EIP-4361 (SIWE): запросите сообщение с nonce, подпишите его через personal_sign и отправьте обратно. Сообщение действительно 10 минут и только один раз. С торговым ключом SENDIT устройство входит без кошелька:

Вход с торговым ключом
const API = "https://sendit.picklechain.xyz/api/social";
const headers = { "Content-Type": "application/json", "X-Pepper-Social": "1" };

// 1. A nonce message issued for the ACCOUNT (the wallet address), not the key.
const { message } = await fetch(`${API}/session/nonce`, {
  method: "POST", headers, body: JSON.stringify({ address: account }), credentials: "include",
}).then((r) => r.json());

// 2. The trading key signs it (personal_sign).
const signature = await key.signMessage({ message });

// 3. The server checks bank.accountOf(key) == account on chain, then sets the cookie.
await fetch(`${API}/session/key`, {
  method: "POST", headers, credentials: "include",
  body: JSON.stringify({ account, key: key.address, message, signature }),
});

Вне браузера сохраните значение Set-Cookie из последнего ответа и передавайте его обратно в заголовке Cookie в последующих вызовах.

Params
none
Returns
{ address: null } or { address, expiresAt }

Who this cookie is signed in as.

Example
{"address":"0x0A0a...","expiresAt":1795129600}
Params
{ address }
Returns
{ message, expiresAt }

An EIP-4361 sign-in message for the address, valid for 10 minutes and usable once. 429 after too many sign-ins from one network.

Example
{"message":"sendit.picklechain.xyz wants you to sign in with your Ethereum account:\n0x0A0a...\n\nSign in to SENDIT. ...","expiresAt":1792538400}
Params
{ message, signature }
Returns
{ address, expiresAt } and Set-Cookie

The wallet's personal_sign of that message. Opens a 30-day session. 401 with the reason when the message or signature does not hold.

Example
{"address":"0x0A0a...","expiresAt":1795129600}
Params
{ account, key, message, signature }
Returns
{ address, expiresAt } and Set-Cookie

The same nonce message, issued for the ACCOUNT, signed by its trading key. The server checks the signature recovers to key and that bank.accountOf(key) is the account on the chain, then opens the same 30-day session: a new device signs in with no wallet prompt. 401 "This key is not the account's trading key", 409 when there is no bank, 503 when the chain could not be read (the nonce is kept; every other refusal spends it).

Example
{"address":"0x0A0a...","expiresAt":1795129600}
Params
{} or { everywhere: true }
Returns
{ address: null }

Signs out this cookie, or every session of the account.

Example
{"address":null}
Два разных сообщения

/session/key подписывает обычное сообщение входа. Сообщение "Unlock my SENDIT trading key" - это другое: способ, которым приложение выводит ключ, и оно никогда не отправляется ни на какой сервер.

social-api: остальное

Социальные маршруты следуют тем же правилам. Основные, по функциям; лимиты описаны на странице Социальные функции руководства пользователя.

ФункцияМаршруты
ПодпискиPOST /follow { as, address, on }GET /follows, /mutuals, /profile, /people
ТезисыPOST /theses { txHash, body }GET /theses, /thesis, /replies; POST /likes, /replies
КланыPOST /clans, /clans/join, /clans/leave, /clans/invite, /clans/apply...GET /clans, /clan?id=, /clans/mine, /clans/list
ПереводыPOST /sends { txHash, note }записывает перевод, уже прошедший в сети, с личной заметкой; GET /sends?box=in|out
Список наблюденияPOST /watchlist { as, kind, target, on }GET /watchlist
Настройки копированияGET /copy/prefs, POST /copy/prefsGET /copies