Skip to content
Docs OpenAPI

OpenAPI

The checked-in OpenAPI contract for generating clients.

openapi: 3.0.3
info:
  title: Agent Passport — Trust Scoring Service
  description: |
    Stateless trust scoring API for AI agents on Algorand testnet.

    Fetches on-chain data from Algorand testnet and computes composite trust scores,
    delegation trust, sybil detection, reputation, credit estimation, and underwriting decisions.
  version: 0.1.0
  contact:
    name: Agent Passport Maintainers
    email: sachncs@gmail.com
    url: https://github.com/sachncs/agent-passport
  license:
    name: MIT

servers:
  - url: http://localhost:3000
    description: Local development

paths:
  /health:
    get:
      summary: Health check with Algorand connectivity probe
      operationId: healthCheck
      tags: [System]
      responses:
        '200':
          description: Service is healthy
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'
        '503':
          description: Service degraded (Algorand unreachable)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'

  /score:
    get:
      summary: Compute trust score for a wallet
      description: |
        Returns a comprehensive trust report for the specified wallet address.
        Fetches account info and transaction history from Algorand testnet,
        then computes a composite trust score (0-100).
      operationId: getScore
      tags: [Trust]
      parameters:
        - name: wallet
          in: query
          required: true
          schema:
            type: string
            pattern: '^[A-Z2-7]{58}$'
          description: Algorand wallet address (58-char base32)
      responses:
        '200':
          description: Trust score
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrustScoreResponse'
        '400':
          description: Invalid wallet address
        '404':
          description: Wallet not found on testnet
        '429':
          description: Rate limit exceeded
        '500':
          description: Internal server error

  /delegation:
    get:
      summary: Compute delegation trust for a wallet
      description: |
        Returns delegation trust score based on the wallet's endorsement network.
        Traverses the delegation graph (BFS) to find trust anchors and compute
        depth, sponsor quality, sponsor count, and delegated amount scores.
      operationId: getDelegation
      tags: [Trust]
      parameters:
        - name: wallet
          in: query
          required: true
          schema:
            type: string
            pattern: '^[A-Z2-7]{58}$'
      responses:
        '200':
          description: Delegation trust score
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DelegationResponse'
        '400':
          description: Invalid wallet address
        '404':
          description: Wallet not found
        '500':
          description: Internal server error

  /counterparty-check:
    post:
      summary: Verify a counterparty for agent commerce
      description: |
        Evaluates whether a buyer wallet is trustworthy for a transaction.
        Combines on-chain trust score with delegation trust to produce
        a combined score, confidence level, and allow/deny decision.
      operationId: checkCounterparty
      tags: [Commerce]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [buyer]
              properties:
                buyer:
                  type: string
                  pattern: '^[A-Z2-7]{58}$'
                  description: Buyer wallet address to verify
      responses:
        '200':
          description: Counterparty verification result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CounterpartyResponse'
        '400':
          description: Missing or invalid buyer wallet
        '404':
          description: Wallet not found
        '500':
          description: Internal server error

  /credit-estimate:
    post:
      summary: Estimate credit capacity for a wallet
      description: |
        Computes the maximum credit a wallet can receive based on on-chain
        capacity: balance, activity, and account age. Optionally validates
        against a specific requested amount.
      operationId: estimateCredit
      tags: [Credit]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [wallet]
              properties:
                wallet:
                  type: string
                  pattern: '^[A-Z2-7]{58}$'
                amount:
                  type: number
                  minimum: 0
                  description: Optional requested amount to validate against capacity
      responses:
        '200':
          description: Credit estimate
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreditEstimateResponse'
        '400':
          description: Invalid wallet or amount
        '404':
          description: Wallet not found
        '500':
          description: Internal server error

  /sybil-check:
    get:
      summary: Detect sybil risk for a wallet
      description: |
        Analyzes whether a wallet is part of a sybil farm using 11 signals:
        creation clustering, interaction density, balance similarity,
        circular activity, timing regularity, amount fingerprint,
        funding correlation, neighborhood clustering, hub score,
        intermediate density, and temporal correlation.
      operationId: checkSybil
      tags: [Security]
      parameters:
        - name: wallet
          in: query
          required: true
          schema:
            type: string
            pattern: '^[A-Z2-7]{58}$'
      responses:
        '200':
          description: Sybil risk analysis
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SybilResponse'
        '400':
          description: Invalid wallet
        '404':
          description: Wallet not found
        '500':
          description: Internal server error

  /reputation:
    get:
      summary: Compute reputation score for a wallet
      description: |
        Returns the reputation score based on on-chain reputation events
        (payments, purchases, disputes, refunds, endorsements, services)
        recorded in the reputation smart contract.
      operationId: getReputation
      tags: [Reputation]
      parameters:
        - name: wallet
          in: query
          required: true
          schema:
            type: string
            pattern: '^[A-Z2-7]{58}$'
      responses:
        '200':
          description: Reputation score
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReputationResponse'
        '400':
          description: Invalid wallet
        '404':
          description: Wallet not found
        '500':
          description: Internal server error

  /reputation/record:
    post:
      summary: Record a reputation event on-chain
      description: |
        Records a reputation event (payment, purchase, dispute, refund,
        endorsement, or service) to the reputation smart contract.
        Disputes and refunds require verified counterparty and on-chain proof.
        Self-reported events require on-chain transaction evidence.
      operationId: recordReputation
      tags: [Reputation]
      parameters:
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            minLength: 8
            maxLength: 255
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [wallet, eventType]
              properties:
                wallet:
                  type: string
                  pattern: '^[A-Z2-7]{58}$'
                eventType:
                  type: string
                  enum: [payment, purchase, dispute, refund, endorsement, service]
                amount:
                  type: number
                  minimum: 0
                  description: Event amount (optional, defaults to 0)
                counterparty:
                  type: string
                  pattern: '^[A-Z2-7]{58}$'
                  description: Counterparty wallet (required for disputes/refunds)
      responses:
        '200':
          description: Event recorded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReputationEventResponse'
        '400':
          description: Invalid input or verification failed
        '500':
          description: Internal server error

  /underwrite:
    get:
      summary: Underwrite a wallet for credit
      description: |
        Makes a credit decision using 4 factors: trust score (0.35),
        delegation trust (0.25), sybil resistance (0.20), and
        reputation (0.20). Returns approval/denial, recommended limit,
        risk level, and detailed explanation.
      operationId: underwrite
      tags: [Credit]
      parameters:
        - name: wallet
          in: query
          required: true
          schema:
            type: string
            pattern: '^[A-Z2-7]{58}$'
      responses:
        '200':
          description: Underwriting decision
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnderwritingResponse'
        '400':
          description: Invalid wallet
        '404':
          description: Wallet not found
        '500':
          description: Internal server error

  /trust-graph:
    get:
      summary: Analyze trust graph for a wallet
      description: |
        Performs BFS traversal of the delegation graph to map trust relationships.
        Returns nodes, edges, trust paths, exposure analysis, and what-if scenarios
        for sponsor removal. Maximum depth: 5 levels.
      operationId: analyzeTrustGraph
      tags: [Graph]
      parameters:
        - name: wallet
          in: query
          required: true
          schema:
            type: string
            pattern: '^[A-Z2-7]{58}$'
      responses:
        '200':
          description: Trust graph analysis
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrustGraphResponse'
        '400':
          description: Invalid wallet
        '404':
          description: Wallet not found
        '500':
          description: Internal server error

  /passport:
    get:
      summary: Generate Agent Passport document
      description: |
        Generates a comprehensive Agent Passport document for a wallet.
        Combines trust score, delegation, reputation, sybil detection,
        and credit estimation into a single verifiable document with
        SHA-256 checksum over all deterministic fields.
      operationId: getPassport
      tags: [Passport]
      parameters:
        - name: wallet
          in: query
          required: true
          schema:
            type: string
            pattern: '^[A-Z2-7]{58}$'
      responses:
        '200':
          description: Agent Passport document
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PassportResponse'
        '400':
          description: Invalid wallet
        '404':
          description: Wallet not found
        '500':
          description: Internal server error

  /verify:
    get:
      summary: Lightweight wallet validity check
      description: |
        Returns whether the supplied wallet is a syntactically valid Algorand address
        and, if so, derives simple on-chain flags (funded, active). Free, no payment required.
        Cached for 60s.
      operationId: verifyWallet
      tags: [Free]
      parameters:
        - name: wallet
          in: query
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Verification result
          content:
            application/json:
              schema:
                type: object
                required: [valid, wallet, flags]
                properties:
                  valid:
                    type: boolean
                    description: Whether the wallet is a valid base32 Algorand address
                  wallet:
                    type: string
                  flags:
                    type: object
                    additionalProperties: true
                  cached:
                    type: boolean
        '400':
          description: Missing wallet parameter

  /discovery/search:
    get:
      summary: Bazaar service discovery
      description: |
        Search the registered Bazaar services by query. Filters by name, description,
        category, and tags.
      operationId: discoverySearch
      tags: [Free]
      parameters:
        - name: q
          in: query
          required: false
          schema:
            type: string
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            default: 20
            maximum: 100
      responses:
        '200':
          description: Search results
          content:
            application/json:
              schema:
                type: object
                required: [query, total, results]
                properties:
                  query:
                    type: string
                  total:
                    type: integer
                  results:
                    type: array
                    items:
                      type: object

  /ready:
    get:
      summary: Readiness probe — checks Algorand connectivity
      operationId: readinessCheck
      tags: [System]
      responses:
        '200':
          description: Service is ready
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReadinessResponse'
        '503':
          description: Algorand endpoint unreachable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReadinessResponse'

  /health/deep:
    get:
      summary: Always-200 informational health (includes Algorand status in body)
      operationId: healthDeep
      tags: [System]
      responses:
        '200':
          description: Service is alive; Algorand status in body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'

  /registry/status:
    get:
      summary: Whether the on-chain contracts are configured
      operationId: registryStatus
      tags: [System]
      responses:
        '200':
          description: Registry status
          content:
            application/json:
              schema:
                type: object
                required: [configured, appId]
                properties:
                  configured:
                    type: boolean
                  appId:
                    type: integer

  /delegate:
    post:
      summary: Submit an on-chain delegation
      description: |
        Submits an `add_delegation` call to `registry.teal`. Requires
        `REGISTRY_APP_ID>0` and `OPERATOR_MNEMONIC` set.
      operationId: delegate
      tags: [On-chain]
      parameters:
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            minLength: 8
            maxLength: 255
            pattern: '^[A-Za-z0-9_\-:]+$'
          description: Recommended for safe retry
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [sponsor, agent, amount]
              properties:
                sponsor:
                  type: string
                  pattern: '^[A-Z2-7]{58}$'
                agent:
                  type: string
                  pattern: '^[A-Z2-7]{58}$'
                amount:
                  type: number
                  minimum: 0
                  exclusiveMinimum: true
      responses:
        '201':
          description: Delegation submitted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DelegationResult'
        '400':
          description: Invalid input
        '402':
          description: x402 payment required
        '409':
          description: Idempotency-Key reused with different body
        '429':
          description: Rate limit exceeded
        '500':
          description: Internal server error
        '503':
          description: Registry not configured (REGISTRY_APP_ID=0)
          content:
            application/json:
              schema:
                type: object
                properties:
                  error: { type: string }
                  code:
                    type: string
                    enum: [REGISTRY_NOT_CONFIGURED]

  /revoke:
    post:
      summary: Revoke an on-chain delegation
      operationId: revoke
      tags: [On-chain]
      parameters:
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [sponsor, agent]
              properties:
                sponsor:
                  type: string
                  pattern: '^[A-Z2-7]{58}$'
                agent:
                  type: string
                  pattern: '^[A-Z2-7]{58}$'
      responses:
        '200':
          description: Revocation submitted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RevocationResult'
        '400':
          description: Invalid input
        '402':
          description: x402 payment required
        '409':
          description: Idempotency-Key reused with different body
        '429':
          description: Rate limit exceeded
        '500':
          description: Internal server error
        '503':
          description: Registry not configured

  /version:
    get:
      summary: Service build metadata
      operationId: version
      tags: [System]
      responses:
        '200':
          description: Build and runtime metadata
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true

  /openapi.json:
    get:
      summary: Runtime OpenAPI document
      operationId: openapi
      tags: [System]
      responses:
        '200':
          description: OpenAPI document
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true

  /reputation/subscribe:
    post:
      summary: Subscribe to reputation events
      operationId: subscribeReputation
      tags: [Reputation]
      parameters:
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            minLength: 8
            maxLength: 255
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [wallet, url]
              properties:
                wallet: { type: string, pattern: '^[A-Z2-7]{58}$' }
                url: { type: string, format: uri }
      responses:
        '201': { description: Subscription created }
        '400': { description: Invalid input }
        '401': { description: HMAC auth failed }

  /reputation/subscribers:
    get:
      summary: List reputation webhook subscribers
      operationId: listReputationSubscribers
      tags: [Reputation]
      parameters:
        - name: wallet
          in: query
          required: false
          schema: { type: string, pattern: '^[A-Z2-7]{58}$' }
      responses:
        '200': { description: Subscriber list }
        '401': { description: HMAC auth failed }

  /reputation/subscribe/{id}:
    delete:
      summary: Remove a reputation webhook subscription
      operationId: unsubscribeReputation
      tags: [Reputation]
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string, format: uuid }
        - name: Idempotency-Key
          in: header
          required: true
          schema: { type: string, minLength: 8, maxLength: 255 }
      responses:
        '204': { description: Subscription removed }
        '404': { description: Subscription not found }
        '401': { description: HMAC auth failed }

  /dashboard:
    get:
      summary: Legacy HTML dashboard
      operationId: dashboard
      tags: [System]
      responses:
        '200': { description: Dashboard HTML }

  /metrics:
    get:
      summary: Prometheus metrics
      operationId: metrics
      tags: [System]
      responses:
        '200':
          description: Prometheus-format metrics
          content:
            text/plain:
              schema:
                type: string
                example: |
                  # HELP agent_passport_http_requests_total Total HTTP requests
                  # TYPE agent_passport_http_requests_total counter
                  agent_passport_http_requests_total{method="GET",path="/score",status="200"} 42

components:
  schemas:
    HealthResponse:
      type: object
      properties:
        status:
          type: string
          enum: [ok, degraded]
        service:
          type: string
        version:
          type: string
        network:
          type: string
        x402:
          type: boolean
        timestamp:
          type: string
          format: date-time
        algorand:
          type: object
          properties:
            connected:
              type: boolean
            round:
              type: integer

    TrustScoreResponse:
      type: object
      properties:
        wallet:
          type: string
        trustScore:
          type: number
          minimum: 0
          maximum: 100
        riskLevel:
          type: string
          enum: [low, medium, high, critical]
        approved:
          type: boolean
        recommendedLimit:
          type: number
        breakdown:
          type: object
          properties:
            ageScore: { type: number }
            activityScore: { type: number }
            volumeScore: { type: number }
            velocityScore: { type: number }
            complianceScore: { type: number }
        onChain:
          type: object
          properties:
            balanceAlgo: { type: number }
            totalTxns: { type: integer }
            assetCount: { type: integer }
            appCount: { type: integer }
            accountAgeDays: { type: integer }
            firstSeenRound: { type: integer }
            lastSeenRound: { type: integer }
        explanation:
          type: array
          items: { type: string }

    DelegationResponse:
      type: object
      properties:
        wallet: { type: string }
        trustScore: { type: number }
        riskLevel: { type: string, enum: [low, medium, high, critical] }
        approved: { type: boolean }
        recommendedLimit: { type: number }
        breakdown:
          type: object
          properties:
            depthScore: { type: number }
            sponsorQualityScore: { type: number }
            sponsorCountScore: { type: number }
            amountScore: { type: number }
        delegation:
          type: object
          properties:
            depth: { type: integer }
            sponsorCount: { type: integer }
            sponsorQuality: { type: number }
            delegationPath: { type: array, items: { type: string } }
            totalDelegatedAmount: { type: number }
            isTrustAnchor: { type: boolean }
            trustedAncestors: { type: integer }
        explanation: { type: array, items: { type: string } }

    CounterpartyResponse:
      type: object
      properties:
        allow: { type: boolean }
        confidence: { type: number }
        riskLevel: { type: string, enum: [low, medium, high, critical] }
        trustScore: { type: number }
        onChainScore: { type: number }
        delegationScore: { type: number }
        explanation: { type: array, items: { type: string } }

    CreditEstimateResponse:
      type: object
      properties:
        wallet: { type: string }
        estimatedLimit: { type: number }
        risk: { type: string, enum: [low, medium, high, critical] }
        confidence: { type: number }
        approved: { type: boolean }
        breakdown:
          type: object
          properties:
            balanceCapacity: { type: number }
            activityBonus: { type: number }
            ageBonus: { type: number }
            riskPenalty: { type: number }
        explanation: { type: array, items: { type: string } }

    SybilResponse:
      type: object
      properties:
        wallet: { type: string }
        sybilRisk: { type: number }
        riskLevel: { type: string, enum: [low, medium, high, critical] }
        confidence: { type: number }
        clusterSize: { type: integer }
        signals:
          type: object
          properties:
            creationClustering: { type: number }
            interactionDensity: { type: number }
            balanceSimilarity: { type: number }
            circularActivity: { type: number }
            timingRegularity: { type: number }
            amountFingerprint: { type: number }
            fundingCorrelation: { type: number }
            neighborhoodClustering: { type: number }
            hubScore: { type: number }
            intermediateDensity: { type: number }
            componentRatio: { type: number }
            temporalCorrelation: { type: number }
        flaggedWallets: { type: array, items: { type: string } }
        explanation: { type: array, items: { type: string } }

    ReputationResponse:
      type: object
      properties:
        wallet: { type: string }
        reputation: { type: number }
        riskLevel: { type: string, enum: [low, medium, high, critical] }
        confidence: { type: number }
        breakdown:
          type: object
          properties:
            successfulPayments: { type: integer }
            successfulPurchases: { type: integer }
            disputes: { type: integer }
            refunds: { type: integer }
            sponsorEndorsements: { type: integer }
            serviceInteractions: { type: integer }
            totalEvents: { type: integer }
            positiveEvents: { type: integer }
            negativeEvents: { type: integer }
        explanation: { type: array, items: { type: string } }

    ReputationEventResponse:
      type: object
      properties:
        wallet: { type: string }
        eventType: { type: string }
        amount: { type: number }
        counterparty: { type: string }
        round: { type: integer }
        timestamp: { type: integer }
        eventHash: { type: string }
        counterpartyVerified: { type: boolean }
        txId: { type: string, description: 'Algorand transaction ID (if submitted)' }

    UnderwritingResponse:
      type: object
      properties:
        wallet: { type: string }
        approved: { type: boolean }
        recommendedLimit: { type: number }
        riskLevel: { type: string, enum: [low, medium, high, critical] }
        confidence: { type: number }
        compositeScore: { type: number }
        factors:
          type: array
          items:
            type: object
            properties:
              name: { type: string }
              score: { type: number }
              weight: { type: number }
              contribution: { type: number }
              status: { type: string, enum: [positive, neutral, negative] }
        explanation: { type: array, items: { type: string } }

    TrustGraphResponse:
      type: object
      properties:
        wallet: { type: string }
        depth: { type: integer }
        nodeCount: { type: integer }
        edges:
          type: array
          items:
            type: object
            properties:
              from: { type: string }
              to: { type: string }
              amount: { type: number }
              round: { type: integer }
        nodes:
          type: array
          items:
            type: object
            properties:
              address: { type: string }
              trustScore: { type: number }
              balanceAlgo: { type: number }
              depth: { type: integer }
        paths:
          type: array
          items:
            type: object
            properties:
              path: { type: array, items: { type: string } }
              depth: { type: integer }
              totalDelegated: { type: number }
              weakestLink: { type: number }
        exposure:
          type: object
          properties:
            totalExposure: { type: number }
            directExposure: { type: number }
            indirectExposure: { type: number }
            exposureByDepth:
              type: array
              items:
                type: object
                properties:
                  depth: { type: integer }
                  amount: { type: number }
                  wallets: { type: integer }
            maxLossIfSponsorFails: { type: number }
        whatIfs:
          type: array
          items:
            type: object
            properties:
              sponsorRemoved: { type: string }
              originalScore: { type: number }
              newScore: { type: number }
              scoreImpact: { type: number }
              affectedWallets: { type: integer }
              explanation: { type: array, items: { type: string } }
        explanation: { type: array, items: { type: string } }

    PassportResponse:
      type: object
      properties:
        wallet: { type: string }
        generatedAt: { type: string, format: date-time }
        blockRound: { type: integer }
        schemaVersion: { type: integer }
        identityStrength: { type: number }
        trustScore: { type: number }
        trustRiskLevel: { type: string, enum: [low, medium, high, critical] }
        reputation: { type: number }
        reputationRiskLevel: { type: string, enum: [low, medium, high, critical] }
        totalEvents: { type: integer }
        paymentReliability: { type: number }
        creditLimit: { type: number }
        creditRisk: { type: string, enum: [low, medium, high, critical] }
        risk: { type: number }
        sybilRisk: { type: number }
        overallRiskLevel: { type: string, enum: [low, medium, high, critical] }
        onChain:
          type: object
          properties:
            balanceAlgo: { type: number }
            totalTxns: { type: integer }
            accountAgeDays: { type: integer }
            assets: { type: integer }
            apps: { type: integer }
        delegation:
          type: object
          properties:
            depth: { type: integer }
            sponsorCount: { type: integer }
            delegatedAmount: { type: number }
            isTrustAnchor: { type: boolean }
        capabilities:
          type: object
          properties:
            trustScoring: { type: boolean }
            delegation: { type: boolean }
            creditEligible: { type: boolean }
            sybilClear: { type: boolean }
            reputationActive: { type: boolean }
        dataSources:
          type: object
          properties:
            trust: { type: boolean }
            delegation: { type: boolean }
            credit: { type: boolean }
            sybil: { type: boolean }
            reputation: { type: boolean }
        summary: { type: string }
        explanation: { type: array, items: { type: string } }
        checksum: { type: string, description: 'SHA-256 checksum over deterministic fields' }

    ErrorResponse:
      type: object
      properties:
        error:
          type: string
        code:
          type: string
        statusCode:
          type: integer
      required: [error]

    ReadinessResponse:
      type: object
      required: [status, service, network, timestamp, algorand]
      properties:
        status:
          type: string
          enum: [ok, degraded]
        service:
          type: string
        network:
          type: string
        timestamp:
          type: string
          format: date-time
        algorand:
          type: object
          required: [connected]
          properties:
            connected:
              type: boolean
            round:
              type: integer
            error:
              type: string

    DelegationResult:
      type: object
      required: [txId, sponsor, agent, amount, round, timestamp]
      properties:
        txId:
          type: string
          description: Algorand transaction ID
        sponsor:
          type: string
          pattern: '^[A-Z2-7]{58}$'
        agent:
          type: string
          pattern: '^[A-Z2-7]{58}$'
        amount:
          type: number
        round:
          type: integer
        timestamp:
          type: integer
          description: Unix seconds

    RevocationResult:
      type: object
      required: [txId, sponsor, agent, round, timestamp]
      properties:
        txId:
          type: string
        sponsor:
          type: string
          pattern: '^[A-Z2-7]{58}$'
        agent:
          type: string
          pattern: '^[A-Z2-7]{58}$'
        round:
          type: integer
        timestamp:
          type: integer