openapi: 3.1.0
info:
  title: ROB Domains Agent API
  description: |
    ROB Domains is a Web3 domain name system built on the Robinhood Chain (Chain ID 4663).
    This API enables AI agents to search domains, read profiles, and generate unsigned
    transaction payloads for minting, setting a primary domain, and updating profiles.

    ## Key Design Principles

    - **Read-only by default** — GET endpoints query on-chain state directly. No auth required.
    - **Transaction payloads, not execution** — POST endpoints return ABI-encoded transaction
      data that the user's wallet signs and broadcasts. The API never holds private keys.
    - **No API keys** — Public read access. Wallet signatures authorize on-chain writes.
    - **Additive workflow** — Use `POST /agent/workflow/register-domain` to get a complete
      multi-step execution plan in one call.

    ## Transaction Flow

    1. Call a `POST /agent/tx/*` endpoint with your parameters.
    2. Receive a `TransactionPayload` with `to`, `data`, `value`, `chainId`.
    3. Pass the payload to the user's wallet (MetaMask, WalletConnect, etc.).
    4. The wallet signs and broadcasts the transaction.
    5. Confirm on-chain via the Robinhood Chain explorer.
  version: 1.0.0
  contact:
    name: ROB Domains
    url: https://robdn.com
  license:
    name: MIT

servers:
  - url: https://robdn.com
    description: Production

tags:
  - name: Status
    description: Platform status and contract addresses
  - name: Read
    description: On-chain domain and profile lookups
  - name: Transactions
    description: Unsigned transaction payload generation
  - name: Workflows
    description: Multi-step execution plans

paths:
  /agent/status:
    get:
      operationId: getAgentStatus
      summary: Platform status
      description: Returns platform status, chain info, contract addresses, and available endpoints.
      tags: [Status]
      responses:
        '200':
          description: Platform is operational
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse_AgentStatus'
              example:
                success: true
                data:
                  status: operational
                  version: '1.0.0'
                  chain:
                    name: Robinhood
                    chainId: 4663
                    rpcUrl: https://rpc.mainnet.chain.robinhood.com
                    explorerUrl: https://robinhoodchain.blockscout.com
                    symbol: ETH
                  contracts:
                    domainNFT: '0x37F7ba486afE8Be3C3C70A4FC3eCacC6DD51c6C8'
                    marketplace: '0xAefec6e65ADE8D95c0Cf69D20e9254556f6cC637'
                    resolver: '0xeDe40500f6047F2Ef8d8F5dec12F096969A1bF47'
                  endpoints:
                    - 'GET  /agent/status'
                    - 'GET  /agent/search/{domain}'
                    - 'GET  /agent/profile/{domain}'
                    - 'GET  /agent/reverse/{wallet}'
                    - 'GET  /agent/primary/{wallet}'
                    - 'POST /agent/tx/mint'
                    - 'POST /agent/tx/set-primary'
                    - 'POST /agent/tx/update-profile'
                    - 'POST /agent/workflow/register-domain'
                  auth: No API keys required. Wallet signatures authorize on-chain writes.
                  docs: https://robdn.com/developer
                  openapi: https://robdn.com/agent/docs/openapi.yaml
                  manifest: https://robdn.com/.well-known/ai-plugin.json
                meta:
                  chain: Robinhood
                  chainId: 4663
                  timestamp: '2024-01-01T00:00:00.000Z'

  /agent/search/{domain}:
    get:
      operationId: searchDomain
      summary: Search for a domain
      description: |
        Search for a .rob domain by name. Returns availability status, current owner,
        token ID, primary owner, and profile summary if the domain is already minted.
        Accepts the domain with or without the ".rob" suffix.
      tags: [Read]
      parameters:
        - name: domain
          in: path
          required: true
          schema:
            type: string
          description: Domain name with or without ".rob" suffix (e.g. "alice" or "alice.rob")
          example: alice
      responses:
        '200':
          description: Domain search result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse_DomainSearch'
              examples:
                available:
                  summary: Domain is available
                  value:
                    success: true
                    data:
                      domain: alice
                      fullDomain: alice.rob
                      available: true
                      owner: null
                      tokenId: null
                      primaryOwner: null
                      profile: null
                    meta:
                      chain: Robinhood
                      chainId: 4663
                      timestamp: '2024-01-01T00:00:00.000Z'
                taken:
                  summary: Domain is taken
                  value:
                    success: true
                    data:
                      domain: alice
                      fullDomain: alice.rob
                      available: false
                      owner: '0xAbCd1234...'
                      tokenId: '42'
                      primaryOwner: '0xAbCd1234...'
                      profile:
                        bio: Web3 builder
                        profilePicture: https://example.com/pic.jpg
                        coverImage: ''
                        background: bg-gradient-to-br from-purple-900 to-indigo-900
                        socialLinks:
                          - platform: twitter
                            url: https://twitter.com/alice
                            icon: twitter
                        customLinks: []
                    meta:
                      chain: Robinhood
                      chainId: 4663
                      timestamp: '2024-01-01T00:00:00.000Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          $ref: '#/components/responses/InternalError'

  /agent/profile/{domain}:
    get:
      operationId: getProfile
      summary: Get domain profile
      description: |
        Returns the full on-chain profile for a .rob domain, including bio, social links,
        custom links, profile picture, cover image, and background.
      tags: [Read]
      parameters:
        - name: domain
          in: path
          required: true
          schema:
            type: string
          description: Domain name with or without ".rob" suffix
          example: alice
      responses:
        '200':
          description: Full domain profile
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse_Profile'
              example:
                success: true
                data:
                  domain: alice
                  fullDomain: alice.rob
                  owner: '0xAbCd1234...'
                  tokenId: '42'
                  profile:
                    bio: Web3 builder and open source contributor
                    profilePicture: https://example.com/pic.jpg
                    coverImage: https://example.com/cover.jpg
                    background: bg-gradient-to-br from-purple-900 to-indigo-900
                    socialLinks:
                      - platform: twitter
                        url: https://twitter.com/alice
                        icon: twitter
                      - platform: github
                        url: https://github.com/alice
                        icon: github
                    customLinks:
                      - title: My Website
                        url: https://alice.com
                meta:
                  chain: Robinhood
                  chainId: 4663
                  timestamp: '2024-01-01T00:00:00.000Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalError'

  /agent/reverse/{wallet}:
    get:
      operationId: reverseResolve
      summary: Reverse resolve wallet to domain
      description: |
        Reverse-resolves an Ethereum wallet address to its primary .rob domain as
        registered in the on-chain RobResolver contract. Returns an empty result
        (not an error) when no primary domain is set.
      tags: [Read]
      parameters:
        - name: wallet
          in: path
          required: true
          schema:
            type: string
            pattern: '^0x[0-9a-fA-F]{40}$'
          description: Ethereum wallet address (0x-prefixed)
          example: '0xAbCd1234567890AbCd1234567890AbCd12345678'
      responses:
        '200':
          description: Reverse resolution result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse_Reverse'
              examples:
                resolved:
                  summary: Primary domain found
                  value:
                    success: true
                    data:
                      wallet: '0xAbCd1234...'
                      tokenId: '42'
                      domainName: alice
                      fullDomain: alice.rob
                      resolved: true
                    meta:
                      chain: Robinhood
                      chainId: 4663
                      timestamp: '2024-01-01T00:00:00.000Z'
                not_set:
                  summary: No primary domain set
                  value:
                    success: true
                    data:
                      wallet: '0xAbCd1234...'
                      tokenId: '0'
                      domainName: ''
                      fullDomain: ''
                      resolved: false
                    meta:
                      chain: Robinhood
                      chainId: 4663
                      timestamp: '2024-01-01T00:00:00.000Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          $ref: '#/components/responses/InternalError'

  /agent/primary/{wallet}:
    get:
      operationId: getPrimaryDomain
      summary: Get primary domain for wallet
      description: |
        Returns the primary .rob domain set for a wallet via the on-chain RobResolver.
        Includes the resolution source for transparency.
      tags: [Read]
      parameters:
        - name: wallet
          in: path
          required: true
          schema:
            type: string
            pattern: '^0x[0-9a-fA-F]{40}$'
          description: Ethereum wallet address (0x-prefixed)
          example: '0xAbCd1234567890AbCd1234567890AbCd12345678'
      responses:
        '200':
          description: Primary domain result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse_Primary'
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          $ref: '#/components/responses/InternalError'

  /agent/tx/mint:
    post:
      operationId: buildMintTransaction
      summary: Build mint transaction
      description: |
        Generates an unsigned transaction payload for minting a .rob domain NFT.
        The domain availability is verified before returning the payload.

        **The API does NOT execute the transaction.**
        Pass the returned payload to the user's wallet for signing and broadcasting.
      tags: [Transactions]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/MintRequest'
            example:
              domain: alice
      responses:
        '200':
          description: Unsigned mint transaction payload
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse_Transaction'
              example:
                success: true
                data:
                  to: '0x37F7ba486afE8Be3C3C70A4FC3eCacC6DD51c6C8'
                  data: '0x...'
                  value: '0'
                  chainId: 4663
                  contractName: DomainNFT
                  method: mintDomain
                  description: Mint the domain "alice.rob" as an NFT on Robinhood Chain.
                meta:
                  chain: Robinhood
                  chainId: 4663
                  timestamp: '2024-01-01T00:00:00.000Z'
        '400':
          $ref: '#/components/responses/BadRequest'
        '409':
          $ref: '#/components/responses/Conflict'
        '500':
          $ref: '#/components/responses/InternalError'

  /agent/tx/set-primary:
    post:
      operationId: buildSetPrimaryTransaction
      summary: Build set-primary transaction
      description: |
        Generates an unsigned transaction payload for setting a .rob domain token as
        the caller's primary domain in the RobResolver contract.
        Verifies token ownership before returning the payload.

        **The API does NOT execute the transaction.**
      tags: [Transactions]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SetPrimaryRequest'
            example:
              tokenId: '42'
              wallet: '0xAbCd1234567890AbCd1234567890AbCd12345678'
      responses:
        '200':
          description: Unsigned set-primary transaction payload
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse_Transaction'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalError'

  /agent/tx/update-profile:
    post:
      operationId: buildUpdateProfileTransaction
      summary: Build update-profile transaction(s)
      description: |
        Generates one or more unsigned transaction payloads for updating a .rob domain profile.

        When `socialLinks` or `customLinks` are provided, multiple payloads are returned.
        **Sign and send them in order.**

        **The API does NOT execute any transaction.**
      tags: [Transactions]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateProfileRequest'
            example:
              tokenId: '42'
              bio: Web3 builder
              profilePicture: https://example.com/pic.jpg
              coverImage: ''
              background: bg-gradient-to-br from-purple-900 to-indigo-900
              socialLinks:
                - platform: twitter
                  url: https://twitter.com/alice
              customLinks:
                - title: My Website
                  url: https://alice.com
      responses:
        '200':
          description: Unsigned update-profile transaction payload(s)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse_UpdateProfile'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalError'

  /agent/workflow/register-domain:
    post:
      operationId: registerDomainWorkflow
      summary: Full domain registration plan
      description: |
        Returns a complete multi-step execution plan for registering a .rob domain.
        All transaction payloads are included — the wallet signs and sends each step in order.

        Steps produced:
        1. **Mint Domain** — always present
        2. **Set Primary** — present when `setPrimary: true`
        3. **Update Profile** — present when profile fields are provided

        **The API does NOT execute anything. This is a planning endpoint only.**
      tags: [Workflows]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RegisterDomainRequest'
            examples:
              mint_only:
                summary: Mint only
                value:
                  domain: alice
                  wallet: '0xAbCd1234567890AbCd1234567890AbCd12345678'
              full_registration:
                summary: Full registration with profile
                value:
                  domain: alice
                  wallet: '0xAbCd1234567890AbCd1234567890AbCd12345678'
                  setPrimary: true
                  profile:
                    bio: Web3 builder
                    profilePicture: https://example.com/pic.jpg
                    socialLinks:
                      - platform: twitter
                        url: https://twitter.com/alice
      responses:
        '200':
          description: Execution plan with all transaction payloads
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SuccessResponse_ExecutionPlan'
        '400':
          $ref: '#/components/responses/BadRequest'
        '409':
          $ref: '#/components/responses/Conflict'
        '500':
          $ref: '#/components/responses/InternalError'

components:
  schemas:
    TransactionPayload:
      type: object
      required: [to, data, value, chainId, contractName, method, description]
      properties:
        to:
          type: string
          description: Contract address to send the transaction to
          example: '0x37F7ba486afE8Be3C3C70A4FC3eCacC6DD51c6C8'
        data:
          type: string
          description: ABI-encoded calldata
          example: '0x...'
        value:
          type: string
          description: Native ETH value in wei (decimal string). "0" for non-payable.
          example: '0'
        chainId:
          type: integer
          description: EIP-155 chain ID
          example: 4663
        contractName:
          type: string
          description: Human-readable contract name
          example: DomainNFT
        method:
          type: string
          description: Solidity function name
          example: mintDomain
        description:
          type: string
          description: Human-readable description of what this transaction does

    SocialLink:
      type: object
      required: [platform, url]
      properties:
        platform:
          type: string
          enum:
            [
              twitter,
              instagram,
              github,
              linkedin,
              youtube,
              facebook,
              twitch,
              discord,
              telegram,
              tiktok,
            ]
        url:
          type: string
          format: uri
        icon:
          type: string
          default: ''

    CustomLink:
      type: object
      required: [title, url]
      properties:
        title:
          type: string
          maxLength: 100
        url:
          type: string
          format: uri

    ProfileSummary:
      type: object
      properties:
        bio:
          type: string
        profilePicture:
          type: string
        coverImage:
          type: string
        background:
          type: string
        socialLinks:
          type: array
          items:
            $ref: '#/components/schemas/SocialLink'
        customLinks:
          type: array
          items:
            $ref: '#/components/schemas/CustomLink'

    MintRequest:
      type: object
      required: [domain]
      properties:
        domain:
          type: string
          minLength: 1
          maxLength: 63
          pattern: '^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$'
          description: Domain label without ".rob" suffix
          example: alice

    SetPrimaryRequest:
      type: object
      required: [tokenId, wallet]
      properties:
        tokenId:
          type: string
          pattern: '^\d+$'
          description: On-chain token ID of the domain
          example: '42'
        wallet:
          type: string
          pattern: '^0x[0-9a-fA-F]{40}$'
          description: Wallet address that owns the token
          example: '0xAbCd1234567890AbCd1234567890AbCd12345678'

    UpdateProfileRequest:
      type: object
      required: [tokenId]
      properties:
        tokenId:
          type: string
          pattern: '^\d+$'
          description: On-chain token ID of the domain to update
          example: '42'
        profilePicture:
          type: string
          description: URL of profile picture (empty string to clear)
          default: ''
        coverImage:
          type: string
          description: URL of cover image (empty string to clear)
          default: ''
        bio:
          type: string
          maxLength: 500
          default: ''
        background:
          type: string
          maxLength: 200
          description: Tailwind CSS background class string
          default: ''
        socialLinks:
          type: array
          items:
            $ref: '#/components/schemas/SocialLink'
          maxItems: 10
          default: []
        customLinks:
          type: array
          items:
            $ref: '#/components/schemas/CustomLink'
          maxItems: 20
          default: []

    RegisterDomainRequest:
      type: object
      required: [domain, wallet]
      properties:
        domain:
          type: string
          minLength: 1
          maxLength: 63
          pattern: '^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$'
          example: alice
        wallet:
          type: string
          pattern: '^0x[0-9a-fA-F]{40}$'
          example: '0xAbCd1234567890AbCd1234567890AbCd12345678'
        setPrimary:
          type: boolean
          default: false
          description: Include a Set Primary Domain step in the plan
        profile:
          type: object
          description: Optional profile to set after minting
          properties:
            bio:
              type: string
              maxLength: 500
            profilePicture:
              type: string
            coverImage:
              type: string
            background:
              type: string
            socialLinks:
              type: array
              items:
                $ref: '#/components/schemas/SocialLink'
            customLinks:
              type: array
              items:
                $ref: '#/components/schemas/CustomLink'

    WorkflowStep:
      type: object
      properties:
        step:
          type: integer
        action:
          type: string
        description:
          type: string
        transaction:
          oneOf:
            - $ref: '#/components/schemas/TransactionPayload'
            - type: array
              items:
                $ref: '#/components/schemas/TransactionPayload'

    ResponseMeta:
      type: object
      properties:
        chain:
          type: string
          example: Robinhood
        chainId:
          type: integer
          example: 4663
        timestamp:
          type: string
          format: date-time

    ErrorResponse:
      type: object
      required: [success, code, message]
      properties:
        success:
          type: boolean
          enum: [false]
        code:
          type: integer
          example: 400
        message:
          type: string
        details:
          type: string

    SuccessResponse_AgentStatus:
      type: object
      properties:
        success:
          type: boolean
          enum: [true]
        data:
          type: object
        meta:
          $ref: '#/components/schemas/ResponseMeta'

    SuccessResponse_DomainSearch:
      type: object
      properties:
        success:
          type: boolean
          enum: [true]
        data:
          type: object
          properties:
            domain:
              type: string
            fullDomain:
              type: string
            available:
              type: boolean
            owner:
              type: string
              nullable: true
            tokenId:
              type: string
              nullable: true
            primaryOwner:
              type: string
              nullable: true
            profile:
              nullable: true
              $ref: '#/components/schemas/ProfileSummary'
        meta:
          $ref: '#/components/schemas/ResponseMeta'

    SuccessResponse_Profile:
      type: object
      properties:
        success:
          type: boolean
          enum: [true]
        data:
          type: object
          properties:
            domain:
              type: string
            fullDomain:
              type: string
            owner:
              type: string
            tokenId:
              type: string
            profile:
              $ref: '#/components/schemas/ProfileSummary'
        meta:
          $ref: '#/components/schemas/ResponseMeta'

    SuccessResponse_Reverse:
      type: object
      properties:
        success:
          type: boolean
          enum: [true]
        data:
          type: object
          properties:
            wallet:
              type: string
            tokenId:
              type: string
            domainName:
              type: string
            fullDomain:
              type: string
            resolved:
              type: boolean
        meta:
          $ref: '#/components/schemas/ResponseMeta'

    SuccessResponse_Primary:
      type: object
      properties:
        success:
          type: boolean
          enum: [true]
        data:
          type: object
          properties:
            wallet:
              type: string
            tokenId:
              type: string
            domainName:
              type: string
            fullDomain:
              type: string
            resolved:
              type: boolean
            source:
              type: string
        meta:
          $ref: '#/components/schemas/ResponseMeta'

    SuccessResponse_Transaction:
      type: object
      properties:
        success:
          type: boolean
          enum: [true]
        data:
          $ref: '#/components/schemas/TransactionPayload'
        meta:
          $ref: '#/components/schemas/ResponseMeta'

    SuccessResponse_UpdateProfile:
      type: object
      properties:
        success:
          type: boolean
          enum: [true]
        data:
          type: object
          properties:
            tokenId:
              type: string
            transactionCount:
              type: integer
            note:
              type: string
              nullable: true
            transactions:
              type: array
              items:
                $ref: '#/components/schemas/TransactionPayload'
        meta:
          $ref: '#/components/schemas/ResponseMeta'

    SuccessResponse_ExecutionPlan:
      type: object
      properties:
        success:
          type: boolean
          enum: [true]
        data:
          type: object
          properties:
            domain:
              type: string
            fullDomain:
              type: string
            wallet:
              type: string
            totalSteps:
              type: integer
            steps:
              type: array
              items:
                $ref: '#/components/schemas/WorkflowStep'
            warning:
              type: string
        meta:
          $ref: '#/components/schemas/ResponseMeta'

  responses:
    BadRequest:
      description: Bad request — validation error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            code: 400
            message: Domain is required
            details: 'Field: domain'

    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            code: 404
            message: Domain not found or not yet minted

    Forbidden:
      description: Wallet does not own the token
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            code: 403
            message: Wallet 0x... does not own token #42

    Conflict:
      description: Domain already taken
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            code: 409
            message: Domain "alice.rob" is already taken

    InternalError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            success: false
            code: 500
            message: Domain search failed — please try again
