Skip to content

Commit 2b95ff6

Browse files
doc(indexer): API doc added (#93)
1 parent 4567fdd commit 2b95ff6

File tree

1 file changed

+265
-0
lines changed

1 file changed

+265
-0
lines changed

docs/indexer-api.md

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
# Indexer API
2+
3+
The indexer API exposes indexed blockchain data from the Open Creator Rails contracts via **GraphQL** and **SQL** endpoints. It is powered by [Ponder](https://ponder.sh) and runs as a dedicated read-only node decoupled from the indexing worker.
4+
5+
## Endpoints
6+
7+
| Network | Base URL |
8+
|---|---|
9+
| Sepolia (testnet) | `https://indexer-api-production-c33d.up.railway.app` |
10+
11+
| Endpoint | Description |
12+
|---|---|
13+
| `GET /` | GraphQL playground (browser UI) |
14+
| `POST /graphql` | GraphQL API |
15+
| `GET /sql/*` | Direct SQL queries via Ponder's SQL endpoint |
16+
| `GET /ready` | Health check — returns `200` when API is live |
17+
18+
---
19+
20+
## GraphQL API
21+
22+
### Explorer
23+
24+
Open the GraphQL playground in your browser:
25+
26+
```
27+
https://indexer-api-production-c33d.up.railway.app/
28+
```
29+
30+
### Making requests
31+
32+
```bash
33+
curl -X POST https://indexer-api-production-c33d.up.railway.app/graphql \
34+
-H "Content-Type: application/json" \
35+
-d '{"query": "{ assets { items { id address owner } } }"}'
36+
```
37+
38+
---
39+
40+
## Data Model
41+
42+
### Entities (mutable state)
43+
44+
#### `AssetEntity`
45+
46+
Current state of each asset contract created through the registry.
47+
48+
| Field | Type | Description |
49+
|---|---|---|
50+
| `id` | `String` | Primary key — `{chainId}_{assetAddress}` |
51+
| `chainId` | `Int` | Chain ID (e.g. `11155111` for Sepolia) |
52+
| `assetId` | `String` | Registry-assigned asset ID |
53+
| `address` | `String` | Asset contract address |
54+
| `registryAddress` | `String` | AssetRegistry contract that created it |
55+
| `owner` | `String` | Current owner address |
56+
57+
**Example query — fetch all assets by owner:**
58+
```graphql
59+
{
60+
assets(where: { owner: "0xYourAddress" }) {
61+
items {
62+
id
63+
assetId
64+
address
65+
chainId
66+
}
67+
}
68+
}
69+
```
70+
71+
---
72+
73+
#### `Subscription`
74+
75+
Current subscription state per asset–subscriber pair. One row per unique `(asset, subscriber)` — not per nonce. Preserves the original `startTime` across mid-subscription term changes.
76+
77+
| Field | Type | Description |
78+
|---|---|---|
79+
| `id` | `String` | Primary key — `{AssetEntity.id}_{subscriber}` |
80+
| `chainId` | `Int` | Chain ID |
81+
| `assetId` | `String` | Links to `AssetEntity.id` |
82+
| `subscriber` | `String` | `bytes32` subscriber identity hash |
83+
| `payer` | `String` | Address that paid (latest nonce) |
84+
| `startTime` | `BigInt` | Unix timestamp — original start of unbroken subscription |
85+
| `endTime` | `BigInt` | Unix timestamp — current expiry |
86+
| `nonce` | `BigInt` | Latest on-chain nonce (increments when terms change) |
87+
| `isActive` | `Boolean` | `false` when revoked or cancelled |
88+
89+
**Example query — check if a subscriber has an active subscription:**
90+
```graphql
91+
{
92+
subscriptions(where: { subscriber: "0x...", isActive: true }) {
93+
items {
94+
assetId
95+
startTime
96+
endTime
97+
payer
98+
}
99+
}
100+
}
101+
```
102+
103+
**Example query — all active subscriptions for an asset:**
104+
```graphql
105+
{
106+
subscriptions(where: { assetId: "{chainId}_{assetAddress}", isActive: true }) {
107+
items {
108+
subscriber
109+
payer
110+
startTime
111+
endTime
112+
}
113+
}
114+
}
115+
```
116+
117+
---
118+
119+
### Event history (immutable)
120+
121+
All event tables follow the same ID format: `{chainId}-{txHash}-{logIndex}`.
122+
123+
#### `AssetRegistry_AssetCreated`
124+
125+
Emitted when a new Asset contract is deployed through the registry.
126+
127+
| Field | Type |
128+
|---|---|
129+
| `assetId` | `String` |
130+
| `asset` | `String` — asset contract address |
131+
| `subscriptionPrice` | `BigInt` |
132+
| `tokenAddress` | `String` — payment token |
133+
| `owner` | `String` |
134+
| `registryAddress` | `String` |
135+
| `blockNumber` | `BigInt` |
136+
| `blockTimestamp` | `BigInt` |
137+
138+
---
139+
140+
#### `Asset_SubscriptionAdded`
141+
142+
Emitted when a new subscription is purchased.
143+
144+
| Field | Type |
145+
|---|---|
146+
| `subscriber` | `String` |
147+
| `payer` | `String` |
148+
| `startTime` | `BigInt` |
149+
| `endTime` | `BigInt` |
150+
| `nonce` | `BigInt` |
151+
| `assetAddress` | `String` |
152+
| `blockNumber` | `BigInt` |
153+
| `blockTimestamp` | `BigInt` |
154+
155+
---
156+
157+
#### `Asset_SubscriptionExtended`
158+
159+
Emitted when an existing subscription's end time is extended.
160+
161+
| Field | Type |
162+
|---|---|
163+
| `subscriber` | `String` |
164+
| `endTime` | `BigInt` — new expiry |
165+
| `assetAddress` | `String` |
166+
| `blockNumber` | `BigInt` |
167+
| `blockTimestamp` | `BigInt` |
168+
169+
---
170+
171+
#### `Asset_SubscriptionRevoked` / `Asset_SubscriptionCancelled`
172+
173+
Emitted when a subscription ends early (revoked by owner, or cancelled by subscriber).
174+
175+
| Field | Type |
176+
|---|---|
177+
| `subscriber` | `String` |
178+
| `assetAddress` | `String` |
179+
| `blockNumber` | `BigInt` |
180+
| `blockTimestamp` | `BigInt` |
181+
182+
---
183+
184+
#### `Asset_SubscriptionPriceUpdated`
185+
186+
| Field | Type |
187+
|---|---|
188+
| `newSubscriptionPrice` | `BigInt` |
189+
| `assetAddress` | `String` |
190+
| `blockNumber` | `BigInt` |
191+
| `blockTimestamp` | `BigInt` |
192+
193+
---
194+
195+
#### `Asset_CreatorFeeClaimed`
196+
197+
| Field | Type |
198+
|---|---|
199+
| `subscriber` | `String` |
200+
| `amount` | `BigInt` |
201+
| `assetAddress` | `String` |
202+
| `blockNumber` | `BigInt` |
203+
| `blockTimestamp` | `BigInt` |
204+
205+
---
206+
207+
#### `AssetRegistry_RegistryFeeClaimedBatch`
208+
209+
| Field | Type |
210+
|---|---|
211+
| `assetId` | `String` |
212+
| `totalAmount` | `BigInt` |
213+
| `registryAddress` | `String` |
214+
| `blockNumber` | `BigInt` |
215+
| `blockTimestamp` | `BigInt` |
216+
217+
---
218+
219+
#### `AssetRegistry_RegistryFeeShareUpdated`
220+
221+
| Field | Type |
222+
|---|---|
223+
| `newRegistryFeeShare` | `BigInt` |
224+
| `registryAddress` | `String` |
225+
| `blockNumber` | `BigInt` |
226+
| `blockTimestamp` | `BigInt` |
227+
228+
---
229+
230+
## SQL endpoint
231+
232+
The `/sql` endpoint allows direct SQL queries against the indexed data. Useful for analytics and complex joins.
233+
234+
```bash
235+
curl "https://indexer-api-production-c33d.up.railway.app/sql/SELECT%20*%20FROM%20ocr_indexer.subscription%20WHERE%20is_active%20%3D%20true%20LIMIT%2010"
236+
```
237+
238+
Table names follow the pattern `ocr_indexer.<table_name>` where table names are the snake_case equivalents of the schema definitions (e.g. `asset_entity`, `subscription`, `asset_subscription_added`).
239+
240+
---
241+
242+
## Pagination
243+
244+
All GraphQL list queries support cursor-based pagination:
245+
246+
```graphql
247+
{
248+
subscriptions(limit: 20, after: "cursor_from_previous_response") {
249+
items { ... }
250+
pageInfo {
251+
hasNextPage
252+
endCursor
253+
}
254+
}
255+
}
256+
```
257+
258+
---
259+
260+
## Notes
261+
262+
- All `BigInt` values are returned as strings to avoid JavaScript integer overflow
263+
- All addresses are lowercase hex strings
264+
- `blockTimestamp` is a Unix timestamp in seconds
265+
- The API serves from a stable views schema — it remains available during indexer redeploys (zero downtime)

0 commit comments

Comments
 (0)