> ## 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 Product

> Create a new card product within a program. POST /programs/{programId}/products. Requires accounts:write scope.

## Overview

Creates a new card product under an existing program. A **Card Product** defines the scheme (VISA or Mastercard), card type (Consumer or Corporate), the feature set (3DS, Apple Pay, JIT), and the lifecycle controls (reloadability, one-time use, TTL, spend limits, cardholder card limits).

Once created, the product `productId` is passed to `POST /cards` to issue cards under that configuration.

<Warning>
  Products are **shared resources** — always written to the LIVE database regardless of whether your API key is LIVE or SANDBOX.
</Warning>

## Path Parameters

| Parameter   | Type   | Description                                             |
| ----------- | ------ | ------------------------------------------------------- |
| `programId` | string | The program to create the product under (prefix `prg_`) |

## Request Body

### Required

| Field    | Type   | Description                                                                        |
| -------- | ------ | ---------------------------------------------------------------------------------- |
| `name`   | string | Human-readable product name (e.g. `"Standard VISA Consumer"`)                      |
| `scheme` | string | Card network — `VISA` or `MASTERCARD`. Must match a scheme enabled on your program |

### Optional — Card Type

| Field      | Type   | Default    | Description               |
| ---------- | ------ | ---------- | ------------------------- |
| `cardType` | string | `CONSUMER` | `CONSUMER` or `CORPORATE` |

### Optional — `features` Object

Features are validated against what your program catalog allows. Requesting a feature not enabled on your program returns `422 FEATURE_NOT_ALLOWED`.

| Field                      | Type    | Default | Description                                      |
| -------------------------- | ------- | ------- | ------------------------------------------------ |
| `features.has3DS`          | boolean | `false` | Enable 3D Secure authentication                  |
| `features.hasApplePay`     | boolean | `false` | Enable Apple Pay tokenisation                    |
| `features.hasGooglePay`    | boolean | `false` | Enable Google Pay tokenisation                   |
| `features.hasJIT`          | boolean | `false` | Enable Just-In-Time funding                      |
| `features.hasSpendControl` | boolean | `false` | Enable per-card spend limits                     |
| `features.hasMccControl`   | boolean | `false` | Enable MCC (merchant category) allow/block rules |

### Optional — `controls` Object

Controls govern card lifecycle rules and are enforced at issuance and funding time.

| Field                            | Type            | Default       | Description                                                                                                                                                                              |
| -------------------------------- | --------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `controls.isReloadable`          | boolean         | `true`        | When `false`, `POST /cards/{id}/fund` is rejected for every card under this product. Set to `false` for one-load prepaid or gift-card style products                                     |
| `controls.isOneTimeUse`          | boolean         | `false`       | When `true`, a card is automatically terminated after its first settled transaction. Ideal for single-purchase virtual cards                                                             |
| `controls.cardTTLMonths`         | integer \| null | `36`          | Cap on card validity in months from issuance. If the card scheme grants a longer expiry, it is capped at this value                                                                      |
| `controls.maxCardsPerCardholder` | integer \| null | `5`           | Maximum simultaneously active (non-TERMINATED) cards a cardholder may hold under this product. Attempting to issue a card beyond this limit returns `409 CARDHOLDER_CARD_LIMIT_EXCEEDED` |
| `controls.spendingLimit`         | number          | `1000`        | Maximum spend amount per `spendingPeriod`. Expressed in the program's currency (USD)                                                                                                     |
| `controls.spendingPeriod`        | string          | `TRANSAMOUNT` | The period over which `spendingLimit` is enforced — `TRANSAMOUNT`, `DAILY`, `WEEKLY`, `MONTHLY`, `QUARTERLY`, `YEARLY`, or `LIFETIME`                                                    |

## Example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.fyatu.com/api/v3.20/programs/prg_01HXYZ9876ABCDEF0000/products \
    -H "Authorization: Bearer $FYATU_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "name":     "Prepaid VISA Consumer 12m",
      "scheme":   "VISA",
      "cardType": "CONSUMER",
      "features": {
        "has3DS":          true,
        "hasApplePay":     true,
        "hasGooglePay":    true,
        "hasJIT":          false,
        "hasSpendControl": false,
        "hasMccControl":   false
      },
      "controls": {
        "isReloadable":          false,
        "isOneTimeUse":          false,
        "cardTTLMonths":         12,
        "maxCardsPerCardholder": 5,
        "spendingLimit":         1000,
        "spendingPeriod":        "TRANSAMOUNT"
      }
    }'
  ```

  ```javascript Node.js theme={null}
  const resp = await fetch(
    'https://api.fyatu.com/api/v3.20/programs/prg_01HXYZ9876ABCDEF0000/products',
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.FYATU_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        name:     'Prepaid VISA Consumer 12m',
        scheme:   'VISA',
        cardType: 'CONSUMER',
        features: {
          has3DS:          true,
          hasApplePay:     true,
          hasGooglePay:    true,
          hasJIT:          false,
          hasSpendControl: false,
          hasMccControl:   false
        },
        controls: {
          isReloadable:          false,
          isOneTimeUse:          false,
          cardTTLMonths:         12,
          maxCardsPerCardholder: 5,
          spendingLimit:         1000,
          spendingPeriod:        'TRANSAMOUNT'
        }
      })
    }
  );
  const { data: product } = await resp.json();
  console.log('Created:', product.productId, '| TTL:', product.controls.cardTTLMonths, 'months');
  ```

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

  resp = requests.post(
      'https://api.fyatu.com/api/v3.20/programs/prg_01HXYZ9876ABCDEF0000/products',
      headers={'Authorization': f'Bearer {os.environ["FYATU_API_KEY"]}'},
      json={
          'name':     'Prepaid VISA Consumer 12m',
          'scheme':   'VISA',
          'cardType': 'CONSUMER',
          'features': {
              'has3DS':          True,
              'hasApplePay':     True,
              'hasGooglePay':    True,
              'hasJIT':          False,
              'hasSpendControl': False,
              'hasMccControl':   False
          },
          'controls': {
              'isReloadable':          False,
              'isOneTimeUse':          False,
              'cardTTLMonths':         12,
              'maxCardsPerCardholder': 5,
              'spendingLimit':         1000,
              'spendingPeriod':        'TRANSAMOUNT'
          }
      }
  )
  product = resp.json()['data']
  print(product['productId'], product['controls']['cardTTLMonths'])
  ```
</CodeGroup>

## Success Response (201)

```json theme={null}
{
  "success": true,
  "status":  201,
  "message": "Product created",
  "data": {
    "productId": "prd_01HXYZ1111ABCDEF0002",
    "programId": "prg_01HXYZ9876ABCDEF0000",
    "name":      "Prepaid VISA Consumer 12m",
    "scheme":    "VISA",
    "cardType":  "CONSUMER",
    "features": {
      "has3DS":          true,
      "hasApplePay":     true,
      "hasGooglePay":    true,
      "hasJIT":          false,
      "hasSpendControl": false,
      "hasMccControl":   false
    },
    "controls": {
      "isReloadable":          false,
      "isOneTimeUse":          false,
      "cardTTLMonths":         12,
      "maxCardsPerCardholder": 5,
      "spendingLimit":         1000,
      "spendingPeriod":        "TRANSAMOUNT"
    },
    "status":    "ACTIVE",
    "createdAt": "2026-05-26T11:00:00Z"
  },
  "meta": {
    "requestId": "req_01HXY123456ABCDEF",
    "platform":  "Fyatu CaaS",
    "timestamp": "2026-05-26T11:00:00Z"
  }
}
```

## Product Controls in Practice

### Non-reloadable (`controls.isReloadable: false`)

Use this for prepaid, gift-card, or expense-allowance products where the cardholder receives a fixed balance at issuance and cannot add more funds. Any call to `POST /cards/{id}/fund` will be rejected with `422 CARD_NOT_RELOADABLE`.

### One-time use (`controls.isOneTimeUse: true`)

Cards are automatically terminated after their first settled transaction. Best for single-purchase virtual cards where you want strong controls on card reuse. The card remains `ACTIVE` until the first settlement clears.

### TTL cap (`controls.cardTTLMonths`)

The card provider normally sets card expiry based on the scheme default (often 3–5 years). Setting `cardTTLMonths` caps that to a shorter window — for example, `12` ensures cards expire within one year of issuance even if the provider would have granted longer. Defaults to `36` months when not provided.

### Per-cardholder card limit (`controls.maxCardsPerCardholder`)

Enforced at issuance time. The limit counts all **non-TERMINATED** cards under this product for the same cardholder. Issuing a card that would breach the limit returns `409 CARDHOLDER_CARD_LIMIT_EXCEEDED`. Defaults to `5` when not provided.

### Spending limit (`controls.spendingLimit` + `controls.spendingPeriod`)

Sets a maximum spend cap on every card issued under this product. `spendingLimit` is expressed in the program currency (USD). `spendingPeriod` sets the window:

| Period        | Description                                         |
| ------------- | --------------------------------------------------- |
| `TRANSAMOUNT` | Cap applied per individual transaction              |
| `DAILY`       | Cap resets at midnight UTC each day                 |
| `WEEKLY`      | Cap resets each Monday at midnight UTC              |
| `MONTHLY`     | Cap resets on the 1st of each month at midnight UTC |
| `QUARTERLY`   | Cap resets every three months                       |
| `YEARLY`      | Cap resets on the 1st of January at midnight UTC    |
| `LIFETIME`    | Cap applies for the entire lifetime of the card     |

Both default to `1000` / `TRANSAMOUNT` when not provided.

## Error Codes

| Code                  | HTTP | Cause                                                                |
| --------------------- | ---- | -------------------------------------------------------------------- |
| `MISSING_FIELD`       | 400  | `name` or `scheme` not provided                                      |
| `INVALID_REQUEST`     | 400  | Request body is not valid JSON                                       |
| `SCHEME_NOT_ALLOWED`  | 422  | The requested scheme is not enabled for your program                 |
| `FEATURE_NOT_ALLOWED` | 422  | A requested feature flag is not available under your program catalog |
| `PROGRAM_NOT_FOUND`   | 404  | Program does not exist or belongs to another business                |
| `INVALID_STATUS`      | 409  | Program is closed or not in a state that allows new products         |
| `INSUFFICIENT_SCOPE`  | 403  | Key lacks `accounts:write` scope                                     |
| `INTERNAL_ERROR`      | 500  | Server error                                                         |


## OpenAPI

````yaml v3.20/openapi.json POST /programs/{id}/products
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:
  /programs/{id}/products:
    parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
        description: Program ID (prefix `prg_`)
        example: prg_01HXYZ9876ABCDEF0000
    post:
      tags:
        - Products
      summary: Create a product
      description: >-
        Create a new card product within a program. A card product defines the
        scheme (VISA or Mastercard), card type, feature set (3DS, Apple Pay,
        JIT), and lifecycle controls (reloadability, one-time use, TTL,
        cardholder card limits). Products are shared resources â€” always
        written to the LIVE database regardless of API key environment.
      operationId: createProduct
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - scheme
              properties:
                name:
                  type: string
                  description: Human-readable product name
                  example: Prepaid VISA Consumer 12m
                scheme:
                  type: string
                  enum:
                    - VISA
                    - MASTERCARD
                  description: Card network. Must match a scheme enabled on your program.
                  example: VISA
                cardType:
                  type: string
                  enum:
                    - CONSUMER
                    - CORPORATE
                  default: CONSUMER
                  example: CONSUMER
                features:
                  type: object
                  description: Feature flags validated against your program catalog.
                  properties:
                    has3DS:
                      type: boolean
                      default: false
                      description: Enable 3D Secure authentication
                      example: true
                    hasApplePay:
                      type: boolean
                      default: false
                      description: Enable Apple Pay tokenisation
                      example: true
                    hasGooglePay:
                      type: boolean
                      default: false
                      description: Enable Google Pay tokenisation
                      example: true
                    hasJIT:
                      type: boolean
                      default: false
                      description: Enable Just-In-Time funding
                      example: false
                    hasSpendControl:
                      type: boolean
                      default: false
                      description: Enable per-card spend limits
                      example: false
                    hasMccControl:
                      type: boolean
                      default: false
                      description: Enable MCC allow/block rules
                      example: false
                controls:
                  type: object
                  description: Card lifecycle and spend controls.
                  properties:
                    isReloadable:
                      type: boolean
                      default: true
                      description: >-
                        When false, POST /cards/{id}/fund is rejected for all
                        cards under this product
                      example: false
                    isOneTimeUse:
                      type: boolean
                      default: false
                      description: >-
                        When true, a card is automatically terminated after its
                        first settled transaction
                      example: false
                    cardTTLMonths:
                      type: integer
                      nullable: true
                      default: 36
                      description: >-
                        Cap on card validity in months from issuance. Defaults
                        to 36.
                      example: 12
                    maxCardsPerCardholder:
                      type: integer
                      nullable: true
                      default: 5
                      description: >-
                        Maximum active cards a cardholder may hold under this
                        product. Defaults to 5.
                      example: 5
                    spendingLimit:
                      type: number
                      format: float
                      default: 1000
                      description: >-
                        Maximum spend amount per spendingPeriod. Defaults to
                        1000.
                      example: 1000
                    spendingPeriod:
                      type: string
                      enum:
                        - TRANSAMOUNT
                        - DAILY
                        - WEEKLY
                        - MONTHLY
                        - QUARTERLY
                        - YEARLY
                        - LIFETIME
                      default: TRANSACTION
                      description: >-
                        The period over which spendingLimit is enforced.
                        Defaults to TRANSACTION.
                      example: TRANSACTION
      responses:
        '201':
          description: Product created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProductResponse'
              example:
                success: true
                status: 201
                message: Product created
                data:
                  productId: prd_01HXYZ1111ABCDEF0002
                  programId: prg_01HXYZ9876ABCDEF0000
                  name: Prepaid VISA Consumer 12m
                  scheme: VISA
                  cardType: CONSUMER
                  features:
                    has3DS: true
                    hasApplePay: true
                    hasGooglePay: true
                    hasJIT: false
                    hasSpendControl: false
                    hasMccControl: false
                  controls:
                    isReloadable: false
                    isOneTimeUse: false
                    cardTTLMonths: 12
                    maxCardsPerCardholder: 1
                  status: ACTIVE
                  createdAt: '2026-05-26T11:00:00Z'
                meta:
                  requestId: req_a1b2c3d4e5f6a7b8c9d0e1f2
                  platform: Fyatu CaaS
                  timestamp: '2026-05-26T11:00:00Z'
        '400':
          description: Missing or invalid fields
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                missingField:
                  value:
                    success: false
                    status: 400
                    message: name is required
                    error:
                      code: MISSING_FIELD
                      detail: name is required
                    meta:
                      requestId: req_a1b2c3d4e5f6a7b8c9d0e1f2
                      platform: Fyatu CaaS
                      timestamp: '2026-05-26T11:00:00Z'
                invalidRequest:
                  value:
                    success: false
                    status: 400
                    message: Request body is not valid JSON
                    error:
                      code: INVALID_REQUEST
                      detail: Request body is not valid JSON
                    meta:
                      requestId: req_a1b2c3d4e5f6a7b8c9d0e1f2
                      platform: Fyatu CaaS
                      timestamp: '2026-05-26T11:00:00Z'
        '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-26T11:00:00Z'
        '409':
          description: Program not in a valid state
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                success: false
                status: 409
                message: Program is not active
                error:
                  code: INVALID_STATUS
                  detail: Program is not active
                meta:
                  requestId: req_a1b2c3d4e5f6a7b8c9d0e1f2
                  platform: Fyatu CaaS
                  timestamp: '2026-05-26T11:00:00Z'
        '422':
          description: Scheme or feature not allowed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                schemeNotAllowed:
                  value:
                    success: false
                    status: 422
                    message: The requested scheme is not enabled for your program
                    error:
                      code: SCHEME_NOT_ALLOWED
                      detail: The requested scheme is not enabled for your program
                    meta:
                      requestId: req_a1b2c3d4e5f6a7b8c9d0e1f2
                      platform: Fyatu CaaS
                      timestamp: '2026-05-26T11:00:00Z'
                featureNotAllowed:
                  value:
                    success: false
                    status: 422
                    message: >-
                      A requested feature is not available under your program
                      catalog
                    error:
                      code: FEATURE_NOT_ALLOWED
                      detail: >-
                        A requested feature is not available under your program
                        catalog
                    meta:
                      requestId: req_a1b2c3d4e5f6a7b8c9d0e1f2
                      platform: Fyatu CaaS
                      timestamp: '2026-05-26T11:00:00Z'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  schemas:
    ProductResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        status:
          type: integer
          example: 200
        message:
          type: string
          example: Product retrieved
        data:
          $ref: '#/components/schemas/Product'
        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'
    Product:
      type: object
      properties:
        productId:
          type: string
          example: prd_01HXYZ1111ABCDEF0001
        programId:
          type: string
          example: prg_01HXYZ9876ABCDEF0000
        name:
          type: string
          example: Standard VISA Consumer
        scheme:
          type: string
          enum:
            - VISA
            - MASTERCARD
          example: VISA
        cardType:
          type: string
          enum:
            - CONSUMER
            - CORPORATE
          example: CONSUMER
        features:
          type: object
          properties:
            has3DS:
              type: boolean
              example: true
            hasApplePay:
              type: boolean
              example: true
            hasGooglePay:
              type: boolean
              example: true
            hasJIT:
              type: boolean
              example: false
            hasSpendControl:
              type: boolean
              example: false
            hasMccControl:
              type: boolean
              example: false
        controls:
          type: object
          properties:
            isReloadable:
              type: boolean
              example: true
            isOneTimeUse:
              type: boolean
              example: false
            cardTTLMonths:
              type: integer
              nullable: true
              example: null
            maxCardsPerCardholder:
              type: integer
              nullable: true
              example: null
            spendingLimit:
              type: number
              format: float
              description: Maximum spend amount per spendingPeriod. Defaults to 1000.
              example: 1000
            spendingPeriod:
              type: string
              enum:
                - TRANSAMOUNT
                - DAILY
                - WEEKLY
                - MONTHLY
                - QUARTERLY
                - YEARLY
                - LIFETIME
              description: The period over which spendingLimit is enforced.
              example: TRANSACTION
        status:
          type: string
          enum:
            - ACTIVE
            - ARCHIVED
          example: ACTIVE
        createdAt:
          type: string
          format: date-time
          example: '2026-01-10T09:00: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:
    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>`.

````