> ## Documentation Index
> Fetch the complete documentation index at: https://docs.glider.fi/llms.txt
> Use this file to discover all available pages before exploring further.

# Enroll User (Stage 2)

> Stage 2 of the two-stage enrollment flow — commits the user's signed session-key and creates the portfolio.

Stage 2 of the two-stage enrollment flow. Takes the `flowId`, `accountIndex`,
and `agentAccountId` returned by `POST /v2/enroll/signature`, plus the user's
`signature`, and provisions the portfolio: one smart account per requested
chain, plus the rebalance schedule derived from your tenant's execution
config. The whole operation succeeds atomically — a failure anywhere rolls
back cleanly.

* Auth: `x-api-key` header (required)
* Scope: `enroll:write`

The endpoint is idempotent on `flowId` (scoped to your API key) with a
24-hour TTL:

* Replaying the same `flowId` with the same body returns the cached response.
* Replaying with a different body returns `409 IDEMPOTENCY_KEY_CONFLICT`.
* Replaying while the original is still in-flight returns
  `409 IDEMPOTENCY_IN_PROGRESS`.
* Replaying after a typed error (e.g., signature mismatch) returns the same
  typed error so retries don't get stuck.

For EVM, the `signature` is computed by signing `message.raw` from the stage-1
response with the user's wallet, using viem's `signMessage({ message: { raw } })`
or the equivalent wallet primitive.

For EVM smart-contract wallets, pass `accountType: "ERC1271"` in both stages
and have the user sign the stage-1 `message.typedData` EIP-712 payload. The
server verifies the signature with ERC-1271 on the requested chain and configures
the Kernel vault with the SmartAccount sudo validator. ERC-1271 enrollment
currently supports exactly one EVM `chainId` per signature.

Wallet addresses are exchanged as [CAIP-10](https://chainagnostic.org/CAIPs/caip-10)
account identifiers. EVM `ownerAccountId` / `agentAccountId` use the
chain-agnostic form `eip155:0:<address>`, and each element of the response
`smartAccounts` array uses the chain-bound form `eip155:<chainId>:<address>`.

### Solana (SVM) enrollment

For a **Solana-rooted** user, omit `signature` and instead send
`signedSolanaTransaction` — the base64 `solanaTransaction` from stage 1 after the
user signs it with their wallet. The server re-derives the expected
instructions, verifies the user signed, co-signs with the pooled agent, and
submits it (the paymaster pays).

For an **EVM-rooted** user (Model A subaccount, client-driven single submit),
send `userSecp256k1PublicKey` (the user's secp256k1 key — its derived address
must equal `ownerAccountId`'s), `signature` (the user's slot-bound secp256k1
authorization over the Swig add-authority payload), `currentSlot` (the slot it
was bound to), and optionally `ownerSignatureScheme` (`eip191` default / `raw`).
The server replays the signature to add the agent + create the sub-account in
one paymaster-sponsored transaction. Capture the slot and submit promptly — Swig
enforces a slot window.

The response `smartAccounts[]` entry's `accountId` is the **sub-account** the
user funds; it is also surfaced as `depositAccountId`, with `swigRoleId`
identifying the owning Swig role. The parent Swig PDA is internal. A second
enroll for the same (tenant, user) reuses the same Swig and returns a new
sub-account at a higher role.

`portfolioName` is an optional display name for the portfolio. It is trimmed
server-side, returned by portfolio read endpoints, and mutable via
`PATCH /v2/portfolios/{portfolioId}`. Names may be up to 64 characters and may
include Unicode letters, digits, spaces, and `- _ . ' : ( ) & /`.

Common error responses:

* `400` for an invalid request body, expired flow, mismatched signature, or
  invalid strategy ownership
* `401` when `x-api-key` header is missing or the key is invalid
* `403` when the API key lacks the `enroll:write` scope
* `409` `IDEMPOTENCY_IN_PROGRESS` when an earlier request with the same `flowId`
  is still running
* `409` `IDEMPOTENCY_KEY_CONFLICT` when the same `flowId` is reused with a
  different request body
* `409` `PORTFOLIO_ALREADY_EXISTS` when the user is already enrolled at the
  given `accountIndex`
* `500` on unexpected server errors

<RequestExample>
  ```bash cURL theme={null}
  curl --request POST \
    --url 'https://api.glider.fi/v2/enroll' \
    --header 'x-api-key: gldr_sk_your_api_key' \
    --header 'Content-Type: application/json' \
    --data '{
      "ownerAccountId": "eip155:0:0xabcdef0000000000000000000000000000000001",
      "strategyId": "01JWZEE2MF30KVRMRX53N88VA4",
      "chainIds": [1, 8453, 56],
      "accountType": "ECDSA",
      "accountIndex": "7",
      "agentAccountId": "eip155:0:0x1111111111111111111111111111111111111111",
      "signature": "0x9412d70d539f889ecec2d3152b68af758d689a4325a43b07811ee708814527c72256fc749d7fd3745a07f0aaa84813955050c461392d9b9ee1f80e106fac39e01b",
      "flowId": "flow_abc123",
      "portfolioName": "Alice Portfolio"
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch("https://api.glider.fi/v2/enroll", {
    method: "POST",
    headers: {
      "x-api-key": "gldr_sk_your_api_key",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      ownerAccountId: "eip155:0:0xabcdef0000000000000000000000000000000001",
      strategyId: "01JWZEE2MF30KVRMRX53N88VA4",
      chainIds: [1, 8453, 56],
      accountType: "ECDSA",
      accountIndex: "7",
      agentAccountId: "eip155:0:0x1111111111111111111111111111111111111111",
      signature:
        "0x9412d70d539f889ecec2d3152b68af758d689a4325a43b07811ee708814527c72256fc749d7fd3745a07f0aaa84813955050c461392d9b9ee1f80e106fac39e01b",
      flowId: "flow_abc123",
      portfolioName: "Alice's Portfolio",
    }),
  });
  ```
</RequestExample>

<ResponseExample>
  ```json 201 theme={null}
  {
    "success": true,
    "data": {
      "portfolioId": "a1b2c3d4",
      "portfolioName": "Alice Portfolio",
      "strategyId": "01JWZEE2MF30KVRMRX53N88VA4",
      "smartAccounts": [
        { "accountId": "eip155:1:0x2222222222222222222222222222222222222222" },
        { "accountId": "eip155:8453:0x3333333333333333333333333333333333333333" }
      ]
    }
  }
  ```

  ```json 201 (Solana / SVM subaccount) theme={null}
  {
    "success": true,
    "data": {
      "portfolioId": "a1b2c3d4",
      "portfolioName": "Alice Portfolio",
      "strategyId": "01JWZEE2MF30KVRMRX53N88VA4",
      "smartAccounts": [
        {
          "accountId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:Dep0sit...PdA",
          "depositAccountId": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:Dep0sit...PdA",
          "swigRoleId": 1
        }
      ]
    }
  }
  ```

  ```json 400 theme={null}
  {
    "success": false,
    "error": {
      "code": "API_400",
      "message": "Signature does not match the session-key message for this user"
    }
  }
  ```

  ```json 409 theme={null}
  {
    "success": false,
    "error": {
      "code": "API_202",
      "message": "User 0xabc... is already enrolled at account index 7"
    }
  }
  ```

  ```json 409 theme={null}
  {
    "success": false,
    "error": {
      "code": "API_007",
      "message": "Another enrollment request with the same flowId is still in progress"
    }
  }
  ```
</ResponseExample>
