Skip to main content
POST
Stage 1 of the two-stage enrollment flow. Validates that the strategyId belongs to your tenant, peeks the user’s next available account index, and returns the signable message the user must sign plus the round-trip values (flowId, accountIndex, agentAccountId) that stage 2 (POST /v2/enroll) will need. Wallet addresses are exchanged as CAIP-10 account identifiers. For EVM, ownerAccountId and agentAccountId use the chain-agnostic form eip155:0:<address> — a single wallet works on every EIP-155 chain. For EVM smart-contract wallets, pass accountType: "ERC1271" in both stages. ERC-1271 enrollment returns an EIP-712 typed-data message and currently requires exactly one EVM chainId per enrollment signature. Omit accountType or pass "ECDSA" for the existing EOA/multichain path.

Solana (SVM) enrollment

A Solana-native user enrolls with ownerAccountId set to a solana: CAIP-10 (solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:<base58>) and chainIds: [1399811149]. The SVM flow mirrors how a Solana user joins the platform: the first portfolio for a given (tenant, user) pair creates a Swig smart wallet (the user’s wallet key is its root authority — a Solana key for a solana: owner, an EVM key for an eip155: owner; see below); subsequent portfolios reuse the same Swig. Each portfolio is a sub-account of that Swig, and the sub-account address is the deposit address the user funds. The pooled agent is attached at create, so the portfolio is operable immediately.

Solana-rooted users (Model B)

Because creating the sub-account is an on-chain action, stage 1 returns a serialized Solana transaction (solanaTransaction) for the user to sign with their wallet — instead of signing message. The response also includes:
  • solanaTransaction — base64 transaction; the user signs it and round-trips the signed bytes to POST /v2/enroll as signedSolanaTransaction. It carries a recent blockhash, so sign and submit promptly.
  • depositAccountId — CAIP-10 of the sub-account the user sends funds to.
  • swigRoleId — the Swig role that owns the sub-account (1 for the first portfolio, higher when reusing an existing Swig).
  • reusedSwigtrue when an existing Swig PDA for this (tenant, user) is reused, false when a new one is created.

EVM-rooted users (Model A)

A user whose primary wallet is EVM enrolls into the same Solana sub-account model by setting ownerAccountId to eip155:0:<address> with chainIds: [1399811149]. Their secp256k1 (EVM) key becomes the Swig root. Stage 1 returns the targets the client needs to build the authorization:
  • swigAccountId — CAIP-10 of the parent Swig PDA.
  • depositAccountId / swigRoleId / reusedSwig — as above.
  • agentAccountId, accountIndex, flowId — round-tripped to stage 2.
The message is an ecdsa digest that is informational only — the user does not sign it. The client builds a slot-bound Swig add-authority payload (from swigAccountId + agentAccountId + a freshly captured slot), personal_signs its hash, then sends userSecp256k1PublicKey + signature + currentSlot to POST /v2/enroll. Capture the slot and submit promptly — Swig enforces a slot window. There is no create-only fallback; these fields are required.
  • Auth: x-api-key header (required)
  • Scope: enroll:write
The returned flowId is the idempotency anchor for the matching POST /v2/enroll call — it is valid for 24 hours. Re-submitting the same flowId on stage 2 replays the cached response. The accountIndex is peeked, not reserved: repeated stage-1 calls for the same user return the same candidate index, and the index only advances when stage 2 commits successfully. Retries, abandoned flows, and tests do not burn account indices. Unknown chains in chainIds are rejected with 400 before any enrollment state is created — the allowed list is in the error details. Common error responses:
  • 400 when the request body is invalid, the strategyId doesn’t exist or your API key cannot access it, or chainIds contains an unsupported chain
  • 401 when x-api-key header is missing or the key is invalid
  • 403 when the API key lacks the enroll:write scope
  • 500 on unexpected server errors