# Submit a KYC verification step (/docs/api-reference/reseller/submitResellerKycStep)

> Run one step of a client's identity verification. One endpoint handles every step: the step path parameter names the step, and the body carries user_id, region, and whatever that step needs. Pick a step from the examples below to see its body.

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

**POST** `/reseller/kyc/steps/{step}`

Run one step of a client's identity verification. One endpoint handles
every step: the `step` path parameter names the step, and the body
carries `user_id`, `region`, and whatever that step needs. Pick a step
from the examples below to see its body.

```yaml
operationId: submitResellerKycStep
parameters:
  - name: step
    in: path
    required: true
    description: The verification step to run.
    schema:
      type: string
      enum:
        - register
        - verify-otp
        - resend-otp
        - verify-pan
        - aadhaar-otp
        - aadhaar-verify
        - verify-gst
        - skip-gst
        - preview
        - accept
requestBody:
  required: true
  content:
    application/json:
      schema:
        type: object
        required:
          - user_id
          - region
        properties:
          user_id:
            type: integer
            description: ID of the client completing verification.
          region:
            type: string
            enum:
              - IN
              - US
            description: Region this verification is for.
          name:
            type: string
            description: Customer's full name. Required for `register`.
            example: Demo User
          email:
            type: string
            format: email
            description: Customer's email address. Required for `register`.
            example: demo@example.com
          phone:
            type: string
            description: Customer's phone number including country code. Required for `register`.
            example: '+919876543210'
          mobile_otp:
            type: string
            description: OTP the client received by mobile. Required for `verify-otp`.
          email_otp:
            type: string
            description: OTP the client received by email. Required for `verify-otp`.
          pan:
            type: string
            description: Customer's PAN. Required for `verify-pan`.
          business_type:
            type: string
            enum:
              - proprietorship
              - company
              - firm
            description: Customer's business type. Required for `verify-pan`.
          aadhaar:
            type: string
            description: |
              Customer's Aadhaar number. Required for `aadhaar-otp`.
              Rate limited to one attempt roughly every 30 seconds.
          otp:
            type: string
            description: |
              OTP the client received for Aadhaar verification.
              Required for `aadhaar-verify`. Rate limited to one
              attempt roughly every 30 seconds.
          gst:
            type: string
            description: Customer's GST number. Required for `verify-gst`.
      example:
        user_id: 1234
        region: IN
        name: Demo User
        email: demo@example.com
        phone: '+919876543210'
responses:
  '200':
    description: |
      Step completed. `preview` is only present in the response
      to the `preview` step, and only carries the fields listed
      below; any field the verification provider does not have
      yet is omitted.
    content:
      application/json:
        schema:
          type: object
          properties:
            success:
              type: boolean
            status:
              type: string
              description: |
                Where this client has reached in verification.
                `not_started` before anything is submitted,
                `completed` once they are verified. Drive your
                integration off `next_step`, not this value.
            next_step:
              type: string
              nullable: true
              description: >-
                The step to run next. Chain to it without re-reading status. `null` once
                verification is complete.
              enum:
                - register
                - verify-otp
                - verify-pan
                - aadhaar-otp
                - aadhaar-verify
                - verify-gst
                - preview
                - accept
            message:
              type: string
              description: Human-readable confirmation for the step that just ran.
            preview:
              type: object
              description: Present only in the response to the `preview` step.
              properties:
                client:
                  type: object
                  properties:
                    name:
                      type: string
                    email:
                      type: string
                      format: email
                    mobile:
                      type: string
                    country_code:
                      type: string
                pan:
                  type: object
                  properties:
                    pan:
                      type: string
                    business_type:
                      type: string
                aadhar:
                  type: object
                  properties:
                    name:
                      type: string
                    address:
                      type: string
                gst:
                  type: object
                  properties:
                    gst_num:
                      type: string
                    gstin:
                      type: string
        examples:
          accept:
            summary: accept
            value:
              success: true
              status: completed
              message: Verification completed.
          preview:
            summary: preview
            value:
              success: true
              status: preview
              message: Review the details, then call accept.
              preview:
                client:
                  name: Demo User
                  email: demo@example.com
                  mobile: '9876543210'
                  country_code: '91'
                pan:
                  pan: ABCDE1234F
                  business_type: proprietorship
                aadhar:
                  name: Demo User
                  address: 123 Example Street, Example City, 560001
                gst:
                  gst_num: 29ABCDE1234F1Z5
                  gstin: 29ABCDE1234F1Z5
  '400':
    description: The request body is missing a field the step requires.
    content:
      application/json:
        schema:
          type: object
          properties:
            error:
              type: string
              description: Machine-readable error code.
            error_description:
              type: string
              description: Human-readable explanation.
        example:
          error: invalid_request
          error_description: 'Missing required fields: pan, business_type.'
  '401':
    description: Missing or invalid API key.
  '403':
    description: |
      A `user_id` was sent by a key that is not a reseller admin, or
      the named client's account is currently unavailable.
    content:
      application/json:
        schema:
          type: object
          properties:
            error:
              type: string
              description: Machine-readable error code.
            error_description:
              type: string
              description: Human-readable explanation.
        example:
          error: forbidden
          error_description: Access denied. Only reseller accounts can use this endpoint.
  '404':
    description: |
      The child user was not found (`not_found`), or verification
      is not available for this region or step (`not_available`).
    content:
      application/json:
        schema:
          type: object
          properties:
            error:
              type: string
              description: Machine-readable error code.
            error_description:
              type: string
              description: Human-readable explanation.
        example:
          error: not_found
          error_description: Child user not found.
  '409':
    description: This step was called before its prerequisite step was completed.
    content:
      application/json:
        schema:
          type: object
          properties:
            error:
              type: string
              description: Machine-readable error code.
            error_description:
              type: string
              description: Human-readable explanation.
        example:
          error: step_order
          error_description: Complete the contact verification (OTP) step first.
  '422':
    description: The verification provider rejected the submitted details.
    content:
      application/json:
        schema:
          type: object
          properties:
            error:
              type: string
              description: Machine-readable error code.
            error_description:
              type: string
              description: Human-readable explanation.
        example:
          error: verification_failed
          error_description: OTP verification failed. Please try again.
  '429':
    description: An Aadhaar step was retried before the cooldown elapsed.
    content:
      application/json:
        schema:
          type: object
          properties:
            error:
              type: string
              description: Machine-readable error code.
            error_description:
              type: string
              description: Human-readable explanation.
        example:
          error: too_fast
          error_description: Please wait a moment before retrying this step.
  '500':
    description: Unexpected server error.
    content:
      application/json:
        schema:
          allOf:
            - type: object
              properties:
                error:
                  type: string
                  description: Machine-readable error code.
                error_description:
                  type: string
                  description: Human-readable explanation.
            - type: object
              properties:
                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
```

**curl**

```bash
curl -X POST "https://omnidim.io/api/v1/reseller/kyc/steps/{step}" \
  -H "Authorization: Bearer $OMNIDIM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "user_id": 1234,
  "region": "IN",
  "name": "Demo User",
  "email": "demo@example.com",
  "phone": "+919876543210"
}'
```

## What each step sends and returns [#what-each-step-sends-and-returns]

Every call is a `POST` to this endpoint with the step name in the path. The body
always carries `user_id` and `region`, plus the fields in the table below. Steps
with no fields of their own take just those two.

Every response has the same shape:

```json
{
  "success": true,
  "status": "otp_verified",
  "next_step": "verify-pan",
  "message": "Contact details verified."
}
```

`next_step` is the field to act on. Run it, read the next one off that response,
and keep going until the status comes back `completed`.

| Step             | Body fields you add       | `next_step` you get back | `message`                                               |
| ---------------- | ------------------------- | ------------------------ | ------------------------------------------------------- |
| `register`       | `name`, `email`, `phone`  | `verify-otp`             | Verification codes sent to the email and mobile number. |
| `verify-otp`     | `mobile_otp`, `email_otp` | `verify-pan`             | Contact details verified.                               |
| `verify-pan`     | `pan`, `business_type`    | `aadhaar-otp`            | PAN verified.                                           |
| `aadhaar-otp`    | `aadhaar`                 | `aadhaar-verify`         | Aadhaar verification code sent.                         |
| `aadhaar-verify` | `otp`                     | `verify-gst`             | Aadhaar verified.                                       |
| `verify-gst`     | `gst`                     | `preview`                | GST verified.                                           |
| `skip-gst`       | none                      | `preview`                | GST step skipped.                                       |
| `preview`        | none                      | `accept`                 | Review the details, then call accept.                   |
| `accept`         | none                      | `null`                   | Verification completed.                                 |

`verify-gst` and `skip-gst` are a choice: run one or the other, both land on
`preview`. Once the status is `completed` the client is verified and can buy a
number.

### The one step that returns more [#the-one-step-that-returns-more]

`preview` returns everything the client should confirm before `accept` commits
it, alongside the usual fields:

```json
{
  "success": true,
  "status": "preview",
  "next_step": "accept",
  "message": "Review the details, then call accept.",
  "preview": {
    "client": { "name": "Demo User", "email": "demo@example.com", "mobile": "9876543210", "country_code": "91" },
    "pan": { "pan": "ABCDE1234F", "business_type": "proprietorship" },
    "aadhar": { "name": "Demo User", "address": "..." },
    "gst": { "gst_num": "29ABCDE1234F1Z5", "gstin": "..." }
  }
}
```

Anything the verification provider does not hold yet is left out, so treat each
block as optional.

### Resending a code [#resending-a-code]

`resend-otp` takes just `user_id` and `region`, and answers
`next_step: verify-otp` like the step it belongs to. It sits outside the
sequence and is never returned as a `next_step`, so call it only when the client
says a code did not arrive.

  Roughly one attempt every 30 seconds on `aadhaar-otp` and `aadhaar-verify`.
  Repeated rapid retries can lock the client out at the identity authority,
  which is not reversible from this API. Calling too soon returns `429`.

### Order is enforced [#order-is-enforced]

Calling a step before its prerequisite is complete returns `409` naming the step
to run first, so following `next_step` is all you need to stay in order.

### What you may keep [#what-you-may-keep]

You are relaying what the client types into your own interface, nothing more.
Never store the values you send in these requests once the call completes, and
redact them from your own request logs. That covers the OTPs, the PAN, the
Aadhaar number, and the GST number.

The full loop, with the step diagram, is in the
[Reseller API guide](/docs/reseller-api).