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

# Create the caller's vault if it does not exist yet

> Idempotent. 200 with the address when the vault is `ready`; 202 while it is `deploying` (a deploy job was queued by this call or earlier) — poll `GET /api/me/vault` until `status=ready`; 409 `vault_deploy_failed` (`retryAfterSec`) when the last deploy job failed and its retry cooldown has not elapsed — a call after the cooldown starts a new job; 503 `vault_create_unavailable` (`retryAfterSec`) when no job could be started. Direct and CCIP deposits need the address before they are signed; relay deposits, on-ramp credits and bonuses call this path server-side.

API key: scope `vault:write` or `vault:migrate`.



## OpenAPI

````yaml /api-reference/openapi.json post /api/me/vault
openapi: 3.1.0
info:
  title: PredictStreet API
  description: >-
    Client-facing HTTP API for the PredictStreet prediction-market platform.
    **Partner kinds.** Every authenticated endpoint except the partner-level
    platform reads (`/api/platform/status`, `/notices`, `/maintenance`) resolves
    a request's *effective wallet* from the partner row. `single_wallet`
    partners bind to one `associatedWallet` set at creation. `multi_wallet`
    partners declare the actor on every request via an `X-User-Wallet:
    0x<40-hex>` header. See the [Partner kinds](/auth/api-keys#partner-kinds)
    doc for the full contract.
  version: '2026-06-16'
  contact:
    name: PredictStreet partners
    email: partners@predictstreet.com
servers:
  - url: https://core-api.adipredictstreet.com
    description: Production — replace with the API host issued at partner onboarding.
security: []
tags:
  - name: Deposits
    description: >-
      Gasless USDC deposit relay - submit a signed EIP-2612 permit and the
      platform broadcasts the on-chain deposit for you (no gas).
  - name: Events
    description: >-
      Polymarket-style event grouping with football metadata (group, stage,
      teams, tags).
  - name: Tags
    description: Curated tag taxonomy used to filter events.
  - name: Markets
    description: 'Public market data: list, detail, orderbook, trades, OHLC.'
  - name: Crypto
    description: >-
      Crypto price markets: the underlying-asset price curve and Up/Down series
      navigation. The markets themselves are ordinary binary markets — list them
      through /api/markets and /api/events.
  - name: Orders
    description: >-
      Signed-order place / cancel / read. Requires `X-Api-Key` with
      `orders:read` / `orders:write` scope; every write additionally requires an
      EIP-712 signature over the order.
  - name: Portfolio
    description: >-
      Balances, positions, trades, fees, vault info for the key's
      `associatedWallet`. Requires `X-Api-Key` with `portfolio:read` scope.
  - name: Matches
    description: >-
      A match groups several events into one fixture/card (1X2 + first-scorer +
      over-under under one matchup).
  - name: Vault
    description: >-
      Backend co-signatures for ERC-1155 split / merge, and recovery for
      off-chain locks when the corresponding chain tx never confirmed. Requires
      `X-Api-Key` with `vault:write` scope plus an EIP-712 signature over the
      operation.
  - name: Leaderboard
    description: Public ranked leaderboard across PnL / volume buckets.
  - name: Search
    description: Global search across users, events, and matches.
  - name: Withdrawal Security
    description: >-
      Self-service withdrawal 2FA (TOTP) and withdrawal-address whitelist. Same
      endpoints serve the web app (user sessions) and API-key integrators.
      Mutations need `vault:write`, reads `portfolio:read`. Both are opt-in per
      wallet and only gate withdrawals once the platform enables
      withdrawal-security enforcement.
  - name: Platform
    description: >-
      Platform reference data and state. The contract registry and the fee
      configuration are public and need no key. The freeze flag (`/status`),
      active notices (`/notices`) and maintenance windows (`/maintenance`) need
      a partner API key in `X-Api-Key`.
paths:
  /api/me/vault:
    post:
      tags:
        - Portfolio
      summary: Create the caller's vault if it does not exist yet
      description: >-
        Idempotent. 200 with the address when the vault is `ready`; 202 while it
        is `deploying` (a deploy job was queued by this call or earlier) — poll
        `GET /api/me/vault` until `status=ready`; 409 `vault_deploy_failed`
        (`retryAfterSec`) when the last deploy job failed and its retry cooldown
        has not elapsed — a call after the cooldown starts a new job; 503
        `vault_create_unavailable` (`retryAfterSec`) when no job could be
        started. Direct and CCIP deposits need the address before they are
        signed; relay deposits, on-ramp credits and bonuses call this path
        server-side.


        API key: scope `vault:write` or `vault:migrate`.
      operationId: PortfolioController_createVault
      parameters:
        - $ref: '#/components/parameters/UserWalletHeader'
      responses:
        '200':
          description: Vault is ready.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateVaultResponseDto'
        '202':
          description: Vault is deploying; poll GET /api/me/vault.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateVaultResponseDto'
        '409':
          description: >-
            `vault_deploy_failed`: the last deploy job failed and the retry
            cooldown has not elapsed; `retryAfterSec` says when a call starts a
            new job.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '503':
          description: >-
            `vault_create_unavailable`: no deploy job could be started; retry
            after `retryAfterSec`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
      security:
        - ApiKey: []
components:
  parameters:
    UserWalletHeader:
      name: X-User-Wallet
      in: header
      required: false
      description: >-
        **Required for `multi_wallet` partners on every authenticated request;
        ignored for `single_wallet`.** Declares the acting end-user wallet for
        this request — drives KYC checks, balances/positions/orders attribution,
        rate-limit buckets, and audit. Lower-cased server-side. Missing on a
        multi_wallet key → 401 `api_key_user_wallet_required`; malformed → 401
        `api_key_user_wallet_invalid`. The on-chain `CTFExchange`/`Vault`
        contracts still verify EIP-712 signer ↔ vault binding, so loosening
        API-layer attribution is safe by construction.
      schema:
        $ref: '#/components/schemas/EthereumAddress'
      example: '0x1234567890abcdef1234567890abcdef12345678'
  schemas:
    CreateVaultResponseDto:
      type: object
      allOf:
        - $ref: '#/components/schemas/VaultRefDto'
      properties:
        enqueued:
          type: boolean
          description: >-
            true when THIS call queued the deploy job; false when it already
            existed or the vault was ready.
      required:
        - enqueued
    ErrorEnvelope:
      type: object
      description: >-
        Error body returned by every API error (same shape as `Error`). Branch
        on `error.code`, not on `message`.
      required:
        - status
        - error
      properties:
        status:
          type: integer
          description: HTTP status, repeated in the body.
          example: 400
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: Machine-readable code.
              example: bad_request
            message:
              type: string
            trace_id:
              type: string
              description: Request trace id; quote it to support. May be absent.
            retryAfterSec:
              type: integer
              description: >-
                Seconds to wait before retrying, when the server says so (also
                sent as the `Retry-After` header).
            retryable:
              type: boolean
              description: >-
                Whether the same request may be sent again as it is. Absent when
                the server does not say.
            details:
              type: object
              additionalProperties: true
              description: Extra structured context thrown with the error.
    EthereumAddress:
      type: string
      pattern: ^0x[a-fA-F0-9]{40}$
      example: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb3'
    VaultRefDto:
      type: object
      properties:
        walletAddress:
          type: string
          description: Authenticated wallet (lowercase 0x-hex).
        vaultAddress:
          type: string
          nullable: true
          description: Per-user vault contract address; null until the vault is deployed.
        deployed:
          type: boolean
          description: true once VaultFactory.vaultOf(wallet) has been on-chain confirmed.
        deployStatus:
          type: string
          enum:
            - not_started
            - pending
            - building
            - submitted
            - confirmed
            - skipped_existing
            - failed_permanent
          description: >-
            Status of the platform's automatic vault deployment: `not_started`
            (no deploy job; also shown for vaults that existed before
            auto-deploy — check `deployed`), `pending` / `building` /
            `submitted` (in progress), `confirmed` (deployed and `vaultAddress`
            set), `skipped_existing` (vault already existed), `failed_permanent`
            (deployment failed; contact support).
        deployTxHash:
          type: string
          nullable: true
          description: >-
            Tx hash of the VaultFactory deploy call once submitted; null until
            then.
        deployRequestedAt:
          type: string
          format: date-time
          nullable: true
          description: When the deploy job was enqueued.
        deployConfirmedAt:
          type: string
          format: date-time
          nullable: true
          description: When the deploy was confirmed on-chain.
        status:
          type: string
          enum:
            - not_created
            - deploying
            - ready
            - failed
          description: >-
            Vault lifecycle (RND-312): `not_created` = no vault and no deploy in
            flight (the first deposit or `POST /api/me/vault` creates it);
            `deploying` = poll; `ready` = `vaultAddress` usable; `failed` = the
            last deploy job failed; a new one starts on the next `POST
            /api/me/vault` (or funding) after a cooldown — the 409 carries
            `retryAfterSec`. Authoritative; `deployed` + `deployStatus` stay for
            existing clients.
        retryAfterSec:
          type: integer
          description: >-
            For `failed`: seconds until a `POST /api/me/vault` (or a funding
            path) starts a new deploy; 0 otherwise.
        generation:
          type: integer
          enum:
            - 1
            - 2
          description: >-
            Contract generation of the active vault (the one deposits and
            trading go to). 1 = original factory, 2 = post-audit factory (the
            beacon proxy). Always equals `vaults.find(v =>
            v.active).generation`. Defaults to 1 before any vault exists.
        targetGeneration:
          type: integer
          enum:
            - 1
            - 2
          description: >-
            The generation a new deploy / activation targets (from the rollout).
            1 while generation 2 is not rolled out to this wallet; 2 once the
            wallet is in the rollout. When `generation` is 1 and
            `targetGeneration` is 2, `POST /api/me/vault/upgrade` migrates the
            account.
        vaults:
          type: array
          items:
            $ref: '#/components/schemas/UserVaultSummaryDto'
          description: >-
            Every vault the authenticated wallet holds, ordered by generation
            ASC (generation 1 first). After `POST /api/me/vault/upgrade` the
            user holds TWO vaults: the generation-1 vault stays here with
            `active: false` (it trades legacy markets and keeps its positions /
            USDC until moved or withdrawn), and the generation-2 vault has
            `active: true`. Empty array before any vault exists.
        upgrade:
          $ref: '#/components/schemas/VaultUpgradeDto'
          nullable: true
          description: >-
            Vault roadmap part 2: present while the account is eligible for
            generation 2 and has not finished the migration in `upgrade`; only
            `POST /api/me/vault/upgrade` moves them to generation 2. A user with
            `generation: 2` already has `upgrade: null`. Lifecycle of the
            generation-2 vault — the migration the FE prompts and `POST
            /api/me/vault/upgrade` runs. `vaultAddress` / `status` above keep
            describing the ACTIVE vault (deposits and trading go there until the
            upgrade is activated). Null otherwise (target 1, no vault yet, or
            already generation 2).
      required:
        - walletAddress
        - vaultAddress
        - deployed
        - status
        - generation
        - targetGeneration
        - vaults
    UserVaultSummaryDto:
      type: object
      description: Summary of one vault contract belonging to the authenticated wallet.
      properties:
        vaultAddress:
          type: string
          description: Vault contract address (lowercase 0x-hex).
        generation:
          type: integer
          enum:
            - 1
            - 2
          description: >-
            Contract generation: 1 = original factory (standalone,
            non-upgradeable vault), 2 = post-audit factory (the vault is a
            beacon proxy, admin-upgradeable only behind a timelock).
        active:
          type: boolean
          description: >-
            The active vault: deposits, on-ramp and bonuses go to it. A
            generation-1 vault that is not active keeps its positions and free
            USDC until they are redeemed, withdrawn or moved, and keeps trading
            the generation-1 (legacy) markets: an order's maker is the vault of
            the market's generation, active or not (`vault_generation_mismatch`
            otherwise).
      required:
        - vaultAddress
        - generation
        - active
    VaultUpgradeDto:
      type: object
      description: Status and address of the generation-2 upgrade deploy.
      properties:
        status:
          type: string
          enum:
            - not_created
            - deploying
            - ready
            - failed
          description: >-
            Lifecycle of the generation-2 vault: `not_created` = none yet —
            `POST /api/me/vault/upgrade` queues its deploy (the migration
            trigger; nothing else starts it); `deploying` = poll `GET
            /api/me/vault`; `ready` = the generation-2 vault exists but is NOT
            the active vault yet — `POST /api/me/vault/upgrade` activates it
            (then `generation` reads 2 and `upgrade` is null); `failed` = the
            last generation-2 deploy failed, `POST /api/me/vault/upgrade` after
            `retryAfterSec` starts a new one.
        vaultAddress:
          type: string
          nullable: true
          description: >-
            The generation-2 vault address once known (lowercase 0x-hex, like
            `vaults[].vaultAddress`).
        deployStatus:
          type: string
          description: Deploy job status of the generation-2 job (see `deployStatus`).
        retryAfterSec:
          type: integer
          description: >-
            For `failed`: seconds until a new generation-2 deploy may start; 0
            otherwise.
      required:
        - status
        - vaultAddress
        - deployStatus
        - retryAfterSec
  securitySchemes:
    ApiKey:
      type: apiKey
      in: header
      name: X-Api-Key
      description: >-
        Partner / integrator key — format `ps_live_<keyId>_<secret>`. Partner
        keys are issued by PredictStreet; a user can also create keys bound to
        their own wallet from their account settings in the web app. Never ship
        to a browser. `multi_wallet` partners must additionally send
        `X-User-Wallet: 0x<40-hex>` on every authenticated request to declare
        the acting wallet. See the [API keys guide](/auth/api-keys) for scope
        taxonomy, partner kinds, rate limits, and rotation procedure.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.