> ## 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 cardholder profile for one of your end users. POST /cardholders. Requires cardholders:write scope.

## Overview

Create a cardholder profile for an end user. KYC is triggered automatically and runs asynchronously — subscribe to `CARDHOLDER_KYC_APPROVED` or `CARDHOLDER_KYC_REJECTED` to be notified when it completes.

Cards can only be issued once `kycStatus` is `APPROVED`.

## Required Fields

| Field             | Type   | Constraint                                                |
| ----------------- | ------ | --------------------------------------------------------- |
| `firstName`       | string | Legal first name                                          |
| `lastName`        | string | Legal last name                                           |
| `email`           | string | Valid email, unique within your environment               |
| `dateOfBirth`     | string | `YYYY-MM-DD` format, cardholder must be 18+               |
| `nationality`     | string | ISO 3166-1 alpha-2 (e.g. `US`)                            |
| `address.address` | string | Full street address (line 1 and optional line 2 combined) |
| `address.city`    | string | City                                                      |
| `address.country` | string | ISO 3166-1 alpha-2                                        |

## Optional Fields

| Field        | Type   | Constraint                                                                                                                                                                                                                                                                                                                                                                                               |
| ------------ | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `middleName` | string | Optional middle name (max 15 chars). Card issuers cap the number of active cards per *identical first + last name*, so set a `middleName` to distinguish two cardholders who share the same first and last name — when present it is forwarded to the card network on card creation so they count as separate holders. Unlike the identity fields below, `middleName` stays editable after KYC approval. |
| `phone`      | string | Phone number in E.164 format                                                                                                                                                                                                                                                                                                                                                                             |
| `externalId` | string | Your platform's cardholder ID                                                                                                                                                                                                                                                                                                                                                                            |
| `metadata`   | object | Arbitrary flat JSON object of custom key-value pairs                                                                                                                                                                                                                                                                                                                                                     |

## KYC-Locked Fields

After KYC approval, these fields become **immutable**: `firstName`, `lastName`, `email`, `dateOfBirth`, `nationality`, `address`. Attempting to change them returns `409 KYC_FIELD_LOCKED`. `middleName` is intentionally **not** locked and remains editable after approval.

## KYC Document

The optional `kycDocument` object lets you supply identity document details alongside the cardholder creation. It is **not** KYC-locked and can be updated via `PATCH` at any time.

| Field            | Type   | Description                                                         |
| ---------------- | ------ | ------------------------------------------------------------------- |
| `documentType`   | string | `PASSPORT`, `NATIONAL_ID`, `DRIVERS_LICENSE`, or `RESIDENCE_PERMIT` |
| `documentNumber` | string | Document number as printed                                          |
| `issuingCountry` | string | ISO 3166-1 alpha-2                                                  |
| `frontUrl`       | string | URL of the document front image                                     |
| `backUrl`        | string | URL of the document back image (not required for passports)         |
| `selfieUrl`      | string | URL of the cardholder selfie                                        |

## Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.fyatu.com/api/v3.20/cardholders \
    -H "Authorization: Bearer $FYATU_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "firstName":   "John",
      "lastName":    "Smith",
      "email":       "john.smith@example.com",
      "phone":       "+12025551234",
      "dateOfBirth": "1990-05-15",
      "nationality": "US",
      "address": {
        "address":    "123 Main Street, Apt 4B",
        "city":       "Newark",
        "state":      "Delaware",
        "postalCode": "19701",
        "country":    "US"
      },
      "kycDocument": {
        "documentType":   "PASSPORT",
        "documentNumber": "AB123456",
        "issuingCountry": "US",
        "frontUrl":       "https://storage.example.com/doc-front.jpg",
        "selfieUrl":      "https://storage.example.com/selfie.jpg"
      },
      "externalId": "usr_123456",
      "metadata":   { "plan": "premium", "region": "us-east" }
    }'
  ```

  ```javascript Node.js theme={null}
  const resp = await fetch('https://api.fyatu.com/api/v3.20/cardholders', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.FYATU_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      firstName:   'John',
      lastName:    'Smith',
      email:       'john.smith@example.com',
      phone:       '+12025551234',
      dateOfBirth: '1990-05-15',
      nationality: 'US',
      address: {
        address:    '123 Main Street, Apt 4B',
        city:       'Newark',
        state:      'Delaware',
        postalCode: '19701',
        country:    'US'
      },
      kycDocument: {
        documentType:   'PASSPORT',
        documentNumber: 'AB123456',
        issuingCountry: 'US',
        frontUrl:       'https://storage.example.com/doc-front.jpg',
        selfieUrl:      'https://storage.example.com/selfie.jpg'
      },
      externalId: 'usr_123456',
      metadata:   { plan: 'premium', region: 'us-east' }
    })
  });

  const body = await resp.json();
  const cardholder = body.data;
  console.log('Cardholder:', cardholder.cardholderId, '| KYC:', cardholder.kycStatus);
  ```

  ```python Python theme={null}
  import os, requests

  resp = requests.post(
      'https://api.fyatu.com/api/v3.20/cardholders',
      headers={'Authorization': f'Bearer {os.environ["FYATU_API_KEY"]}'},
      json={
          'firstName':   'John',
          'lastName':    'Smith',
          'email':       'john.smith@example.com',
          'phone':       '+12025551234',
          'dateOfBirth': '1990-05-15',
          'nationality': 'US',
          'address': {
              'address':    '123 Main Street, Apt 4B',
              'city':       'Newark',
              'postalCode': '19701',
              'country':    'US'
          },
          'kycDocument': {
              'documentType':   'PASSPORT',
              'documentNumber': 'AB123456',
              'issuingCountry': 'US',
              'frontUrl':       'https://storage.example.com/doc-front.jpg',
              'selfieUrl':      'https://storage.example.com/selfie.jpg'
          },
          'externalId': 'usr_123456'
      }
  )
  body = resp.json()
  cardholder = body['data']
  print(cardholder['cardholderId'], cardholder['kycStatus'])
  ```
</CodeGroup>

## Success Response (201)

```json theme={null}
{
  "success": true,
  "status": 201,
  "message": "Cardholder created",
  "data": {
    "cardholderId":  "chl_01HXYZ1234ABCDEF5678",
    "firstName":     "John",
    "lastName":      "Smith",
    "email":         "john.smith@example.com",
    "phone":         "+12025551234",
    "dateOfBirth":   "1990-05-15",
    "nationality":   "US",
    "address": {
      "address":    "123 Main Street, Apt 4B",
      "city":       "Newark",
      "state":      "Delaware",
      "postalCode": "19701",
      "country":    "US"
    },
    "kycDocument":   null,
    "externalId":    "usr_123456",
    "metadata":      { "plan": "premium", "region": "us-east" },
    "status":        "ACTIVE",
    "kycStatus":     "PENDING",
    "kycVerifiedAt": null,
    "totalCards":    0,
    "suspendedAt":   null,
    "createdAt":     "2026-05-22T10:00:00Z",
    "updatedAt":     "2026-05-22T10:00:00Z"
  },
  "meta": {
    "requestId": "req_01HXY123456ABCDEF",
    "platform": "Fyatu CaaS",
    "timestamp": "2026-05-22T10:00:00Z"
  }
}
```

**Conditional fields** — present only in specific states:

| Field                | Condition                           |
| -------------------- | ----------------------------------- |
| `kycRejectionReason` | Only when `kycStatus` is `REJECTED` |
| `terminatedAt`       | Only when `status` is `TERMINATED`  |

## Webhook

A `CARDHOLDER_CREATED` event fires after successful creation. A few seconds later in SANDBOX (async in LIVE), one of these fires:

```json theme={null}
{
  "event":      "CARDHOLDER_KYC_APPROVED",
  "eventId":    "evt_01HXY123456ABCDEF",
  "businessId": "BUS1A2B3C4D5E6F",
  "environment": "LIVE",
  "timestamp":  "2026-05-22T10:00:05Z",
  "data": {
    "cardholderId":  "chl_01HXYZ1234ABCDEF5678",
    "kycStatus":     "APPROVED",
    "kycVerifiedAt": "2026-05-22T10:00:05Z"
  }
}
```

## Error Codes

| Code                      | HTTP | Cause                                                              |
| ------------------------- | ---- | ------------------------------------------------------------------ |
| `INVALID_BODY`            | 400  | Request body is not valid JSON                                     |
| `VALIDATION_ERROR`        | 422  | Missing or invalid fields (e.g. bad email, missing `address.city`) |
| `CARDHOLDER_UNDER_AGE`    | 422  | `dateOfBirth` indicates cardholder is under 18                     |
| `CARDHOLDER_EMAIL_EXISTS` | 409  | Email already registered in this environment                       |
| `PROGRAM_NOT_FOUND`       | 404  | No active program found for your account                           |
| `PROGRAM_CLOSED`          | 409  | Program is closed and cannot accept new cardholders                |
| `INSUFFICIENT_SCOPE`      | 403  | Key lacks `cardholders:write` scope                                |
| `INTERNAL_ERROR`          | 500  | Server error                                                       |


## OpenAPI

````yaml v3.20/openapi.json POST /cardholders
openapi: 3.1.0
info:
  title: FYATU CaaS API v3.20
  description: >-
    FYATU Cards-as-a-Service API â€” API key authentication, Cardholder
    lifecycle, Card issuance, Transactions, Webhooks, and Programs.
  version: 3.20.0
  contact:
    name: FYATU Support
    url: https://fyatu.com
    email: support@fyatu.com
servers:
  - url: https://api.fyatu.com/api/v3.20
    description: >-
      FYATU CaaS API â€” the environment (LIVE or SANDBOX) is determined by the
      API key, not the URL
security:
  - BearerAuth: []
tags:
  - name: Meta
    description: Liveness, account info, and supported event types
  - name: Account
    description: Account-level balance and funding status
  - name: Programs
    description: Read card program configuration
  - name: Cardholders
    description: Create and manage cardholder profiles
  - name: Cards
    description: Issue, fund, freeze, and terminate virtual cards
  - name: Transactions
    description: Read-only card transaction history
  - name: Webhooks
    description: Manage webhook endpoints for real-time event delivery
  - name: Products
    description: Read card product configurations
paths:
  /cardholders:
    post:
      tags:
        - Cardholders
      summary: Create a cardholder
      description: >-
        Create a new cardholder profile under one of your card programs. KYC is
        initiated asynchronously after creation. Cards can only be issued once
        `kycStatus` is `APPROVED`.
      operationId: createCardholder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - firstName
                - lastName
                - email
                - dateOfBirth
                - nationality
                - address
              properties:
                firstName:
                  type: string
                  example: John
                lastName:
                  type: string
                  example: Smith
                email:
                  type: string
                  format: email
                  example: john.smith@example.com
                phone:
                  type: string
                  example: '+12025551234'
                dateOfBirth:
                  type: string
                  format: date
                  description: YYYY-MM-DD â€” must be 18+
                  example: '1990-05-15'
                nationality:
                  type: string
                  description: ISO 3166-1 alpha-2
                  example: US
                address:
                  $ref: '#/components/schemas/Address'
                kycDocument:
                  $ref: '#/components/schemas/KycDocument'
                externalId:
                  type: string
                  example: usr_123456
                metadata:
                  type: object
                  example:
                    plan: premium
      responses:
        '201':
          description: Cardholder created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CardholderResponse'
              example:
                success: true
                status: 201
                message: Cardholder created
                data:
                  cardholderId: chl_01HXYZ1234ABCDEF5678
                  firstName: John
                  lastName: Smith
                  email: john.smith@example.com
                  phone: '+12025551234'
                  dateOfBirth: '1990-05-15'
                  nationality: US
                  address:
                    address: 123 Main Street, Apt 4B
                    city: Newark
                    state: Delaware
                    postalCode: '19701'
                    country: US
                  kycDocument: null
                  externalId: usr_123456
                  metadata:
                    plan: premium
                  status: ACTIVE
                  kycStatus: PENDING
                  kycVerifiedAt: null
                  totalCards: 0
                  suspendedAt: null
                  createdAt: '2026-05-01T09:00:00Z'
                  updatedAt: '2026-05-01T09:00:00Z'
                meta:
                  requestId: req_a1b2c3d4e5f6a7b8c9d0e1f2
                  platform: Fyatu CaaS
                  timestamp: '2026-05-01T09:00:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          description: Program not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                success: false
                status: 404
                message: Program not found
                error:
                  code: PROGRAM_NOT_FOUND
                  detail: Program not found
                meta:
                  requestId: req_a1b2c3d4e5f6a7b8c9d0e1f2
                  platform: Fyatu CaaS
                  timestamp: '2026-05-22T15:00:00Z'
        '409':
          description: Email already exists in this environment
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                success: false
                status: 409
                message: Cardholder with this email already exists
                error:
                  code: CARDHOLDER_EMAIL_EXISTS
                  detail: >-
                    A cardholder with this email already exists in this
                    environment
                meta:
                  requestId: req_a1b2c3d4e5f6a7b8c9d0e1f2
                  platform: Fyatu CaaS
                  timestamp: '2026-05-22T15:00:00Z'
        '422':
          $ref: '#/components/responses/ValidationError'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  schemas:
    Address:
      type: object
      properties:
        address:
          type: string
          example: 123 Main Street, Apt 4B
        city:
          type: string
          example: Newark
        state:
          type: string
          nullable: true
          example: Delaware
        postalCode:
          type: string
          nullable: true
          example: '19701'
        country:
          type: string
          description: ISO 3166-1 alpha-2
          example: US
      required:
        - address
        - city
        - country
    KycDocument:
      type: object
      description: >-
        Identity document details for KYC verification. Optional on create;
        patchable via PATCH. Not locked after KYC approval.
      properties:
        documentType:
          type: string
          enum:
            - PASSPORT
            - NATIONAL_ID
            - DRIVERS_LICENSE
            - RESIDENCE_PERMIT
          example: PASSPORT
        documentNumber:
          type: string
          example: AB123456
        issuingCountry:
          type: string
          description: ISO 3166-1 alpha-2
          example: US
        frontUrl:
          type: string
          format: uri
          example: https://storage.example.com/doc-front.jpg
        backUrl:
          type: string
          format: uri
          nullable: true
          description: Not required for passports
          example: null
        selfieUrl:
          type: string
          format: uri
          example: https://storage.example.com/selfie.jpg
    CardholderResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        status:
          type: integer
          example: 200
        message:
          type: string
          example: Cardholder retrieved
        data:
          $ref: '#/components/schemas/Cardholder'
        meta:
          $ref: '#/components/schemas/Meta'
    Error:
      type: object
      properties:
        success:
          type: boolean
          example: false
        status:
          type: integer
          example: 422
        message:
          type: string
          example: Human readable message
        error:
          $ref: '#/components/schemas/ErrorBody'
        meta:
          $ref: '#/components/schemas/Meta'
    Cardholder:
      type: object
      properties:
        cardholderId:
          type: string
          example: chl_01HXYZ1234ABCDEF5678
        firstName:
          type: string
          example: John
        lastName:
          type: string
          example: Smith
        email:
          type: string
          format: email
          example: john.smith@example.com
        phone:
          type: string
          nullable: true
          example: '+12025551234'
        dateOfBirth:
          type: string
          format: date
          example: '1990-05-15'
        nationality:
          type: string
          description: ISO 3166-1 alpha-2
          example: US
        address:
          $ref: '#/components/schemas/Address'
        kycDocument:
          $ref: '#/components/schemas/KycDocument'
          nullable: true
        externalId:
          type: string
          nullable: true
          example: usr_123456
        metadata:
          type: object
          nullable: true
          example:
            plan: premium
        status:
          type: string
          enum:
            - ACTIVE
            - SUSPENDED
            - TERMINATED
          example: ACTIVE
        kycStatus:
          type: string
          enum:
            - PENDING
            - APPROVED
            - REJECTED
          example: APPROVED
        kycVerifiedAt:
          type: string
          format: date-time
          nullable: true
          example: '2026-05-10T14:23:00Z'
        kycRejectionReason:
          type: string
          nullable: true
          description: Only present when kycStatus is REJECTED
          example: null
        totalCards:
          type: integer
          example: 2
        suspendedAt:
          type: string
          format: date-time
          nullable: true
          example: null
        terminatedAt:
          type: string
          format: date-time
          nullable: true
          description: Only present when status is TERMINATED
          example: null
        createdAt:
          type: string
          format: date-time
          example: '2026-05-01T09:00:00Z'
        updatedAt:
          type: string
          format: date-time
          example: '2026-05-10T14:23:00Z'
    Meta:
      type: object
      properties:
        requestId:
          type: string
          example: req_a1b2c3d4e5f6a7b8c9d0e1f2
        platform:
          type: string
          example: Fyatu CaaS
        timestamp:
          type: string
          format: date-time
          example: '2026-05-22T15:00:00Z'
    ErrorBody:
      type: object
      properties:
        code:
          type: string
          example: VALIDATION_ERROR
        detail:
          type: string
          example: dateOfBirth must be in YYYY-MM-DD format
  responses:
    ValidationError:
      description: Request validation failed
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            success: false
            status: 422
            message: Validation failed
            error:
              code: VALIDATION_ERROR
              detail: dateOfBirth must be in YYYY-MM-DD format
            meta:
              requestId: req_a1b2c3d4e5f6a7b8c9d0e1f2
              platform: Fyatu CaaS
              timestamp: '2026-05-22T15:00:00Z'
    Unauthorized:
      description: Missing or invalid API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            missing:
              summary: API key missing
              value:
                success: false
                status: 401
                message: API key is required
                error:
                  code: AUTH_TOKEN_MISSING
                  detail: API key is required
                meta:
                  requestId: req_a1b2c3d4e5f6a7b8c9d0e1f2
                  platform: Fyatu CaaS
                  timestamp: '2026-05-22T15:00:00Z'
            invalid:
              summary: API key invalid
              value:
                success: false
                status: 401
                message: Invalid API key
                error:
                  code: AUTH_TOKEN_INVALID
                  detail: Invalid API key
                meta:
                  requestId: req_a1b2c3d4e5f6a7b8c9d0e1f2
                  platform: Fyatu CaaS
                  timestamp: '2026-05-22T15:00:00Z'
    Forbidden:
      description: Scope denied or business suspended
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            success: false
            status: 403
            message: Scope denied
            error:
              code: INSUFFICIENT_SCOPE
              detail: This endpoint requires the cards:write scope
            meta:
              requestId: req_a1b2c3d4e5f6a7b8c9d0e1f2
              platform: Fyatu CaaS
              timestamp: '2026-05-22T15:00:00Z'
    RateLimitExceeded:
      description: Rate limit exceeded
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            success: false
            status: 429
            message: Rate limit exceeded
            error:
              code: RATE_LIMIT_EXCEEDED
              detail: Too many requests
            meta:
              requestId: req_a1b2c3d4e5f6a7b8c9d0e1f2
              platform: Fyatu CaaS
              timestamp: '2026-05-22T15:00:00Z'
    InternalError:
      description: Unexpected server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            success: false
            status: 500
            message: Internal error
            error:
              code: INTERNAL_ERROR
              detail: An unexpected error occurred
            meta:
              requestId: req_a1b2c3d4e5f6a7b8c9d0e1f2
              platform: Fyatu CaaS
              timestamp: '2026-05-22T15:00:00Z'
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: >-
        API key from the FYATU CaaS portal. Pass as `Authorization: Bearer
        <key>`.

````