---
title: "Initiate server-to-server KYC verification"
description: "Initiates server-to-server (S2S) KYC verification for a customer who has already uploaded the required documents using the `/v1/customers/kyc/upload-document` endpoint."
protocol: rest
method: POST
endpoint: "/v1/customers/kyc/start-s2s"
baseUrl: "https://api.gravv.xyz"
group: "KYC"
auth:
  label: "Api-Key"
  name: "Api-Key"
  in: header
---

# Initiate server-to-server KYC verification

<Endpoint method="POST" path="/v1/customers/kyc/start-s2s" />

Initiates server-to-server (S2S) KYC verification for a customer who has already uploaded the required documents using the `/v1/customers/kyc/upload-document` endpoint.

This is step 2 of the S2S verification process. Before calling this endpoint, upload all required documents.

For business customers, complete KYC verification for every associated person before starting the business's verification.

On success, verification is initiated and the review outcome is delivered later by webhook. Business customers must upload a registration certificate first, or the request fails with a 400.

## Headers

<ParamField name="Idempotency-Key" header="Idempotency-Key" type="string" required>
  Required for POST requests. Reuse only for an identical retry.
</ParamField>

## Request Body

<ParamField name="customer_id" body="customer_id" type="string" required>
  Customer ID to initiate KYC verification for. Documents must be uploaded for this customer before calling this endpoint.
</ParamField>

### Request Examples

<RequestExample>

```json title="IndividualKYC"
{
  "customer_id": "aabe0a33-6716-42e2-bbca-7abf1a8bd91c"
}
```

```json title="BusinessKYC"
{
  "customer_id": "b84d9a52-2978-43e5-9b86-eac9839e1146"
}
```

</RequestExample>

## Responses

| Status | Description |
| --- | --- |
| `200` | KYC verification initiated |
| `400` | Invalid request |
| `404` | Customer or KYC record not found |
| `409` | KYC already completed for this customer |
| `412` | Required documents have not been uploaded |
| `500` | Internal processing error during S2S initiation |

### 200 response

<ResponseField name="data" type="object" required>

  <Expandable title="properties">
    <ResponseField name="status" type="string" required>

      Possible values: `pending`
    </ResponseField>

    <ResponseField name="message" type="string" required>
    </ResponseField>

  </Expandable>
</ResponseField>

<ResponseField name="error" type="any" required>
  Error information if the request failed
</ResponseField>

<ResponseExample>

```json title="Response"
{
  "data": {
    "status": "pending",
    "message": "KYC verification initiated. Status will be updated via webhook."
  },
  "error": null
}
```

</ResponseExample>

### 400 response

<ResponseField name="data" type="any" required>
  Always null for error responses
</ResponseField>

<ResponseField name="error" type="string" required>
  Human-readable error message
</ResponseField>

<ResponseField name="error" type="any">

  <Expandable title="properties">
    <ResponseField name="code" type="any">
    </ResponseField>

    <ResponseField name="message" type="any">
    </ResponseField>

  </Expandable>
</ResponseField>

<ResponseExample>

```json title="Response"
{
  "data": null,
  "error": {
    "code": "INVALID_REQUEST",
    "message": "The customer ID provided is not valid"
  }
}
```

</ResponseExample>

### 404 response

<ResponseField name="data" type="any" required>
  Always null for error responses
</ResponseField>

<ResponseField name="error" type="string" required>
  Human-readable error message
</ResponseField>

<ResponseField name="error" type="any">

  <Expandable title="properties">
    <ResponseField name="code" type="any">
    </ResponseField>

    <ResponseField name="message" type="any">
    </ResponseField>

  </Expandable>
</ResponseField>

<ResponseExample>

```json title="CustomerNotFound"
{
  "data": null,
  "error": {
    "code": "CUSTOMER_NOT_FOUND",
    "message": "Customer not found. Please verify the customer exists in our system."
  }
}
```

```json title="KycNotFound"
{
  "data": null,
  "error": {
    "code": "KYC_NOT_FOUND",
    "message": "KYC record not found for the specified level."
  }
}
```

</ResponseExample>

### 409 response

<ResponseField name="data" type="any" required>
  Always null for error responses
</ResponseField>

<ResponseField name="error" type="string" required>
  Human-readable error message
</ResponseField>

<ResponseExample>

```json title="Response"
{
  "data": null,
  "error": {
    "code": "KYC_ALREADY_COMPLETED",
    "message": "KYC verification has already been completed for this customer."
  }
}
```

</ResponseExample>

### 412 response

<ResponseField name="data" type="any" required>
  Always null for error responses
</ResponseField>

<ResponseField name="error" type="string" required>
  Human-readable error message
</ResponseField>

<ResponseExample>

```json title="NoDocuments"
{
  "data": null,
  "error": {
    "code": "INVALID_REQUEST",
    "message": "no documents found for customer; please upload required documents before initiating S2S verification"
  }
}
```

```json title="MissingIdDocument"
{
  "data": null,
  "error": {
    "code": "INVALID_REQUEST",
    "message": "missing required ID document"
  }
}
```

```json title="MissingSelfie"
{
  "data": null,
  "error": {
    "code": "INVALID_REQUEST",
    "message": "missing required selfie document"
  }
}
```

</ResponseExample>

### 500 response

<ResponseField name="data" type="any" required>
  Always null for error responses
</ResponseField>

<ResponseField name="error" type="string" required>
  Human-readable error message
</ResponseField>

<ResponseExample>

```json title="Response"
{
  "data": null,
  "error": {
    "code": "KYC_PROCESSING_ERROR",
    "message": "failed to upload documents to verification provider"
  }
}
```

</ResponseExample>

## Authorization

- **ApiKeyAuth**
