{
  "generated_from": "live_platform_state",
  "community_reviews": {
    "review_rule": "One review per account per project: a text (3 to 2000 characters, required). Creating a second review for the same project is refused with community_review_exists. Creating seals the review to the ledger and returns its ledger_seq. Updating is partial through the update route: only the fields sent change (text and receipt_id); receipt_id sent as null clears the binding, and a sent receipt_id is verified again at update time. Every update that changes the text or the binding retains the prior version — its text, its binding state and the time it was superseded — in the review's edit history, which is exposed to the review's owner only; the public view shows the current text, an edited marker and the edit count, never the prior texts.",
    "binding_rule": "A review may bind to one sealed record: receipt_id, when sent, must be the ledger sequence number of a sealed record (a positive integer, sent as a number or as decimal text), verified at post time with the read-only ledger lookup — the same existence-check pattern the community profiles use for their agent binding. If no ledger entry exists with that sequence number, the binding is refused with community_review_receipt_not_found and nothing is stored. The record keeps only the verified reference and the verified-at timestamp — never any receipt internals — and a bound review renders with the mark bound to a sealed record and a link to the ledger entry. An unbound review carries receipt_id: null and renders as an unbound review.",
    "sorting_rule": "In every list, bound reviews sort above unbound reviews; within each group, reviews order oldest first, then by id. The ordering is never sold and no other signal moves it.",
    "isolation_rule": "Tenant isolation is the threat model: no tenant reads or writes another tenant's private state; reviews on a private project read exactly like absence (404) to everyone but the project's owner and the review's author through their owner surfaces; public review lists and public review JSON are the only public surface; the reviewer tenant and the project owner tenant never appear in any output.",
    "rating_rule": "A review may carry an optional star rating: rating, when sent, must be a JSON integer from 1 to 5; 0, 6, 2.5, \"5\" and true are all refused with community_review_invalid and nothing is stored. A review sent without a rating carries rating: null and keeps working exactly as before rating existed. On update the rating is partial: unsent leaves it unchanged, a valid integer replaces it, and null clears it. Every update that changes the rating retains the prior rating in the edit history, alongside the prior text and binding state, exposed to the review's owner only.",
    "aggregation_rule": "Every rating aggregate prints its count n as rated_count. No average is printed without an n: when rated_count is 0 the average is null and surfaces render no ratings yet, never 0. When rated_count is above 0 the average is the mean of the rated reviews rounded to two decimal places, printed beside its n and the full distribution across 1, 2, 3, 4 and 5 stars.",
    "weighting_rule": "The headline aggregate is unweighted: every rated review counts equally. Bound-receipt weighting: none is blended into the headline — bound and unbound aggregates are published as separate slices, each with its own rated_count and average, so the bound signal is visible without hidden multipliers. Account-age weighting: none — account age never moves any aggregate.",
    "callable_surfaces": [
      "GET /v1/community-reviews (free, no key) — this index document: the review rule, the binding rule, the sorting rule, the isolation rule, the callable surfaces and the live usage counts",
      "POST /v1/projects/projects/{id}/reviews (account key, owner only) — post this account's one review of a project: a text (3 to 2000 characters), an optional rating (a JSON integer from 1 to 5) and an optional receipt_id naming a sealed ledger record by its sequence number, verified at post time",
      "GET /v1/projects/projects/{id}/reviews (free, no key) — list the reviews of one public project, bound reviews first: only when the project is public and its owner account is live; reviews on a private project read exactly like absence",
      "GET /v1/projects/projects/{id}/reviews/{review_id} (free, no key) — read one review of a public project, under the same visibility rule",
      "POST /v1/projects/projects/{id}/reviews/{review_id} (account key, owner only) — update this account's own review of a project: only the fields sent change (text, receipt_id, rating; rating sent as null clears it); the prior version is retained in the edit history; a review that is not this account's reads exactly like absence",
      "GET /v1/community-reviews/me (account key, owner only) — list this account's own reviews in full, including the retained edit history and reviews on private projects",
      "GET /v1/services (free, no key) — the full services catalog"
    ]
  },
  "usage": {
    "reviews_total": 0,
    "public_reviews": 0,
    "bound_reviews": 0,
    "unbound_reviews": 0,
    "rated_reviews": 0
  },
  "ratings": {
    "rated_count": 0,
    "average": null,
    "distribution": {
      "1": 0,
      "2": 0,
      "3": 0,
      "4": 0,
      "5": 0
    },
    "bound": {
      "rated_count": 0,
      "average": null
    },
    "unbound": {
      "rated_count": 0,
      "average": null
    }
  },
  "pricing_url": "https://www.a2ally.com/pricing",
  "page": "/community-reviews",
  "note": "Every number in this document is computed live from the platform state at the moment of the read — the usage counts from the community reviews of live accounts (sandbox and canary accounts excluded). Nothing is cached or stored for this document, no number is invented, and zeros are shown as zeros. The public document shows aggregate counts only — never a tenant, a credential or a review. Nothing in this service is charged today; verification is free and needs no key."
}