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

# Register payment account

> Registers a bank account or wallet for the organization in the route and returns it with its per-rail readiness.

State where the account lives in one of two ways:
- **Bank account:** set `kind` to `bank`, send `country` and `currency` (for example `MX` and `MXN`), and describe the account in `accountDetails` (for example a `clabe`).
- **Wallet:** set `kind` to `wallet` and send `accountDetails` of kind `address` with its `chain`. Do not send `country` or `currency` for a wallet. A wallet requires no `registrationInfo`, and we ignore any you send.

Registering does not prove that the customer owns the account.

**Registration info.** A bank account route requires holder fields such as `accountHolderName` and `taxId`. We fill each one from the organization's verified identity when we hold it. When a required field is still missing, the request fails with `422` `registration-info-required`, and the problem lists every required field in `required` and the ones still needed in `missing`, as JSON Pointers into the request (for example `#/registrationInfo/taxId`). Send the missing fields in `registrationInfo` and retry. A supplied `taxId` must match the verified one (`422` `holder-tax-id-mismatch`).

You can send any registration field, including ones this route does not require; we check each value's format and ignore the fields the route does not use. A name that is not a registration field returns `400` `validation-error`.

**Readiness.** A new account starts `pending`, and so does each rail direction. We register it with the payment provider, then move each direction to `ready` or `rejected`. Subscribe to `paymentAccountUpdated`, or read the account again, and quote a direction once it is `ready`.

**Registering an account again.** If the organization already holds the account, you get it back with `200`. When a direction was `rejected`, we register the account with the provider again using the `registrationInfo` you send, and those directions go back to `pending`. Otherwise we change nothing. If another organization holds it, the request fails with `409` `payment-account-unavailable`.



## OpenAPI

````yaml openapi-v2.json POST /organization/{organizationId}/payment_account
openapi: 3.1.0
info:
  title: Etherfuse FX API
  description: >-
    Partner-facing ramp API, version 2: payment accounts, quotes, orders and
    webhooks.
  license:
    name: Proprietary
  version: 2.0.0
servers:
  - url: https://api.sand.etherfuse.com
    description: Sandbox
security:
  - ApiKeyAuth: []
tags:
  - name: Payment Accounts
    description: >-
      Register the bank accounts and wallets that money moves from and to, and
      read their readiness per rail.
  - name: Quotes
    description: Price a transfer between two registered payment accounts.
  - name: Orders
    description: Create orders from quotes and track them.
  - name: Webhook Subscriptions
    description: Subscribe to webhook events.
  - name: Webhook Events
    description: Payloads Etherfuse sends to your webhook subscriptions.
  - name: Sandbox
    description: Endpoints that work only in sandbox, for testing.
  - name: Assets
    description: The markets and tokens a quote can use, and the balances of a wallet.
  - name: Authentication
    description: >-
      Partner JWT authentication: exchange a JWT for an access token, or launch
      a user into the app.
  - name: KYC
    description: >-
      Submit a customer's verification data, documents and questionnaire
      answers, import a verification they already passed elsewhere, and check
      where their KYC stands.
  - name: KYB
    description: Check where a business organization's KYB stands.
  - name: Lookup
    description: >-
      Public reference data: exchange rates, stablebonds, bond costs, country
      codes.
  - name: Organizations
    description: >-
      Your organization, your default partner fee, and the customer
      organizations you create.
  - name: Members
    description: >-
      The people who belong to a customer business organization and complete its
      KYB.
  - name: Statements
    description: >-
      Monthly revenue share statements for closed months, as a list or as a CSV
      or PDF download.
paths:
  /organization/{organizationId}/payment_account:
    post:
      tags:
        - Payment Accounts
      summary: Register payment account
      description: >-
        Registers a bank account or wallet for the organization in the route and
        returns it with its per-rail readiness.


        State where the account lives in one of two ways:

        - **Bank account:** set `kind` to `bank`, send `country` and `currency`
        (for example `MX` and `MXN`), and describe the account in
        `accountDetails` (for example a `clabe`).

        - **Wallet:** set `kind` to `wallet` and send `accountDetails` of kind
        `address` with its `chain`. Do not send `country` or `currency` for a
        wallet. A wallet requires no `registrationInfo`, and we ignore any you
        send.


        Registering does not prove that the customer owns the account.


        **Registration info.** A bank account route requires holder fields such
        as `accountHolderName` and `taxId`. We fill each one from the
        organization's verified identity when we hold it. When a required field
        is still missing, the request fails with `422`
        `registration-info-required`, and the problem lists every required field
        in `required` and the ones still needed in `missing`, as JSON Pointers
        into the request (for example `#/registrationInfo/taxId`). Send the
        missing fields in `registrationInfo` and retry. A supplied `taxId` must
        match the verified one (`422` `holder-tax-id-mismatch`).


        You can send any registration field, including ones this route does not
        require; we check each value's format and ignore the fields the route
        does not use. A name that is not a registration field returns `400`
        `validation-error`.


        **Readiness.** A new account starts `pending`, and so does each rail
        direction. We register it with the payment provider, then move each
        direction to `ready` or `rejected`. Subscribe to
        `paymentAccountUpdated`, or read the account again, and quote a
        direction once it is `ready`.


        **Registering an account again.** If the organization already holds the
        account, you get it back with `200`. When a direction was `rejected`, we
        register the account with the provider again using the
        `registrationInfo` you send, and those directions go back to `pending`.
        Otherwise we change nothing. If another organization holds it, the
        request fails with `409` `payment-account-unavailable`.
      operationId: v2CreatePaymentAccount
      parameters:
        - $ref: '#/components/parameters/OrganizationId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreatePaymentAccountRequest'
            examples:
              bank:
                summary: Mexican bank account on SPEI
                value:
                  kind: bank
                  label: Operating account
                  country: MX
                  currency: MXN
                  accountDetails:
                    kind: clabe
                    clabe: '012180000000000002'
                  registrationInfo:
                    accountHolderName: Ejemplo Comercial SA de CV
                    taxId: XAXX010101000
              wallet:
                summary: Stellar wallet
                value:
                  kind: wallet
                  label: Treasury wallet
                  accountDetails:
                    kind: address
                    address: GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF
                    chain: stellar
      responses:
        '200':
          description: >-
            The organization already holds this account. Returns it. A
            `rejected` direction is registered again and returns as `pending`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaymentAccount'
        '201':
          description: >-
            The registered account: a new registration, or an account the
            organization removed and registered again.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaymentAccount'
        '400':
          description: >-
            `invalid-request`: the body is malformed, has an unknown enum value,
            a blank `label`, an invalid CLABE, PIX key or wallet address, an
            unknown `chain`, or an incomplete or conflicting location.
            `validation-error`: `accountDetails` is missing a field its kind
            requires or has a field it does not allow, or `registrationInfo` has
            a field that is not a registration field or has a malformed value.
            See `errors` for the pointers.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/OrganizationNotFound'
        '409':
          description: >-
            `payment-account-unavailable`: another organization holds this
            account. `payment-account-disabled`: the account is disabled and
            cannot be registered. `conflict`: the organization holds this
            account in a different market.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        '413':
          $ref: '#/components/responses/PayloadTooLarge'
        '422':
          description: >-
            `registration-info-required`: required registration fields are
            missing; see `required` and `missing`. `holder-tax-id-mismatch`:
            `registrationInfo.taxId` does not match the verified tax ID.
            `unsupported`: this kind of account, or this identifier in this
            location, cannot be registered.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  parameters:
    OrganizationId:
      name: organizationId
      in: path
      required: true
      description: >-
        The organization this call acts for: your API key's organization or one
        of its customer organizations. Any other id returns `404` `not-found`.
      schema:
        type: string
        format: uuid
  schemas:
    CreatePaymentAccountRequest:
      type: object
      required:
        - kind
        - label
        - accountDetails
      properties:
        kind:
          $ref: '#/components/schemas/PaymentAccountKind'
        label:
          type: string
          description: >-
            Your organization's name for the account. Must not be blank. We trim
            it.
          example: Operating account
        country:
          type: string
          description: >-
            Two-letter country code of the account's market. Send with
            `currency` for a bank account. Omit for a wallet.
          example: MX
        currency:
          type: string
          description: >-
            Three-letter currency code of the account's market. Send with
            `country` for a bank account. Omit for a wallet.
          example: MXN
        accountDetails:
          $ref: '#/components/schemas/AccountDetails'
        registrationInfo:
          $ref: '#/components/schemas/RegistrationInfo'
    PaymentAccount:
      type: object
      required:
        - id
        - status
        - label
        - rails
        - createdAt
      properties:
        id:
          type: string
          format: uuid
        status:
          $ref: '#/components/schemas/PaymentAccountStatus'
        label:
          type: string
          description: Your organization's name for the account.
        rails:
          type: object
          description: >-
            The rails this account can move money on, keyed by rail name (a
            `PaymentRail` value such as `spei` or `stellar`). A rail the account
            cannot move money on is absent. The account identifier you
            registered is not returned.
          additionalProperties:
            $ref: '#/components/schemas/AccountRail'
        createdAt:
          type: string
          format: date-time
      example:
        id: 3f9a2c1e-5b7d-4e8f-9a01-2b3c4d5e6f70
        status: pending
        label: Operating account
        rails:
          spei:
            currency: MXN
            source:
              isDefault: true
              status: ready
            destination:
              isDefault: true
              status: pending
        createdAt: '2026-09-28T17:04:11.482Z'
    Problem:
      type: object
      description: >-
        RFC 9457 problem details. Every v2 error uses this body with content
        type `application/problem+json`. `errors` is present only on
        `validation-error`. `required` and `missing` are present only on
        `registration-info-required`.
      required:
        - type
        - title
        - status
        - detail
        - instance
      properties:
        type:
          type: string
          format: uri
          description: >-
            `https://docs.etherfuse.com/ramp/v2/errors/` followed by the problem
            slug, for example
            `https://docs.etherfuse.com/ramp/v2/errors/quote-expired`. Switch on
            this value.
          example: https://docs.etherfuse.com/ramp/v2/errors/quote-expired
        title:
          type: string
          description: Short summary of the problem type.
          example: Quote expired
        status:
          type: integer
          description: The HTTP status code.
          example: 400
        detail:
          type: string
          description: Human-readable explanation of this occurrence. Do not parse it.
          example: >-
            quote 5e6f7a8b-9c0d-4e1f-8a2b-3c4d5e6f7a8b has expired or does not
            exist
        instance:
          type: string
          description: Unique identifier for this occurrence. Quote it to support.
          example: >-
            tag:etherfuse.com,2026-08-28:R2-10120/0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d
        errors:
          type: array
          description: Present on `validation-error`. One entry per invalid field.
          items:
            $ref: '#/components/schemas/FieldViolation'
        required:
          type: array
          description: >-
            Present on `registration-info-required`. Every registration field
            the route requires, as JSON Pointers into the request, for example
            `#/registrationInfo/taxId`.
          items:
            type: string
        missing:
          type: array
          description: >-
            Present on `registration-info-required`. The required fields you
            must still send, as JSON Pointers into the request.
          items:
            type: string
    PaymentAccountKind:
      type: string
      enum:
        - bank
        - wallet
        - embeddedWallet
        - debitCard
        - cash
      description: >-
        The kind of account. `embeddedWallet` is a wallet we create on a chain;
        see [Embedded wallets](/v2/guides/embedded-wallets). `debitCard` and
        `cash` return `422` `unsupported`.
    AccountDetails:
      description: >-
        The identifier that names the account, tagged by `kind`. The kind
        decides the field set: send exactly the fields your kind declares. A
        missing field or a field the kind does not declare returns `400`
        `validation-error` with a pointer per field.
      oneOf:
        - $ref: '#/components/schemas/ClabeAccountDetails'
        - $ref: '#/components/schemas/PixKeyAccountDetails'
        - $ref: '#/components/schemas/AddressAccountDetails'
        - $ref: '#/components/schemas/SignerPublicKeyAccountDetails'
      discriminator:
        propertyName: kind
        mapping:
          clabe: '#/components/schemas/ClabeAccountDetails'
          pixKey: '#/components/schemas/PixKeyAccountDetails'
          address: '#/components/schemas/AddressAccountDetails'
          signerPublicKey: '#/components/schemas/SignerPublicKeyAccountDetails'
    RegistrationInfo:
      type: object
      additionalProperties: false
      description: >-
        Account holder information a bank account route requires. The route and
        the organization's account type (personal or business) decide which
        fields are required: `accountHolderName` and `taxId` for bank accounts,
        plus the personal or business fields on routes that need them. We fill a
        field from the organization's verified identity when we hold it, so you
        only need to send the fields a `registration-info-required` problem
        lists in `missing`. You may send other registration fields too: we check
        their format and ignore the ones the route does not use. A name that is
        not listed here returns `400` `validation-error`. Wallets require no
        fields.
      properties:
        accountHolderName:
          type: string
          description: Name of the account holder.
        taxId:
          type: string
          description: >-
            Tax ID of the account holder. Must match the verified tax ID when we
            hold one.
          example: XAXX010101000
        curp:
          type: string
          description: Mexican CURP (personal accounts).
          example: XEXX010101HNEXXXA4
        firstName:
          type: string
          description: First name (personal accounts).
        paternalLastName:
          type: string
          description: Paternal last name (personal accounts).
        maternalLastName:
          type: string
          description: Maternal last name (personal accounts).
        dateOfBirth:
          type: string
          format: date
          description: '`YYYY-MM-DD` (personal accounts).'
        countryOfBirth:
          type: string
          description: Two-letter country code (personal accounts).
          example: MX
        dateOfIncorporation:
          type: string
          format: date
          description: '`YYYY-MM-DD` (business accounts).'
        countryOfIncorporation:
          type: string
          description: Two-letter country code (business accounts).
          example: MX
    PaymentAccountStatus:
      type: string
      enum:
        - pending
        - ready
        - rejected
        - disabled
      description: >-
        The account as a whole. `ready` when any rail direction is `ready`.
        `rejected` when every rail direction is `rejected`; register the account
        again with corrected `registrationInfo` to retry. `disabled` when we
        turned the account off; it cannot be registered or quoted. Otherwise
        `pending`. Readiness per rail and direction is in `rails`.
    AccountRail:
      type: object
      required:
        - source
        - destination
      properties:
        currency:
          type: string
          description: The fiat currency of this rail. Absent for a wallet rail.
          example: MXN
        source:
          description: >-
            The account as the source of a transfer on this rail. Null when the
            rail cannot carry money from this account.
          anyOf:
            - $ref: '#/components/schemas/RailTransferDetails'
            - type: 'null'
        destination:
          description: >-
            The account as the destination of a transfer on this rail. Null when
            the rail cannot carry money to this account.
          anyOf:
            - $ref: '#/components/schemas/RailTransferDetails'
            - type: 'null'
    FieldViolation:
      type: object
      required:
        - pointer
        - detail
      properties:
        pointer:
          type: string
          description: >-
            JSON Pointer to the field in the request, for example
            `#/accountDetails/clabe`.
          example: '#/accountDetails/clabe'
        detail:
          type: string
          description: What is wrong with the field.
          example: This field is required.
    ClabeAccountDetails:
      type: object
      title: clabe
      required:
        - kind
        - clabe
      additionalProperties: false
      properties:
        kind:
          type: string
          const: clabe
        clabe:
          type: string
          description: An 18-digit Mexican CLABE. We check its length and check digit.
          example: '012180000000000002'
    PixKeyAccountDetails:
      type: object
      title: pixKey
      required:
        - kind
        - pixKey
      additionalProperties: false
      properties:
        kind:
          type: string
          const: pixKey
        pixKey:
          type: string
          description: >-
            A Brazilian PIX key: an email, a phone, a CPF, a CNPJ or a random
            key. 1 to 77 characters.
          maxLength: 77
    AddressAccountDetails:
      type: object
      title: address
      required:
        - kind
        - address
        - chain
      additionalProperties: false
      properties:
        kind:
          type: string
          const: address
        address:
          type: string
          description: The wallet address. We validate it for `chain`.
          example: GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF
        chain:
          $ref: '#/components/schemas/Chain'
    SignerPublicKeyAccountDetails:
      type: object
      title: signerPublicKey
      description: >-
        Register with `kind` `embeddedWallet`. We create a wallet on `chain`
        that only the holder of `signerPublicKey` can move funds from. Stellar
        is the only chain today.
      required:
        - kind
        - chain
        - signerPublicKey
      additionalProperties: false
      properties:
        kind:
          type: string
          const: signerPublicKey
        chain:
          $ref: '#/components/schemas/Chain'
        signerPublicKey:
          type: string
          description: >-
            The holder's P-256 public key, hex-encoded SEC1, compressed or
            uncompressed. The holder signs order authorizations with the
            matching private key.
          example: 02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc
    RailTransferDetails:
      type: object
      description: What the account can do on this rail in one direction.
      required:
        - isDefault
        - status
      properties:
        isDefault:
          type: boolean
          description: >-
            True when a quote that does not name a rail for this side uses this
            rail.
        status:
          $ref: '#/components/schemas/RouteRegistrationStatus'
        minAmount:
          type: string
          description: >-
            Minimum amount on this rail, as a decimal string in the rail's
            `currency`. Absent when we know of no minimum.
        maxAmount:
          type: string
          description: >-
            Maximum amount on this rail, as a decimal string in the rail's
            `currency`. Absent when we know of no maximum; that does not mean
            unlimited.
    Chain:
      type: string
      enum:
        - stellar
        - solana
        - base
        - polygon
        - monad
        - avalanche
      description: The chain a wallet is on.
    RouteRegistrationStatus:
      type: string
      enum:
        - pending
        - ready
        - rejected
        - disabled
      description: >-
        The account's status on one rail in one direction. Only `ready` can be
        quoted.
  responses:
    Unauthorized:
      description: >-
        `unauthorized`: the `Authorization` header is missing, not ASCII, or
        does not hold a valid API key.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    OrganizationNotFound:
      description: '`not-found`: your API key cannot act for this organization.'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    PayloadTooLarge:
      description: '`payload-too-large`: the request body is too large.'
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    InternalError:
      description: >-
        `internal-error`: an unexpected error on our side. Retry with backoff,
        and quote `instance` to support if it persists.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: Authorization
      description: API key sent in the Authorization header, with no `Bearer` prefix.

````

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