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

# Generate a hosted KYC link for an existing customer

> Generate a single-use hosted URL the customer can complete to verify their identity, and (where supported) a provider-specific `token` for embedding the verification flow directly via the provider's SDK.

The customer must already exist — create them with `POST /customers` first. Calling this endpoint does not change the customer's `kycStatus`; the customer remains `PENDING` until they complete (or fail) the hosted flow.

Each call returns a fresh link. Previously-issued links are not invalidated, but they remain single-use and will expire on their own. For request-level retry safety, include an `Idempotency-Key` header.




## OpenAPI

````yaml https://app.stainless.com/api/spec/documented/grid/openapi.documented.yml post /customers/{customerId}/kyc-link
openapi: 3.1.0
info:
  title: Grid API
  description: >
    API for managing global payments on the open Money Grid. Built by
    Lightspark. See the full documentation at https://docs.lightspark.com/.
  version: '2025-10-13'
  contact:
    name: Lightspark Support
    email: support@lightspark.com
  license:
    name: Proprietary
    url: https://lightspark.com/terms
servers:
  - url: https://api.lightspark.com/grid/2025-10-13
    description: Production server
security:
  - BasicAuth: []
  - AgentAuth: []
tags:
  - name: Platform Configuration
    description: >-
      Platform configuration endpoints for managing global settings. You can
      also configure these settings in the Grid dashboard.
  - name: Customers
    description: >-
      Customer management endpoints for creating and updating customer
      information
  - name: KYC/KYB Verifications
    description: >-
      Endpoints for Know Your Customer (KYC) and Know Your Business (KYB)
      verification, including managing beneficial owners and triggering
      verification for customers.
  - name: Documents
    description: >-
      Endpoints for uploading and managing verification documents for customers
      and beneficial owners. Supports KYC and KYB document requirements.
  - name: Internal Accounts
    description: >-
      Internal account management endpoints for creating and managing internal
      accounts
  - name: External Accounts
    description: >-
      External account management endpoints for creating and managing external
      bank accounts
  - name: Same-Currency Transfers
    description: >-
      Endpoints for transferring funds between internal and external accounts
      with the same currency
  - name: Cross-Currency Transfers
    description: Endpoints for creating and confirming quotes for cross-currency transfers
  - name: Transactions
    description: Endpoints for retrieving transaction information
  - name: Webhooks
    description: Webhook endpoints and configuration for receiving notifications
  - name: Invitations
    description: Endpoints for creating, claiming and managing UMA invitations
  - name: Sandbox
    description: Endpoints to trigger test cases in sandbox
  - name: API Tokens
    description: Endpoints to programmatically manage API tokens
  - name: Exchange Rates
    description: >-
      Endpoints for retrieving cached foreign exchange rates. Rates are cached
      for approximately 5 minutes and include platform-specific fees.
  - name: Discoveries
    description: >-
      Endpoints for discovering available payment rails, banks, and providers
      for a given country and currency corridor.
  - name: Embedded Wallet Auth
    description: >-
      Endpoints for registering and verifying end-user authentication
      credentials (email OTP, OAuth, passkey) used to sign Embedded Wallet
      actions.
  - name: Agent Management
    description: >-
      Endpoints for creating and managing agents (experimental), called by the
      partner's backend using platform credentials. Covers the full agent
      lifecycle: creation, policy configuration, pausing, deletion, the device
      code installation flow, and approving or rejecting transactions initiated
      by agents.
  - name: Agent Operations
    description: >-
      Endpoints called by the agent itself using its own credentials (obtained
      via device code redemption). Scoped to the agent's associated customer —
      all requests automatically operate on behalf of that customer and are
      subject to the agent's policy. When an action requires approval, the
      resulting transaction enters a pending state and must be approved by the
      platform via `POST /transactions/{transactionId}/approve`.
  - name: Cards
    description: >-
      Card management endpoints. Issue debit cards against an internal account,
      freeze / unfreeze, close, manage card funding sources, and list card
      transactions.
paths:
  /customers/{customerId}/kyc-link:
    parameters:
      - name: customerId
        in: path
        description: The Grid customer ID to generate a KYC link for.
        required: true
        schema:
          type: string
    post:
      tags:
        - KYC/KYB Verifications
      summary: Generate a hosted KYC link for an existing customer
      description: >
        Generate a single-use hosted URL the customer can complete to verify
        their identity, and (where supported) a provider-specific `token` for
        embedding the verification flow directly via the provider's SDK.


        The customer must already exist — create them with `POST /customers`
        first. Calling this endpoint does not change the customer's `kycStatus`;
        the customer remains `PENDING` until they complete (or fail) the hosted
        flow.


        Each call returns a fresh link. Previously-issued links are not
        invalidated, but they remain single-use and will expire on their own.
        For request-level retry safety, include an `Idempotency-Key` header.
      operationId: createCustomerKycLink
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          description: >
            A unique identifier for the request. If the same key is sent
            multiple times, the server will return the same response as the
            first request.
          schema:
            type: string
            example: <uuid>
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/KycLinkCreateRequest'
      responses:
        '201':
          description: KYC link generated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/KycLinkResponse'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error400'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error401'
        '404':
          description: Customer not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error404'
        '500':
          description: Internal service error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error500'
      security:
        - BasicAuth: []
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import LightsparkGrid from '@lightsparkdev/grid';


            const client = new LightsparkGrid({
              username: process.env['GRID_CLIENT_ID'], // This is the default and can be omitted
              password: process.env['GRID_CLIENT_SECRET'], // This is the default and can be omitted
            });


            const kycLinkResponse = await
            client.customers.createKYCLink('customerId');


            console.log(kycLinkResponse.provider);
        - lang: Python
          source: |-
            import os
            from grid import LightsparkGrid

            client = LightsparkGrid(
                username=os.environ.get("GRID_CLIENT_ID"),  # This is the default and can be omitted
                password=os.environ.get("GRID_CLIENT_SECRET"),  # This is the default and can be omitted
            )
            kyc_link_response = client.customers.create_kyc_link(
                customer_id="customerId",
            )
            print(kyc_link_response.provider)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/stainless-sdks/grid-go\"\n\t\"github.com/stainless-sdks/grid-go/option\"\n)\n\nfunc main() {\n\tclient := grid.NewClient(\n\t\toption.WithUsername(\"My Username\"),\n\t\toption.WithPassword(\"My Password\"),\n\t)\n\tkycLinkResponse, err := client.Customers.NewKYCLink(\n\t\tcontext.TODO(),\n\t\t\"customerId\",\n\t\tgrid.CustomerNewKYCLinkParams{},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", kycLinkResponse.Provider)\n}\n"
        - lang: Kotlin
          source: >-
            package com.lightspark.grid.example


            import com.lightspark.grid.client.LightsparkGridClient

            import com.lightspark.grid.client.okhttp.LightsparkGridOkHttpClient

            import
            com.lightspark.grid.models.customers.CustomerCreateKycLinkParams

            import com.lightspark.grid.models.customers.KycLinkResponse


            fun main() {
                val client: LightsparkGridClient = LightsparkGridOkHttpClient.fromEnv()

                val kycLinkResponse: KycLinkResponse = client.customers().createKycLink("customerId")
            }
        - lang: Ruby
          source: >-
            require "grid"


            lightspark_grid = Grid::Client.new(username: "My Username",
            password: "My Password")


            kyc_link_response =
            lightspark_grid.customers.create_kyc_link("customerId")


            puts(kyc_link_response)
        - lang: PHP
          source: |-
            <?php

            require_once dirname(__DIR__) . '/vendor/autoload.php';

            use Grid\Client;
            use Grid\Core\Exceptions\APIException;

            $client = new Client(
              username: getenv('GRID_CLIENT_ID') ?: 'My Username',
              password: getenv('GRID_CLIENT_SECRET') ?: 'My Password',
            );

            try {
              $kycLinkResponse = $client->customers->createKYCLink(
                'customerId',
                kycLinkCreateRequest: [
                  'redirectUri' => 'https://app.example.com/onboarding/completed'
                ],
                idempotencyKey: '<uuid>',
              );

              var_dump($kycLinkResponse);
            } catch (APIException $e) {
              echo $e->getMessage();
            }
        - lang: C#
          source: >-
            using System;

            using Grid;

            using Grid.Models.Customers;


            LightsparkGridClient client = new();


            CustomerCreateKycLinkParams parameters = new() { CustomerID =
            "customerId" };


            var kycLinkResponse = await
            client.Customers.CreateKycLink(parameters);


            Console.WriteLine(kycLinkResponse);
        - lang: CLI
          source: |-
            grid customers create-kyc-link \
              --username 'My Username' \
              --password 'My Password' \
              --customer-id customerId
components:
  schemas:
    KycLinkCreateRequest:
      type: object
      description: Request body for generating a hosted KYC link for an existing customer.
      properties:
        redirectUri:
          type: string
          format: uri
          description: >-
            URI the customer is redirected to after completing the hosted KYC
            flow. Must start with `https://` (or `http://` for local
            development). Embedded in the returned `kycUrl`.
          example: https://app.example.com/onboarding/completed
    KycLinkResponse:
      type: object
      description: >-
        A hosted KYC link that the customer can complete to verify their
        identity.
      required:
        - kycUrl
        - expiresAt
        - provider
      properties:
        kycUrl:
          type: string
          description: >-
            Hosted URL the customer should be sent to in order to complete
            verification. The URL is single-use and expires at `expiresAt`. To
            generate a new link (for example, after the previous one expires or
            is abandoned), call this endpoint again.
          example: https://kyc.lightspark.com/onboard/abc123def456
        expiresAt:
          type: string
          format: date-time
          description: Time at which the hosted link expires and can no longer be used.
          example: '2027-01-15T14:32:00Z'
        provider:
          $ref: '#/components/schemas/KycProvider'
        token:
          type: string
          description: >-
            Provider-specific token that can be used in place of the hosted URL
            — for example, to embed the provider's SDK directly in your
            application. Only returned for providers that support direct SDK
            integration. Whether to use the hosted URL or the embedded SDK is up
            to you; both flows result in the same `kycStatus` update on the
            customer.
          example: _act-sbx-jwt-eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
    Error400:
      type: object
      required:
        - message
        - status
        - code
      properties:
        status:
          type: integer
          enum:
            - 400
          description: HTTP status code
        code:
          type: string
          description: >
            | Error Code | Description |

            |------------|-------------|

            | INVALID_INPUT | Invalid input provided |

            | MISSING_MANDATORY_USER_INFO | Required customer information is
            missing |

            | INVITATION_ALREADY_CLAIMED | Invitation has already been claimed |

            | INVITATIONS_NOT_CONFIGURED | Invitations are not configured |

            | INVALID_UMA_ADDRESS | UMA address format is invalid |

            | INVITATION_CANCELLED | Invitation has been cancelled |

            | QUOTE_REQUEST_FAILED | An issue occurred during the quote process;
            this is retryable |

            | INVALID_PAYREQ_RESPONSE | Counterparty Payreq response was invalid
            |

            | INVALID_RECEIVER | Receiver is invalid |

            | PARSE_PAYREQ_RESPONSE_ERROR | Error parsing receiver PayReq
            response |

            | CERT_CHAIN_INVALID | Counterparty certificate chain is invalid |

            | CERT_CHAIN_EXPIRED | Counterparty certificate chain has expired |

            | INVALID_PUBKEY_FORMAT | Counterparty Public key format is invalid
            |

            | MISSING_REQUIRED_UMA_PARAMETERS | Counterparty required UMA
            parameters are missing |

            | SENDER_NOT_ACCEPTED | Sender is not accepted |

            | AMOUNT_OUT_OF_RANGE | Amount is out of range |

            | INVALID_CURRENCY | Currency is invalid |

            | INVALID_TIMESTAMP | Timestamp is invalid |

            | INVALID_NONCE | Nonce is invalid |

            | INVALID_REQUEST_FORMAT | Request format is invalid |

            | INVALID_BANK_ACCOUNT | Bank account is invalid |

            | SELF_PAYMENT | Self payment not allowed |

            | LOOKUP_REQUEST_FAILED | Lookup request failed |

            | PARSE_LNURLP_RESPONSE_ERROR | Error parsing LNURLP response |

            | INVALID_AMOUNT | Amount is invalid |

            | WEBHOOK_ENDPOINT_NOT_SET | Webhook endpoint is not set |

            | WEBHOOK_DELIVERY_ERROR | Webhook delivery error |

            | LOW_QUALITY | Document quality too low to process |

            | DATA_MISMATCH | Document details don't match provided information
            |

            | EXPIRED | Document has expired |

            | SUSPECTED_FRAUD | Document suspected of being forged or edited |

            | UNSUITABLE_DOCUMENT | Document type is not accepted or not
            supported |

            | INCOMPLETE | Document is missing pages or sides |

            | EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS | An EMAIL_OTP credential is
            already registered on the target internal account; only one email
            OTP credential is supported per internal account at this time |

            | PASSKEY_CREDENTIAL_ALREADY_EXISTS | A PASSKEY credential with the
            same WebAuthn credentialId is already registered on the target
            internal account |
          enum:
            - INVALID_INPUT
            - MISSING_MANDATORY_USER_INFO
            - INVITATION_ALREADY_CLAIMED
            - INVITATIONS_NOT_CONFIGURED
            - INVALID_UMA_ADDRESS
            - INVITATION_CANCELLED
            - QUOTE_REQUEST_FAILED
            - INVALID_PAYREQ_RESPONSE
            - INVALID_RECEIVER
            - PARSE_PAYREQ_RESPONSE_ERROR
            - CERT_CHAIN_INVALID
            - CERT_CHAIN_EXPIRED
            - INVALID_PUBKEY_FORMAT
            - MISSING_REQUIRED_UMA_PARAMETERS
            - SENDER_NOT_ACCEPTED
            - AMOUNT_OUT_OF_RANGE
            - INVALID_CURRENCY
            - INVALID_TIMESTAMP
            - INVALID_NONCE
            - INVALID_REQUEST_FORMAT
            - INVALID_BANK_ACCOUNT
            - SELF_PAYMENT
            - LOOKUP_REQUEST_FAILED
            - PARSE_LNURLP_RESPONSE_ERROR
            - INVALID_AMOUNT
            - WEBHOOK_ENDPOINT_NOT_SET
            - WEBHOOK_DELIVERY_ERROR
            - LOW_QUALITY
            - DATA_MISMATCH
            - EXPIRED
            - SUSPECTED_FRAUD
            - UNSUITABLE_DOCUMENT
            - INCOMPLETE
            - EMAIL_OTP_CREDENTIAL_ALREADY_EXISTS
            - PASSKEY_CREDENTIAL_ALREADY_EXISTS
        message:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    Error401:
      type: object
      required:
        - message
        - status
        - code
      properties:
        status:
          type: integer
          enum:
            - 401
          description: HTTP status code
        code:
          type: string
          description: >
            | Error Code | Description |

            |------------|-------------|

            | UNAUTHORIZED | Issue with API credentials |

            | INVALID_SIGNATURE | Signature header is invalid |

            | WALLET_SIGNATURE_MISSING | The `Grid-Wallet-Signature` header is
            required for this Embedded Wallet action but was not supplied |

            | WALLET_SIGNATURE_MALFORMED | The `Grid-Wallet-Signature` header
            could not be parsed (bad encoding, structure, or fields) |

            | WALLET_SIGNATURE_BODY_MISMATCH | The `Grid-Wallet-Signature` was
            computed over a different request body than the one received |

            | WALLET_SIGNATURE_INVALID | The `Grid-Wallet-Signature` failed
            cryptographic verification against the registered credential |

            | REQUEST_ID_MISSING | The `Request-Id` header is required on the
            signed retry but was not supplied (paired with
            `Grid-Wallet-Signature`) |
          enum:
            - UNAUTHORIZED
            - INVALID_SIGNATURE
            - WALLET_SIGNATURE_MISSING
            - WALLET_SIGNATURE_MALFORMED
            - WALLET_SIGNATURE_BODY_MISMATCH
            - WALLET_SIGNATURE_INVALID
            - REQUEST_ID_MISSING
        message:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    Error404:
      type: object
      required:
        - message
        - status
        - code
      properties:
        status:
          type: integer
          enum:
            - 404
          description: HTTP status code
        code:
          type: string
          description: >
            | Error Code | Description |

            |------------|-------------|

            | TRANSACTION_NOT_FOUND | Transaction not found |

            | INVITATION_NOT_FOUND | Invitation not found |

            | USER_NOT_FOUND | Customer not found |

            | QUOTE_NOT_FOUND | Quote not found |

            | LOOKUP_REQUEST_NOT_FOUND | Lookup request not found |

            | TOKEN_NOT_FOUND | Token not found |

            | BULK_UPLOAD_JOB_NOT_FOUND | Bulk upload job not found |

            | REFERENCE_NOT_FOUND | Reference not found |

            | UMA_NOT_FOUND | The UMA address is well-formed but no receiver
            exists at the counterparty VASP |
          enum:
            - TRANSACTION_NOT_FOUND
            - INVITATION_NOT_FOUND
            - USER_NOT_FOUND
            - QUOTE_NOT_FOUND
            - LOOKUP_REQUEST_NOT_FOUND
            - TOKEN_NOT_FOUND
            - BULK_UPLOAD_JOB_NOT_FOUND
            - REFERENCE_NOT_FOUND
            - UMA_NOT_FOUND
        message:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    Error500:
      type: object
      required:
        - message
        - status
        - code
      properties:
        status:
          type: integer
          enum:
            - 500
          description: HTTP status code
        code:
          type: string
          description: |
            | Error Code | Description |
            |------------|-------------|
            | GRID_SWITCH_ERROR | Grid switch error |
            | INTERNAL_ERROR | Internal server or UMA error |
          enum:
            - GRID_SWITCH_ERROR
            - INTERNAL_ERROR
        message:
          type: string
          description: Error message
        details:
          type: object
          description: Additional error details
          additionalProperties: true
    KycProvider:
      type: string
      description: >-
        The KYC provider that will perform identity verification for the
        customer. Grid selects the provider based on the customer's region and
        platform configuration; the value is informational for platforms that
        want to integrate directly with the provider's SDK.
      enum:
        - SUMSUB
      example: SUMSUB
  securitySchemes:
    BasicAuth:
      type: http
      scheme: basic
      description: >-
        API token authentication using format `<api token id>:<api client
        secret>`
    AgentAuth:
      type: http
      scheme: bearer
      description: >-
        Bearer token authentication for agent-scoped endpoints. The token is the
        `accessToken` returned when redeeming a device code via `POST
        /agents/device-codes/{code}/redeem`. Agent credentials are user-scoped:
        all requests are automatically bound to the agent's associated customer
        and subject to the agent's policy.

````