Taliware Developer

CL2C raw specification

The OpenAPI 3.0.3 document backing the CL2C API reference, viewable as YAML or JSON.

openapi: 3.0.3
info:
  title: Taliware CL2C API
  version: 1.0.0
  description: |
    The CL2C API lets your software do everything the Taliware Developer Platform
    dashboard does: create smart contracts, invite users, issue digital assets that
    their owners approve in the Confida app, and check credit balances.

    ## Authentication
    Every request needs an API key. Create one in the Developer Platform under
    **API keys**, keep it on your server, and send it in either header:
    - `x-api-key: <your-api-key>` (recommended)
    - `Authorization: Bearer <your-api-key>`

    ## Base URL
    `https://developer.taliware.com/api/v1`

    ## What you can manage
    | Resource | Path | What it's for |
    |---|---|---|
    | Contract | `/cl2c/contract` | The on-chain collection your assets are issued from |
    | User | `/cl2c/user` | The people who own and approve your assets |
    | NFT | `/cl2c/nft` | The digital assets themselves, their owners, and certificates |
    | Token | `/cl2c/token` | CRDB credits that pay for blockchain activity |

    > **Approved partners** that need their own in-app biometric approval or Confida QR
    > login should also read the **Cordoba API** (`/api/v1/cordoba`). It uses the same
    > API key and data model.

    ## How an asset is issued
    1. `POST /cl2c/contract` creates a contract (status `CREATED`).
    2. `PUT /cl2c/contract/{id}` deploys it to the blockchain (status `DEPLOYED`).
    3. `POST /cl2c/user` invites each owner (or `POST /cl2c/user/bulk` for up to 100).
       They finish setting up in Confida and become `ACTIVE`.
    4. `POST /cl2c/nft` issues the asset (status `CREATED`) and asks every owner to approve.
    5. Owners approve in Confida. Once all have approved, the asset is recorded on the
       blockchain automatically (status `MINTED`).
    6. `GET /cl2c/nft/{id}` returns the result, including on-chain details (transaction
       hash, IPFS addresses, gas fee) in `metadata`.

    ## CRDB credits
    - **Test (faucet) CRDB** is free and runs on the Polygon Amoy test network. Use it while you build.
    - **Live (mainnet) CRDB** runs on Polygon mainnet and is required for production assets.

    Buy or claim credits in the Developer Platform (card via Stripe, or MATIC via MetaMask).
servers:
  - url: https://developer.taliware.com/api/v1
    description: Production

tags:
  - name: Contract
    description: Create and deploy the on-chain collection your assets are issued from
  - name: User
    description: Invite the people who own and approve your assets
  - name: NFT
    description: Issue digital assets, track owner approvals, and get certificates
  - name: Tokens
    description: Check and send the CRDB credits that pay for blockchain activity

components:
  securitySchemes:
    apiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
    apiKeyBearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API key
    cookieAuth:
      type: apiKey
      in: cookie
      name: next-auth.session-token

  schemas:

    # Generic error shape used across endpoints
    Error:
      type: object
      description: Standard error payload
      properties:
        error:
          type: string
        details:
          type: array
          items:
            type: string

    # Simple pagination envelope used by list endpoints
    Pagination:
      type: object
      properties:
        page:
          type: integer
          description: Current page (1-indexed)
        limit:
          type: integer
          description: Items per page
        total:
          type: integer
          description: Total number of matching records
        totalPages:
          type: integer
        hasNext:
          type: boolean
        hasPrev:
          type: boolean

    PaginatedResponse:
      type: object
      properties:
        data:
          type: array
          items:
            type: object
        pagination:
          $ref: '#/components/schemas/Pagination'
        filters:
          type: object
          additionalProperties: true

    # Contract
    Contract:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        symbol:
          type: string
        description:
          type: string
        network:
          type: string
        env:
          type: string
        status:
          type: string
          enum: [CREATED, DEPLOYED, FAILED]
        contractAddress:
          type: string
        deployedAt:
          type: string
          format: date-time
        metadata:
          type: object
        totalNFTs:
          type: integer
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time

    ContractCreate:
      type: object
      required: [name, symbol]
      properties:
        name:
          type: string
        symbol:
          type: string
        description:
          type: string
        network:
          type: string
          enum: [POLYGON]
        env:
          type: string
          enum: [TESTNET, MAINNET]
        metadata:
          type: object

    ContractUpdate:
      type: object
      required: [id]
      properties:
        id:
          type: string
        status:
          type: string
          enum: [CREATED, DEPLOYED, FAILED]
        contractAddress:
          type: string
        deployedAt:
          type: string
          format: date-time

    ContractDelete:
      type: object
      required: [contractId]
      properties:
        contractId:
          type: string

    # User
    CreateUser:
      type: object
      required:
        - email
      properties:
        email:
          type: string
          format: email
        phone:
          type: string
          nullable: true

    BulkUserEntry:
      type: object
      required:
        - email
      properties:
        email:
          type: string
          format: email
        phone:
          type: string
          nullable: true

    BulkCreateUser:
      type: object
      required:
        - users
      properties:
        users:
          type: array
          minItems: 1
          maxItems: 100
          items:
            $ref: '#/components/schemas/BulkUserEntry'

    BulkUserResult:
      type: object
      properties:
        email:
          type: string
          format: email
        status:
          type: string
          enum: [invited, skipped, failed]
        reason:
          type: string
          nullable: true
          description: Present when status is skipped or failed.
        id:
          type: string
          nullable: true
          description: Invitation ID, present when status is invited.

    BulkCreateUserResponse:
      type: object
      properties:
        data:
          type: object
          properties:
            results:
              type: array
              items:
                $ref: '#/components/schemas/BulkUserResult'
            summary:
              type: object
              properties:
                total:
                  type: integer
                  description: Number of unique emails processed.
                invited:
                  type: integer
                skipped:
                  type: integer
                  description: Already a member or pending invitation.
                failed:
                  type: integer
                  description: Unexpected errors during invitation creation.

    UserOrgSummary:
      type: object
      properties:
        id:
          type: string
        email:
          type: string
        walletAddress:
          type: string
          nullable: true
        totalNFTs:
          type: integer
        status:
          type: string
          enum: [ACTIVE, INVITED]
        createdAt:
          type: string
          format: date-time
          nullable: true
        updatedAt:
          type: string
          format: date-time
          nullable: true
        user:
          $ref: '#/components/schemas/UserInfo'

    UserOrgItem:
      type: object
      properties:
        data:
          $ref: '#/components/schemas/UserOrgSummary'

    UserInfo:
      type: object
      properties:
        id:
          type: string
          nullable: true
        name:
          type: string
          nullable: true
        image:
          type: string
          nullable: true
        emailVerified:
          type: boolean
          nullable: true
        isRegistered:
          type: boolean
   
    UpdateUser:
      type: object
      properties:
        email:
          type: string
          format: email
        resend:
          type: boolean
          description: Resend invitation (only for pending invitations)

    UserOrgDetail:
      type: object
      properties:
        id:
          type: string
        email:
          type: string
        walletAddress:
          type: string
          nullable: true
        user:
          type: object
          properties:
            name:
              type: string
              nullable: true
            image:
              type: string
              nullable: true
        endUserNfts:
          type: array
          items:
            type: object
            properties:
              status:
                type: string
              percentage:
                type: number
              copyAuthLocation:
                type: string
                nullable: true
              copyAuthTimestamp:
                type: string
                format: date-time
                nullable: true
              nft:
                type: object
                properties:
                  id:
                    type: string
                  status:
                    type: string
                  mintedAt:
                    type: string
                    format: date-time
                  supply:
                    type: integer
                  name:
                    type: string
                  description:
                    type: string
                    nullable: true
                  externalUrl:
                    type: string
                    nullable: true
                  imagePath:
                    type: string
                    nullable: true
                  smartContract:
                    type: object
                    properties:
                      name:
                        type: string
                      symbol:
                        type: string
                      contractAddress:
                        type: string
        status:
          type: string
          enum: [ACTIVE, INVITED]

    UserOrgUpdateResponse:
      type: object
      properties:
        id:
          type: string
        email:
          type: string
          format: email
        walletAddress:
          type: string
          nullable: true
   
    # NFT
    MintRequest:
        type: object
        required: [name, smartContractId, copyright]
        properties:
          name:
            type: string
          description:
            type: string
          externalUrl:
            type: string
          imagePath:
            type: string
            description: >
              Optional. ipfs:// URI, data URI, raw base64, http(s) URL, or local path for
              the asset's image. If omitted, the NFT is created as traceability-only —
              an on-chain record and metadata JSON are created with no IPFS file upload.
          metadata:
            type: object
          smartContractId:
            type: string
          supply:
            type: integer
          copyright:
            type: array
            items:
              type: object
              required: [userId, percentage]
              properties:
                userId:
                  type: string
                percentage:
                  type: number

    UpdateNFT:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        description:
          type: string
        status:
          type: string
          # enum: [PENDING, CREATED, MINTED, ATTACHED, FAILED]
          enum: [PENDING, CREATED, MINTED, FAILED]
        mintedAt:
          type: string
          format: date-time

    DeleteNFTRequest:
      type: object
      properties:
        nftId:
          type: string

    NFTSummary:
      type: object
      properties:
        id:
          type: string
        status:
          type: string
        name:
          type: string
        description:
          type: string
        externalUrl:
          type: string
        imagePath:
          type: string
        supply:
          type: integer
        mintedAt:
          type: string
          format: date-time
        smartContract:
          type: object
          properties:
            id:
              type: string
            name:
              type: string
            symbol:
              type: string
            contractAddress:
              type: string
            status:
              type: string
            network:
              type: string
            env:
              type: string

    NFTDetail:
      allOf:
        - $ref: '#/components/schemas/NFTSummary'
        - type: object
          properties:
            metadata:
              type: object
              description: |
                On-chain metadata recorded after minting. Common fields:
                - `transactionHash` — blockchain tx hash (0x…, 66 chars)
                - `gasFee` — gas paid in wei (string, convert with /1e18 to get ETH)
                - `confirmationTime` — ISO 8601 timestamp when tx was confirmed
                - `ipfsMetadata` — IPFS CID of the metadata JSON (Qm…)
                - `ipfsCopyright` — IPFS CID of the copyright JSON
              additionalProperties: true
            copyright:
              type: array
              description: Per-owner copyright and confirmation records.
              items:
                type: object
                properties:
                  id:
                    type: string
                  status:
                    type: string
                    enum: [PENDING, CONFIRMED, REJECTED, MINTED]
                    description: Confirmation status for this copyright holder.
                  percentage:
                    type: number
                    description: Ownership percentage (0–100). All owners must sum to 100.
                  email:
                    type: string
                  copyAuthLocation:
                    type: string
                    nullable: true
                    description: Geolocation or description recorded at time of biometric confirmation.
                  copyAuthTimestamp:
                    type: string
                    format: date-time
                    nullable: true
                    description: When the owner confirmed (or rejected) the copyright.
                  walletAddress:
                    type: array
                    nullable: true
                    items:
                      type: object
                      properties:
                        address:
                          type: string
                        isPrimary:
                          type: boolean
                  userName:
                    type: string
                    nullable: true
                  userImage:
                    type: string
                    nullable: true
            totalOwners:
              type: integer

    UpdateNFTbyID:
      type: object
      properties:
        name:
          type: string
        description:
          type: string
        externalUrl:
          type: string
          format: uri
        supply:
          type: integer
          minimum: 1
        metadata:
          type: object
          additionalProperties: true

    TokenBalanceResponse:
      type: object
      properties:
        balance:
          type: number
        walletAddress:
          type: string
          nullable: true

    TokenBalancesResponse:
      type: object
      properties:
        mainnet:
          $ref: '#/components/schemas/TokenBalanceResponse'
        faucet:
          allOf:
            - $ref: '#/components/schemas/TokenBalanceResponse'
          nullable: true

    TokenSendRequest:
      type: object
      required: [memberId, amount]
      properties:
        memberId:
          type: string
        amount:
          type: number
          minimum: 0
          exclusiveMinimum: true
        tokenType:
          type: string
          enum: [mainnet, faucet]
          default: mainnet

    TokenSendResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        transactionHash:
          type: string
          nullable: true
        tokenType:
          type: string
          enum: [mainnet, faucet]

    StripeCheckoutRequest:
      type: object
      required: [amount]
      properties:
        amount:
          type: number
          minimum: 1

    StripeCheckoutResponse:
      type: object
      properties:
        sessionId:
          type: string
        checkoutUrl:
          type: string

paths:

  # Contracts
  /cl2c/contract:
    get:
      tags:
        - Contract
      summary: List contracts
      description: 'Returns your organization''s smart contracts. Filter by `status` or `search`, and page through results with `page` and `limit`.'
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: query
          name: page
          schema:
            type: integer
        - in: query
          name: limit
          schema:
            type: integer
        - in: query
          name: search
          schema:
            type: string
        - in: query
          name: status
          schema:
            type: string
            enum: [CREATED, DEPLOYED, FAILED]
        - in: query
          name: sortBy
          schema:
            type: string
        - in: query
          name: sortOrder
          schema:
            type: string
            enum: [asc, desc]
      responses:
        '200':
          description: Array of contracts with pagination info
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Contract'
                  pagination:
                    $ref: '#/components/schemas/Pagination'
                  filters:
                    type: object
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    post:
      tags:
        - Contract
      summary: Create a contract
      description: 'Creates a smart contract record. It is not on the blockchain yet: deploy it next with `PUT /cl2c/contract/{id}`. Use `"env": "TESTNET"` while you build.'
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContractCreate'
      responses:
        '201':
          description: Contract created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Contract already exists
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    put:
      tags:
        - Contract
      summary: Update a contract record
      description: 'Updates a contract''s `status`, `contractAddress`, or `deployedAt`. Most integrations never need this; to deploy a contract, use `PUT /cl2c/contract/{id}`.'
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContractUpdate'
      responses:
        '200':
          description: Contract updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Contract not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    delete:
      tags:
        - Contract
      summary: Delete a contract (ID in body)
      description: 'Deletes the contract named by `contractId` in the request body. Only possible while it is not deployed and has no assets.'
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ContractDelete'
      responses:
        '200':
          description: Contract deleted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  deletedContract:
                    type: object
                    properties:
                      id:
                        type: string
                      name:
                        type: string
        '400':
          description: Bad request / cannot delete
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Contract not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # Contracts by ID
  /cl2c/contract/{id}:
    put:
      tags:
        - Contract
      summary: Deploy a contract
      description: 'Publishes the contract to the blockchain so you can start issuing assets from it. Fails if it is already deployed.'
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
          description: The ID of the contract to deploy
      responses:
        '200':
          description: Contract deployed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contract'
        '400':
          description: Bad request (e.g., invalid ID or contract already deployed)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Contract not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    delete:
      tags:
        - Contract
      summary: Delete a contract
      description: 'Deletes a contract by its ID. Only possible while it is not deployed and has no assets.'
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
          description: The ID of the contract to delete
      responses:
        '200':
          description: Contract deleted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  deletedContract:
                    type: object
                    properties:
                      id:
                        type: string
                      name:
                        type: string
        '400':
          description: Bad request (contract deployed or has associated NFTs)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Contract not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # Users (organization-scoped)
  /cl2c/user:
    get:
      tags:
        - User
      summary: List users
      description: 'Returns everyone in your organization: active users and pending invitations. Users who have finished setting up Confida have `status: ACTIVE`; use their `id` as the `userId` when issuing an asset.'
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: query
          name: page
          schema:
            type: integer
        - in: query
          name: limit
          schema:
            type: integer
        - in: query
          name: search
          schema:
            type: string
        - in: query
          name: status
          schema:
            type: string
        - in: query
          name: sortBy
          schema:
            type: string
        - in: query
          name: sortOrder
          schema:
            type: string
            enum: [asc, desc]
        - in: query
          name: hasWallet
          schema:
            type: boolean
      responses:
        '200':
          description: Paginated list of userOrganization records
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/UserOrgSummary'
                  pagination:
                    $ref: '#/components/schemas/Pagination'
                  filters:
                    type: object
                    additionalProperties: true
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    post:
      tags:
        - User
      summary: Invite a user
      description: 'Sends an invitation email. The person installs Confida to finish setting up, then appears as `ACTIVE` in `GET /cl2c/user`. If the email already belongs to a registered Taliware user, they are linked straight away.'
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUser'
      responses:
        '201':
          description: Created userOrganization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserOrgItem'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Conflict (already exists)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # Bulk user invite
  /cl2c/user/bulk:
    post:
      tags:
        - User
      summary: Invite many users at once
      description: |
        Invite up to 100 people in one request. Each email is handled separately, and the
        response (HTTP 207) lists the result for each one. Duplicate emails in the request
        are merged. Emails that already belong to a member or have a pending invitation come
        back as `skipped`, not `failed`.
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BulkCreateUser'
            example:
              users:
                - email: alice@example.com
                - email: bob@example.com
                  phone: "+1 555 000 0001"
      responses:
        '207':
          description: Multi-status — individual results per email address
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BulkCreateUserResponse'
        '400':
          description: Bad request (invalid body, exceeds 100-user limit, etc.)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Organization not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # User by ID
  /cl2c/user/{id}:
    get:
      tags:
        - User
      summary: Get a user
      description: 'Returns one user or pending invitation, with their account details and the assets they own.'
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
          description: The ID of the userOrganization or invitation
      responses:
        '200':
          description: userOrganization detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserOrgDetail'
        '400':
          description: Bad request (invalid ID)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Not found (user or invitation does not exist)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    put:
      tags:
        - User
      summary: Update a user's email
      description: 'Changes the email address of a user or invitation. Re-sending the invitation email is only possible for invitations.'
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
          description: The ID of the userOrganization or invitation
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateUser'
      responses:
        '200':
          description: Updated userOrganization or invitation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserOrgUpdateResponse'
        '400':
          description: Bad request (invalid body, resend for member, etc.)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '409':
          description: Conflict (duplicate email or wallet)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    delete:
      tags:
        - User
      summary: Remove a user
      description: 'Removes a user or cancels a pending invitation. Users who already have a wallet or own assets cannot be removed.'
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
          description: The ID of the userOrganization or invitation
      responses:
        '200':
          description: Deleted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
        '400':
          description: Bad request (user has wallet or NFTs)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # NFT collection
  /cl2c/nft:
    get:
      tags:
        - NFT
      summary: List digital assets
      description: 'Returns the assets (NFTs) issued from your organization''s contracts. Filter by `search`, `status`, or `contractId`, and page through results with `page` and `limit`.'
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: query
          name: page
          schema:
            type: integer
        - in: query
          name: limit
          schema:
            type: integer
        - in: query
          name: search
          schema:
            type: string
          description: Search NFTs by name or description
        - in: query
          name: status
          schema:
            type: string
          description: Filter by NFT status (CREATED, PENDING, MINTED, FAILED)
        - in: query
          name: contractId
          schema:
            type: string
          description: Filter NFTs by a specific smart contract ID
        - in: query
          name: sortBy
          schema:
            type: string
        - in: query
          name: sortOrder
          schema:
            type: string
            enum: [asc, desc]
      responses:
        '200':
          description: Paginated list of NFTs
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/NFTSummary'
                  pagination:
                    $ref: '#/components/schemas/Pagination'
                  filters:
                    type: object
                    additionalProperties: true
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    post:
      tags:
        - NFT
      summary: Issue a digital asset
      description: 'Registers a new asset with status `CREATED` and asks every owner listed in `copyright` to approve it in Confida. Owner percentages must add up to exactly 100. Once every owner approves, the asset is recorded on the blockchain and its status becomes `MINTED`. Include `imagePath` to store an image, or leave it out for a record-only asset.'
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MintRequest'
      responses:
        '201':
          description: NFT created (userNft entries created and notifications sent)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NFTDetail'
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Related resource not found (e.g., smart contract or user)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    put:
      tags:
        - NFT
      summary: Update an asset (ID in body)
      description: 'Updates an asset''s `name`, `description`, `status`, or `mintedAt`.'
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateNFT'
      responses:
        '200':
          description: NFT updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NFTSummary'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: NFT not found in organization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    delete:
      tags:
        - NFT
      summary: Delete an asset (ID in body)
      description: 'Deletes an asset and its pending owner approvals. Minted assets cannot be deleted.'
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DeleteNFTRequest'
      responses:
        '200':
          description: NFT deleted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
                  deletedNft:
                    type: object
                    properties:
                      id:
                        type: string
                      name:
                        type: string
        '400':
          description: Bad request (e.g., cannot delete minted NFT)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: NFT not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # NFT collection by ID
  /cl2c/nft/{id}:
    get:
      tags:
        - NFT
      summary: Get a digital asset
      description: 'Returns one asset with each owner''s share and approval status, plus its blockchain details once minted.'
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
          description: NFT ID
      responses:
        '200':
          description: NFT detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NFTDetail'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden (NFT does not belong to organization)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: NFT not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    put:
      tags:
        - NFT
      summary: Update an asset
      description: 'Updates an asset''s `name`, `description`, `status`, or `mintedAt`. Only possible before it is finalized (while `CREATED` or `PENDING`).'
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateNFTbyID'
      responses:
        '200':
          description: Updated NFT
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NFTSummary'
        '400':
          description: Bad request / invalid state (e.g., NFT already MINTED or ATTACHED)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden (NFT does not belong to your organization)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: NFT not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    delete:
      tags:
        - NFT
      summary: Delete an asset
      description: 'Deletes an asset and its owner records. Not possible once it is `MINTED` or `ATTACHED`.'
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Deleted
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
        '400':
          description: Bad request (e.g., cannot delete minted NFT)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Forbidden (NFT does not belong to your organization)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: NFT not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

  # Token balance and transfer
  /cl2c/nft/{id}/certificate:
    get:
      tags:
        - NFT
      summary: Get a certificate of authenticity
      description: >-
        Returns a printable certificate of authenticity for an asset owned by the
        authenticated organization, including shared ownership and the chain of
        ownership. Pass `format=html` to be redirected (303) to the printable page.
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
        - in: query
          name: format
          required: false
          schema:
            type: string
            enum: [json, html]
            default: json
      responses:
        '200':
          description: Certificate and its links
          content:
            application/json:
              schema:
                type: object
                properties:
                  verificationUrl:
                    type: string
                    description: Public, printable certificate page. The link is an unguessable capability, not derived from the asset id.
                    example: https://developer.taliware.com/certificate/3q2-7Xk9mVYb0cRf4Lw8pZ1sTnHaUe6J
                  printUrl:
                    type: string
                    description: Same page with `?print=1`, which opens the print dialog (print or save as PDF).
                  certificate:
                    type: object
                    description: >-
                      Certificate data: certificateNumber, state (VERIFIED | PROVISIONAL | VOID),
                      asset, issuer, blockchain record, owners with their shares and
                      authorization times, the chain of ownership, and a document fingerprint
                      that changes if any recorded detail changes.
        '303':
          description: "`format=html`: redirect to the printable certificate page"
        '401':
          description: Missing or invalid credentials
        '403':
          description: The NFT belongs to another organization
        '404':
          description: NFT not found

  /certificates/{token}:
    get:
      tags:
        - NFT
      summary: Verify a certificate (public)
      description: >-
        Public verification of a certificate by its token — the last path segment of
        `verificationUrl`. No credentials are needed; holding the link is the access.
      security: []
      parameters:
        - in: path
          name: token
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Certificate and its links
          content:
            application/json:
              schema:
                type: object
                properties:
                  verificationUrl:
                    type: string
                    description: Public, printable certificate page. The link is an unguessable capability, not derived from the asset id.
                    example: https://developer.taliware.com/certificate/3q2-7Xk9mVYb0cRf4Lw8pZ1sTnHaUe6J
                  printUrl:
                    type: string
                    description: Same page with `?print=1`, which opens the print dialog (print or save as PDF).
                  certificate:
                    type: object
                    description: >-
                      Certificate data: certificateNumber, state (VERIFIED | PROVISIONAL | VOID),
                      asset, issuer, blockchain record, owners with their shares and
                      authorization times, the chain of ownership, and a document fingerprint
                      that changes if any recorded detail changes.
        '404':
          description: No certificate has this token

  /cl2c/token:
    get:
      tags:
        - Tokens
      summary: Check credit balances
      description: 'Returns the organization owner''s CRDB balance. Pass `type=mainnet` (live) or `type=faucet` (test) for one balance, or leave it out to get both.'
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
        - cookieAuth: []
      parameters:
        - in: query
          name: type
          required: false
          schema:
            type: string
            enum: [mainnet, faucet]
          description: When provided, returns a single balance object. If omitted, returns both balances.
      responses:
        '200':
          description: Token balance response
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/TokenBalanceResponse'
                  - $ref: '#/components/schemas/TokenBalancesResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

    post:
      tags:
        - Tokens
      summary: Send credits to a user
      description: 'Sends CRDB credits from the organization owner''s wallet to one user. Only organization owners can do this.'
      security:
        - apiKeyAuth: []
        - apiKeyBearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenSendRequest'
      responses:
        '200':
          description: Tokens sent successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenSendResponse'
        '400':
          description: Invalid request body or wallet configuration
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: Only owners can send tokens
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: Recipient or member not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Server or token transfer error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
CL2C API Reference | Taliware Developer