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

# Get KYC Data

> Retrieve signed KYC data for granted permissions

<Info>
  This endpoint returns the user's actual KYC data. Only call this after verifying grants via `/kyc/validate`.
</Info>

## Response Signing

All KYC data responses must be signed by your private key. Recipients verify the signature using your public key from `/metadata.json`. This proves:

* The data came from your KYC provider
* The data hasn't been tampered with
* You attested to the verification at the specified time

## Data Structure

The `data` object contains fields based on the requested grants:

### Identity Fields

```json theme={null}
{
  "name": {
    "first_name": "John",
    "last_name": "Doe",
    "full_name": "John Michael Doe"
  },
  "email": "john.doe@example.com",
  "phone": "+1-555-123-4567",
  "date_of_birth": "1990-05-15",
  "nationality": "US",
  "address": {
    "street": "123 Main Street",
    "city": "San Francisco",
    "state": "CA",
    "postal_code": "94102",
    "country": "US"
  }
}
```

### Document Fields

```json theme={null}
{
  "id_card": {
    "type": "national_id",
    "number": "XXX-XX-1234",
    "country": "US",
    "issued_at": "2020-01-15",
    "expires_at": "2030-01-15",
    "image_url": "https://kyc.provider.example/secure/docs/id_abc123.jpg"
  },
  "passport": {
    "type": "passport",
    "number": "XXXXXXXXX",
    "country": "US",
    "issued_at": "2019-06-01",
    "expires_at": "2029-06-01",
    "image_url": "https://kyc.provider.example/secure/docs/passport_abc123.jpg"
  }
}
```

### Verification Fields

```json theme={null}
{
  "liveness": {
    "verified": true,
    "selfie_url": "https://kyc.provider.example/secure/docs/selfie_abc123.jpg",
    "verified_at": 1706500000
  },
  "aml": {
    "status": "clear",
    "checked_at": 1706500000
  }
}
```

## Implementation

```javascript theme={null}
app.post('/kyc/data', verifyAuth, async (req, res) => {
  const requestingOcid = req.headers['x-oc-id'];
  const { user_ocid, grants } = req.body;

  // Verify user exists
  const userKyc = await db.getUserKyc(user_ocid);
  if (!userKyc) {
    return res.status(404).json({
      error: { code: 'USER_NOT_FOUND', message: 'User not found' }
    });
  }

  // Verify grants for this OCID
  const userGrants = await db.getKycGrants(user_ocid, requestingOcid);

  // Check all requested grants are authorized
  for (const grant of grants) {
    if (!userGrants.granted?.includes(grant)) {
      return res.status(403).json({
        error: {
          code: 'GRANT_NOT_AUTHORIZED',
          message: `Grant '${grant}' not authorized`,
          details: { grant }
        }
      });
    }
  }

  // Build response data
  const data = {};
  for (const grant of grants) {
    data[grant] = userKyc.items[grant].value;

    // Generate secure URLs for documents
    if (data[grant]?.image_url) {
      data[grant].image_url = await generateSecureUrl(
        data[grant].image_url,
        { expires_in: 3600 } // 1 hour
      );
    }
  }

  const issuedAt = Math.floor(Date.now() / 1000);
  const expiresAt = issuedAt + 3600; // Data valid for 1 hour

  // Sign the data
  const dataToSign = { user_ocid, data, issued_at: issuedAt, expires_at: expiresAt };
  const canonical = JSON.stringify(dataToSign, Object.keys(dataToSign).sort());
  const signature = secp256k1_sign(PRIVATE_KEY, SHA256(canonical));

  res.json({
    user_ocid,
    data,
    issued_at: issuedAt,
    expires_at: expiresAt,
    signature
  });
});
```

## Verifying the Signature

Recipients should verify the KYC data signature:

```javascript theme={null}
async function verifyKycData(kycProviderOcid, kycDataResponse) {
  // Fetch KYC provider's public key
  const metadata = await fetchOcidMetadata(kycProviderOcid);
  const publicKey = metadata.config.publicKey;

  // Reconstruct signed data
  const { signature, ...dataToVerify } = kycDataResponse;
  const canonical = JSON.stringify(dataToVerify, Object.keys(dataToVerify).sort());

  // Verify signature
  const isValid = secp256k1_verify(publicKey, SHA256(canonical), signature);

  if (!isValid) {
    throw new Error('Invalid KYC data signature');
  }

  // Check expiration
  if (kycDataResponse.expires_at < Math.floor(Date.now() / 1000)) {
    throw new Error('KYC data has expired');
  }

  return kycDataResponse.data;
}
```

## Security Considerations

* Document image URLs are time-limited and should expire within 1 hour
* Sensitive fields like full ID numbers may be partially redacted
* Cache KYC data responses only until `expires_at`
* Always verify signatures before trusting the data


## OpenAPI

````yaml kyc-provider-api/endpoint/kyc-provider-api/openapi.json post /kyc/data
openapi: 3.1.0
info:
  title: Opencharge KYC Provider API
  description: >-
    API specification for KYC providers integrating with the Opencharge
    protocol. KYC providers collect and verify user identity information, then
    share it with authorized OCN entities.
  version: 0.1.0
  license:
    name: MIT
servers:
  - url: https://kyc.provider.example/opencharge
    description: Example KYC provider Opencharge endpoint
security: []
paths:
  /kyc/data:
    post:
      summary: Get KYC data
      description: >-
        Retrieve the user's KYC data for granted permissions. Returns signed
        data that can be verified using the KYC provider's public key.
      operationId: getKycData
      parameters:
        - $ref: '#/components/parameters/X-OC-ID'
        - $ref: '#/components/parameters/X-OC-Timestamp'
        - $ref: '#/components/parameters/X-OC-Nonce'
        - $ref: '#/components/parameters/X-OC-Signature'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/KycDataRequest'
            example:
              user_ocid: 12345
              grants:
                - name
                - email
                - phone
                - id_card
                - liveness
      responses:
        '200':
          description: Signed KYC data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/KycDataResponse'
              example:
                user_ocid: 12345
                data:
                  name:
                    first_name: John
                    last_name: Doe
                    full_name: John Michael Doe
                  email: john.doe@example.com
                  phone: +1-555-123-4567
                  id_card:
                    type: national_id
                    number: XXX-REDACTED
                    country: US
                    issued_at: '2020-01-15'
                    expires_at: '2030-01-15'
                    image_url: https://kyc.provider.example/secure/docs/id_abc123.jpg
                  liveness:
                    verified: true
                    selfie_url: https://kyc.provider.example/secure/docs/selfie_abc123.jpg
                    verified_at: 1706500000
                issued_at: 1706500500
                expires_at: 1706504100
                signature: a1b2c3d4e5f6...
        '401':
          description: Authentication failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Access denied - user has not granted permission
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: User not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  parameters:
    X-OC-ID:
      name: X-OC-ID
      in: header
      required: true
      description: Caller's OCID
      schema:
        type: string
      example: '300'
    X-OC-Timestamp:
      name: X-OC-Timestamp
      in: header
      required: true
      description: Unix timestamp in seconds
      schema:
        type: string
      example: '1706500000'
    X-OC-Nonce:
      name: X-OC-Nonce
      in: header
      required: true
      description: Unique request identifier
      schema:
        type: string
      example: req_abc123
    X-OC-Signature:
      name: X-OC-Signature
      in: header
      required: true
      description: secp256k1 ECDSA signature
      schema:
        type: string
      example: a1b2c3d4...7f1b
  schemas:
    KycDataRequest:
      type: object
      required:
        - user_ocid
        - grants
      properties:
        user_ocid:
          type: integer
          description: The end user's OCID
        grants:
          type: array
          items:
            type: string
          description: KYC data fields to retrieve
    KycDataResponse:
      type: object
      required:
        - user_ocid
        - data
        - issued_at
        - signature
      properties:
        user_ocid:
          type: integer
        data:
          type: object
          description: KYC data for granted permissions
          properties:
            name:
              type: object
              properties:
                first_name:
                  type: string
                last_name:
                  type: string
                full_name:
                  type: string
            email:
              type: string
              format: email
            phone:
              type: string
            date_of_birth:
              type: string
              format: date
            nationality:
              type: string
              description: ISO 3166-1 alpha-2 country code
            address:
              type: object
              properties:
                street:
                  type: string
                city:
                  type: string
                state:
                  type: string
                postal_code:
                  type: string
                country:
                  type: string
            id_card:
              $ref: '#/components/schemas/DocumentData'
            passport:
              $ref: '#/components/schemas/DocumentData'
            driver_license:
              $ref: '#/components/schemas/DocumentData'
            proof_of_address:
              $ref: '#/components/schemas/DocumentData'
            liveness:
              type: object
              properties:
                verified:
                  type: boolean
                selfie_url:
                  type: string
                  format: uri
                verified_at:
                  type: integer
            aml:
              type: object
              properties:
                status:
                  type: string
                  enum:
                    - clear
                    - flagged
                    - pending
                checked_at:
                  type: integer
        issued_at:
          type: integer
          description: Unix timestamp when this data was issued
        expires_at:
          type: integer
          description: Unix timestamp when this data expires
        signature:
          type: string
          description: KYC provider's signature of the data object
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
              enum:
                - INVALID_SIGNATURE
                - TIMESTAMP_EXPIRED
                - NONCE_REUSED
                - UNKNOWN_OCID
                - USER_NOT_FOUND
                - KYC_NOT_COMPLETE
                - GRANT_NOT_AUTHORIZED
                - GRANT_EXPIRED
                - INVALID_GRANT
                - INVALID_CALLBACK_URL
            message:
              type: string
            details:
              type: object
    DocumentData:
      type: object
      properties:
        type:
          type: string
          description: Document type (national_id, passport, etc.)
        number:
          type: string
          description: Document number (may be partially redacted)
        country:
          type: string
          description: Issuing country (ISO 3166-1 alpha-2)
        issued_at:
          type: string
          format: date
        expires_at:
          type: string
          format: date
        image_url:
          type: string
          format: uri
          description: Secure URL to document image

````