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

# Request Virtual Account (User)

> Starts banking onboarding for one of your users so Capa can provision a named virtual account in their own name. Onboarding runs asynchronously — a `SUBMITTED` response only means the request was accepted. Poll the status endpoint until it reports `COMPLETED`, then read the provisioned account from the bank accounts endpoint. Currently available for the United States only.

Starts banking onboarding for one of your users so Capa can provision a **named virtual account** — a US bank account (account number + ABA routing number) issued in the user's own name. See the [Virtual Accounts guide](/docs/virtual-accounts) for the full flow.

Currently available for the **United States (`US`)** only. Any other `country` value is rejected with `400`.

<Note>
  Onboarding is **asynchronous**. A `SUBMITTED` response only means the request was accepted — the account is not usable yet. Poll [Get Virtual Account Status](/reference/bankingonboardingpartnerv2controller_getonboardingstatus) until it reports `COMPLETED`, then read the account from [List Bank Accounts](/reference/userbankinfopartnerv2controller_getuserbankinfosbyuserid).
</Note>

***

## Response Statuses

A `201` does not always mean a new onboarding was started. Always branch on `data.status`:

| `status`               | Meaning                                                                              | What to do                                                                                                       |
| ---------------------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
| `SUBMITTED`            | Onboarding was created and sent to the banking provider.                             | Poll the status endpoint until `COMPLETED`.                                                                      |
| `ALREADY_IN_PROGRESS`  | An onboarding for this user and country is already running. Nothing new was created. | Keep polling — the call is safe to repeat.                                                                       |
| `ALREADY_COMPLETED`    | The user already completed onboarding for this country.                              | Read the account from [List Bank Accounts](/reference/userbankinfopartnerv2controller_getuserbankinfosbyuserid). |
| `REQUIREMENTS_MISSING` | The user's KYB is not complete. **Nothing was submitted.**                           | Send the user to `kybLink` to finish KYB, then retry.                                                            |

The endpoint is safe to call repeatedly: when an onboarding is already in flight or already complete, it returns the existing record instead of starting a second one.

## Missing Requirements

`status: "REQUIREMENTS_MISSING"` means the user's KYB is not complete. Nothing was submitted. The response carries:

* **`kybLink`** — a hosted link where the user can finish their KYB. It may be absent if a link could not be minted; in that case, direct the user through your usual KYB flow.
* **`missingRequirements`** — what is still outstanding, each entry with a `field` path and a human-readable `message`. Useful for surfacing a reason in your UI, but you do not need to collect these fields yourself — completing KYB satisfies them.

```json theme={null}
{
  "success": true,
  "data": {
    "status": "REQUIREMENTS_MISSING",
    "kybLink": "https://verify.capa.fi/s/2f9c1b7e",
    "missingRequirements": [
      { "field": "taxId", "message": "Tax ID is required for business onboarding" }
    ]
  }
}
```

Once KYB is complete, call this endpoint again.

***

## Error Codes

### Common Errors

| HTTP Status | Code                   | Message                            |
| ----------- | ---------------------- | ---------------------------------- |
| 401         | `UNAUTHORIZED`         | "API Key is missing"               |
| 401         | `UNAUTHORIZED`         | "Invalid API Key format"           |
| 401         | `UNAUTHORIZED`         | "Invalid API Key"                  |
| 403         | `INVALID_PARTNER_FLOW` | "The partner has an invalid flow." |

### User Ownership Errors

| HTTP Status | Code           | Message                                              |
| ----------- | -------------- | ---------------------------------------------------- |
| 401         | `UNAUTHORIZED` | "Partner information is required for this operation" |
| 401         | `UNAUTHORIZED` | "User is not associated with the partner"            |

### Endpoint-Specific Errors

| HTTP Status | Code                       | Message                                                                                |
| ----------- | -------------------------- | -------------------------------------------------------------------------------------- |
| 400         | `INVALID_USER_INPUT_ERROR` | "Invalid User Input" — `country` is missing, not `US`, or `userId` is not a valid UUID |
| 404         | `NOT_FOUND_ERROR`          | "User {userId} not found"                                                              |


## OpenAPI

````yaml reference/openapi/BankingOnboardingPartnerV2Controller_requestOnboarding.json POST /api/partner/v2/banks/users/{userId}/banking-onboarding
openapi: 3.0.0
info:
  title: Capa Partner API - V2
  description: >-
    Partner API for integrating cryptocurrency on-ramp and off-ramp services
    using Capa's infrastructure. Enable your users to seamlessly convert between
    fiat and crypto.
  version: v2
  contact: {}
servers:
  - url: https://api.sandbox.capa.fi
  - url: https://production-api.capa.fi
security: []
paths:
  /api/partner/v2/banks/users/{userId}/banking-onboarding:
    post:
      tags:
        - banks
      summary: Request a virtual account for a user
      description: >-
        Starts banking onboarding for one of your users so Capa can provision a
        named virtual account in their own name. Onboarding runs asynchronously
        — a `SUBMITTED` response only means the request was accepted. Poll the
        status endpoint until it reports `COMPLETED`, then read the provisioned
        account from the bank accounts endpoint. Currently available for the
        United States only.
      operationId: BankingOnboardingPartnerV2Controller_requestOnboarding
      parameters:
        - name: userId
          required: true
          in: path
          description: >-
            The ID of the user to onboard. Must belong to the authenticated
            partner.
          schema:
            example: 8374f327-38bd-4b0b-b8a7-2524599eb903
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                country:
                  description: >-
                    The country whose banking provider should issue the virtual
                    account. Only `US` is currently supported.
                  enum:
                    - US
                  type: string
                  example: US
              required:
                - country
            example:
              country: US
      responses:
        '201':
          description: >-
            Onboarding request processed. Inspect `status` to know what actually
            happened — a 201 does not always mean a new onboarding was started.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    type: object
                    properties:
                      status:
                        description: |-
                          Outcome of the request.
                           - `SUBMITTED`: onboarding was created and sent to the banking provider.
                           - `ALREADY_IN_PROGRESS`: an onboarding for this user and country is already running; nothing new was created.
                           - `ALREADY_COMPLETED`: the user already completed onboarding for this country.
                           - `REQUIREMENTS_MISSING`: the user's KYB is not complete; nothing was submitted.
                        enum:
                          - SUBMITTED
                          - ALREADY_IN_PROGRESS
                          - ALREADY_COMPLETED
                          - REQUIREMENTS_MISSING
                        type: string
                      onboarding:
                        description: >-
                          The onboarding record. Absent when `status` is
                          `REQUIREMENTS_MISSING`.
                        type: object
                        properties:
                          id:
                            type: string
                            description: Onboarding ID.
                            example: 0f1f0d0a-9c62-4d0e-9a3f-0b0f2f8a1c34
                          country:
                            description: Country the onboarding belongs to.
                            enum:
                              - US
                            type: string
                          status:
                            description: Lifecycle state of the onboarding.
                            enum:
                              - IN_PROGRESS
                              - COMPLETED
                              - FAILED
                            type: string
                          result:
                            description: Provider decision, once one has been made.
                            enum:
                              - APPROVED
                              - PENDING_REVIEW
                              - REJECTED
                            type: string
                          createdAt:
                            type: string
                            format: date-time
                          updatedAt:
                            type: string
                            format: date-time
                        required:
                          - id
                          - country
                          - status
                          - createdAt
                          - updatedAt
                      kybLink:
                        type: string
                        description: >-
                          Hosted link where the user can finish their KYB. Only
                          returned when `status` is `REQUIREMENTS_MISSING`, and
                          only when a link could be minted.
                        example: https://verify.capa.fi/s/2f9c1b7e
                      missingRequirements:
                        description: >-
                          What is still outstanding on the user's KYB. Surfaced
                          so you can show a reason in your UI — you do not need
                          to collect these fields yourself; completing KYB
                          satisfies them. Only returned when `status` is
                          `REQUIREMENTS_MISSING`.
                        type: array
                        items:
                          type: object
                          properties:
                            field:
                              type: string
                              description: >-
                                Path into the user's identity that is still
                                outstanding, e.g. `taxId`, `documents.TAX_ID`.
                            message:
                              type: string
                              description: >-
                                Human-readable description of what is
                                outstanding.
                          required:
                            - field
                            - message
                    required:
                      - status
              examples:
                SUBMITTED:
                  summary: Onboarding started
                  value:
                    success: true
                    data:
                      status: SUBMITTED
                      onboarding:
                        id: 0f1f0d0a-9c62-4d0e-9a3f-0b0f2f8a1c34
                        country: US
                        status: IN_PROGRESS
                        createdAt: '2026-08-25T14:03:11.482Z'
                        updatedAt: '2026-08-25T14:03:11.482Z'
                ALREADY_IN_PROGRESS:
                  summary: Onboarding already running
                  value:
                    success: true
                    data:
                      status: ALREADY_IN_PROGRESS
                      onboarding:
                        id: 0f1f0d0a-9c62-4d0e-9a3f-0b0f2f8a1c34
                        country: US
                        status: IN_PROGRESS
                        createdAt: '2026-08-25T14:03:11.482Z'
                        updatedAt: '2026-08-25T14:03:11.482Z'
                ALREADY_COMPLETED:
                  summary: User already has a virtual account
                  value:
                    success: true
                    data:
                      status: ALREADY_COMPLETED
                      onboarding:
                        id: 0f1f0d0a-9c62-4d0e-9a3f-0b0f2f8a1c34
                        country: US
                        status: COMPLETED
                        result: APPROVED
                        createdAt: '2026-08-20T09:12:44.001Z'
                        updatedAt: '2026-08-21T16:40:02.118Z'
                REQUIREMENTS_MISSING:
                  summary: User's KYB is not complete
                  value:
                    success: true
                    data:
                      status: REQUIREMENTS_MISSING
                      kybLink: https://verify.capa.fi/s/2f9c1b7e
                      missingRequirements:
                        - field: taxId
                          message: Tax ID is required for business onboarding
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                code: INVALID_USER_INPUT_ERROR
                message: Invalid User Input
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                UNAUTHORIZED:
                  value:
                    success: false
                    code: UNAUTHORIZED
                    message: API Key is missing
                UNAUTHORIZED_2:
                  value:
                    success: false
                    code: UNAUTHORIZED
                    message: Invalid API Key format
                UNAUTHORIZED_3:
                  value:
                    success: false
                    code: UNAUTHORIZED
                    message: Invalid API Key
                UNAUTHORIZED_4:
                  value:
                    success: false
                    code: UNAUTHORIZED
                    message: User is not associated with the partner
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                code: INVALID_PARTNER_FLOW
                message: The partner has an invalid flow.
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                code: NOT_FOUND_ERROR
                message: User 8374f327-38bd-4b0b-b8a7-2524599eb903 not found
        '500':
          description: Internal Server Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                code: INTERNAL_SERVER_ERROR
                message: Internal server error
      security:
        - PartnerApiKey: []
components:
  schemas:
    ErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          example: false
        code:
          type: string
          description: Machine-readable error code.
        message:
          type: string
          description: Human-readable error message.
        errors:
          type: array
          description: Optional per-field validation details.
          items:
            type: object
            properties:
              field:
                type: string
              message:
                type: string
      required:
        - success
        - code
        - message
  securitySchemes:
    PartnerApiKey:
      type: apiKey
      in: header
      name: partner-api-key
      description: API key for the affiliated partner performing the request.

````