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

# Create a customer

> 

**Requires:** `entity:write` or `ledger:write`



## OpenAPI

````yaml /openapi/core.openapi.json post /v1/entities
openapi: 3.0.0
info:
  title: KeyPair Core
  description: |-
    The API a merchant's server calls directly. Provisioning — creating
    tenants, issuing keys — happens through the control plane and is not
    described here.

    ## Authentication

    Every route needs an API key, and the tenant comes from the key, never
    from a header you control.

    `Authorization: Bearer kp_test_…` is the simple form. For anything that
    moves money, prefer **signed requests**: send only the key's public
    prefix plus an Ed25519 signature over
    `timestamp \n nonce \n METHOD \n path \n sha256(body)`, so no secret
    travels and a captured request cannot be replayed or altered. See
    `examples/api-client.ts` for a zero-dependency implementation.

    ## Amounts

    Always **signed integer strings in minor units** — debits negative,
    credits positive, summing to zero per currency. Never floats: JSON
    numbers lose precision above 2^53, and 1 ETH is 10^18 wei.

    ## Idempotency

    Every mutating call takes an `idempotencyKey`. Derive it from your own
    domain objects — an order id, a payout id — never randomly. A random
    key makes a timeout unrecoverable: you cannot tell whether the first
    call landed, and retrying pays twice.

    ## Errors

    Every error, from any layer, has the shape `{ code, message }`. Retry
    `429` (honouring `Retry-After`) and `5xx` with the *same* idempotency
    key. `422 INSUFFICIENT_FUNDS`, `400 UNBALANCED_TRANSACTION` and
    `403 DESTINATION_SCREENED` are terminal.
  version: 1.0.0
  contact: {}
servers:
  - url: http://localhost:3000
    description: Local
security: []
tags:
  - name: Entities
    description: The people and businesses you serve
  - name: Accounts
    description: Balances, statements, verification
  - name: Transactions
    description: Posting and reversing money movement
  - name: Activity
    description: Recent transactions and holdings, summarised
  - name: Compliance
    description: KYC on your customers, and held deposits
  - name: Policies & allowlists
    description: What a customer withdrawal is checked against
  - name: Treasury
    description: 'Company wallets: gas, policy, and spending them'
  - name: Reconciliation
    description: Whether the ledger agrees with custody
  - name: Tenant administration
    description: Your own keys, custody key, and webhooks
  - name: Operations
    description: Everything else
paths:
  /v1/entities:
    post:
      tags:
        - Entities
      summary: Create a customer
      description: |-


        **Requires:** `entity:write` or `ledger:write`
      operationId: EntityController_create
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateEntityDto'
      responses:
        '201':
          description: ''
        '400':
          description: Malformed request, or a ledger rule refused it. `code` says which.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing, malformed, or revoked API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: The key is valid but lacks the scope this operation requires.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: >-
            Idempotency conflict: this key was used with a different request
            body.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
      security:
        - apiKey: []
        - signed: []
components:
  schemas:
    CreateEntityDto:
      type: object
      properties:
        id:
          type: string
          description: |-
            Optional. Supply one to share a single id between your systems and
            core — the same uuid then appears in both services' logs, and there
            is no mapping to keep. Core generates one when omitted.
        externalId:
          type: string
          description: |-
            Your own identifier for this person. Opaque to core, and rejected if
            it looks like an email address or a phone number — core is
            deliberately free of personal data.
          minLength: 1
          maxLength: 200
          pattern: /^(?!.*@)(?!\+?[0-9][0-9 ()-]{8,}$).+$/
        assets:
          description: |-
            Optional. Accounts are normally opened on demand, when a customer
            actually needs an asset — pre-provisioning every asset for every
            customer derives deposit addresses nobody uses and grows the
            indexer's watch set for no benefit.

            Pass assets here only when you already know which ones are needed.
          type: array
          items:
            type: string
        metadata:
          type: object
      required:
        - externalId
    Error:
      type: object
      properties:
        code:
          type: string
          description: Stable, machine-readable. Branch on this, not on the message.
          example: INSUFFICIENT_FUNDS
        message:
          type: string
          description: Human-readable, and subject to change. Do not parse.
        details:
          type: array
          items:
            type: string
          description: >-
            Present on validation failures: one entry per field that was
            rejected. Absent otherwise.
      required:
        - code
        - message
  securitySchemes:
    apiKey:
      scheme: bearer
      bearerFormat: JWT
      type: http
      description: Your API key, `kp_test_…` or `kp_live_…`.
    signed:
      type: apiKey
      in: header
      name: x-keypair-key-id
      description: >-
        Signed requests: the key's public prefix. Send with x-keypair-timestamp,
        x-keypair-nonce and x-keypair-signature.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.