Creating Orders
The two-step intent/confirm flow that creates every order type, and the fields they share.
Every order type is created by the same two calls. POST /orders/intent returns an unsigned deposit transaction; the user signs it; POST /orders/confirm submits it and activates the order. Nothing moves until that signature lands.
Creation is two steps because the order's input comes from the user's external wallet, and only the user can sign that deposit. orderType and config are the only things that change between a DCA order and a stop-loss — see Order Types for each config shape.
Get an unsigned deposit transaction
const intent = await callTitanDca('/orders/intent', {
method: 'POST',
sub,
body: {
orderType: 'dca', // or 'stop_loss' | 'take_profit' | 'oco' | 'slice'
userPubkey,
onboardIfNeeded: true, // optional; must be exactly true
outputRecipientAddress: userPubkey, // optional; defaults to userPubkey
platformFee: { bps: 50 }, // optional per-order override
config: { /* per order type — see Order Types */ },
},
});
const { pendingOrderId, transaction, expiresAt } = intent.data;orderType
✅
dca | stop_loss | take_profit | oco | slice. Anything else returns 400 INVALID_ORDER_TYPE.
userPubkey
✅
The user's external wallet. Pays Solana network fees on the deposit and, by default, receives swap output. Must be the wallet attested at onboarding.
onboardIfNeeded
❌
Exactly true to provision a brand-new user inline instead of returning 401. See Single-transaction onboarding.
outputRecipientAddress
❌
Where swap output is delivered. Either userPubkey (default) or the user's manager address. Immutable once confirmed. With onFillOco, defaults to — and must be — the manager.
platformFee.bps
❌
Per-order override of your tenant default. Must be 0 ≤ bps ≤ maxFeeBps. See Platform Fees.
A userPubkey that isn't the wallet the user attested at onboarding returns 403 VALIDATION_ERROR.
The response carries the transaction the user has to sign, plus the details you'd want to show them before they do:
feeLamports is "0" when execution fees are sponsored on your environment, which is the default for partners. Otherwise it is "5000000" (0.005 SOL), already included in the deposit transaction the user signs — so read it rather than assuming zero.
The user has until expiresAt — 5 minutes — to sign. After that the pending order is dead and you request a fresh intent.
Confirm with the signed transaction
Titan co-signs, submits to Solana, and activates the order. It is active right away — a DCA order begins running cycles on its cadence, a trigger order begins watching the price, and a Slice Order begins executing its first slice. The one exception is a DCA order with a future config.startAt, which lands in pending and flips to active when that time arrives.
Where swap output lands
By default, output goes straight to the user's external wallet (userPubkey) — each cycle for a DCA order, the single fill for a trigger order, or each slice for a Slice Order. To keep it inside the manager instead, useful when the user is accumulating before one withdrawal, set outputRecipientAddress to the manager address (walletAddress from onboarding). It must be one of those two addresses, and it's immutable once the order is confirmed.
Verify the deposit
GET /orders/{orderId}/deposit returns the funding transfer that created the order — external wallet to manager. It works for every order type:
Returns 404 NOT_FOUND if the order doesn't exist, isn't this user's, or has no recorded deposit — either an order created before deposit linkage existed, or one whose deposit never reached a submitted signature. GET /partners/me/orders/{id}/deposit is the tenant-scoped twin for back-office use.
Errors
Intent-step failures:
HTTP
error.code
When
400
VALIDATION_ERROR
Invalid body, address, or amount. details.issues lists each failure.
400
INVALID_ORDER_TYPE
orderType isn't one of the five.
400
INVALID_MINTS
inputMint === outputMint.
400
MIN_NOTIONAL_NOT_MET
Below your tenant's minimum ($10 USD default) — per cycle for DCA, on the full amount for trigger orders, per slice for Slice Orders. details: mint, amount, usdCents, requiredCents.
400
MIN_OUTPUT_UNSATISFIABLE
Trigger orders only: a configuration that could never fill — a stop-loss minOutputAmount above the output implied by triggerPrice, or a trigger already crossed with minOutputAmount above the input's market value. See Unfillable configurations.
401
UNAUTHORIZED
Missing X-Titan-User, or a sub that was never onboarded.
403
VALIDATION_ERROR
userPubkey isn't the wallet the user attested at onboarding.
409
ONBOARDING_INCOMPLETE
The manager isn't fully provisioned. Re-call POST /partner/onboard (idempotent), then retry.
409
USER_PUBKEY_CONFLICT
With onboardIfNeeded: true — the wallet already belongs to a Titan account. Fall back to SIWS.
409
ACTIVE_BURST_EXISTS
Slice Orders only: the user already has an active or executing Slice Order. Also returned by confirm if a concurrent create won the race — see Slice Orders.
422
IDEMPOTENCY_KEY_REUSED
Same X-Idempotency-Key with a different body.
503
PRICE_ORACLE_UNAVAILABLE
Couldn't price a non-stable inputMint to enforce the minimum. Transient — retry shortly.
Confirm-step failures:
HTTP
error.code
When
400
MISSING_PARAMS
Body missing pendingOrderId or signedTransaction.
400
INVALID_STATUS
The pending order was already used — e.g. previously confirmed.
400
EXPIRED
The 5-minute window elapsed. Request a new intent.
400
INVALID_STATE
The server-side stashed transaction is missing or expired. Request a new intent.
400
TRANSACTION_TAMPERED
The signed tx doesn't match the one Titan stashed.
403
FORBIDDEN
The pending order belongs to another user or tenant.
404
NOT_FOUND
The pending order doesn't exist.
500
SUBMIT_FAILED / CONFIRMATION_FAILED / ORDER_FINALIZE_FAILED
Server-side failure during submit, on-chain confirm, or finalize. Safe to retry with an idempotency key. Structural problems with the submitted transaction — bad base64, wrong shape, missing signatures — also surface as SUBMIT_FAILED.
The full catalog is in Error Codes.
Related pages
Order Types — the
configshape for each typeManaging Orders — pause, resume, cancel, retry
Onboarding (SIWS) — what has to happen before the first intent
Last updated

