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

# Create Cardholder

> Create a new cardholder for virtual card issuing. Submit personal details and start issuing cards immediately. POST /cardholders.

## Overview

Create a new cardholder for your card issuing program. The cardholder will be created with `ACTIVE` status. You can issue cards to the cardholder immediately after creation.

## Required Fields

| Field         | Type   | Description                                         |
| ------------- | ------ | --------------------------------------------------- |
| `firstName`   | string | Cardholder's first name (1-100 chars)               |
| `lastName`    | string | Cardholder's last name (1-100 chars)                |
| `email`       | string | Email address (unique per business)                 |
| `phone`       | string | Phone number in E.164 format (e.g. `+15550001234`)  |
| `dateOfBirth` | string | Date of birth (`YYYY-MM-DD`, must be 18+ years old) |
| `gender`      | string | Gender (`MALE` or `FEMALE`)                         |
| `country`     | string | ISO 3166-1 alpha-2 country code (e.g. `US`)         |

## Address Fields

| Field     | Type   | Required | Description                       |
| --------- | ------ | -------- | --------------------------------- |
| `address` | string | Yes      | Street address (max 255 chars)    |
| `city`    | string | Yes      | City (max 100 chars)              |
| `state`   | string | Yes      | State or province (max 100 chars) |
| `zipCode` | string | Yes      | Postal/ZIP code (max 20 chars)    |

## Optional Fields

| Field        | Type   | Description                                                         |
| ------------ | ------ | ------------------------------------------------------------------- |
| `externalId` | string | Your platform's cardholder ID (unique per business, max 100 chars)  |
| `metadata`   | object | Arbitrary key-value pairs to store custom data about the cardholder |

## KYC Object (Shared KYC — Enabled Businesses Only)

If your business has **Shared KYC** enabled, you can include a `kyc` object at cardholder creation to submit pre-verified identity documents in the same request. The cardholder will immediately be set to `PENDING` and processed in the background; a `cardholder.kyc_approved` webhook is dispatched once documents are uploaded and verified.

If your business does **not** have Shared KYC enabled, the `kyc` object is silently ignored regardless of what is sent.

| Field              | Type   | Required | Description                                    |
| ------------------ | ------ | -------- | ---------------------------------------------- |
| `kyc.idFrontUrl`   | string | Yes      | URL to the front image of the ID document      |
| `kyc.selfieUrl`    | string | Yes      | URL to a selfie photo of the cardholder        |
| `kyc.idBackUrl`    | string | No       | URL to the back image of the ID document       |
| `kyc.documentType` | string | No       | `PASSPORT`, `NATIONAL_ID`, or `DRIVER_LICENSE` |

<Note>
  For standard identity verification (cardholder completes verification themselves), use [Initiate KYC Verification](/v3/api-reference/cardholders/kyc-session) after creating the cardholder.
</Note>

## Metadata Object (Optional)

The `metadata` field accepts any flat JSON object. Use it to store your own data alongside the cardholder — for example, department, employee ID, or tier level.

```json theme={null}
{
  "metadata": {
    "department": "Engineering",
    "employee_id": "EMP-1234",
    "tier": "VIP",
    "cost_center": "CC-500"
  }
}
```

Metadata is returned in all cardholder responses and displayed in the business panel.

## Example Usage

<CodeGroup>
  ```php PHP theme={null}
  <?php
  $data = [
      'externalId' => 'EXT-0001',
      'firstName' => 'Alice',
      'lastName' => 'Example',
      'email' => 'alice@example.com',
      'phone' => '+15550001234',
      'dateOfBirth' => '1990-01-01',
      'gender' => 'MALE',
      'address' => '123 Main Street, Apt 4B',
      'city' => 'Newark',
      'state' => 'Delaware',
      'country' => 'US',
      'zipCode' => '000000',
      'metadata' => [
          'department' => 'Engineering',
          'employee_id' => 'EMP-1234',
      ],
  ];

  $response = file_get_contents(
      'https://api.fyatu.com/api/v3/cardholders',
      false,
      stream_context_create([
          'http' => [
              'method' => 'POST',
              'header' => [
                  'Authorization: Bearer ' . $accessToken,
                  'Content-Type: application/json'
              ],
              'content' => json_encode($data)
          ]
      ])
  );

  $result = json_decode($response, true);
  echo "Created cardholder: " . $result['data']['id'] . "\n";
  ```

  ```javascript Node.js theme={null}
  const data = {
    externalId: 'EXT-0001',
    firstName: 'Alice',
    lastName: 'Example',
    email: 'alice@example.com',
    phone: '+15550001234',
    dateOfBirth: '1990-01-01',
    gender: 'MALE',
    address: '123 Main Street, Apt 4B',
    city: 'Newark',
    state: 'Delaware',
    country: 'US',
    zipCode: '000000',
    metadata: {
      department: 'Engineering',
      employee_id: 'EMP-1234',
    },
  };

  const response = await fetch('https://api.fyatu.com/api/v3/cardholders', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${accessToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(data)
  });

  const result = await response.json();
  console.log('Created cardholder:', result.data.id);
  ```
</CodeGroup>

## Example Response

```json theme={null}
{
  "success": true,
  "status": 201,
  "message": "Cardholder created successfully",
  "data": {
    "id": "CH1a2b3c4d5e6f",
    "externalId": "EXT-0001",
    "firstName": "Alice",
    "lastName": "Example",
    "email": "alice@example.com",
    "phone": "+15550001234",
    "dateOfBirth": "1990-01-01",
    "gender": "MALE",
    "address": {
      "line1": "123 Main Street, Apt 4B",
      "city": "Newark",
      "state": "Delaware",
      "country": "US",
      "zipCode": "000000"
    },
    "status": "ACTIVE",
    "metadata": {
      "department": "Engineering",
      "employee_id": "EMP-1234"
    },
    "createdAt": "2026-01-15T21:31:39+00:00"
  },
  "meta": {
    "requestId": "req_2f7fb4227a1007418773122f",
    "timestamp": "2026-01-15T21:31:39+00:00"
  }
}
```

## Next Steps

After creating a cardholder, you can:

1. **Issue a card immediately** — Use [Create Card](/v3/api-reference/cards/create) to issue a virtual card to this cardholder
2. **Verify identity (optional)** — Use [Initiate KYC Verification](/v3/api-reference/cardholders/kyc-session) to let the cardholder verify themselves, or [Submit KYC Documents](/v3/api-reference/cardholders/kyc) to submit documents on their behalf

<Note>
  KYC verification is optional and does not block card issuance. You can issue cards to cardholders without completing KYC.
</Note>

## Error Responses

### Duplicate Email (409)

```json theme={null}
{
  "success": false,
  "status": 409,
  "message": "A cardholder with this email already exists",
  "error": { "code": "CONFLICT" }
}
```

### Duplicate External ID (409)

```json theme={null}
{
  "success": false,
  "status": 409,
  "message": "A cardholder with this externalId already exists",
  "error": { "code": "CONFLICT" }
}
```

### Underage Cardholder (400)

```json theme={null}
{
  "success": false,
  "status": 400,
  "message": "Validation failed",
  "error": {
    "code": "VALIDATION_ERROR",
    "details": [
      { "field": "dateOfBirth", "message": "Cardholder must be at least 18 years old." }
    ]
  }
}
```

### Validation Error (400)

```json theme={null}
{
  "success": false,
  "status": 400,
  "message": "Validation failed",
  "error": {
    "code": "VALIDATION_ERROR",
    "details": [
      { "field": "gender", "message": "The gender field is required." }
    ]
  }
}
```

<Warning>
  Email addresses and external IDs must be unique within your business. If you try to create a cardholder with an email or externalId that already exists, you'll receive a 409 Conflict error.
</Warning>

<Tip>
  Use the `externalId` field to store your platform's cardholder/user ID. This makes it easy to link FYATU cardholders to users in your own system.
</Tip>


## OpenAPI

````yaml v3/openapi.json POST /cardholders
openapi: 3.1.0
info:
  title: FYATU API v3
  description: >-
    FYATU API v3 with JWT authentication for Collections, Payouts, and Card
    Issuing.
  version: 3.0.0
  contact:
    name: FYATU Support
    url: https://fyatu.com
    email: support@fyatu.com
servers:
  - url: https://api.fyatu.com/api/v3
    description: Production
security: []
tags:
  - name: Authentication
    description: JWT token management endpoints
  - name: Account
    description: Business account, wallet, and address management
  - name: Collections
    description: Accept payments from customers via checkout sessions
  - name: Refunds
    description: Issue refunds for completed collections
  - name: Payouts
    description: Send money to Fyatu account holders
  - name: Cardholders
    description: Cardholder management for card issuing programs
  - name: Cards
    description: Issue, fund, freeze, and manage virtual cards
  - name: Webhooks
    description: Webhook configuration and management
paths:
  /cardholders:
    post:
      tags:
        - Cardholders
      summary: Create Cardholder
      description: >-
        Create a new cardholder for your card issuing program. The cardholder
        will be created with ACTIVE status. You can optionally include KYC
        documents in the request body - if provided, the kycStatus will be set
        to SUBMITTED, otherwise it defaults to UNSUBMITTED.
      operationId: createCardholder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CardholderCreateRequest'
            example:
              externalId: EXT-0001
              firstName: Alice
              lastName: Example
              email: alice@example.com
              phone: '+15550001234'
              dateOfBirth: '1990-05-15'
              gender: MALE
              address: 123 Main Street, Apt 4B
              city: Newark
              state: Delaware
              country: US
              zipCode: '000000'
              kyc:
                documentType: PASSPORT
                documentNumber: AB1234567
                idFrontUrl: https://storage.example.com/docs/passport_front.jpg
                idBackUrl: https://storage.example.com/docs/passport_back.jpg
                idSelfieUrl: https://storage.example.com/docs/selfie.jpg
      responses:
        '201':
          description: Cardholder created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CardholderResponse'
              example:
                success: true
                status: 201
                message: Cardholder created successfully
                data:
                  id: ch_1a2b3c4d5e6f7890abcdef1234567890
                  externalId: EXT-0001
                  firstName: John
                  lastName: Example
                  email: alice@example.com
                  phone: '+15550001234'
                  dateOfBirth: '1990-01-01'
                  gender: FEMALE
                  address:
                    line1: 123 Main Street, Apt 4B
                    city: Newark
                    state: Delaware
                    country: US
                    zipCode: '000000'
                  document:
                    type: null
                    number: null
                  kyc:
                    status: UNSUBMITTED
                    idFrontUrl: null
                    idBackUrl: null
                    selfieUrl: null
                  status: ACTIVE
                  kycStatus: UNSUBMITTED
                  createdAt: '2026-01-17T10:30:00+00:00'
                meta:
                  requestId: req_c1d2e3f4g5h6
                  timestamp: '2026-01-17T10:30:00+00:00'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '409':
          description: Conflict - cardholder with email or externalId already exists
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        - BearerAuth: []
components:
  schemas:
    CardholderCreateRequest:
      type: object
      required:
        - firstName
        - lastName
        - email
        - phone
        - dateOfBirth
        - country
      properties:
        externalId:
          type: string
          maxLength: 100
          description: Your platform's cardholder ID (unique per app)
        firstName:
          type: string
          minLength: 1
          maxLength: 100
          description: Cardholder's first name
        lastName:
          type: string
          minLength: 1
          maxLength: 100
          description: Cardholder's last name
        email:
          type: string
          format: email
          maxLength: 255
          description: Cardholder's email address (unique per app)
        phone:
          type: string
          minLength: 6
          maxLength: 20
          description: Phone number with country code
        dateOfBirth:
          type: string
          format: date
          description: Date of birth (YYYY-MM-DD)
        gender:
          type: string
          enum:
            - MALE
            - FEMALE
            - OTHER
          description: Gender (optional)
        address:
          type: string
          maxLength: 255
          description: Street address
        city:
          type: string
          maxLength: 100
          description: City
        state:
          type: string
          maxLength: 100
          description: State or province
        country:
          type: string
          minLength: 2
          maxLength: 2
          description: ISO 3166-1 alpha-2 country code
        zipCode:
          type: string
          maxLength: 20
          description: Postal/ZIP code
        kyc:
          type: object
          description: >-
            Optional KYC documents. When any KYC data is provided, kycStatus
            will be set to SUBMITTED. All fields are optional - use the Submit
            KYC endpoint for mandatory document submission.
          properties:
            documentType:
              type: string
              enum:
                - PASSPORT
                - NATIONAL_ID
                - DRIVER_LICENSE
              description: Type of identification document
            documentNumber:
              type: string
              maxLength: 50
              description: Document identification number
            idFrontUrl:
              type: string
              format: uri
              description: URL to the front image of the ID document
            idBackUrl:
              type: string
              format: uri
              description: URL to the back image (optional for passports)
            idSelfieUrl:
              type: string
              format: uri
              description: URL to a selfie photo holding the ID document
    CardholderResponse:
      type: object
      description: Response for create and update cardholder operations
      properties:
        success:
          type: boolean
          example: true
        status:
          type: integer
          example: 201
        message:
          type: string
        data:
          type: object
          properties:
            id:
              type: string
              example: ch_1a2b3c4d5e6f7890abcdef1234567890
            externalId:
              type: string
              nullable: true
            firstName:
              type: string
            lastName:
              type: string
            email:
              type: string
            phone:
              type: string
            dateOfBirth:
              type: string
              format: date
            gender:
              type: string
              enum:
                - MALE
                - FEMALE
                - OTHER
              nullable: true
            address:
              type: object
              properties:
                line1:
                  type: string
                  nullable: true
                city:
                  type: string
                  nullable: true
                state:
                  type: string
                  nullable: true
                country:
                  type: string
                zipCode:
                  type: string
                  nullable: true
            document:
              type: object
              description: Document info (included in update response)
              properties:
                type:
                  type: string
                  enum:
                    - PASSPORT
                    - NATIONAL_ID
                    - DRIVER_LICENSE
                  nullable: true
                number:
                  type: string
                  nullable: true
            kyc:
              type: object
              description: KYC info (included in update response)
              properties:
                status:
                  type: string
                  enum:
                    - UNSUBMITTED
                    - SUBMITTED
                    - ACCEPTED
                    - REJECTED
                idFrontUrl:
                  type: string
                  nullable: true
                idBackUrl:
                  type: string
                  nullable: true
                selfieUrl:
                  type: string
                  nullable: true
            status:
              type: string
              enum:
                - ACTIVE
                - INACTIVE
                - SUSPENDED
            kycStatus:
              type: string
              enum:
                - UNSUBMITTED
                - SUBMITTED
                - ACCEPTED
                - REJECTED
              description: 'Deprecated: use kyc.status instead'
            createdAt:
              type: string
              format: date-time
            updatedAt:
              type: string
              format: date-time
              nullable: true
        meta:
          $ref: '#/components/schemas/Meta'
    ErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          example: false
        status:
          type: integer
          example: 401
        message:
          type: string
          example: Invalid credentials
        error:
          $ref: '#/components/schemas/Error'
        meta:
          $ref: '#/components/schemas/Meta'
    Meta:
      type: object
      properties:
        requestId:
          type: string
          description: Unique request ID for tracking
          example: req_abc123def456
        timestamp:
          type: string
          format: date-time
          description: ISO 8601 timestamp of the response
    Error:
      type: object
      properties:
        code:
          type: string
          description: Error code for programmatic handling
          example: AUTH_INVALID_CREDENTIALS
        details:
          type: array
          items:
            $ref: '#/components/schemas/ValidationError'
          description: Validation error details (for VALIDATION_ERROR)
    ValidationError:
      type: object
      properties:
        field:
          type: string
          description: Field that failed validation
          example: appId
        message:
          type: string
          description: Validation error message
          example: AppId is required
  responses:
    ValidationError:
      description: Validation failed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            status: 400
            message: Validation failed
            error:
              code: VALIDATION_ERROR
              details:
                - field: currency
                  message: Currency is required
            meta:
              requestId: req_abc123
              timestamp: '2026-01-05T10:30:00+00:00'
    Unauthorized:
      description: Authentication required or token invalid
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            status: 401
            message: Unable to identify business
            error:
              code: AUTH_TOKEN_INVALID
            meta:
              requestId: req_abc123
              timestamp: '2026-01-05T10:30:00+00:00'
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: JWT access token obtained from /auth/token

````