Funding & Preflight
Authentication success does not prove an institutional integration can trade. An unfunded account can pass HMAC, FIX Logon, FIXP negotiate, connectivity, and schema tests while every bid fails at the risk layer.
This page covers funding in both environments. The preflight further down is the same call in both; only where the balance comes from differs.
Which environment
| Sandbox | Production | |
|---|---|---|
| Base URL | https://api.sentico-sandbox.xyz | https://api.sentico-labs.xyz |
| Collateral | Mock USDC, booked by the venue | Real USDC in the on-chain vault |
| How you get funded | Self-service faucet, 100,000 USDC per grant | Vault deposit, or an operator-provisioned beta bundle |
| Access | Self-service, no whitelist | Whitelisted private beta |
| Risk of the test | None; the balance is not real | Real collateral you must be prepared to lose |
Run conformance in the sandbox first. Use production only for the checks that genuinely require the live venue, and size them accordingly: tiny post-only quotes, cleanup after every test, and a funded account whose balance you are prepared to risk.
Sandbox funding
The sandbox grants mock USDC on request. The published contract is 100,000
USDC per grant, permitted only while total account equity is below 25,000
USDC, capped at 5 grants per account per rolling 24 hours, idempotent per
requestId:
POST /sandbox/v1/faucet
Content-Type: application/json
{
"account": "0xEngineAccount",
"requestId": "ci-run-1043-topup-2",
"amountUsdcMicro": "100000000000",
"issuedAt": 1760000000000,
"expiresAt": 1760000300000,
"signature": "0x..."
}
The first call also materialises the account, so there is no separate
provisioning step. The response carries grantsRemaining, grantsTotal and
resetAtMs. There are no deposits and no withdrawals in the sandbox, and base
assets are acquired by trading rather than by grant. The signed-message grammar,
the quota errors and the full sandbox contract are on the
Sandbox (launching — see Status) page.
Then run the same funding preflight as production, below.
Production beta model
Institutional and market-maker testing uses an operator-provisioned, funded beta credential bundle, or a self-funded account: deposit USDC (and the base asset of any spot book you intend to quote) into the vault, wait for the engine credit, and confirm it in the fresh bootstrap read below.
Depositing into the vault
- Vault:
MultiCollateralVault0xd1f0fffb736d75fd402121eb14bdc46bfe80ca02on Arbitrum One.deposit(assetId, amount)anddepositWithPermitcreditmsg.senderonly; there is no deposit-for-another-account, so send from the wallet that owns the engine account.assetIdiskeccak256(abi.encode(token, decimals, chainid)). - USDC means native USDC
0xaf88d065e77c8cC2239327C5EDb3A432268e5831, not USDC.e. - Engine credit arrives about 15–20 minutes after the on-chain deposit; the listener waits for confirmations. Do not debug signing or risk until the fresh bootstrap shows the balance.
- Binary and prediction asks sell YES/NO shares and there is no mint-on-fill:
run the
splitaction first (1 USDC → 1 YES + 1 NO, fee 1 bp) and size asks belowyesFree/noFree.
The bundle should be delivered as JSON. A copyable template lives at
scripts/e2e/institutional-beta-bundle.example.json.
The bundle must include:
| Field | Required |
|---|---|
environment, apiBaseUrl | Target environment (sandbox, beta) and its API base URL. |
ownerWallet | 20-byte owner wallet. |
accountIdHex | 32-byte trading-account id for control-plane operations. |
engineAccount | 20-byte engine account for balances, signed actions, FIX/FIXP, and streams. |
agent.id, agent.type, agent.scopes | institutional_agent with at least read, trade, cancel, quote, drop_copy. |
hmac.apiKeyId, hmac.apiSecret, hmac.apiPassphrase | HMAC credential for machine auth and FIX/FIXP. |
orderEntry.submitPath, orderEntry.signedBatchFile, orderEntry.idempotencyKey | Known-good signed batch and submit route for conformance. |
expectedFunding.freeUsdcMicroAtLeast | Minimum free USDC the first known-good quote requires. |
knownGoodQuote | Market, book, side, price, qty, and time-in-force for a tiny post-only quote. |
Minimal shape:
{
"environment": "beta",
"apiBaseUrl": "https://api.sentico-labs.xyz",
"ownerWallet": "0x1111111111111111111111111111111111111111",
"accountIdHex": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"engineAccount": "0x2222222222222222222222222222222222222222",
"agent": {
"id": "agt_...",
"type": "institutional_agent",
"scopes": ["read", "trade", "cancel", "quote", "drop_copy"]
},
"hmac": {
"apiKeyId": "spk_...",
"apiSecret": "sps_...",
"apiPassphrase": "spp_..."
},
"orderEntry": {
"submitPath": "/api/order-entry/binary",
"contentType": "application/x-senticore-order-entry-batch",
"signedBatchFile": "tmp/known-good-bsl-batch.json",
"idempotencyKey": "quote-1-yes-bid-0"
},
"expectedFunding": {
"asset": "USDC",
"freeUsdcMicroAtLeast": "100000000"
},
"knownGoodQuote": {
"kind": "QuoteReplace",
"market": 1,
"book": "YES",
"side": "Bid",
"price": 100000,
"qty": 100000,
"timeInForce": "post_only",
"requiredUsdcMicro": "10000"
}
}
Funding preflight
Before any quote submit:
GET /api/v1/accounts/:engineAccount/bootstrap?fresh=true
Authorization: Bearer <read token>
Required response properties:
{
"account": "0x2222222222222222222222222222222222222222",
"balanceSource": "matching_engine",
"syncStatus": "synced",
"balances": {
"freeUsdcMicro": "100000000",
"lockedUsdcMicro": "0",
"inOrdersUsdcMicro": "0"
},
"risk": {
"freeUsdcMicro": "100000000"
}
}
Use balances.freeUsdcMicro as the operational check for bid notional. It is
not sufficient for every order:
- A spot ask requires the base token in
assetBalances[].free; match the market's base asset and use its nativeassetDecimals. - A binary/outcome ask requires free
YESorNOinventory for the selected book. - A bid requires free USDC for notional plus fees and configured risk headroom.
If any leg lacks the asset it spends, stop before submitting the batch. Do not infer CKT, TCO, WETH, or USDT availability from the USDC fields.
GET /api/v1/accounts/{engineAccount} is the per-asset view: usdcFree /
usdcLocked, assetBalances[{asset, free, locked, assetDecimals}],
yesFree / yesLocked / noFree / noLocked per market, openOrders,
holds, feeSchedule, nonceFloor / nonceHoles / nonceUsedInWindow,
nextNonce and syncStatus. Spot orders also need a notional of at least
instrumentConfig.minNotionalMicro from the markets list (1 USDC on current
spot markets); a leg below it drops the whole action it belongs to (for a
SpotQuoteReplace, all of its legs).
You can run the same check through the external conformance harness:
node scripts/e2e/institutional-conformance-kit.cjs \
--bundle-file tmp/institutional-beta-bundle.json
The harness validates the manifest, applies missing env vars from it, then fails
the configured funding gate. expectedFunding.freeUsdcMicroAtLeast only proves
the USDC side; configure and verify base/outcome inventory separately for ask
legs.
Required notional
For a bid:
requiredUsdcMicro = floor(priceMicro * qtyMicro / 1_000_000)
Example:
priceMicro = 100000
qtyMicro = 100000
required = 10000 micro USDC
The funded bundle should exceed this by a wide margin because open-order holds, fees, and account-level risk can raise the effective requirement.
What to send support
When a funded account still rejects with risk:
{
"engineAccount": "0x...",
"accountIdHex": "0x...",
"market": 1,
"book": "YES",
"side": "Bid",
"price": 100000,
"qty": 100000,
"requiredUsdcMicro": "10000",
"bootstrap": {
"balanceSource": "matching_engine",
"syncStatus": "synced",
"balances": {
"freeUsdcMicro": "0",
"lockedUsdcMicro": "0",
"inOrdersUsdcMicro": "0"
},
"risk": {
"freeUsdcMicro": "0"
}
},
"submitResponse": {
"ok": false,
"code": "insufficient_balance",
"error": "..."
}
}
If the bootstrap shows no funding, the fix is provisioning, not client code.