# Get KYC requirements for a region (/docs/api-reference/reseller-verification/getResellerKycRequirements)

> Get the ordered list of verification steps for a carrier and the fields each step needs, so your integration can build a verification form without hardcoding the sequence.

> **Reseller accounts only.** [Request access](https://omnidim.io/contact-us?reason=reseller_api&lock=1).

**Call this before you build anything.** The steps, the fields each one needs, and the `method`
telling you how to perform it all come from here. A region can have more than one carrier, and
carriers do not run the same checks, so the sequence is data rather than something to hard-code.

Each step lists its fixed values in `choices`, the shape a value must match in `patterns`, fields
needed only for some answers in `required_if`, and fields it accepts but never needs in
`optional`. An `upload` step carries `document`: the form field, the accepted formats and the
size limit.

Calling without a `carrier` returns `409 carrier_required` listing the region's carriers with what
each one stocks, so you discover them at runtime rather than hard-coding the list. A region we
cannot verify returns `404 not_available`.

The per-carrier steps and the full loop are on the
[Verify a client](/docs/reseller/your-clients/verification) guide.

**GET** `/reseller/kyc/requirements`

Get the ordered list of verification steps for a carrier and the
fields each step needs, so your integration can build a verification
form without hardcoding the sequence.

Carriers in the same region do not share a step list. One verifies
contact details with an OTP pair and ends in a preview-then-accept;
another has no OTP step at all and verifies Aadhaar by sending the
client to a verification page in a browser. Read this endpoint per carrier,
branch on each step's `method`, and one integration drives both.

```yaml
operationId: getResellerKycRequirements
parameters:
  - name: region
    in: query
    required: true
    description: Region to get verification requirements for.
    schema:
      type: string
      enum:
        - IN
        - US
    example: IN
  - name: carrier
    in: query
    description: |
      Which carrier's steps to return: carriers in a region do not share
      a step list. Required where a region has more than one carrier;
      omitting it there returns `409 carrier_required` naming the
      region's carriers.
    schema:
      type: string
    example: carrier-1
responses:
  '200':
    description: Verification requirements for the region.
    content:
      application/json:
        schema:
          type: object
          properties:
            success:
              type: boolean
            region:
              type: string
              enum:
                - IN
                - US
            steps:
              type: array
              items:
                type: object
                properties:
                  step:
                    type: string
                    description: |
                      The step name to pass in the path. Not a fixed
                      list: carriers do not run the same checks, so
                      read the names from this response rather than
                      hard-coding them.
                  required:
                    type: array
                    items:
                      type: string
                    description: Body fields this step requires, beyond `user_id`, `region`, and `carrier`.
                  cooldown:
                    type: boolean
                    description: >-
                      Whether this step is rate-limited. When true, wait about 30 seconds between
                      attempts.
                  choices:
                    type: object
                    additionalProperties:
                      type: array
                      items:
                        type: string
                    description: |
                      Fixed vocabularies, per field, for the fields that
                      have one. Build your control from this rather than
                      hard-coding the values: sending anything outside a
                      published list is refused with `400
                      invalid_request` naming what is allowed. `{}` when
                      the step has no such field.
                  required_if:
                    type: object
                    additionalProperties:
                      type: object
                      additionalProperties:
                        type: string
                    description: |
                      Fields required only for some answers, as
                      `{field: {other_field: value}}`. For example
                      `{"business_name": {"account_type": "business"}}`.
                      `{}` when the step has none.
                  optional:
                    type: array
                    items:
                      type: string
                    description: |
                      Fields this step accepts but never requires, such
                      as `return_url` on a `redirect` step.
                  patterns:
                    type: object
                    additionalProperties:
                      type: string
                    description: |
                      The shape a field must match, as a regular
                      expression checked against the upper-cased value,
                      such as the PAN or GSTIN format. A value that does
                      not match is refused with `400 invalid_request`
                      before anything is verified. `{}` when the step
                      has none.
                  method:
                    type: string
                    enum:
                      - submit
                      - otp
                      - redirect
                      - upload
                    description: |
                      How to perform this step.

                      `submit` posts the `required` fields. `otp` posts
                      a code the customer received. `redirect` returns a
                      `redirect_url` for the customer to open in their
                      own browser, after which you poll `poll_step`.
                      `upload` sends a document file as
                      `multipart/form-data`, described in `document`.

                      Branch on this, not on the region or the step
                      name, and one integration drives every carrier.
                  document:
                    type: object
                    description: |
                      Only on an `upload` step. Send the file in the
                      form field named by `field`, with `user_id`,
                      `region` and `carrier` as form fields beside it.
                      The step decides which document it is.
                    properties:
                      field:
                        type: string
                        example: document
                      formats:
                        type: array
                        items:
                          type: string
                        description: |
                          Accepted file types. The file's contents must
                          match its extension, so a renamed file is
                          refused.
                        example:
                          - jpg
                          - jpeg
                          - png
                          - pdf
                      max_bytes:
                        type: integer
                        example: 10485760
        example:
          success: true
          region: IN
          steps:
            - step: register
              required:
                - email
                - name
                - phone
              cooldown: false
              choices: {}
              method: submit
            - step: verify-otp
              required:
                - email_otp
                - mobile_otp
              cooldown: false
              method: submit
            - step: resend-otp
              required: []
              cooldown: false
              method: submit
            - step: verify-pan
              required:
                - business_type
                - pan
              cooldown: false
              method: submit
            - step: aadhaar-otp
              required:
                - aadhaar
              cooldown: true
              method: otp
            - step: aadhaar-verify
              required:
                - otp
              cooldown: true
              method: otp
            - step: verify-gst
              required:
                - gst
              cooldown: false
              method: submit
            - step: skip-gst
              required: []
              cooldown: false
              method: submit
            - step: preview
              required: []
              cooldown: false
              method: submit
            - step: accept
              required: []
              cooldown: false
              method: submit
  '401':
    description: Missing or invalid API key.
  '403':
    description: The caller's account cannot use the reseller API.
    content:
      application/json:
        schema:
          type: object
          properties:
            error:
              type: string
              description: Machine-readable error code.
            error_description:
              type: string
              description: Human-readable explanation.
            code:
              type: string
              description: |
                Stable, specific reason. Present on the newer endpoints (Voice
                cloning, WhatsApp); older endpoints return only `error`.
            request_id:
              type: string
              description: |
                Correlates this response with the server log. Present alongside
                `code`. Also sent as the `X-Request-Id` header.
        example:
          error: forbidden
          error_description: Access denied. Only reseller accounts can use this endpoint.
  '404':
    description: |
      No verification flow is configured for this region
      (`not_available`), or the request named a carrier the region
      does not have (`unknown_carrier`).
    content:
      application/json:
        schema:
          type: object
          properties:
            error:
              type: string
              description: Machine-readable error code.
            error_description:
              type: string
              description: Human-readable explanation.
            code:
              type: string
              description: |
                Stable, specific reason. Present on the newer endpoints (Voice
                cloning, WhatsApp); older endpoints return only `error`.
            request_id:
              type: string
              description: |
                Correlates this response with the server log. Present alongside
                `code`. Also sent as the `X-Request-Id` header.
        examples:
          not_available:
            summary: not_available
            value:
              error: not_available
              error_description: Verification is not available for this region.
          unknown_carrier:
            summary: unknown_carrier
            value:
              error: unknown_carrier
              error_description: There is no carrier 'carrier-9' in region IN.
              region: IN
              carriers:
                - carrier: carrier-1
                  label: Carrier 1
                  description: Landline numbers, 80 series.
                  kyc_required: true
                  unavailable: false
                  unavailable_note: null
  '409':
    description: |
      This region has more than one carrier and the request named
      none. The body lists the carriers to choose from.
    content:
      application/json:
        schema:
          type: object
          properties:
            error:
              type: string
              description: Machine-readable error code.
            error_description:
              type: string
              description: Human-readable explanation.
            code:
              type: string
              description: |
                Stable, specific reason. Present on the newer endpoints (Voice
                cloning, WhatsApp); older endpoints return only `error`.
            request_id:
              type: string
              description: |
                Correlates this response with the server log. Present alongside
                `code`. Also sent as the `X-Request-Id` header.
            region:
              type: string
              enum:
                - IN
                - US
            carriers:
              type: array
              description: |
                The carriers to choose from, each with what it stocks, so the
                refusal carries its own fix and is where you discover the
                names.
              items:
                type: object
                description: |
                  One carrier in a region: a network you can buy numbers from, with its
                  own stock and its own identity verification.
                properties:
                  carrier:
                    type: string
                    description: How you address this carrier. Stable across a rename, so it is safe to store.
                    example: carrier-1
                  label:
                    type: string
                    description: Display name for the carrier.
                    example: Carrier 1
                  description:
                    type: string
                    description: What this carrier stocks, so you can tell them apart.
                    example: Landline numbers, 80 series.
                  kyc_required:
                    type: boolean
                    description: >-
                      Whether a client must complete this carrier's verification before buying on
                      it.
                  unavailable:
                    type: boolean
                    description: |
                      `true` while this carrier is not taking orders. It is still
                      named, so a stored name keeps resolving; buy on the other one
                      meanwhile.
                  unavailable_note:
                    type: string
                    nullable: true
                    description: Short reason, when the carrier is unavailable. `null` otherwise.
        example:
          error: carrier_required
          error_description: >-
            Region IN has more than one carrier and no default. Pass carrier=<name> with one of the
            carriers listed here.
          region: IN
          carriers:
            - carrier: carrier-1
              label: Carrier 1
              description: Landline numbers, 80 series.
              kyc_required: true
              unavailable: false
              unavailable_note: null
            - carrier: carrier-2-new
              label: Carrier 2 (new)
              description: Mobile numbers, 94 and 79 series.
              kyc_required: true
              unavailable: false
              unavailable_note: null
  '500':
    description: Unexpected server error.
    content:
      application/json:
        schema:
          type: object
          properties:
            error:
              type: string
              description: Machine-readable error code.
            error_description:
              type: string
              description: Human-readable explanation.
            code:
              type: string
              description: |
                Stable, specific reason. Present on the newer endpoints (Voice
                cloning, WhatsApp); older endpoints return only `error`.
            request_id:
              type: string
              description: |
                Correlates this response with the server log. Present alongside
                `code`. Also sent as the `X-Request-Id` header.
            ref:
              type: string
              description: Reference to quote to support.
        example:
          error: server_error
          error_description: >-
            Something went wrong on our side. Please try again shortly, or contact support with
            reference a1b2c3d4e5f6.
          ref: a1b2c3d4e5f6
```

**Python SDK**

```python
from omnidimension import Client
client = Client(api_key)

# Call this before you build anything: the steps, the fields each
# one needs, and how each one is performed all come from here.
requirements = client.reseller.kyc_requirements("IN", carrier="carrier-1")["json"]

for step in requirements["steps"]:
    print(step["step"], step["method"], step["required"])
```

**curl**

```bash
curl -X GET "https://backend.omnidim.io/api/v1/reseller/kyc/requirements?region=IN&carrier=carrier-1" \
  -H "Authorization: Bearer $OMNIDIM_API_KEY"
```