Every list view, every dashboard, every "what does this org look like right now" answer in Poa is a GraphQL query against a subgraph. The contracts emit events. The subgraph indexes them into queryable data. The frontend (and the pop CLI, and any tool you might build) reads from it. There is no separate backend server holding the data. The indexer is the only "API."
This is what lets a community-owned organization stay verifiable. The data is not ours to gatekeep. It is emitted on-chain by contracts in POP, indexed by an open-source subgraph in subgraph-pop, and queryable by anyone.
About 25 contract types across the protocol. The full schema is in the subgraph repo. The high-level domains:
The subgraph is hosted on The Graph Studio at two deployment slugs:
poa-arb-v-1 for Arbitrum One (the identity home chain)poa-gnosis-v-1 for Gnosis (the default org-deployment chain)The frontend automatically routes queries to the right slug based on the org's chain. For browse pages that need to look across every chain, it queries each subgraph separately and merges results client-side.
If you want to query directly, the endpoints are listed on the protocol dashboard.
Two reasons.
First: no data lock-in. If we shut down tomorrow, every organization's data would still be readable. The smart contracts keep running. The indexer keeps doing its job. Anyone could spin up a new frontend against the same data. The platform can be replaced. The underlying rails cannot.
Second: no private state. A traditional backend would have decisions, member rosters, and treasury logs in a database that only the platform can read. With a subgraph, every member can verify every claim the UI makes. The indexer's data matches what is on-chain. What is on-chain matches what the contracts say.
You do not have to be inside the Poa app to read the data. The subgraph endpoints are public. A few things people do with this:
pop CLI does exactly this.The full schema, queryable entity list, and event-to-entity mapping live in subgraph-pop. New deployments happen automatically on merges to that repo's main branch.
A few things to keep in mind if you are building against the subgraph:
{contractAddress}-{numericId}. Contracts expect just the numeric portion. The frontend's services/web3/utils/encoding.js has parseTaskId, parseProjectId, parseModuleId helpers for this round-trip. Wrong format produces silent contract reverts.util/formatToken.js.bytes32. The frontend resolves CIDs through Pinata.