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

# Quickstart

> Create a Sumvin user and query their first wallet balance in under five minutes.

At the end of this page, you will have created a Sumvin user, added a wallet, and made a real call to:

[`GET /v0/wallets/{wallet_id}/assets`](/api-reference/wallet-assets/list-wallet-assets) — Returns a JSON body listing the wallet's balances with <Tooltip headline="HAL" tip="Hypertext Application Language — the link format used on every Sumvin response." cta="Glossary →" href="/glossary">HAL</Tooltip> `_links` for navigation.

**Nothing here is a toy. The call you make in five minutes is the call you run in production.**

## Prerequisites

* A Platform API JWT from your configured auth provider (Dynamic Labs, Privy, or <Tooltip headline="SIWE" tip="Sign-In With Ethereum — wallet-based authentication." cta="Glossary →" href="/glossary">SIWE</Tooltip>). See [Authentication](/authentication).
* Your platform's base URL — `https://api.sumvin.com/v0`.
* A wallet address for the user (<Tooltip headline="EOA" tip="Externally Owned Account — a single-keypair Ethereum address." cta="Glossary →" href="/glossary">EOA</Tooltip>) on Sei (`chain_id: 1329`).
* Your `x-juno-orgid` if your credentials span multiple Sumvin organisations (see [Authentication](/authentication)). Single-org integrations can omit this header.

<Note>
  The Platform API uses the `x-juno-jwt` header (no `Bearer` prefix). Reserve `Authorization: Bearer` for SIS API calls. Multi-tenant callers also pass `x-juno-orgid: <your-org-id>` to scope the request to a specific organisation.
</Note>

<Tip>
  **The first call you make is `POST`, not `GET`.** `GET /v0/user/me` returns `404 USR-404-001` until the user is created — it does not auto-provision. If you see a 404 on the first request, follow it with `POST /v0/user/` rather than retrying the GET.
</Tip>

<Steps>
  <Step title="Create the user">
    Register the user with their primary EOA address. Sumvin queues a Safe smart account for deployment on the specified chain in the background — you do not wait for it here.

    <Note>
      Background Safe deployment is the default `ai_agent` cohort behaviour. Environments configured for **user-signed Safe deploy** (recommended for partners who want user-controlled wallets) or **BYO Safe** (existing on-chain Safe) follow a different onboarding step instead. See [Onboarding cohorts](/dashboard/features#onboarding-cohort-summary).
    </Note>

    ### Supported `chain_id` values

    `chain_id` follows EIP-155 and must be one of Sumvin's supported EVM networks:

    | `chain_id` | Network           | Type    |
    | ---------: | ----------------- | ------- |
    |        `1` | Ethereum Mainnet  | mainnet |
    |       `10` | Optimism          | mainnet |
    |       `56` | BNB Smart Chain   | mainnet |
    |      `137` | Polygon           | mainnet |
    |     `8453` | Base              | mainnet |
    |    `42161` | Arbitrum One      | mainnet |
    |    `43114` | Avalanche C-Chain | mainnet |
    |     `1329` | Sei               | mainnet |
    |     `1328` | Sei Testnet       | testnet |

    The example below uses `1329` (Sei). Pass any value from the table — Sumvin deploys the user's Safe on the chain you specify.

    <CodeGroup>
      ```bash curl theme={null}
      curl -X POST https://api.sumvin.com/v0/user/ \
        -H "x-juno-jwt: <your-jwt-token>" \
        -H "x-juno-orgid: <your-org-id>" \
        -H "Content-Type: application/json" \
        -d '{
          "primary_eoa_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD78",
          "chain_id": 1329
        }'
      ```

      ```typescript TypeScript theme={null}
      const res = await fetch("https://api.sumvin.com/v0/user/", {
        method: "POST",
        headers: {
          "x-juno-jwt": process.env.SUMVIN_JWT!,
          "x-juno-orgid": process.env.SUMVIN_ORG_ID!,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          primary_eoa_address: "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD78",
          chain_id: 1329,
        }),
      });
      const user = await res.json();
      ```

      ```python Python theme={null}
      import httpx
      import os

      res = httpx.post(
          "https://api.sumvin.com/v0/user/",
          headers={
              "x-juno-jwt": os.environ["SUMVIN_JWT"],
              "x-juno-orgid": os.environ["SUMVIN_ORG_ID"],
          },
          json={
              "primary_eoa_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD78",
              "chain_id": 1329,
          },
      )
      user = res.json()
      ```
    </CodeGroup>

    **Response:** `201 Created`

    ```json theme={null}
    {
      "user": {
        "id": "usr_abc123",
        "username": null,
        "first_name": null,
        "last_names": null,
        "auth_provider": "dynamic",
        "auth_provider_id": "dyn_xxxxxxxxxx",
        "primary_eoa_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD78",
        "primary_smart_wallet_address": null,
        "safe_creation_status": "processing",
        "email": "user@example.com",
        "phone": null,
        "phone_verified_at": null,
        "created_at": 1740000000000,
        "updated_at": 1740000000000,
        "deleted_at": null,
        "profile_picture_url": null
      },
      "_links": {
        "self": { "href": "/v0/user/me" },
        "wallets": { "href": "/v0/wallets" },
        "kyc-status": { "href": "/v0/kyc/status" }
      }
    }
    ```

    ### `safe_creation_status` values

    The Safe smart wallet is deployed by a background worker after the user record is created. Poll `GET /v0/user/me` to watch this field transition.

    | Value        | Meaning                                                                            | What the UI should show                                                                                                                    |
    | ------------ | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
    | `pending`    | Initial state — Safe creation has been queued but not yet picked up by the worker. | Brief; treat the same as `processing`.                                                                                                     |
    | `processing` | Worker is deploying the Safe contract on-chain.                                    | "Setting up your wallet…" with a spinner. Continue polling.                                                                                |
    | `completed`  | Safe deployed. `primary_smart_wallet_address` is now populated.                    | Reveal the smart-wallet-dependent surfaces (transactions, cards).                                                                          |
    | `failed`     | Deployment failed and will not retry automatically.                                | Surface a contact-support path. Do not block onboarding on this field — `phone_verification` and `kyc_verification` proceed independently. |

    Treat `pending` and `processing` as the same non-terminal state. `completed` and `failed` are terminal — once you observe one, stop polling this field.

    ### When the user already exists — `208 Already Reported`

    If the authenticated identity already maps to a Sumvin user, `POST /v0/user/` returns `208 Already Reported` with the existing record. The body is the **same `UserResponse` shape** as the `201 Created` response above — same `user` object, same `_links`, no slim variant. This means you can call `POST /v0/user/` idempotently from your onboarding entry point without checking for duplicates first.

    ### Error responses

    All errors follow the [RFC 7807 Problem Details](/error-handling) format. The three you should design UI for:

    <AccordionGroup>
      <Accordion title="401 Unauthorized — missing or invalid JWT">
        ```json theme={null}
        {
          "type": "https://api.sumvin.com/errors/usr-401-001",
          "title": "User Authentication Failed",
          "status": 401,
          "detail": "Missing or invalid x-juno-jwt header.",
          "instance": "/v0/user",
          "error_code": "USR-401-001",
          "trace_id": "abc-123-def"
        }
        ```

        The `x-juno-jwt` header is missing, malformed, or expired. Refresh the token from your auth provider and retry.
      </Accordion>

      <Accordion title="422 Unprocessable Entity — bad payload">
        Returned when FastAPI rejects the request body before it reaches the handler — for example, a non-integer `chain_id`, a missing required field, or an invalid email shape.

        ```json theme={null}
        {
          "detail": [
            {
              "type": "int_parsing",
              "loc": ["body", "chain_id"],
              "msg": "Input should be a valid integer, unable to parse string as an integer",
              "input": "sei"
            }
          ]
        }
        ```

        Fix the field at `loc` and retry. This response uses FastAPI's default validation error envelope, not RFC 7807 — it is the one place in the user-creation flow where the shape differs.
      </Accordion>

      <Accordion title="208 Already Reported — user exists for this JWT">
        Not strictly an error — Sumvin returns `208` to signal an idempotent replay rather than `409 Conflict`. The body is the existing user record (see above).

        ```json theme={null}
        {
          "user": {
            "id": "usr_abc123",
            "primary_eoa_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD78",
            "safe_creation_status": "completed",
            "email": "user@example.com",
            "created_at": 1740000000000,
            "updated_at": 1740000050000
          },
          "_links": {
            "self": { "href": "/v0/user/me" },
            "wallets": { "href": "/v0/wallets" },
            "kyc-status": { "href": "/v0/kyc/status" }
          }
        }
        ```

        Treat `201` and `208` as the same success path — the record is yours either way.
      </Accordion>
    </AccordionGroup>

    Other status codes worth handling defensively: `400 WAL-400-001` if `primary_eoa_address` is not a valid EVM address, and `403 WAL-403-003` if the address cannot be verified against your auth provider's connected identity. Both follow the RFC 7807 shape shown above.
  </Step>

  <Step title="Check onboarding status">
    The onboarding state machine tells you which step the user is on. `is_complete: true` gates the next step.

    ```bash theme={null}
    curl "https://api.sumvin.com/v0/user/me/onboarding/steps" \
      -H "x-juno-jwt: <your-jwt-token>" \
      -H "x-juno-orgid: <your-org-id>"
    ```

    **Response:** `200 OK`

    ```json theme={null}
    {
      "onboarding": {
        "current_step": "phone_verification",
        "is_complete": false,
        "steps": [
          { "step": "phone_verification", "status": "current", "gated": false, "meta": null },
          { "step": "kyc_verification", "status": "pending", "gated": false, "meta": null },
          { "step": "open_banking", "status": "pending", "gated": false, "meta": null },
          { "step": "card_setup", "status": "pending", "gated": false, "meta": null },
          { "step": "feature_selection", "status": "pending", "gated": false, "meta": null }
        ]
      },
      "_links": {
        "self": { "href": "/v0/user/me/onboarding/steps" },
        "onboarding-events": { "href": "/v0/user/me/onboarding/events" }
      }
    }
    ```

    The server drives the transitions — you submit the prompted data for each step until `is_complete` is `true`. See the [onboarding guide](/guides/onboarding) for the full flow, including phone and KYC.
  </Step>

  <Step title="Add a wallet">
    Once onboarding completes, add the user's wallet. If you use Dynamic Labs, pass the `credential_id` from the user's verified credential — Sumvin resolves the address and chain from Dynamic's API.

    <CodeGroup>
      ```bash curl theme={null}
      curl -X POST https://api.sumvin.com/v0/wallets/ \
        -H "x-juno-jwt: <your-jwt-token>" \
        -H "x-juno-orgid: <your-org-id>" \
        -H "Content-Type: application/json" \
        -d '{
          "credential_id": "cred_abc123"
        }'
      ```

      ```typescript TypeScript theme={null}
      const res = await fetch("https://api.sumvin.com/v0/wallets/", {
        method: "POST",
        headers: {
          "x-juno-jwt": process.env.SUMVIN_JWT!,
          "x-juno-orgid": process.env.SUMVIN_ORG_ID!,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ credential_id: "cred_abc123" }),
      });
      const wallet = await res.json();
      ```

      ```python Python theme={null}
      import httpx
      import os

      res = httpx.post(
          "https://api.sumvin.com/v0/wallets/",
          headers={
              "x-juno-jwt": os.environ["SUMVIN_JWT"],
              "x-juno-orgid": os.environ["SUMVIN_ORG_ID"],
          },
          json={"credential_id": "cred_abc123"},
      )
      wallet = res.json()
      ```
    </CodeGroup>

    **Response:** `201 Created`

    ```json theme={null}
    {
      "id": "wal-a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
      "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD78",
      "chain_id": 1329,
      "is_primary": true,
      "is_eoa": true,
      "nickname": null,
      "logo_uri": null,
      "created_at": 1740000001000,
      "deleted_at": null,
      "_links": {
        "self": { "href": "/v0/wallets/wal-a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4" },
        "user": { "href": "/v0/user/me" }
      }
    }
    ```

    For auth providers that do not issue credential IDs, use the SIWE challenge/verify flow instead — see the [wallets guide](/guides/wallets) for all three ownership verification methods.
  </Step>

  <Step title="Query wallet balances">
    This is the working artefact. The response lists token balances with USD valuations, plus HAL `_links` to navigate to the wallet itself and set-primary action.

    <CodeGroup>
      ```bash curl theme={null}
      curl https://api.sumvin.com/v0/wallets/wal-a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4/assets \
        -H "x-juno-jwt: <your-jwt-token>" \
        -H "x-juno-orgid: <your-org-id>"
      ```

      ```typescript TypeScript theme={null}
      const res = await fetch("https://api.sumvin.com/v0/wallets/wal-a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4/assets", {
        headers: {
          "x-juno-jwt": process.env.SUMVIN_JWT!,
          "x-juno-orgid": process.env.SUMVIN_ORG_ID!,
        },
      });
      const assets = await res.json();
      console.log(assets);
      ```

      ```python Python theme={null}
      import httpx
      import os

      res = httpx.get(
          "https://api.sumvin.com/v0/wallets/wal-a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4/assets",
          headers={
              "x-juno-jwt": os.environ["SUMVIN_JWT"],
              "x-juno-orgid": os.environ["SUMVIN_ORG_ID"],
          },
      )
      print(res.json())
      ```
    </CodeGroup>

    **Response:** `200 OK`

    ```json theme={null}
    {
      "wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f2bD78",
      "wallet_chain_id": 1329,
      "assets": [
        {
          "symbol": "ETH",
          "name": "Ether",
          "chain_id": 1329,
          "balance": "0.05423100",
          "balance_usd": "173.54",
          "balance_updated_at": 1740000010000,
          "transactions_href": "/v0/wallets/wal-a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4/assets/eth-1329/transactions",
          "_links": {
            "self": { "href": "/v0/wallets/wal-a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4/assets" }
          }
        }
      ],
      "total_assets": 1,
      "_links": {
        "self": { "href": "/v0/wallets/wal-a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4/assets" },
        "wallet": { "href": "/v0/wallets/wal-a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4" }
      }
    }
    ```

    <Check>You have a live Sumvin integration. The `_links` field is how every response advertises its next actions — use them instead of hardcoding URLs.</Check>
  </Step>
</Steps>

## Minting a public RPC key

If your integration calls the RPC endpoint from a browser or mobile bundle, mint a **public-partition** key with `rpc.invoke`. Public-partition keys are scoped to a single namespace (`rpc.*`) and are safe to ship in client-side code.

```bash theme={null}
curl -X POST https://api.sumvin.com/v0/organisation/<org_id>/keys \
  -H "x-juno-jwt: <your-jwt-token>" \
  -H "x-juno-orgid: <org-id>:<env-id>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "browser-rpc-key",
    "env_id": "<env-id>",
    "scopes": ["rpc.invoke"]
  }'
```

The response carries `partition: "public"`, `scopes: ["rpc.invoke"]`, and a `_links.rpc_docs` HAL link pointing at the [RPC method reference](https://docs.sumvin.com/rpc). All requests authenticate with the returned `key` via `Authorization: Bearer`:

```bash theme={null}
curl <rpc-endpoint>/<method> \
  -H "Authorization: Bearer <returned-key>"
```

<Note>
  The RPC method catalogue is documented separately. Use `_links.rpc_docs` from the create response to find the latest method list.
</Note>

For the server-side flow (full `sis.*` scope set) and the partition / scope reference, see [SIS scopes & partitions](/authentication#scopes--partitions).

## What's next

| Next              | Where                                                                      | When                                                                  |
| ----------------- | -------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| Full account flow | [Stand up an account](/guides/quickstarts/stand-up-account)                | Before putting real users through KYC and Safe deployment             |
| Auth reference    | [Authentication](/authentication)                                          | When you're wiring up your JWT provider or SIS key                    |
| API conventions   | [API conventions](/api-conventions)                                        | When a response shape surprises you — HAL, 7807, expand, 202 patterns |
| Wallet depth      | [Wallets guide](/guides/wallets)                                           | When you need multi-chain or manual Safe flows                        |
| SIS RPC keys      | [Authentication → Scopes & partitions](/authentication#scopes--partitions) | When you need a public-safe key for a browser or mobile bundle        |
