{
  "openapi": "3.1.0",
  "info": {
    "title": "ccledger Community API",
    "version": "3.4.0",
    "summary": "Canton records and payments API — non-custodial. On-chain record actions (attest, attest-mutual, mint) are fully live; value movement is moving to a delegated facilitation flow where you sign from your own wallet (rolling out).",
    "description": "ccledger is a non-custodial records and payments API for Canton Network — no validator node, no Daml development, no keys held by us. 15 REST endpoints — 7 action endpoints (including mutual attestation), a dry-run pricing endpoint (`/quote`), 1 unified balance endpoint (fee credits + CCL discount balance + tier + discounted rates + mint progress), 2 query endpoints, 1 chart data endpoint, registration, and stats. 15 MCP tools.\n\n**Non-custodial — enforced in code.** Prepaid CC is a **non-refundable service credit (\"fee credits\")** that pays per-action fees only and never funds on-chain value movement. There is no withdrawal endpoint — credits are a one-way prepayment, not a custodial balance. The legacy balance-funded value-movement mode on `/transfer`, `/lock`, and `/settle` is **permanently disabled**: those endpoints return `403 non_custodial` today. On-chain value movement is moving to the **delegated facilitation flow** — you keep your own wallet and keys, sign a `CreateTransferCommand` yourself, and ccledger relays and submits it as the featured application (CIP-0104 confirmer). Status: rolling out (not yet callable). `/unlock` remains live and free — it only releases a previously created lock back to its owner.\n\n## How It Works\n\n1. Your app calls an action endpoint\n2. Record actions (attest, attest-mutual, mint) write an immutable on-chain record — fully live today. The disabled balance-funded value endpoints (`/transfer`, `/lock`, `/settle`) return `403 non_custodial`; value movement arrives via the delegated facilitation flow (you sign from your own wallet, ccledger relays)\n3. Live actions return an on-chain proof (contract ID, update/transaction ID, ledger offset)\n\n## Action Types\n\n| Action | Status | What It Does | Cost |\n|--------|--------|--------------|------|\n| **Attest** | Live | Immutable record — audit trails, compliance | 2.0 CC fee (or 1 trial credit) |\n| **Attest-Mutual** | Live | Immutable record — two-party co-signed non-repudiation | 5.0 CC fee (or 1 trial credit) |\n| **Mint** | Live | Immutable issuance/certificate record (non-issuer — not CC) | 2.0 CC fee (or 1 trial credit) |\n| **Unlock** | Live | Release a previously created LockedAmulet back to its owner | Free (no fee) |\n| **Transfer** | Disabled (403) | Balance-funded CC transfer — permanently disabled; replaced by the delegated facilitation flow (rolling out) | — |\n| **Lock** | Disabled (403) | Balance-funded time-locked escrow — permanently disabled; facilitation flow rolling out | — |\n| **Settle** | Disabled (403) | Balance-funded atomic settlement — permanently disabled; facilitation flow rolling out | — |\n\nUse `POST /quote` (authenticated) for a dry-run pricing preview of any action — fee, CCL discount, balance, and affordability — before spending.\n\n## Custody\n\nccledger is non-custodial, and the policy is enforced in code. Prepaid CC fee credits are non-refundable, pay per-action fees only, and never fund on-chain value movement; there is no withdrawal endpoint. When the delegated facilitation flow ships, on-chain CC moves from your own wallet: you sign the `CreateTransferCommand`, ccledger relays and submits it as the featured application (CIP-0104 confirmer). It never takes custody of your CC.\n\n## Authentication\n\nAll action, quote, 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 record actions (max 25/day) + quote/query/balance/stats | — | Register at `/register` |\n| Paid | 100 RPM | Unlimited record actions + CCL discount rewards | 2.0 CC fee per record action, 5.0 CC fee per mutual attestation (hold CCL for discount) | Send CC to the Blueprint validator (auto-credited as fee credits) |\n\nNew API keys include 50 trial credits (max 25/day) that fund record actions only (attest, attest-mutual, mint) — no top-up needed to start. Once exhausted, top up fee credits by sending CC from your Loop wallet to the Blueprint validator — auto-credited within seconds (minimum 1.0 CC; non-refundable; fees only). `POST /deposit` exists as a manual fallback. Your Canton party is bound to your key on the first **verified** top-up (the verified transfer is the proof of control); `cantonParty` at registration is an optional echoed hint only. CCL Builder Rewards is an account-based discount credit: every 10 CC-paid actions earns 1 CCL (trial actions do not count). Hold CCL to reduce your CC fee per action. Your discount is based on the CCL you hold (cclOnchainHoldings), tracked on your ccledger account — discount = 50% * (1 - 1/(1 + cclOnchainHoldings/200)). CCL is never spent on actions; only CC is charged at the discounted rate. Your discount is shown in GET /balance (cclOnchainHoldings, discountedCostPerAction, discountedCostPerMutual, discountPercent). Billing order: fee credits (at discounted rate) -> trial credits -> 402. Fee-insufficient responses are x402/p402-style: the 402 body carries `x402Version: 1` and an `accepts` array (scheme `exact`, network `canton`, asset `CC`, `payTo` = the gateway party) so an agent can pay-and-retry — paying means topping up fee credits by sending CC to the gateway party. If you receive 402, check GET /balance — if trialCreditsRemaining is 0, send CC to the Blueprint validator; if tier is paid but fee credits are 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.xyz/llms.txt",
      "llmsFullTxt": "https://ccledger.xyz/llms-full.txt",
      "agentCard": "https://ccledger.xyz/.well-known/agent.json",
      "mcpEndpoint": "https://ccledger.xyz/mcp",
      "mcpToolCount": 14
    }
  },
  "servers": [
    {
      "url": "https://ccledger.xyz",
      "description": "Production (Canton MainNet)"
    }
  ],
  "tags": [
    {
      "name": "Registration",
      "description": "Self-service API key registration. No authentication required."
    },
    {
      "name": "Actions",
      "description": "On-chain actions. Record actions (attest, attest-mutual, mint) are fully live and write immutable records with an on-chain proof. The balance-funded value endpoints (transfer, lock, settle) are permanently disabled by the enforced non-custodial policy (403 non_custodial); unlock remains live and free. 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": "Fee-credit management. Prepaid CC is a non-refundable service credit that pays per-action fees only — it never funds on-chain value movement and cannot be withdrawn. Top-ups are auto-detected when you send CC from your Loop wallet to the Blueprint validator (your Canton party is bound on the first verified top-up). Check your fee credits and tier status; quote fees before spending."
    },
    {
      "name": "Stats",
      "description": "Public network statistics. No authentication required."
    },
    {
      "name": "CCL",
      "description": "CCL Builder Rewards — CCL is an account-based, non-transferable discount credit for the ccledger ecosystem. Earn 1 CCL per 10 CC-paid actions (trial actions do not count). Hold CCL to reduce your CC cost per action; the discount approaches ~50%. Discount is based on the CCL you hold (cclOnchainHoldings), tracked on your ccledger account. Formula: discount = 50% * (1 - 1/(1 + cclOnchainHoldings/200)). CCL is never spent on actions; only CC is charged at the discounted rate. Your discount is shown in GET /balance. 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 with 50 trial record-action credits (max 25/day). cantonParty is an OPTIONAL echoed hint only — it is NOT bound at registration; your Canton party is bound on your first VERIFIED fee-credit top-up (the verified transfer is the proof of control).",
        "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 record, quote, and query endpoints. Store it securely — it cannot be retrieved again.\n\n`cantonParty` is optional and is an **echoed hint only** — it is not bound to your key at registration (registration is unauthenticated with respect to the party, so binding it then would be unsafe). Your Canton party is bound on your **first verified fee-credit top-up**: a verified on-chain CC transfer from that party, claimed by your authenticated key, is the proof of control. From then on, top-ups must come from the bound party.",
        "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 a verifiable, timestamped on-chain record 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, max 25/day) or fee credits (paid tier, 2.0 CC fee per action, CCL-discountable). Returns an x402-style 402 when trial credits are exhausted and fee credits are insufficient.",
        "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, max 25/day) or fee credits (paid tier, 5.0 CC fee, CCL-discountable). Returns an x402-style 402 when trial credits are exhausted and fee credits are insufficient.",
        "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": "DISABLED — returns 403 non_custodial on every call. The balance-funded transfer mode is permanently disabled: prepaid CC credits pay fees only and never fund value movement. On-chain CC will move from the caller's OWN wallet via the delegated facilitation flow (sender-signed CreateTransferCommand relayed by ccledger) — rolling out, not yet callable. Use record actions (attest, attest-mutual, mint) today; use POST /quote to preview fees.",
        "summary": "Transfer Canton Coin (disabled — non-custodial policy)",
        "description": "**Permanently disabled (403 `non_custodial`).** ccledger is non-custodial, enforced in code: prepaid CC credits pay per-action fees only and never fund on-chain value movement, so the legacy balance-funded transfer mode is permanently disabled. Every call to this endpoint currently returns HTTP 403 with error `non_custodial`.\n\nOn-chain value movement is moving to the **delegated facilitation flow**: you keep your own wallet and keys, sign a `CreateTransferCommand` yourself, and ccledger relays and submits it as the featured application (CIP-0104 confirmer). Status: rolling out — not yet callable.\n\nThe request shape below (recipient, amount, `Idempotency-Key` header, per-call and daily CC caps) is retained for reference and for the facilitation rollout; it is not accepted for balance-funded execution.",
        "tags": [
          "Actions"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Required for real-money actions — a stable client-supplied key so a retried POST cannot double-send. Must match `^[a-zA-Z0-9-]+$` and be at most 64 characters. Returns 400 if missing, blank, or malformed.",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-zA-Z0-9-]+$",
              "maxLength": 64
            },
            "example": "transfer-2026-0315-001"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TransferRequest"
              },
              "example": {
                "appName": "acme-trading-platform",
                "recipient": "payee::1220def4567890abcdef1234567890abcdef1234567890abcdef1234567890abcd",
                "amount": "1.0",
                "dataHash": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
                "reference": "payment-2026-0315"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Transfer confirmed on-chain. (Not returned today — the balance-funded mode is disabled; retained for the facilitation rollout.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ActionResponse"
                }
              }
            }
          },
          "202": {
            "description": "Accepted but not yet reconciled (indeterminate on-chain submit; `status: needs_reconciliation`). Not returned today — the balance-funded mode is disabled; retained for the facilitation rollout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ActionResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ActionBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "description": "Non-custodial policy gate — **always returned today**. The balance-funded /transfer mode is permanently disabled: prepaid CC credits pay per-action fees only and never fund on-chain value movement (403, not 402 — no payment unlocks this mode). On-chain CC moves from your own wallet via the delegated facilitation flow, which is rolling out and not yet callable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "non_custodial",
                  "message": "ccledger is non-custodial: prepaid CC credits pay per-action fees only and never fund on-chain value movement. Balance-funded /transfer is permanently disabled. On-chain CC moves from your own wallet via the delegated facilitation flow (rolling out now — see https://ccledger.xyz/docs). Record actions (attest, attest-mutual, mint records) remain fully available.",
                  "status": 403
                }
              }
            }
          },
          "409": {
            "description": "Conflict — an in-progress Idempotency-Key replay is still resolving, or the gateway's on-chain CC balance is temporarily insufficient. Retry the identical request (same Idempotency-Key) after a short delay.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ActionFailed"
          }
        }
      }
    },
    "/api/v1/community/lock": {
      "post": {
        "operationId": "submitLock",
        "x-llm-hint": "DISABLED — returns 403 non_custodial on every call. Balance-funded locks are permanently disabled: prepaid CC credits pay fees only. On-chain escrow will run from the caller's OWN wallet via the delegated facilitation flow (rolling out, not yet callable). /unlock remains live and free for previously created locks. Use record actions today.",
        "summary": "Lock Canton Coin (disabled — non-custodial policy)",
        "description": "**Permanently disabled (403 `non_custodial`).** ccledger is non-custodial, enforced in code: prepaid CC credits pay per-action fees only and never fund on-chain value movement, so the legacy balance-funded lock (time-locked `LockedAmulet` escrow) is permanently disabled. Every call to this endpoint currently returns HTTP 403 with error `non_custodial`.\n\nValue movement — including escrow — is moving to the **delegated facilitation flow** (you sign from your own wallet; ccledger relays as the featured application). Status: rolling out — not yet callable. `POST /unlock` remains live and free: it releases a previously created lock back to its owner.\n\nThe request shape below (amount, duration, `Idempotency-Key` header, CC caps) is retained for reference and for the facilitation rollout; it is not accepted for balance-funded execution.",
        "tags": [
          "Actions"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Required for real-money actions — a stable client-supplied key so a retried POST cannot double-send. Must match `^[a-zA-Z0-9-]+$` and be at most 64 characters. Returns 400 if missing, blank, or malformed.",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-zA-Z0-9-]+$",
              "maxLength": 64
            },
            "example": "lock-2026-0315-001"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LockRequest"
              },
              "example": {
                "appName": "acme-escrow-app",
                "amount": "1.0",
                "duration": "PT1H",
                "dataHash": "b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3",
                "reference": "escrow-42"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Lock confirmed on-chain. (Not returned today — the balance-funded mode is disabled; retained for the facilitation rollout.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ActionResponse"
                }
              }
            }
          },
          "202": {
            "description": "Accepted but not yet reconciled (indeterminate on-chain submit; `status: needs_reconciliation`). Not returned today — the balance-funded mode is disabled; retained for the facilitation rollout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ActionResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ActionBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "description": "Non-custodial policy gate — **always returned today**. The balance-funded /lock mode is permanently disabled: prepaid CC credits pay per-action fees only and never fund on-chain value movement (403, not 402 — no payment unlocks this mode). On-chain CC moves from your own wallet via the delegated facilitation flow, which is rolling out and not yet callable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "non_custodial",
                  "message": "ccledger is non-custodial: prepaid CC credits pay per-action fees only and never fund on-chain value movement. Balance-funded /lock is permanently disabled. On-chain CC moves from your own wallet via the delegated facilitation flow (rolling out now — see https://ccledger.xyz/docs). Record actions (attest, attest-mutual, mint records) remain fully available.",
                  "status": 403
                }
              }
            }
          },
          "409": {
            "description": "Conflict — an in-progress Idempotency-Key replay is still resolving, or the gateway's on-chain CC balance is temporarily insufficient. Retry the identical request (same Idempotency-Key) after a short delay.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ActionFailed"
          }
        }
      }
    },
    "/api/v1/community/unlock": {
      "post": {
        "operationId": "submitUnlock",
        "x-llm-hint": "LIVE and FREE (no fee). Releases a previously created LockedAmulet back to its owner and restores the balance. NOT affected by the non-custodial 403 gate — it only unwinds an existing lock. Requires X-Api-Key and lockId (from the lock's proof).",
        "summary": "Unlock Canton Coin (live, free)",
        "description": "Release a `LockedAmulet` created by a previous `/lock` and restore the locked CC to its owner. Releases the on-chain lock (`LockedAmulet_UnlockV2`).\n\n**Still live, and free (no action fee)** — unlike `/transfer`, `/lock`, and `/settle`, this endpoint is NOT gated by the non-custodial policy, because it does not fund new value movement: it only releases/unwinds a previously created lock back to its owner.\n\nRequired body field: `lockId` — the `LockedAmulet` contract id returned in the lock's proof. An `Idempotency-Key` header is optional here (validated only if supplied).\n\nReturns 402 for a key that is not paid-tier with a bound Canton party (only such keys can own a lock), 404 if no matching lock is found, or 409 if the lock was already unlocked or is not releasable.",
        "tags": [
          "Actions"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UnlockRequest"
              },
              "example": {
                "appName": "acme-escrow-app",
                "lockId": "00a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
                "dataHash": "c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4",
                "reference": "release-42"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Unlock confirmed on-chain.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ActionResponse"
                }
              }
            }
          },
          "202": {
            "description": "Accepted but not yet reconciled. The on-chain release was ambiguous (indeterminate outcome); returns an `ActionResponse` with `status: needs_reconciliation`. The caller is not credited — the reconciliation sweeper resolves it (fail-closed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ActionResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ActionBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "404": {
            "description": "No lock found for this API key with the supplied `lockId`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "not_found",
                  "message": "No lock found for this API key with lockId ...",
                  "status": 404
                }
              }
            }
          },
          "409": {
            "description": "Conflict — the lock was already unlocked or is not releasable (e.g. still `unlocking`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "conflict",
                  "message": "Lock already unlocked",
                  "status": 409
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ActionFailed"
          }
        }
      }
    },
    "/api/v1/community/mint": {
      "post": {
        "operationId": "submitMint",
        "x-llm-hint": "Record an issuance/certificate on-chain. NON-ISSUER: does NOT mint Canton Coin or a tradeable token — writes an immutable record. Requires X-Api-Key. Returns on-chain proof.",
        "summary": "Record an issuance",
        "description": "Record an issuance or certificate on-chain. **ccledger is a non-issuer** — this does NOT mint Canton Coin (only the DSO can) or a tradeable token. It writes an immutable on-chain record for issuance and certificate use cases.\n\nProvide the owner, token/certificate details, amount, and optional metadata.\n\nCost: 2.0 CC fee (paid tier, CCL-discountable) or 1 trial credit. Returns an x402-style 402 when trial credits are exhausted and fee credits are insufficient.",
        "tags": [
          "Actions"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MintTokenRequest"
              },
              "example": {
                "appName": "acme-issuer",
                "owner": "issuer-acme",
                "tokenId": "cert-2026-001",
                "tokenName": "Compliance Certificate",
                "amount": "1",
                "metadata": "{\"type\":\"certificate\",\"scope\":\"kyc\"}",
                "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": "DISABLED — returns 403 non_custodial on every call. Balance-funded settlement is permanently disabled: prepaid CC credits pay fees only. Atomic settlement will run from the caller's OWN wallet via the delegated facilitation flow (rolling out, not yet callable). Use record actions today.",
        "summary": "Settle Canton Coin (disabled — non-custodial policy)",
        "description": "**Permanently disabled (403 `non_custodial`).** ccledger is non-custodial, enforced in code: prepaid CC credits pay per-action fees only and never fund on-chain value movement, so the legacy balance-funded settlement (token-standard allocation flow) is permanently disabled. Every call to this endpoint currently returns HTTP 403 with error `non_custodial`.\n\nValue movement is moving to the **delegated facilitation flow** (you sign from your own wallet; ccledger relays as the featured application, the CIP-0104 confirmer). Status: rolling out — not yet callable.\n\nThe request shape below (recipient party in `parties`, amount, `Idempotency-Key` header, allowlist, CC caps) is retained for reference and for the facilitation rollout; it is not accepted for balance-funded execution.",
        "tags": [
          "Actions"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "Required for real-money actions — a stable client-supplied key so a retried POST cannot double-send. Must match `^[a-zA-Z0-9-]+$` and be at most 64 characters. Returns 400 if missing, blank, or malformed.",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-zA-Z0-9-]+$",
              "maxLength": 64
            },
            "example": "settle-2026-0315-001"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SettleRequest"
              },
              "example": {
                "appName": "acme-trading-platform",
                "parties": "payee::1220abcdef1234567890abcdef1234567890abcdef1234567890abcdef123456",
                "settlementId": "trade-2026-0315-001",
                "amount": "1.0",
                "dataHash": "e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6",
                "reference": "settlement-batch-42"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Settlement confirmed on-chain. (Not returned today — the balance-funded mode is disabled; retained for the facilitation rollout.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ActionResponse"
                }
              }
            }
          },
          "202": {
            "description": "Accepted but not yet reconciled (indeterminate on-chain submit; `status: needs_reconciliation`). Not returned today — the balance-funded mode is disabled; retained for the facilitation rollout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ActionResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ActionBadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "description": "Non-custodial policy gate — **always returned today**. The balance-funded /settle mode is permanently disabled: prepaid CC credits pay per-action fees only and never fund on-chain value movement (403, not 402 — no payment unlocks this mode). On-chain CC moves from your own wallet via the delegated facilitation flow, which is rolling out and not yet callable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": "non_custodial",
                  "message": "ccledger is non-custodial: prepaid CC credits pay per-action fees only and never fund on-chain value movement. Balance-funded /settle is permanently disabled. On-chain CC moves from your own wallet via the delegated facilitation flow (rolling out now — see https://ccledger.xyz/docs). Record actions (attest, attest-mutual, mint records) remain fully available.",
                  "status": 403
                }
              }
            }
          },
          "409": {
            "description": "Conflict — an in-progress Idempotency-Key replay is still resolving, or the gateway's on-chain CC balance is temporarily insufficient. Retry the identical request (same Idempotency-Key) after a short delay.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/ActionFailed"
          }
        }
      }
    },
    "/api/v1/community/quote": {
      "post": {
        "operationId": "quoteAction",
        "x-llm-hint": "Dry-run pricing preview — learn the exact CC fee, CCL discount, fee-credit balance, and affordability BEFORE spending. Read-only, no side effects. Requires X-Api-Key. For transfer/lock/settle the note states the non_custodial gate (those endpoints return 403 today).",
        "summary": "Quote an action (dry-run pricing preview)",
        "description": "Dry-run pricing preview for an action. Returns the base fee, the CCL-discounted fee you would actually pay, your discount percent, the CCL you hold, your fee-credit balance, and whether you can afford the fee — before spending anything. Read-only; no side effects.\n\nFor `transfer`, `lock`, and `settle` the response `note` states the non-custodial gate: the balance-funded mode is disabled (403 `non_custodial`), prepaid credits pay fees only, and on-chain value moves from your own wallet via the delegated facilitation flow (rolling out). Quoting a gated action does not imply it is callable.\n\nPass `amount` for transfer/lock/settle to have it echoed back as `moveAmount` (the on-chain value, distinct from the fee).",
        "tags": [
          "Balance"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              },
              "examples": {
                "record_action": {
                  "summary": "Quote an attest",
                  "value": {
                    "actionType": "attest"
                  }
                },
                "gated_value_action": {
                  "summary": "Quote a (gated) transfer",
                  "value": {
                    "actionType": "transfer",
                    "amount": 25.0
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Pricing preview.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuoteResponse"
                },
                "examples": {
                  "record_action": {
                    "summary": "attest with 100 CCL held",
                    "value": {
                      "actionType": "attest",
                      "currency": "CC",
                      "baseFee": 2.0,
                      "fee": 1.667,
                      "discountPercent": "16.7%",
                      "cclOnchainHoldings": 100.0,
                      "balance": 42.5,
                      "sufficientForFee": true,
                      "moveAmount": null,
                      "note": null
                    }
                  },
                  "gated_value_action": {
                    "summary": "transfer — gated by the non-custodial policy",
                    "value": {
                      "actionType": "transfer",
                      "currency": "CC",
                      "baseFee": 2.0,
                      "fee": 2.0,
                      "discountPercent": "0.0%",
                      "cclOnchainHoldings": 0,
                      "balance": 10.0,
                      "sufficientForFee": true,
                      "moveAmount": 25.0,
                      "note": "NON-CUSTODIAL: balance-funded transfer is disabled (403 non_custodial) — prepaid credits pay fees only. On-chain value moves from your own wallet via the delegated facilitation flow (rolling out). Record actions (attest, attest-mutual, mint) are fully available."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid actionType — must be one of attest, attest-mutual, transfer, lock, unlock, mint, settle.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/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 balance, your CCL discount balance (drives discount), tier, CCL mint progress, and discounted action rates. Shows community vs paid tier, rate limit, CC balance, CCL balance, cclOnchainHoldings (your CCL discount balance), CCL mint info, and your current discount.",
        "summary": "Get balance (CC + CCL discount balance + tier + discounted rates)",
        "description": "Returns your current CC balance, CCL balance, your CCL discount balance (cclOnchainHoldings — the CCL you hold 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, tracked on your ccledger account. The more CCL you hold, the larger your discount. This is the unified balance endpoint.",
        "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": "Top up FEE CREDITS (manual fallback). Prepaid CC is a NON-REFUNDABLE service credit that pays per-action fees only — never a custodial value balance, never withdrawable. Primary method: send CC (min 1.0) from your own Loop wallet to the Blueprint validator — fee credits auto-credit within seconds and your Canton party is bound on that first VERIFIED top-up. Use this endpoint only if auto-detection hasn't caught up.",
        "summary": "Top up fee credits (manual fallback)",
        "description": "Top up your **fee credits** — the non-refundable prepaid CC that pays per-action fees only. Fee credits never fund on-chain value movement and cannot be withdrawn (there is no withdrawal endpoint; credits are a one-way prepayment, not a custodial balance).\n\nThe primary top-up method is automatic: send CC from your Loop wallet to the Blueprint validator party, and a background watcher auto-detects the transfer and credits your fee credits within seconds. This endpoint is a manual fallback for edge cases where auto-detection hasn't processed your transfer yet.\n\nVerifies the transaction on the Canton ledger via gRPC Ledger API (extracts the amount from contract arguments, verifies the sender is a signatory, filters non-transfer contract types). Minimum top-up: 1.0 CC (below-minimum verified amounts are rejected). Your Canton party is **bound to your key on the first verified top-up** — the verified transfer from that party is the proof of control (`cantonParty` at registration is an echoed hint only). After binding, the sender party must match (403 on mismatch). 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`. Fee credits auto-credit. 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": "Fee credits topped up 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 top-up request (missing/invalid transactionId or amount, verified amount below the 1.0 CC minimum, or amount not confirmable on-chain).",
            "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 Canton party bound to this API key by its first verified top-up.",
            "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 ccledger 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/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 — an echoed hint only. It is NOT bound to your key at registration: your Canton party is bound on your first VERIFIED fee-credit top-up (the verified on-chain transfer from that party is the proof of control), and CCL Builder Rewards attach to the bound party.",
            "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 cantonParty you supplied, echoed back (hint only — not bound until your first verified fee-credit top-up).",
            "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 proof for the proposal contract (contract ID, update/transaction ID, ledger offset)."
          },
          "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",
          "recipient",
          "amount"
        ],
        "properties": {
          "appName": {
            "type": "string",
            "description": "Your application name.",
            "minLength": 1,
            "maxLength": 100
          },
          "sender": {
            "type": "string",
            "description": "Optional label. ccledger facilitates the transfer of your own CC that you authorize over the token standard (non-custodial).",
            "maxLength": 255
          },
          "recipient": {
            "type": "string",
            "description": "Destination Canton party ID that receives the CC. Required for a real transfer.",
            "maxLength": 255
          },
          "assetId": {
            "type": "string",
            "description": "Optional reference label. Real transfers move Canton Coin (CC)."
          },
          "amount": {
            "type": "string",
            "description": "Amount of Canton Coin (CC) to send, as a string to preserve precision. Required for a real transfer."
          },
          "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",
          "amount",
          "duration"
        ],
        "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 of Canton Coin (CC) to lock, as a string to preserve precision. Required."
          },
          "currency": {
            "type": "string",
            "description": "Currency or denomination code."
          },
          "duration": {
            "type": "string",
            "description": "Lock duration — integer seconds or ISO-8601 (e.g. `3600` or `PT1H`). Required."
          },
          "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",
          "lockId"
        ],
        "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": "Optional/ignored — the locked amount is restored automatically from the original lock."
          },
          "currency": {
            "type": "string",
            "description": "Currency or denomination code."
          },
          "lockId": {
            "type": "string",
            "description": "The `LockedAmulet` contract id returned in the lock's proof. Required."
          },
          "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": "Owner/creator recorded in the issuance record.",
            "maxLength": 255
          },
          "tokenId": {
            "type": "string",
            "description": "Unique token identifier."
          },
          "tokenName": {
            "type": "string",
            "description": "Human-readable name for the issuance/certificate record."
          },
          "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",
          "parties",
          "amount"
        ],
        "properties": {
          "appName": {
            "type": "string",
            "description": "Your application name.",
            "minLength": 1,
            "maxLength": 100
          },
          "parties": {
            "type": "string",
            "description": "The recipient Canton party (must be a gateway-hosted, allowlisted party for single-leg settle). Required."
          },
          "settlementId": {
            "type": "string",
            "description": "Unique settlement identifier."
          },
          "amount": {
            "type": "string",
            "description": "Amount of Canton Coin (CC) to settle, as a string to preserve precision. Required."
          },
          "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 proof from Canton Network (contract ID, update/transaction ID, ledger offset). 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": "On-chain proof from Canton Network confirming the transaction, verifiable against the Canton participant.",
        "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",
          "needs_reconciliation",
          "unlocking",
          "unlocked"
        ],
        "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. `needs_reconciliation`: a real transfer/lock/unlock/settle submit was ambiguous (indeterminate on-chain outcome) and is awaiting the reconciliation sweeper — not refunded. `unlocking`: an unlock has claimed the lock row and is releasing it on-chain. `unlocked`: the lock was released and the CC restored to the caller's balance."
      },
      "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 Builder Rewards earned counts."
          },
          "activeKeys": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChartPoint"
            },
            "description": "Daily distinct active API key counts."
          }
        }
      },
      "CommunityStats": {
        "type": "object",
        "description": "Aggregate statistics for the ccledger 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": "Reserved legacy counter — always 0. ccledger does not emit activity markers; under CIP-0104, featured-app rewards accrue to the app as the confirmer of real transfers, not from markers."
          },
          "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 Builder Rewards earned for paid actions (1 per 10 CC-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 Builder Rewards earned across all accounts."
          },
          "cclBlueprintTreasury": {
            "type": "number",
            "description": "Reserved CCL discount-credit balance held by Blueprint."
          },
          "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: fee-credit balance (non-refundable prepaid CC that pays per-action fees only — never a withdrawable value balance), CCL balance, your CCL discount balance (drives discount), trial credits, tier info, CCL mint progress, discounted action rates, and recent transaction history. Your discount is based on the CCL you hold (cclOnchainHoldings), tracked on your ccledger account.",
        "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 fee-credit balance in CC — non-refundable prepaid credits that pay per-action fees only. Never funds on-chain value movement and cannot be withdrawn."
          },
          "costPerAction": {
            "type": "number",
            "description": "Base CC cost per standard action (before CCL discount)."
          },
          "costPerMutualAction": {
            "type": "number",
            "description": "Base CC cost per mutual attestation (before CCL discount)."
          },
          "discountedCostPerAction": {
            "type": "number",
            "description": "Actual CC cost per standard action after discount based on the CCL you hold. This is what you pay."
          },
          "discountedCostPerMutual": {
            "type": "number",
            "description": "Actual CC cost per mutual attestation after discount based on the CCL you hold. This is what you pay."
          },
          "discountPercent": {
            "type": "string",
            "description": "Your current discount percentage based on the CCL you hold (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": "Your CCL Builder Rewards balance.",
            "examples": [
              100.0
            ]
          },
          "cclOnchainHoldings": {
            "type": "number",
            "description": "Your CCL discount balance — the CCL you hold that drives your discount. Tracked on your ccledger account. The more CCL you hold, the larger your discount.",
            "examples": [
              100.0
            ]
          },
          "cantonParty": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your Canton party ID, bound on your first verified fee-credit top-up (the verified transfer is the proof of control). Null until then."
          },
          "cclTotalMinted": {
            "type": "integer",
            "description": "Total CCL Builder Rewards earned on your account."
          },
          "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",
              "withdrawal",
              "lock_release",
              "ccl_redeem",
              "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 credit a fee-credit top-up from a Canton transaction (manual fallback for the automatic watcher).",
        "required": [
          "transactionId",
          "amount"
        ],
        "properties": {
          "transactionId": {
            "type": "string",
            "description": "Canton transaction ID containing the CC transfer to the Blueprint validator.",
            "maxLength": 512
          },
          "amount": {
            "type": "number",
            "description": "CC amount of the top-up (minimum 1.0 CC). The credited amount is the on-chain verified amount.",
            "minimum": 1.0
          }
        }
      },
      "DepositResponse": {
        "type": "object",
        "description": "Successful fee-credit top-up response.",
        "required": [
          "credited",
          "newBalance",
          "tier",
          "rateLimitRpm",
          "message"
        ],
        "properties": {
          "credited": {
            "type": "number",
            "description": "CC amount credited to your fee credits (non-refundable; pays per-action fees only)."
          },
          "newBalance": {
            "type": "number",
            "description": "New fee-credit balance after the top-up."
          },
          "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."
          }
        }
      },
      "QuoteRequest": {
        "type": "object",
        "description": "Request body for the dry-run pricing preview.",
        "required": [
          "actionType"
        ],
        "properties": {
          "actionType": {
            "type": "string",
            "enum": [
              "attest",
              "attest-mutual",
              "transfer",
              "lock",
              "unlock",
              "mint",
              "settle"
            ],
            "description": "The action to price."
          },
          "amount": {
            "type": [
              "number",
              "null"
            ],
            "description": "For transfer/lock/settle: the CC amount you would move or lock — echoed back as moveAmount. Ignored for record actions and unlock."
          }
        }
      },
      "QuoteResponse": {
        "type": "object",
        "description": "Dry-run pricing preview — the exact fee, CCL discount, and affordability for an action, with no side effects.",
        "required": [
          "actionType",
          "currency",
          "baseFee",
          "fee",
          "discountPercent",
          "balance",
          "sufficientForFee"
        ],
        "properties": {
          "actionType": {
            "type": "string",
            "description": "The action quoted."
          },
          "currency": {
            "type": "string",
            "const": "CC",
            "description": "Fee currency — always CC."
          },
          "baseFee": {
            "type": "number",
            "description": "Base CC fee before CCL discount (0 for unlock — it is free)."
          },
          "fee": {
            "type": "number",
            "description": "The CCL-discounted CC fee you would actually pay."
          },
          "discountPercent": {
            "type": "string",
            "description": "Your current CCL discount, e.g. '16.7%'."
          },
          "cclOnchainHoldings": {
            "type": [
              "number",
              "null"
            ],
            "description": "The CCL you hold that drives the discount."
          },
          "balance": {
            "type": "number",
            "description": "Your current fee-credit balance in CC."
          },
          "sufficientForFee": {
            "type": "boolean",
            "description": "Whether you can afford the fee (paid tier: fee credits >= fee; community tier: trial credits remaining)."
          },
          "moveAmount": {
            "type": [
              "number",
              "null"
            ],
            "description": "For transfer/lock/settle: the amount echoed from the request (the on-chain value distinct from the fee). Null otherwise."
          },
          "note": {
            "type": [
              "string",
              "null"
            ],
            "description": "Context note. For transfer/lock/settle it states the non_custodial gate: the balance-funded mode is disabled (403) and value moves from your own wallet via the delegated facilitation flow (rolling out)."
          }
        }
      }
    },
    "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": "Fee-insufficient: trial credits exhausted (community tier) or fee credits insufficient (paid tier). The body is x402/p402-style — it includes `x402Version: 1` and an `accepts` array (scheme `exact`, network `canton`, asset `CC`, `payTo` = the gateway party) so an agent client can pay-and-retry. Paying = topping up fee credits by sending CC from your Loop wallet to the `payTo` gateway party (auto-credited), or use POST /api/v1/community/deposit as a manual fallback.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "description": "x402/p402-compliant 402 body — a superset of ErrorResponse (same error/message/status) plus x402Version and the accepts array, so an agent client can pay-and-retry with no bespoke integration. Paying means topping up fee credits by sending CC to the payTo gateway party, then retrying the request.",
              "required": [
                "error",
                "message",
                "status",
                "x402Version",
                "accepts"
              ],
              "properties": {
                "error": {
                  "type": "string",
                  "const": "payment_required",
                  "description": "Machine-readable error code."
                },
                "message": {
                  "type": "string",
                  "description": "Human-readable explanation of the shortfall."
                },
                "status": {
                  "type": "integer",
                  "const": 402
                },
                "x402Version": {
                  "type": "integer",
                  "const": 1,
                  "description": "x402 protocol version."
                },
                "accepts": {
                  "type": "array",
                  "description": "x402 payment options. Paying = topping up fee credits by sending CC to the payTo party, then retrying.",
                  "items": {
                    "type": "object",
                    "required": [
                      "scheme",
                      "network",
                      "asset",
                      "resource",
                      "payTo",
                      "description",
                      "maxTimeoutSeconds"
                    ],
                    "properties": {
                      "scheme": {
                        "type": "string",
                        "const": "exact"
                      },
                      "network": {
                        "type": "string",
                        "const": "canton"
                      },
                      "asset": {
                        "type": "string",
                        "const": "CC"
                      },
                      "maxAmountRequired": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "description": "The fundable CC amount as a decimal string (the fee shortfall); null when not quantifiable."
                      },
                      "resource": {
                        "type": "string",
                        "description": "The API resource the payment funds (e.g. /api/v1/community/attest)."
                      },
                      "payTo": {
                        "type": "string",
                        "description": "The gateway (Blueprint validator) Canton party to send CC to. Sending CC here tops up your fee credits."
                      },
                      "description": {
                        "type": "string",
                        "description": "Human-readable payment instruction."
                      },
                      "maxTimeoutSeconds": {
                        "type": "integer",
                        "description": "Suggested maximum time to complete payment and retry (300)."
                      }
                    }
                  }
                }
              }
            },
            "example": {
              "error": "payment_required",
              "message": "Trial credits exhausted. Send CC to the Blueprint validator to fund your fee credits (auto-credited), or use POST /deposit as fallback.",
              "status": 402,
              "x402Version": 1,
              "accepts": [
                {
                  "scheme": "exact",
                  "network": "canton",
                  "asset": "CC",
                  "maxAmountRequired": "2.0",
                  "resource": "/api/v1/community/attest",
                  "payTo": "blueprint-validator-1::1220daab58adcae026bd2ca7ad95014f678bda3ce2a6f91b744cf3ec3d87f09deeac",
                  "description": "Fund this action by sending CC to the Blueprint validator party, then retry.",
                  "maxTimeoutSeconds": 300
                }
              ]
            }
          }
        }
      },
      "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": []
    }
  ]
}
