{
  "openapi": "3.1.0",
  "info": {
    "title": "CC Ledger Community API",
    "version": "3.4.0",
    "summary": "Canton Network access for developers and AI agents — no validator, no Daml, just call an API",
    "description": "CC Ledger gives developers and AI agents access to Canton Network through standard REST and MCP interfaces. No validator node, no Daml development, no Canton integration complexity. 15 REST endpoints — 7 action endpoints (including mutual attestation), 1 unified balance endpoint (CC + CCL + on-chain CCL holdings + tier + discounted rates + mint progress), 2 query endpoints, 1 CCL deposit endpoint, 1 chart data endpoint, registration, and stats. 15 MCP tools. Build your app on Canton — every action you submit creates an on-chain record and earns CCL. Hold CCL to reduce your CC cost per action.\n\n## How It Works\n\nEvery action endpoint follows the same flow:\n1. Your app calls one of 7 action endpoints (attest, attest-mutual, transfer, lock, unlock, mint, settle)\n2. CC Ledger creates an immutable on-chain record on Canton MainNet\n3. You receive the on-chain proof (contract ID, transaction ID, ledger offset)\n\n## Action Types\n\n| Action | Use Case | Cost |\n|--------|----------|------|\n| **Attest** | Data integrity proofs, audit trails, compliance records | 2.0 CC |\n| **Attest-Mutual** | Two-party non-repudiation with counterparty co-signature | 5.0 CC |\n| **Transfer** | Asset transfers, token movements, payment records | 2.0 CC |\n| **Lock** | Escrow, collateral, time-locks | 2.0 CC |\n| **Unlock** | Release from escrow, collateral release | 2.0 CC |\n| **Mint** | Token creation, NFT minting, certificate issuance | 2.0 CC |\n| **Settle** | Trade settlement, payment finalization, batch close | 2.0 CC |\n\n## Authentication\n\nAll action, balance, and query endpoints require an API key via the `X-API-Key` header. Get one instantly from the registration endpoint — no approval needed.\n\n## Pricing & Tiers\n\n| Tier | Rate Limit | Access | Cost | How to Get |\n|------|-----------|--------|------|------------|\n| Community | 10 RPM | 50 trial transactions (max 25/day) + query/balance/stats | — | Register at `/register` |\n| Paid | 100 RPM | Unlimited actions + CCL discount rewards | 2.0 CC base per action, 5.0 CC base per mutual (hold CCL for discount) | Send CC to Blueprint validator (auto-credited) |\n\nNew API keys include 50 trial transactions (max 25/day). Trial credits work for all action types — no deposit needed to start. Once exhausted, send CC from your Loop wallet to the Blueprint validator — your balance is auto-credited within seconds. POST /deposit exists as a manual fallback. Every 10 paid actions mints 1 CCL token on-chain to your Canton party. Your discount is based on the CCL you hold in your Canton wallet (cclOnchainHoldings), tracked in real-time from the Canton ledger — discount = 50% * (1 - 1/(1 + cclOnchainHoldings/200)). Two ways to use CCL: HOLD in wallet for discount, or SEND to CC Ledger for 1:1 CC prepay credit. Two ways to use CCL: hold in wallet for discount on CC-priced actions, or send to CC Ledger for 1:1 CC prepay credit that pays for actions. Your discount is shown in GET /balance (cclOnchainHoldings, discountedCostPerAction, discountedCostPerMutual, discountPercent). Billing order: CC (at discounted rate based on on-chain CCL) -> trial credits -> 402. If you receive 402, check GET /balance — if trialCreditsRemaining is 0, send CC to the Blueprint validator; if tier is paid but ccBalance is insufficient, send more CC.\n\n## Rate Limits\n\nRate limit is enforced per API key using a sliding 60-second window. When exceeded, the API returns `429` with a `retryAfterSeconds` field. Wait that many seconds before retrying.\n\nRegistration is limited to 3 requests per IP address per 24 hours.",
    "termsOfService": "https://theblueprint.xyz/terms",
    "contact": {
      "name": "Blueprint Support",
      "url": "https://theblueprint.xyz",
      "email": "contact@theblueprint.xyz"
    },
    "license": {
      "name": "Apache 2.0",
      "identifier": "Apache-2.0"
    },
    "x-llm-discovery": {
      "llmsTxt": "https://ccledger.theblueprint.xyz/llms.txt",
      "llmsFullTxt": "https://ccledger.theblueprint.xyz/llms-full.txt",
      "agentCard": "https://ccledger.theblueprint.xyz/.well-known/agent.json",
      "mcpEndpoint": "https://ccledger.theblueprint.xyz/mcp",
      "mcpToolCount": 15
    }
  },
  "servers": [
    {
      "url": "https://ccledger.theblueprint.xyz",
      "description": "Production (Canton MainNet)"
    }
  ],
  "tags": [
    {
      "name": "Registration",
      "description": "Self-service API key registration. No authentication required."
    },
    {
      "name": "Actions",
      "description": "On-chain actions. Each call creates a Canton contract with cryptographic proof. Requires a valid Community API key."
    },
    {
      "name": "Query",
      "description": "Query past actions and retrieve on-chain proofs. Results are scoped to the authenticated API key."
    },
    {
      "name": "Balance",
      "description": "Prepaid CC balance management. CC deposits are auto-detected when you send CC from your Loop wallet to the Blueprint validator. Check your balance and tier status. Each action deducts from your balance."
    },
    {
      "name": "Stats",
      "description": "Public network statistics. No authentication required."
    },
    {
      "name": "CCL",
      "description": "CCL Builder Rewards — CCL is the CIP-56 builder rewards token for the CC Ledger ecosystem (template: Gateway.CCLToken.CCLToken, package: be3e1f78d0578eb75029a3c8d938fdbb290da4bd797b1b8bc8304afef4941749). 1 CCL minted per 10 CC-paid actions. Discount is based on REAL on-chain wallet holdings (cclOnchainHoldings), tracked in real-time from the Canton ledger. Formula: discount = 50% * (1 - 1/(1 + cclOnchainHoldings/200)). Two ways to use CCL: (1) HOLD in Loop wallet for automatic discount, (2) SEND to CC Ledger for 1:1 CC prepay credit. 1 CCL = 1 CC prepay value (accepted via /ccl/deposit). Billing order: CC (at discounted rate) -> trial -> 402."
    }
  ],
  "paths": {
    "/api/v1/community/register": {
      "post": {
        "operationId": "registerApiKey",
        "x-llm-hint": "Self-service API key registration. No auth needed. Returns key instantly. POST with appName in body. Optional cantonParty field for Canton party ID (from Loop wallet) — required before first CC deposit.",
        "summary": "Register for an API key",
        "description": "Self-service endpoint to obtain a Community-tier API key. No authentication required. Each IP address is limited to 3 registrations per 24 hours.\n\nThe returned API key grants access to all action and query endpoints. Store it securely — it cannot be retrieved again.\n\nOptionally provide your Canton party ID (`cantonParty`) from your Loop wallet at registration. This is the on-chain identity that CC deposits must come from, and where CCL tokens are minted. If not set at registration, the first CC deposit permanently binds the sender party to your API key.",
        "tags": ["Registration"],
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegistrationRequest"
              },
              "examples": {
                "minimal": {
                  "summary": "App name only",
                  "value": {
                    "appName": "my-defi-app"
                  }
                },
                "with_party": {
                  "summary": "With Canton party ID",
                  "value": {
                    "appName": "acme-trading-platform",
                    "cantonParty": "user-party::1220abc123def456..."
                  }
                },
                "full": {
                  "summary": "With all optional fields",
                  "value": {
                    "appName": "acme-trading-platform",
                    "cantonParty": "user-party::1220abc123def456...",
                    "contactEmail": "dev@acme.io"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "API key created successfully.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RegistrationResponse"
                },
                "example": {
                  "apiKey": "cb_community_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
                  "appName": "acme-trading-platform",
                  "cantonParty": "user-party::1220abc123def456...",
                  "tier": "community",
                  "rateLimitRpm": 10,
                  "scopes": ["community:write"],
                  "trialCreditsRemaining": 50,
                  "message": "Store this API key securely -- it will not be shown again. Use it in the X-Api-Key header for all community API requests."
                }
              }
            }
          },
          "400": {
            "description": "Invalid registration request.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" },
                "examples": {
                  "missing": {
                    "summary": "Missing appName",
                    "value": { "error": "bad_request", "message": "appName is required", "status": 400 }
                  },
                  "too_short": {
                    "summary": "appName too short",
                    "value": { "error": "bad_request", "message": "appName must be 3-100 characters", "status": 400 }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Registration rate limit exceeded (3 per IP per 24 hours).",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" },
                "example": {
                  "error": "rate_limit_exceeded",
                  "message": "Maximum 3 registrations per IP per 24 hours",
                  "status": 429
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" },
                "example": {
                  "error": "internal_error",
                  "message": "An unexpected error occurred",
                  "status": 500
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/community/attest": {
      "post": {
        "operationId": "submitAttestation",
        "x-llm-hint": "Record data attestation on Canton MainNet. Requires X-Api-Key. Send appName + dataHash. Returns on-chain proof with contractId.",
        "summary": "Attest data on-chain",
        "description": "Record a data attestation on Canton Network. Use this for data integrity proofs, audit trails, compliance records, and any scenario where you need cryptographic proof that specific data existed at a specific time.\n\nThe `dataHash` field should be a hex-encoded hash (e.g., SHA-256) of the data you are attesting.\n\nUses trial credits (community tier, 50 included) or CC balance (paid tier, 2.0 CC per action). Returns 402 when trial credits exhausted and no CC balance.",
        "tags": ["Actions"],
        "security": [{ "ApiKeyAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/AttestRequest" },
              "example": {
                "appName": "acme-audit-trail",
                "dataOwner": "acme-corp",
                "dataId": "invoice-2026-0042",
                "dataHash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
                "description": "Monthly invoice attestation for March 2026"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Attestation confirmed on-chain.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ActionResponse" },
                "example": {
                  "actionId": "7f3a9b2c-1d4e-5f6a-8b9c-0d1e2f3a4b5c",
                  "actionType": "attest",
                  "status": "confirmed",
                  "appName": "acme-audit-trail",
                  "dataHash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
                  "mintedAt": "2026-03-04T14:30:00.000Z",
                  "proof": {
                    "contractId": "005a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a",
                    "transactionId": "12345abcdef67890",
                    "ledgerOffset": 2001542,
                    "synchronizerId": "global-domain::1220abcdef",
                    "effectiveAt": "2026-03-04T14:30:00.000Z",
                    "packageId": null,
                    "templateId": null
                  },
                  "rewardMarkerCreated": false
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ActionBadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ActionFailed" }
        }
      }
    },
    "/api/v1/community/attest-mutual": {
      "post": {
        "operationId": "submitMutualAttestation",
        "x-llm-hint": "Create mutual attestation proposal on Canton MainNet. Requires X-Api-Key (paid tier). Attestor must co-sign. Costs 5.0 CC.",
        "summary": "Create mutual attestation proposal",
        "description": "Creates an on-chain attestation proposal that requires the attestor party to co-sign via their Canton wallet for non-repudiation. Costs 5.0 CC.\n\nUses trial credits (community tier, 50 included) or CC balance (paid tier, 5.0 CC). Returns 402 when trial credits exhausted and no CC balance.",
        "tags": ["Actions"],
        "security": [{ "ApiKeyAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/MutualAttestRequest" },
              "example": {
                "appName": "acme-audit-trail",
                "dataHash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
                "attestorParty": "attestor::1220abcdef0123456789",
                "dataOwner": "acme-corp",
                "dataId": "contract-2026-0042",
                "description": "Mutual attestation for Q1 audit report",
                "assetType": "amulet"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Mutual attestation proposal created.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/MutualAttestResponse" },
                "example": {
                  "actionId": "8a4b9c3d-2e5f-6a7b-9c0d-1e2f3a4b5c6d",
                  "actionType": "attest-mutual",
                  "status": "pending_signature",
                  "appName": "acme-audit-trail",
                  "dataHash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
                  "mintedAt": "2026-03-18T10:00:00.000Z",
                  "proof": {
                    "contractId": "006b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b",
                    "transactionId": "67890abcdef12345",
                    "ledgerOffset": 2050100,
                    "synchronizerId": "global-domain::1220abcdef",
                    "effectiveAt": "2026-03-18T10:00:00.000Z",
                    "packageId": null,
                    "templateId": null
                  },
                  "proposalContractId": "006b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b",
                  "attestorParty": "attestor::1220abcdef0123456789",
                  "signingInstructions": "The attestor party must exercise the Accept choice on the proposal contract using their Canton wallet to complete the mutual attestation."
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ActionBadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ActionFailed" }
        }
      }
    },
    "/api/v1/community/transfer": {
      "post": {
        "operationId": "submitTransfer",
        "x-llm-hint": "Record asset transfer on Canton MainNet. Requires X-Api-Key. Send sender, recipient, amount. Returns on-chain proof.",
        "summary": "Record an asset transfer",
        "description": "Record an asset or token transfer on Canton Network. Use this for asset movements, token transfers, payment records, and cross-account transfers.\n\nProvide sender, recipient, and asset details. The `dataHash` should be the hash of the full transfer payload.\n\nUses trial credits (community tier, 50 included) or CC balance (paid tier, 2.0 CC per action). Returns 402 when trial credits exhausted and no CC balance.",
        "tags": ["Actions"],
        "security": [{ "ApiKeyAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/TransferRequest" },
              "example": {
                "appName": "acme-trading-platform",
                "sender": "alice-wallet",
                "recipient": "bob-wallet",
                "assetId": "USDC",
                "amount": "1000.00",
                "currency": "USD",
                "dataHash": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
                "reference": "payment-2026-0315"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transfer confirmed on-chain.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ActionResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ActionBadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ActionFailed" }
        }
      }
    },
    "/api/v1/community/lock": {
      "post": {
        "operationId": "submitLock",
        "x-llm-hint": "Record asset lock (escrow/collateral) on Canton MainNet. Requires X-Api-Key. Returns on-chain proof.",
        "summary": "Record an asset lock",
        "description": "Record an asset lock or escrow event on Canton Network. Use this for collateral locks, escrow deposits, time-locks, and any scenario where assets are locked for a duration.\n\nProvide the owner, asset, amount, and optional lock duration.\n\nUses trial credits (community tier, 50 included) or CC balance (paid tier, 2.0 CC per action). Returns 402 when trial credits exhausted and no CC balance.",
        "tags": ["Actions"],
        "security": [{ "ApiKeyAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/LockRequest" },
              "example": {
                "appName": "acme-escrow-app",
                "owner": "escrow-001",
                "assetId": "ETH",
                "amount": "32.0",
                "currency": "ETH",
                "duration": "365d",
                "dataHash": "b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3",
                "reference": "lock-epoch-42"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Lock confirmed on-chain.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ActionResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ActionBadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ActionFailed" }
        }
      }
    },
    "/api/v1/community/unlock": {
      "post": {
        "operationId": "submitUnlock",
        "x-llm-hint": "Record asset unlock on Canton MainNet. Requires X-Api-Key. Returns on-chain proof.",
        "summary": "Record an asset unlock",
        "description": "Record an asset release from lock or escrow on Canton Network. Use this for collateral release, escrow settlements, and unlocking previously locked assets.\n\nProvide the owner, asset, amount, and the `lockId` referencing the original lock.\n\nUses trial credits (community tier, 50 included) or CC balance (paid tier, 2.0 CC per action). Returns 402 when trial credits exhausted and no CC balance.",
        "tags": ["Actions"],
        "security": [{ "ApiKeyAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/UnlockRequest" },
              "example": {
                "appName": "acme-escrow-app",
                "owner": "escrow-001",
                "assetId": "ETH",
                "amount": "32.0",
                "currency": "ETH",
                "lockId": "lock-epoch-42",
                "dataHash": "c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4",
                "reference": "release-epoch-42"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Unlock confirmed on-chain.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ActionResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ActionBadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ActionFailed" }
        }
      }
    },
    "/api/v1/community/mint": {
      "post": {
        "operationId": "submitMint",
        "x-llm-hint": "Record token mint on Canton MainNet. Requires X-Api-Key. Returns on-chain proof.",
        "summary": "Record a token mint",
        "description": "Record a token or NFT minting event on Canton Network. Use this for token creation, NFT minting, certificate issuance, credential generation, and any scenario involving creation of new digital assets.\n\nProvide the owner, token details, amount, and optional metadata.\n\nUses trial credits (community tier, 50 included) or CC balance (paid tier, 2.0 CC per action). Returns 402 when trial credits exhausted and no CC balance.",
        "tags": ["Actions"],
        "security": [{ "ApiKeyAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/MintTokenRequest" },
              "example": {
                "appName": "acme-nft-marketplace",
                "owner": "creator-alice",
                "tokenId": "nft-collection-42-001",
                "tokenName": "Genesis Collection #1",
                "amount": "1",
                "metadata": "{\"collection\":\"genesis\",\"rarity\":\"legendary\"}",
                "dataHash": "d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5",
                "reference": "mint-genesis-001"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Mint confirmed on-chain.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ActionResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ActionBadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ActionFailed" }
        }
      }
    },
    "/api/v1/community/settle": {
      "post": {
        "operationId": "submitSettle",
        "x-llm-hint": "Record trade settlement on Canton MainNet. Requires X-Api-Key. Returns on-chain proof.",
        "summary": "Record a settlement",
        "description": "Record a settlement or finalization event on Canton Network. Use this for trade settlements, payment finalization, batch closings, and any multi-party agreement confirmation.\n\nProvide the parties involved, settlement ID, and amount.\n\nUses trial credits (community tier, 50 included) or CC balance (paid tier, 2.0 CC per action). Returns 402 when trial credits exhausted and no CC balance.",
        "tags": ["Actions"],
        "security": [{ "ApiKeyAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/SettleRequest" },
              "example": {
                "appName": "acme-trading-platform",
                "parties": "alice-trading,bob-trading",
                "settlementId": "trade-2026-0315-001",
                "amount": "50000.00",
                "currency": "USD",
                "dataHash": "e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6",
                "reference": "settlement-batch-42"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Settlement confirmed on-chain.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ActionResponse" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ActionBadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/ActionFailed" }
        }
      }
    },
    "/api/v1/community/actions": {
      "get": {
        "operationId": "queryActions",
        "x-llm-hint": "List past actions. Filterable by appName, actionType. Paginated (max 100). Returns actions with on-chain proof.",
        "summary": "Query actions",
        "description": "Retrieve past actions submitted with your API key. Supports filtering by action type, app name, data owner, status, and reference. Results are ordered by creation time, newest first.",
        "tags": ["Query"],
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "actionType",
            "in": "query",
            "description": "Filter by action type.",
            "required": false,
            "schema": { "$ref": "#/components/schemas/ActionType" },
            "example": "transfer"
          },
          {
            "name": "appName",
            "in": "query",
            "description": "Filter by application name (exact match).",
            "required": false,
            "schema": { "type": "string" },
            "example": "acme-trading-platform"
          },
          {
            "name": "dataOwner",
            "in": "query",
            "description": "Filter by data owner (exact match).",
            "required": false,
            "schema": { "type": "string" }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Filter by action status.",
            "required": false,
            "schema": { "$ref": "#/components/schemas/ActionStatus" }
          },
          {
            "name": "reference",
            "in": "query",
            "description": "Filter by reference identifier (exact match).",
            "required": false,
            "schema": { "type": "string" }
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number (0-indexed).",
            "required": false,
            "schema": { "type": "integer", "minimum": 0, "default": 0 }
          },
          {
            "name": "size",
            "in": "query",
            "description": "Number of results per page.",
            "required": false,
            "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 50 }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of actions matching the specified filters.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": { "$ref": "#/components/schemas/ActionDetail" }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/community/actions/{actionId}": {
      "get": {
        "operationId": "getAction",
        "x-llm-hint": "Get single action by UUID. Returns full details with on-chain proof (contractId, transactionId, ledgerOffset).",
        "summary": "Get a specific action",
        "description": "Retrieve a single action by its unique identifier. Returns the full record including on-chain proof. The action must belong to your API key.",
        "tags": ["Query"],
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [
          {
            "name": "actionId",
            "in": "path",
            "description": "The unique action identifier (UUID) returned when the action was created.",
            "required": true,
            "schema": { "type": "string", "format": "uuid" },
            "example": "7f3a9b2c-1d4e-5f6a-8b9c-0d1e2f3a4b5c"
          }
        ],
        "responses": {
          "200": {
            "description": "Action found.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ActionDetail" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": {
            "description": "Action not found.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" },
                "example": {
                  "error": "not_found",
                  "message": "Action not found",
                  "status": 404
                }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/community/balance": {
      "get": {
        "operationId": "getBalance",
        "x-llm-hint": "Check CC + CCL balance, on-chain CCL holdings (drives discount in real-time), tier, CCL mint progress, and discounted action rates. Shows community vs paid tier, rate limit, CC balance, CCL balance, cclOnchainHoldings (real-time on-chain wallet balance), CCL mint info, and your current discount.",
        "summary": "Get balance (CC + CCL + on-chain holdings + tier + discounted rates)",
        "description": "Returns your current CC balance, CCL balance, on-chain CCL holdings (cclOnchainHoldings — the real-time CCL balance in your Canton wallet that drives your discount), tier, rate limit, base and discounted cost per action, CCL mint progress (cantonParty, cclTotalMinted, cclActionsTowardNext, cclNextMilestone), and recent transaction history. Your discount is based on the CCL you hold in your Canton wallet, tracked in real-time from the Canton ledger via DepositWatcherService. If you transfer CCL away, your discount drops. If you receive more, it increases. This is the unified balance endpoint — no separate CCL balance endpoint needed.",
        "tags": ["Balance"],
        "security": [{ "ApiKeyAuth": [] }],
        "responses": {
          "200": {
            "description": "Current balance and transaction history.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/BalanceResponse" },
                "example": {
                  "tier": "paid",
                  "rateLimitRpm": 100,
                  "ccBalance": 42.5,
                  "costPerAction": 2.0,
                  "costPerMutualAction": 5.0,
                  "discountedCostPerAction": 1.667,
                  "discountedCostPerMutual": 4.167,
                  "discountPercent": "16.7%",
                  "trialCreditsRemaining": 48,
                  "trialCreditsTotal": 50,
                  "trialDailyUsed": 2,
                  "trialDailyLimit": 25,
                  "cclBalance": 100.0,
                  "cclOnchainHoldings": 100.0,
                  "cantonParty": "user-party::1220abcdef0123456789",
                  "cclTotalMinted": 100,
                  "cclActionsTowardNext": 2,
                  "cclNextMilestone": 10,
                  "recentTransactions": [
                    {
                      "id": 1,
                      "type": "deposit",
                      "amount": 50.0,
                      "balanceAfter": 50.0,
                      "referenceId": null,
                      "cantonTxId": "tx-abc123",
                      "description": "CC deposit from Canton tx tx-abc123",
                      "createdAt": "2026-03-04T14:30:00Z"
                    },
                    {
                      "id": 2,
                      "type": "action_debit",
                      "amount": 1.667,
                      "balanceAfter": 48.333,
                      "referenceId": "7f3a9b2c-1d4e-5f6a-8b9c-0d1e2f3a4b5c",
                      "cantonTxId": null,
                      "description": "attest action (1.667 CC, 16.7% CCL discount)",
                      "createdAt": "2026-03-04T14:35:00Z"
                    }
                  ]
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/community/deposit": {
      "post": {
        "operationId": "depositCc",
        "x-llm-hint": "Manual deposit fallback. Primary method: send CC from Loop wallet to Blueprint validator — balance is auto-credited within seconds. Use this endpoint only if auto-detection hasn't caught up.",
        "summary": "Deposit CC to balance (manual fallback)",
        "description": "Manual deposit fallback. The primary deposit method is automatic: send CC from your Loop wallet to the Blueprint validator party, and a background service auto-detects the transfer and credits your balance within seconds.\n\nThis endpoint is for edge cases where auto-detection hasn't processed your transfer yet. Verifies the transaction on the Canton ledger via gRPC Ledger API (extracts amount from contract arguments, verifies sender is signatory, filters non-transfer contract types). Sender party must match your registered cantonParty (403 if mismatch); first deposit binds the sender party if not set at registration. Auto-upgrades community tier to paid tier (100 RPM).\n\n**How to fund**: Buy CC on [Coinbase](https://coinbase.com), transfer to your [Loop wallet](https://loop.canton.network), then send CC to the Blueprint validator party: `blueprint-validator-1::1220daab58adcae026bd2ca7ad95014f678bda3ce2a6f91b744cf3ec3d87f09deeac`. Your balance is auto-credited. Use this endpoint only as a manual fallback.",
        "tags": ["Balance"],
        "security": [{ "ApiKeyAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/DepositRequest" },
              "example": {
                "transactionId": "canton-tx-abc123def456",
                "amount": 50.0
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deposit credited successfully.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/DepositResponse" },
                "example": {
                  "credited": 50.0,
                  "newBalance": 92.5,
                  "tier": "paid",
                  "rateLimitRpm": 100,
                  "message": "Deposited 50.0 CC. Paid tier active (100 RPM)."
                }
              }
            }
          },
          "400": {
            "description": "Invalid deposit request.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" },
                "example": { "error": "bad_request", "message": "transactionId is required", "status": 400 }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": {
            "description": "Sender party mismatch. The Canton transaction sender does not match the registered cantonParty for this API key.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" },
                "example": { "error": "forbidden", "message": "Transaction sender does not match your registered Canton party. Deposits must come from the same Canton party used for your first deposit.", "status": 403 }
              }
            }
          },
          "409": {
            "description": "Transaction already credited.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" },
                "example": { "error": "conflict", "message": "Transaction already credited", "status": 409 }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/community/stats": {
      "get": {
        "operationId": "getStats",
        "x-llm-hint": "Public network statistics. No auth needed. Returns totalActions, activeApps, actionsByType, ledgerSubmissions, ccRevenue, ccDeposits, cclBlueprintTreasury, partyStats, successRate, trialToPaidRate, avgDailyActions7d, weekOverWeekPct, busiestDay, peakHour, avgSecondsToConfirm.",
        "summary": "Get network statistics",
        "description": "Public aggregate statistics for the CC Ledger Community API. No authentication required. Includes total counts and a breakdown by action type.",
        "tags": ["Stats"],
        "security": [],
        "responses": {
          "200": {
            "description": "Current network statistics.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/CommunityStats" },
                "example": {
                  "totalActions": 4820,
                  "confirmedActions": 4790,
                  "rewardMarkersCreated": 0,
                  "activeApps": 42,
                  "actionsByType": {
                    "attest": 1200,
                    "transfer": 1800,
                    "lock": 400,
                    "unlock": 380,
                    "mint": 640,
                    "settle": 400,
                    "ledger-submit": 14
                  },
                  "costPerAction": 2.0,
                  "costPerMutualAction": 5.0,
                  "paidTierRpm": 100,
                  "communityTierRpm": 10,
                  "loyaltyTokensMinted": 0,
                  "registeredKeys": 42,
                  "registeredParties": 8,
                  "cclTotalSupply": 15,
                  "cclBlueprintTreasury": 0,
                  "partyStats": [],
                  "ledgerSubmissions": 14,
                  "confirmedLedgerSubmissions": 14,
                  "successRate": 99.6,
                  "ccRevenue": 45.0,
                  "ccDeposits": 100.0,
                  "trialToPaidRate": 25.0,
                  "avgDailyActions7d": 18.3,
                  "weekOverWeekPct": 12.5,
                  "busiestDay": "2026-03-25",
                  "peakHour": 14,
                  "avgSecondsToConfirm": 3.2
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/community/ccl/deposit": {
      "post": {
        "operationId": "depositCcl",
        "x-llm-hint": "Deposit CCL tokens as CC credit. Requires X-Api-Key.",
        "summary": "Deposit CCL as CC credit",
        "description": "Send CCL to CC Ledger as CC credit at 1 CCL = 1 CC. Your CCL is converted to CC credit on your balance. Note: sending CCL reduces your cclOnchainHoldings, which lowers your discount. Two ways to use CCL: (1) HOLD in wallet for discount, (2) SEND to CC Ledger for 1:1 CC prepay credit.",
        "tags": ["CCL"],
        "security": [{ "ApiKeyAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CclDepositRequest" },
              "example": { "amount": 2.0 }
            }
          }
        },
        "responses": {
          "200": {
            "description": "CCL deposited as CC credit.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/CclDepositResponse" },
                "example": {
                  "cclDeposited": 2.0,
                  "newCcBalance": 44.0,
                  "tier": "paid",
                  "message": "Deposited 2.0 CCL as 2.0 CC credit."
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/ActionBadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/community/chart": {
      "get": {
        "operationId": "getChartData",
        "x-llm-hint": "Public time-series chart data for analytics dashboards. No auth needed. Returns daily counts for community actions, actions by type, ledger submissions, registrations, CCL minted, and active keys.",
        "summary": "Get chart data (time-series)",
        "description": "Public endpoint returning time-series chart data for analytics dashboards. Returns daily counts for community actions, actions by type (including ledger-submit), ledger submissions, registrations, CCL minted, and active keys. No authentication required.",
        "tags": ["Stats"],
        "security": [],
        "parameters": [
          {
            "name": "window",
            "in": "query",
            "description": "Time window for chart data.",
            "required": false,
            "schema": { "type": "string", "enum": ["7d", "30d", "90d"], "default": "30d" },
            "example": "30d"
          }
        ],
        "responses": {
          "200": {
            "description": "Time-series chart data.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ChartResponse" },
                "example": {
                  "window": "30d",
                  "communityActions": [
                    { "date": "2026-03-15", "count": 12 },
                    { "date": "2026-03-16", "count": 8 }
                  ],
                  "actionsByType": {
                    "attest": [
                      { "date": "2026-03-15", "count": 5 },
                      { "date": "2026-03-16", "count": 3 }
                    ],
                    "transfer": [
                      { "date": "2026-03-15", "count": 4 }
                    ]
                  },
                  "ledgerSubmissions": [
                    { "date": "2026-03-15", "count": 2 }
                  ],
                  "registrations": [
                    { "date": "2026-03-15", "count": 3 }
                  ],
                  "cclMinted": [],
                  "activeKeys": [
                    { "date": "2026-03-15", "count": 5 }
                  ]
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Community API key obtained from the `/api/v1/community/register` endpoint. Prefix: `cb_community_`."
      }
    },
    "schemas": {
      "RegistrationRequest": {
        "type": "object",
        "description": "Request body for self-service API key registration.",
        "required": ["appName"],
        "properties": {
          "appName": {
            "type": "string",
            "description": "A name identifying your application. Must be between 3 and 100 characters.",
            "minLength": 3,
            "maxLength": 100,
            "examples": ["my-defi-app", "acme-trading-platform"]
          },
          "cantonParty": {
            "type": "string",
            "description": "Your Canton party ID from your Loop wallet (e.g., user-party::1220abc...). Optional at registration but required before your first CC deposit. CC deposits must come from this party. CCL tokens are minted to this address on-chain. If not set at registration, the first deposit permanently binds the sender party to your API key.",
            "maxLength": 512,
            "examples": ["user-party::1220abc123def456..."]
          },
          "contactEmail": {
            "type": "string",
            "format": "email",
            "description": "Optional contact email for your developer account.",
            "examples": ["dev@acme.io"]
          }
        }
      },
      "RegistrationResponse": {
        "type": "object",
        "description": "Successful registration response containing the new API key with trial credits.",
        "required": ["apiKey", "appName", "tier", "rateLimitRpm", "scopes", "trialCreditsRemaining", "message"],
        "properties": {
          "apiKey": {
            "type": "string",
            "description": "Your API key. Store this securely — it is shown only once and cannot be retrieved again.",
            "examples": ["cb_community_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"]
          },
          "appName": {
            "type": "string",
            "description": "The application name you registered."
          },
          "cantonParty": {
            "type": ["string", "null"],
            "description": "The Canton party ID associated with this API key, or null if not yet set. Required before first CC deposit. CCL tokens are minted to this address.",
            "examples": ["user-party::1220abc123def456...", null]
          },
          "tier": {
            "type": "string",
            "const": "community",
            "description": "The API key tier. Always `community` for self-service registration."
          },
          "rateLimitRpm": {
            "type": "integer",
            "description": "Maximum requests per minute allowed for this key.",
            "examples": [10]
          },
          "scopes": {
            "type": "array",
            "items": { "type": "string" },
            "description": "Permission scopes granted to this key.",
            "examples": [["community:write"]]
          },
          "trialCreditsRemaining": {
            "type": "integer",
            "description": "Number of trial transactions remaining. New keys start with 50.",
            "examples": [50]
          },
          "message": {
            "type": "string",
            "description": "A human-readable message with usage instructions."
          }
        }
      },
      "AttestRequest": {
        "type": "object",
        "description": "Request body for the attest action.",
        "required": ["appName", "dataHash"],
        "properties": {
          "appName": {
            "type": "string",
            "description": "Your application name.",
            "minLength": 1,
            "maxLength": 100
          },
          "dataOwner": {
            "type": "string",
            "description": "Owner of the attested data. Defaults to `appName` if omitted.",
            "maxLength": 255
          },
          "dataId": {
            "type": "string",
            "description": "Unique identifier for the data record being attested.",
            "maxLength": 255
          },
          "dataHash": {
            "type": "string",
            "description": "Hash of the data being attested (e.g., hex-encoded SHA-256).",
            "minLength": 1,
            "maxLength": 128
          },
          "description": {
            "type": "string",
            "description": "Human-readable description of the attestation.",
            "maxLength": 512
          },
          "assetType": {
            "type": "string",
            "description": "CIP-56 asset type classification (e.g., amulet, locked-amulet, or custom type).",
            "maxLength": 50
          }
        }
      },
      "MutualAttestRequest": {
        "type": "object",
        "description": "Request body for the mutual attestation action.",
        "required": ["appName", "dataHash", "attestorParty"],
        "properties": {
          "appName": {
            "type": "string",
            "description": "Your application name.",
            "minLength": 1,
            "maxLength": 100
          },
          "dataHash": {
            "type": "string",
            "description": "Hash of the data being attested (e.g., hex-encoded SHA-256).",
            "minLength": 1,
            "maxLength": 128
          },
          "attestorParty": {
            "type": "string",
            "description": "Canton party ID of the attestor who must co-sign the proposal.",
            "maxLength": 450
          },
          "dataOwner": {
            "type": "string",
            "description": "Owner of the attested data. Defaults to `appName` if omitted.",
            "maxLength": 255
          },
          "dataId": {
            "type": "string",
            "description": "Unique identifier for the data record being attested.",
            "maxLength": 255
          },
          "description": {
            "type": "string",
            "description": "Human-readable description of the attestation.",
            "maxLength": 512
          },
          "assetType": {
            "type": "string",
            "description": "CIP-56 asset type classification (e.g., amulet, locked-amulet, or custom type).",
            "maxLength": 50
          }
        }
      },
      "MutualAttestResponse": {
        "type": "object",
        "description": "Response returned when a mutual attestation proposal is created. The attestor party must co-sign the proposal contract to complete the attestation.",
        "required": ["actionId", "actionType", "status", "appName", "dataHash", "mintedAt", "proof", "proposalContractId", "attestorParty", "signingInstructions"],
        "properties": {
          "actionId": {
            "type": "string",
            "format": "uuid",
            "description": "Unique action identifier."
          },
          "actionType": {
            "type": "string",
            "const": "attest-mutual",
            "description": "The action type for mutual attestations."
          },
          "status": {
            "type": "string",
            "const": "pending_signature",
            "description": "Status is always `pending_signature` until the attestor co-signs."
          },
          "appName": {
            "type": "string",
            "description": "The application name that submitted this action."
          },
          "dataHash": {
            "type": "string",
            "description": "The data hash that was submitted."
          },
          "mintedAt": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "ISO-8601 timestamp of when the proposal was created on-chain."
          },
          "proof": {
            "oneOf": [{ "$ref": "#/components/schemas/OnChainProof" }, { "type": "null" }],
            "description": "On-chain cryptographic proof for the proposal contract."
          },
          "proposalContractId": {
            "type": "string",
            "description": "Canton contract ID of the proposal that the attestor must sign."
          },
          "attestorParty": {
            "type": "string",
            "description": "Canton party ID of the attestor who must co-sign."
          },
          "signingInstructions": {
            "type": "string",
            "description": "Human-readable instructions for completing the mutual attestation."
          }
        }
      },
      "TransferRequest": {
        "type": "object",
        "description": "Request body for the transfer action.",
        "required": ["appName", "dataHash"],
        "properties": {
          "appName": {
            "type": "string",
            "description": "Your application name.",
            "minLength": 1,
            "maxLength": 100
          },
          "sender": {
            "type": "string",
            "description": "Sender identifier.",
            "maxLength": 255
          },
          "recipient": {
            "type": "string",
            "description": "Recipient identifier.",
            "maxLength": 255
          },
          "assetId": {
            "type": "string",
            "description": "Asset or token identifier."
          },
          "amount": {
            "type": "string",
            "description": "Transfer amount as a string to preserve precision."
          },
          "currency": {
            "type": "string",
            "description": "Currency or denomination code."
          },
          "dataHash": {
            "type": "string",
            "description": "Hash of the transfer payload.",
            "minLength": 1,
            "maxLength": 128
          },
          "reference": {
            "type": "string",
            "description": "Application-defined reference identifier for this transfer.",
            "maxLength": 255
          },
          "assetType": {
            "type": "string",
            "description": "CIP-56 asset type classification (e.g., amulet, locked-amulet, or custom type).",
            "maxLength": 50
          }
        }
      },
      "LockRequest": {
        "type": "object",
        "description": "Request body for the lock action.",
        "required": ["appName", "dataHash"],
        "properties": {
          "appName": {
            "type": "string",
            "description": "Your application name.",
            "minLength": 1,
            "maxLength": 100
          },
          "owner": {
            "type": "string",
            "description": "Asset owner.",
            "maxLength": 255
          },
          "assetId": {
            "type": "string",
            "description": "Asset identifier."
          },
          "amount": {
            "type": "string",
            "description": "Amount to lock as a string to preserve precision."
          },
          "currency": {
            "type": "string",
            "description": "Currency or denomination code."
          },
          "duration": {
            "type": "string",
            "description": "Lock duration (e.g., `30d`, `365d`, `6h`)."
          },
          "dataHash": {
            "type": "string",
            "description": "Hash of the lock payload.",
            "minLength": 1,
            "maxLength": 128
          },
          "reference": {
            "type": "string",
            "description": "Application-defined reference identifier for this lock.",
            "maxLength": 255
          },
          "assetType": {
            "type": "string",
            "description": "CIP-56 asset type classification (e.g., amulet, locked-amulet, or custom type).",
            "maxLength": 50
          }
        }
      },
      "UnlockRequest": {
        "type": "object",
        "description": "Request body for the unlock action.",
        "required": ["appName", "dataHash"],
        "properties": {
          "appName": {
            "type": "string",
            "description": "Your application name.",
            "minLength": 1,
            "maxLength": 100
          },
          "owner": {
            "type": "string",
            "description": "Asset owner.",
            "maxLength": 255
          },
          "assetId": {
            "type": "string",
            "description": "Asset identifier."
          },
          "amount": {
            "type": "string",
            "description": "Amount to unlock as a string to preserve precision."
          },
          "currency": {
            "type": "string",
            "description": "Currency or denomination code."
          },
          "lockId": {
            "type": "string",
            "description": "Reference to the original lock (e.g., reference ID from the lock action)."
          },
          "dataHash": {
            "type": "string",
            "description": "Hash of the unlock payload.",
            "minLength": 1,
            "maxLength": 128
          },
          "reference": {
            "type": "string",
            "description": "Application-defined reference identifier for this unlock.",
            "maxLength": 255
          },
          "assetType": {
            "type": "string",
            "description": "CIP-56 asset type classification (e.g., amulet, locked-amulet, or custom type).",
            "maxLength": 50
          }
        }
      },
      "MintTokenRequest": {
        "type": "object",
        "description": "Request body for the mint action.",
        "required": ["appName", "dataHash"],
        "properties": {
          "appName": {
            "type": "string",
            "description": "Your application name.",
            "minLength": 1,
            "maxLength": 100
          },
          "owner": {
            "type": "string",
            "description": "Token owner or creator.",
            "maxLength": 255
          },
          "tokenId": {
            "type": "string",
            "description": "Unique token identifier."
          },
          "tokenName": {
            "type": "string",
            "description": "Human-readable token name."
          },
          "amount": {
            "type": "string",
            "description": "Quantity to mint as a string to preserve precision."
          },
          "metadata": {
            "type": "string",
            "description": "JSON-encoded metadata for the token."
          },
          "dataHash": {
            "type": "string",
            "description": "Hash of the mint payload.",
            "minLength": 1,
            "maxLength": 128
          },
          "reference": {
            "type": "string",
            "description": "Application-defined reference identifier for this mint.",
            "maxLength": 255
          },
          "assetType": {
            "type": "string",
            "description": "CIP-56 asset type classification (e.g., amulet, locked-amulet, or custom type).",
            "maxLength": 50
          }
        }
      },
      "SettleRequest": {
        "type": "object",
        "description": "Request body for the settle action.",
        "required": ["appName", "dataHash"],
        "properties": {
          "appName": {
            "type": "string",
            "description": "Your application name.",
            "minLength": 1,
            "maxLength": 100
          },
          "parties": {
            "type": "string",
            "description": "Comma-separated party identifiers involved in the settlement."
          },
          "settlementId": {
            "type": "string",
            "description": "Unique settlement identifier."
          },
          "amount": {
            "type": "string",
            "description": "Settlement amount as a string to preserve precision."
          },
          "currency": {
            "type": "string",
            "description": "Currency or denomination code."
          },
          "dataHash": {
            "type": "string",
            "description": "Hash of the settlement payload.",
            "minLength": 1,
            "maxLength": 128
          },
          "reference": {
            "type": "string",
            "description": "Application-defined reference identifier for this settlement.",
            "maxLength": 255
          },
          "assetType": {
            "type": "string",
            "description": "CIP-56 asset type classification (e.g., amulet, locked-amulet, or custom type).",
            "maxLength": 50
          }
        }
      },
      "ActionResponse": {
        "type": "object",
        "description": "Response returned by all action endpoints. On success (`status: confirmed`), includes on-chain proof. On ledger failure (`status: failed`), `mintedAt` and `proof` are null. Validation errors (400) return an `ErrorResponse` instead.",
        "required": ["actionId", "actionType", "status", "appName", "dataHash", "mintedAt", "proof", "rewardMarkerCreated"],
        "properties": {
          "actionId": {
            "type": "string",
            "format": "uuid",
            "description": "Unique action identifier."
          },
          "actionType": {
            "$ref": "#/components/schemas/ActionType",
            "description": "The type of action that was submitted."
          },
          "status": {
            "$ref": "#/components/schemas/ActionStatus",
            "description": "Current status of the action."
          },
          "appName": {
            "type": "string",
            "description": "The application name that submitted this action."
          },
          "dataHash": {
            "type": "string",
            "description": "The data hash that was submitted."
          },
          "mintedAt": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "ISO-8601 timestamp of when the action was confirmed on-chain. Null if the action failed."
          },
          "proof": {
            "oneOf": [{ "$ref": "#/components/schemas/OnChainProof" }, { "type": "null" }],
            "description": "On-chain cryptographic proof from Canton Network. Null if the action failed."
          },
          "rewardMarkerCreated": {
            "type": "boolean",
            "description": "Reserved. Always `false`."
          }
        }
      },
      "ActionDetail": {
        "type": "object",
        "description": "Full detail record for a past action, including all metadata and on-chain proof.",
        "required": ["actionId", "actionType", "appName", "dataOwner", "dataId", "dataHash", "description", "reference", "assetType", "status", "proof", "rewardMarkerCreated", "createdAt", "completedAt"],
        "properties": {
          "actionId": {
            "type": "string",
            "format": "uuid",
            "description": "Unique action identifier."
          },
          "actionType": {
            "$ref": "#/components/schemas/ActionType",
            "description": "The type of action."
          },
          "appName": {
            "type": "string",
            "description": "The application that submitted this action."
          },
          "dataOwner": {
            "type": ["string", "null"],
            "description": "The data owner for this action."
          },
          "dataId": {
            "type": ["string", "null"],
            "description": "The client-provided data identifier."
          },
          "dataHash": {
            "type": "string",
            "description": "The hash of the submitted data."
          },
          "description": {
            "type": ["string", "null"],
            "description": "Human-readable description of the action."
          },
          "reference": {
            "type": ["string", "null"],
            "description": "Application-defined reference identifier."
          },
          "status": {
            "$ref": "#/components/schemas/ActionStatus",
            "description": "Current status of the action."
          },
          "proof": {
            "oneOf": [{ "$ref": "#/components/schemas/OnChainProof" }, { "type": "null" }],
            "description": "On-chain proof. Null if the action has not been confirmed."
          },
          "rewardMarkerCreated": {
            "type": "boolean",
            "description": "Reserved. Always `false`."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO-8601 timestamp of when the action was submitted."
          },
          "completedAt": {
            "type": ["string", "null"],
            "format": "date-time",
            "description": "ISO-8601 timestamp of when the action was confirmed or failed. Null if still pending."
          },
          "assetType": {
            "type": ["string", "null"],
            "description": "CIP-56 asset type classification (e.g., amulet, locked-amulet, or custom type).",
            "maxLength": 50
          }
        }
      },
      "OnChainProof": {
        "type": "object",
        "description": "Cryptographic proof from Canton Network confirming the on-chain transaction.",
        "required": ["contractId", "transactionId", "ledgerOffset", "synchronizerId", "effectiveAt"],
        "properties": {
          "contractId": {
            "type": "string",
            "description": "Canton contract identifier for the on-chain record."
          },
          "transactionId": {
            "type": "string",
            "description": "Canton transaction (update) identifier."
          },
          "ledgerOffset": {
            "type": "integer",
            "format": "int64",
            "description": "Ledger offset at which the transaction was recorded."
          },
          "synchronizerId": {
            "type": "string",
            "description": "Canton synchronizer domain identifier."
          },
          "effectiveAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO-8601 timestamp of the transaction effective time on the ledger."
          },
          "packageId": {
            "type": ["string", "null"],
            "description": "Daml package identifier. Null for community action proofs."
          },
          "templateId": {
            "type": ["string", "null"],
            "description": "Fully qualified Daml template identifier. Null for community action proofs."
          }
        }
      },
      "ActionType": {
        "type": "string",
        "enum": ["attest", "attest-mutual", "transfer", "lock", "unlock", "mint", "settle"],
        "description": "The type of on-chain action."
      },
      "ActionStatus": {
        "type": "string",
        "enum": ["pending", "pending_signature", "confirmed", "failed"],
        "description": "Status of an action. `pending`: submitted but not yet confirmed. `pending_signature`: mutual attestation proposal created, awaiting attestor co-signature. `confirmed`: recorded on-chain with proof. `failed`: on-chain transaction failed."
      },
      "ChartPoint": {
        "type": "object",
        "description": "A single data point in a time-series chart.",
        "required": ["date", "count"],
        "properties": {
          "date": { "type": "string", "description": "Date in YYYY-MM-DD format.", "examples": ["2026-03-15"] },
          "count": { "type": "integer", "format": "int64", "description": "Count for this date." }
        }
      },
      "ChartResponse": {
        "type": "object",
        "description": "Time-series chart data for analytics dashboards. Each series contains daily data points within the requested window.",
        "required": ["window", "communityActions", "actionsByType", "ledgerSubmissions", "registrations", "cclMinted", "activeKeys"],
        "properties": {
          "window": { "type": "string", "description": "Resolved time window (7d, 30d, or 90d).", "enum": ["7d", "30d", "90d"] },
          "communityActions": { "type": "array", "items": { "$ref": "#/components/schemas/ChartPoint" }, "description": "Daily community action counts." },
          "actionsByType": { "type": "object", "description": "Daily action counts broken down by type. Keys are action type names (attest, transfer, etc. plus ledger-submit). Only types with data are included.", "additionalProperties": { "type": "array", "items": { "$ref": "#/components/schemas/ChartPoint" } } },
          "ledgerSubmissions": { "type": "array", "items": { "$ref": "#/components/schemas/ChartPoint" }, "description": "Daily ledger submission counts." },
          "registrations": { "type": "array", "items": { "$ref": "#/components/schemas/ChartPoint" }, "description": "Daily API key registration counts." },
          "cclMinted": { "type": "array", "items": { "$ref": "#/components/schemas/ChartPoint" }, "description": "Daily CCL token mint counts." },
          "activeKeys": { "type": "array", "items": { "$ref": "#/components/schemas/ChartPoint" }, "description": "Daily distinct active API key counts." }
        }
      },
      "CommunityStats": {
        "type": "object",
        "description": "Aggregate statistics for the CC Ledger Community API.",
        "required": ["totalActions", "confirmedActions", "rewardMarkersCreated", "activeApps", "actionsByType", "costPerAction", "costPerMutualAction", "paidTierRpm", "communityTierRpm", "loyaltyTokensMinted", "registeredKeys", "registeredParties", "cclTotalSupply", "ledgerSubmissions", "confirmedLedgerSubmissions", "successRate", "trialToPaidRate", "avgDailyActions7d"],
        "properties": {
          "totalActions": {
            "type": "integer",
            "format": "int64",
            "description": "Total number of actions submitted across all apps."
          },
          "confirmedActions": {
            "type": "integer",
            "format": "int64",
            "description": "Number of actions confirmed on-chain."
          },
          "rewardMarkersCreated": {
            "type": "integer",
            "format": "int64",
            "description": "Featured App activity markers submitted (Canton Foundation compliant batches). 0 until Featured App approval."
          },
          "activeApps": {
            "type": "integer",
            "format": "int64",
            "description": "Number of distinct applications that have submitted actions."
          },
          "actionsByType": {
            "type": "object",
            "description": "Action count broken down by action type. Only types with at least one action are included.",
            "additionalProperties": {
              "type": "integer",
              "format": "int64"
            }
          },
          "costPerAction": { "type": "number", "description": "CC cost per standard action (paid tier)." },
          "costPerMutualAction": { "type": "number", "description": "CC cost per mutual attestation (paid tier)." },
          "paidTierRpm": { "type": "integer", "description": "Paid tier rate limit (requests per minute)." },
          "communityTierRpm": { "type": "integer", "description": "Community tier rate limit (requests per minute)." },
          "loyaltyTokensMinted": { "type": "integer", "format": "int64", "description": "Number of CCL on-chain tokens minted for paid actions (1 per 10 paid actions)." },
          "registeredKeys": { "type": "integer", "format": "int64", "description": "Total active API keys registered." },
          "registeredParties": { "type": "integer", "format": "int64", "description": "Distinct Canton parties registered (with cantonParty set)." },
          "cclTotalSupply": { "type": "integer", "format": "int64", "description": "Total CCL tokens minted (total supply)." },
          "cclBlueprintTreasury": { "type": "number", "description": "Total CCL deposited back by users as CC credit." },
          "partyStats": { "type": "array", "description": "Per-party breakdown of actions, spending, and CCL activity.", "items": { "type": "object", "properties": { "cantonParty": { "type": "string" }, "appName": { "type": "string" }, "tier": { "type": "string" }, "totalActions": { "type": "integer" }, "confirmedActions": { "type": "integer" }, "cclEarned": { "type": "integer" }, "ccSpent": { "type": "number" }, "cclOnchainHoldings": { "type": "number" } } } },
          "ledgerSubmissions": { "type": "integer", "format": "int64", "description": "Total ledger submissions (scope-gated /api/v1/ledger/** actions)." },
          "confirmedLedgerSubmissions": { "type": "integer", "format": "int64", "description": "Ledger submissions confirmed on-chain." },
          "successRate": { "type": "number", "description": "Overall success rate as a percentage (confirmed / total across both community actions and ledger submissions)." },
          "ccRevenue": { "type": "number", "description": "Total CC earned from action debits." },
          "ccDeposits": { "type": "number", "description": "Total CC deposited by users." },
          "trialToPaidRate": { "type": "number", "description": "Percentage of trial users who converted to paid tier." },
          "avgDailyActions7d": { "type": "number", "description": "Average daily community actions over the past 7 days." },
          "weekOverWeekPct": { "type": ["number", "null"], "description": "Week-over-week percentage change in community actions. Null if no data in the previous 7-day window." },
          "busiestDay": { "type": ["string", "null"], "description": "Date (YYYY-MM-DD) with the highest community action count. Null if no actions." },
          "peakHour": { "type": ["integer", "null"], "description": "Hour of day (0-23 UTC) with the most community actions. Null if no actions." },
          "avgSecondsToConfirm": { "type": ["number", "null"], "description": "Average seconds between action submission and on-chain confirmation across both community actions and ledger submissions. Null if no confirmed actions." }
        }
      },
      "BalanceResponse": {
        "type": "object",
        "description": "Unified balance response: CC balance, CCL balance, on-chain CCL holdings (drives discount in real-time), trial credits, tier info, CCL mint progress, discounted action rates, and recent transaction history. Your discount is based on the CCL you hold in your Canton wallet (cclOnchainHoldings), tracked in real-time from the Canton ledger.",
        "required": ["tier", "rateLimitRpm", "ccBalance", "costPerAction", "costPerMutualAction", "discountedCostPerAction", "discountedCostPerMutual", "discountPercent", "trialCreditsRemaining", "trialCreditsTotal", "trialDailyUsed", "trialDailyLimit", "cclBalance", "cclOnchainHoldings", "cantonParty", "cclTotalMinted", "cclActionsTowardNext", "cclNextMilestone", "recentTransactions"],
        "properties": {
          "tier": { "type": "string", "description": "Current tier: community or paid.", "enum": ["community", "paid"] },
          "rateLimitRpm": { "type": "integer", "description": "Current rate limit (requests per minute)." },
          "ccBalance": { "type": "number", "description": "Current CC balance." },
          "costPerAction": { "type": "number", "description": "Base CC cost per standard action (before on-chain CCL discount)." },
          "costPerMutualAction": { "type": "number", "description": "Base CC cost per mutual attestation (before on-chain CCL discount)." },
          "discountedCostPerAction": { "type": "number", "description": "Actual CC cost per standard action after discount based on on-chain CCL holdings. This is what you pay." },
          "discountedCostPerMutual": { "type": "number", "description": "Actual CC cost per mutual attestation after discount based on on-chain CCL holdings. This is what you pay." },
          "discountPercent": { "type": "string", "description": "Your current discount percentage based on on-chain CCL holdings (e.g., '16.7%'). Formula: 50% * (1 - 1/(1 + cclOnchainHoldings/200))." },
          "trialCreditsRemaining": { "type": "integer", "description": "Remaining trial transaction credits.", "examples": [50] },
          "trialCreditsTotal": { "type": "integer", "description": "Total trial credits originally granted.", "examples": [50] },
          "trialDailyUsed": { "type": "integer", "description": "Number of trial actions used today (UTC)." },
          "trialDailyLimit": { "type": "integer", "description": "Maximum trial actions allowed per day.", "examples": [25] },
          "cclBalance": { "type": "number", "description": "CCL credit balance (deposited CCL converted to CC Ledger credit).", "examples": [100.0] },
          "cclOnchainHoldings": { "type": "number", "description": "Real-time on-chain CCL balance in your Canton wallet. This drives your discount. Tracked via DepositWatcherService from Canton ledger events. If you transfer CCL away, this drops and your discount decreases. If you receive more, it increases.", "examples": [100.0] },
          "cantonParty": { "type": ["string", "null"], "description": "Your Canton party ID (captured from CC deposit transaction). Null if no deposit has been made." },
          "cclTotalMinted": { "type": "integer", "description": "Total CCL tokens minted to your Canton party." },
          "cclActionsTowardNext": { "type": "integer", "description": "Number of paid actions toward the next CCL mint (resets at 10)." },
          "cclNextMilestone": { "type": "integer", "description": "Actions needed for the next CCL mint (always 10)." },
          "recentTransactions": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/CcTransactionView" },
            "description": "Most recent CC transactions (up to 20)."
          }
        }
      },
      "CcTransactionView": {
        "type": "object",
        "description": "A single CC balance transaction.",
        "required": ["id", "type", "amount", "balanceAfter", "createdAt"],
        "properties": {
          "id": { "type": "integer", "format": "int64" },
          "type": { "type": "string", "description": "Transaction type.", "enum": ["deposit", "action_debit", "adjustment", "action_refund", "trial_debit", "ccl_deposit"] },
          "amount": { "type": "number", "description": "Transaction amount (always positive)." },
          "balanceAfter": { "type": "number", "description": "Balance after this transaction." },
          "referenceId": { "type": ["string", "null"], "description": "Action ID for debits." },
          "cantonTxId": { "type": ["string", "null"], "description": "Canton transaction ID for deposits." },
          "description": { "type": ["string", "null"], "description": "Human-readable description." },
          "createdAt": { "type": "string", "format": "date-time", "description": "When the transaction occurred." }
        }
      },
      "DepositRequest": {
        "type": "object",
        "description": "Request to deposit CC from a Canton transaction.",
        "required": ["transactionId", "amount"],
        "properties": {
          "transactionId": { "type": "string", "description": "Canton transaction ID containing the CC transfer.", "maxLength": 512 },
          "amount": { "type": "number", "description": "Amount of CC to deposit.", "minimum": 1.0 }
        }
      },
      "DepositResponse": {
        "type": "object",
        "description": "Successful deposit response.",
        "required": ["credited", "newBalance", "tier", "rateLimitRpm", "message"],
        "properties": {
          "credited": { "type": "number", "description": "Amount of CC credited." },
          "newBalance": { "type": "number", "description": "New CC balance after deposit." },
          "tier": { "type": "string", "description": "Updated tier." },
          "rateLimitRpm": { "type": "integer", "description": "Updated rate limit." },
          "message": { "type": "string", "description": "Human-readable confirmation." }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "description": "Standard error response body.",
        "required": ["error", "message", "status"],
        "properties": {
          "error": {
            "type": "string",
            "description": "Machine-readable error code.",
            "enum": ["bad_request", "unauthorized", "forbidden", "payment_required", "not_found", "conflict", "rate_limit_exceeded", "internal_error", "not_implemented"]
          },
          "message": {
            "type": "string",
            "description": "Human-readable error description."
          },
          "status": {
            "type": "integer",
            "description": "HTTP status code."
          }
        }
      },
      "CclDepositRequest": {
        "type": "object",
        "description": "Request to deposit CCL tokens as CC Ledger credit.",
        "required": ["amount"],
        "properties": {
          "amount": { "type": "number", "description": "CCL amount to send to CC Ledger as CC credit (1 CCL = 1 CC). Note: sending CCL reduces your on-chain holdings (cclOnchainHoldings), which lowers your discount.", "minimum": 0.01 }
        }
      },
      "CclDepositResponse": {
        "type": "object",
        "description": "Successful CCL deposit response.",
        "required": ["cclDeposited", "newCcBalance", "tier", "message"],
        "properties": {
          "cclDeposited": { "type": "number", "description": "CCL amount deposited." },
          "newCcBalance": { "type": "number", "description": "New CC balance after deposit." },
          "tier": { "type": "string", "description": "Current tier after deposit." },
          "message": { "type": "string", "description": "Human-readable confirmation." }
        }
      }
    },
    "responses": {
      "ActionBadRequest": {
        "description": "Validation failed. Missing or invalid required fields (`appName`, `dataHash`).",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" },
            "examples": {
              "missing_appName": {
                "summary": "Missing appName",
                "value": {
                  "error": "bad_request",
                  "message": "appName is required",
                  "status": 400
                }
              },
              "missing_dataHash": {
                "summary": "Missing dataHash",
                "value": {
                  "error": "bad_request",
                  "message": "dataHash is required",
                  "status": 400
                }
              },
              "dataHash_too_long": {
                "summary": "dataHash exceeds maximum length",
                "value": {
                  "error": "bad_request",
                  "message": "dataHash must not exceed 128 characters",
                  "status": 400
                }
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid API key.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": ["message"],
              "properties": {
                "message": { "type": "string", "description": "Error description." }
              }
            },
            "examples": {
              "missing": {
                "summary": "No API key provided",
                "value": { "message": "Unauthorized" }
              },
              "invalid": {
                "summary": "Invalid or deactivated API key",
                "value": { "message": "Invalid API key" }
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "API key rate limit exceeded. Community keys are limited to 10 requests per minute.",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait before retrying.",
            "schema": { "type": "string", "example": "60" }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": ["message", "retryAfterSeconds"],
              "properties": {
                "message": { "type": "string", "description": "Rate limit error message." },
                "retryAfterSeconds": { "type": "integer", "description": "Seconds to wait before retrying." }
              }
            },
            "example": {
              "message": "Rate limit exceeded",
              "retryAfterSeconds": 60
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "Trial credits exhausted (community tier) or insufficient CC balance (paid tier). Send CC from your Loop wallet to the Blueprint validator (auto-credited) or use POST /api/v1/community/deposit as manual fallback.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorResponse" },
            "example": {
              "error": "payment_required",
              "message": "Trial credits exhausted. Send CC to the Blueprint validator to fund your balance (auto-credited), or use POST /deposit as fallback.",
              "status": 402
            }
          }
        }
      },
      "ActionFailed": {
        "description": "On-chain transaction failed. The action was recorded in the database but the Canton ledger transaction could not be confirmed. Returns an `ActionResponse` with `status: \"failed\"` and null proof.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ActionResponse" },
            "example": {
              "actionId": "7f3a9b2c-1d4e-5f6a-8b9c-0d1e2f3a4b5c",
              "actionType": "attest",
              "status": "failed",
              "appName": "acme-audit-trail",
              "dataHash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
              "mintedAt": null,
              "proof": null,
              "rewardMarkerCreated": false
            }
          }
        }
      }
    }
  },
  "security": [{ "ApiKeyAuth": [] }]
}
