{
	"openapi": "3.1.0",
	"info": {
		"title": "musegod.org API",
		"version": "1.0.0",
		"description": "Public, read-only JSON about the MUSEGOD token on Robinhood Chain: holder payouts, a wallet's rewards and the transactions that claim them, the bell, holders, the community wall and wallet names. No keys. Versioning: the version is in the path (/api/v1/...), every response carries an API-Version header, and the unversioned /api/... paths are aliases of the current version. v1 only changes additively; a breaking change ships as /api/v2, and v1 then sends Deprecation and Sunset headers for at least 90 days. Errors are JSON: {error, message, hint, docs, status}.",
		"contact": {
			"name": "MUSEGOD",
			"url": "https://musegod.org/docs"
		}
	},
	"servers": [
		{
			"url": "https://musegod.org"
		}
	],
	"paths": {
		"/api/v1/offering": {
			"get": {
				"operationId": "getOffering",
				"tags": ["Payouts"],
				"summary": "Payouts so far",
				"description": "Everything the home page shows about payouts: MUSEGOD streamed to holders so far, the current 24h stream, the biggest and latest distribute() receipts, daily totals, the biggest claims, bell ringers and the latest onchain events. Cached 5 minutes.",
				"responses": {
					"200": {
						"description": "Cached 5 minutes at the edge.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Offering"
								}
							}
						},
						"headers": {
							"API-Version": {
								"$ref": "#/components/headers/API-Version"
							}
						}
					},
					"503": {
						"$ref": "#/components/responses/Unavailable"
					}
				}
			}
		},
		"/api/v1/rewards": {
			"get": {
				"operationId": "getRewards",
				"tags": ["Wallets"],
				"summary": "A wallet's rewards",
				"description": "A wallet's unclaimed MUSEGOD (earned), its balance and all it has claimed so far, by address or .eth name, with the transactions that claim it, ready to sign. Cached 5 minutes per wallet.",
				"parameters": [
					{
						"name": "q",
						"in": "query",
						"required": true,
						"description": "An address or a .eth name.",
						"schema": {
							"type": "string",
							"maxLength": 120
						},
						"example": "ralxz.eth"
					}
				],
				"responses": {
					"200": {
						"description": "Cached 5 minutes at the edge.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Rewards"
								}
							}
						},
						"headers": {
							"API-Version": {
								"$ref": "#/components/headers/API-Version"
							}
						}
					},
					"400": {
						"$ref": "#/components/responses/BadQuery"
					},
					"404": {
						"$ref": "#/components/responses/NotFound"
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"503": {
						"$ref": "#/components/responses/Unavailable"
					}
				}
			}
		},
		"/api/v1/bell": {
			"get": {
				"operationId": "getBell",
				"tags": ["Payouts"],
				"summary": "Bell status",
				"description": "Whether distribute() would go through right now (a free simulation), and the transaction that rings it. Whoever rings gets a tip of 0.5% of the buyback; with ?to= the tip goes to that address. Cached a minute.",
				"parameters": [
					{
						"name": "to",
						"in": "query",
						"required": false,
						"description": "Who gets the tip. Leave it out and the tip goes to whoever sends the transaction.",
						"schema": {
							"type": "string",
							"pattern": "^0x[0-9a-fA-F]{40}$"
						}
					}
				],
				"responses": {
					"200": {
						"description": "Cached a minute.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Bell"
								}
							}
						},
						"headers": {
							"API-Version": {
								"$ref": "#/components/headers/API-Version"
							}
						}
					},
					"400": {
						"$ref": "#/components/responses/BadQuery"
					},
					"503": {
						"$ref": "#/components/responses/Unavailable"
					}
				}
			}
		},
		"/api/v1/holders": {
			"get": {
				"operationId": "getHolders",
				"tags": ["Community"],
				"summary": "Top holders",
				"description": "The 50 largest holders that are wallets, not contracts, and the holder count. With ?a= it adds those wallets' balances and ranks (1 is the biggest bag).",
				"responses": {
					"200": {
						"description": "Cached 5 minutes at the edge.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Holders"
								}
							}
						},
						"headers": {
							"API-Version": {
								"$ref": "#/components/headers/API-Version"
							}
						}
					},
					"503": {
						"$ref": "#/components/responses/Unavailable"
					}
				},
				"parameters": [
					{
						"name": "a",
						"in": "query",
						"required": false,
						"description": "Comma-separated addresses, up to 100, to get balances for.",
						"schema": {
							"type": "string"
						},
						"example": "0x00a839de7922491683f547a67795204763ff8237"
					}
				]
			}
		},
		"/api/v1/faithful": {
			"get": {
				"operationId": "getFaithful",
				"tags": ["Community"],
				"summary": "The community wall",
				"description": "Bell ringers and top holders merged into one list, with balances, ring counts and a profile card per wallet.",
				"responses": {
					"200": {
						"description": "Cached 5 minutes at the edge, 1 minute while cards are missing.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Faithful"
								}
							}
						},
						"headers": {
							"API-Version": {
								"$ref": "#/components/headers/API-Version"
							}
						}
					},
					"503": {
						"$ref": "#/components/responses/Unavailable"
					}
				}
			}
		},
		"/api/v1/names": {
			"get": {
				"operationId": "getNames",
				"tags": ["Wallets"],
				"summary": "Names and profiles",
				"description": "For each wallet: its OpenSea username or ENS name, picture, banner, bio, the NFT it wears as a PFP and a profile score. Cached a day per wallet.",
				"parameters": [
					{
						"name": "a",
						"in": "query",
						"required": true,
						"description": "Comma-separated addresses, up to 100.",
						"schema": {
							"type": "string"
						},
						"example": "0x00a839de7922491683f547a67795204763ff8237"
					}
				],
				"responses": {
					"200": {
						"description": "Each name is cached a day at the edge.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Names"
								}
							}
						},
						"headers": {
							"API-Version": {
								"$ref": "#/components/headers/API-Version"
							}
						}
					},
					"400": {
						"$ref": "#/components/responses/BadQuery"
					}
				}
			}
		},
		"/api/v1/standing": {
			"get": {
				"operationId": "getStanding",
				"tags": ["Muses"],
				"summary": "Presale standing",
				"description": "One wallet's standing for the MUSEGOD Muses presale: whether it is on the list (before Wednesday's lock) or chosen (after), why, its presale mints, Altar offerings made (read live, up to 3), and its total with public. The list itself is never returned. Cached a minute per address.",
				"parameters": [
					{
						"name": "address",
						"in": "query",
						"required": false,
						"description": "The wallet. Pass this or name.",
						"schema": {
							"type": "string",
							"pattern": "^0x[0-9a-fA-F]{40}$"
						}
					},
					{
						"name": "name",
						"in": "query",
						"required": false,
						"description": "An ENS name, resolved on Ethereum mainnet.",
						"schema": {
							"type": "string"
						}
					},
					{
						"name": "summary",
						"in": "query",
						"required": false,
						"description": "Only the list's totals and how the chosen were chosen: { asOf, locked, counts, groups: [{ id, label, wallets, mints }], communities }. Each wallet counts once, under its first group.",
						"schema": {
							"type": "string"
						}
					}
				],
				"responses": {
					"200": {
						"description": "Cached a minute.",
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Standing"
								}
							}
						},
						"headers": {
							"API-Version": {
								"$ref": "#/components/headers/API-Version"
							}
						}
					},
					"400": {
						"$ref": "#/components/responses/BadQuery"
					},
					"404": {
						"$ref": "#/components/responses/NotFound"
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					},
					"503": {
						"$ref": "#/components/responses/Unavailable"
					}
				}
			}
		}
	},
	"components": {
		"schemas": {
			"Error": {
				"type": "object",
				"additionalProperties": false,
				"required": ["error", "message", "hint", "docs", "status"],
				"properties": {
					"error": {
						"type": "string",
						"enum": [
							"invalid_query",
							"not_found",
							"unknown_endpoint",
							"rate_limited",
							"upstream_unavailable"
						]
					},
					"message": {
						"type": "string"
					},
					"hint": {
						"type": "string"
					},
					"docs": {
						"type": "string"
					},
					"status": {
						"type": "integer"
					},
					"reason": {
						"type": "string"
					}
				}
			},
			"Day": {
				"type": "object",
				"additionalProperties": false,
				"required": ["day", "streamed"],
				"properties": {
					"day": {
						"type": "string",
						"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
					},
					"streamed": {
						"type": "string",
						"pattern": "^[0-9]+$",
						"description": "An 18-decimal token amount in wei, as a decimal string."
					}
				}
			},
			"Receipt": {
				"type": "object",
				"additionalProperties": false,
				"required": ["tx", "block", "time", "streamed", "wethIn", "bounty", "ringer"],
				"properties": {
					"no": {
						"type": "integer",
						"minimum": 1
					},
					"tx": {
						"type": "string",
						"pattern": "^0x[0-9a-fA-F]{64}$"
					},
					"block": {
						"type": "integer"
					},
					"time": {
						"type": "integer",
						"minimum": 0,
						"description": "Unix seconds."
					},
					"streamed": {
						"type": "string",
						"pattern": "^[0-9]+$",
						"description": "An 18-decimal token amount in wei, as a decimal string."
					},
					"wethIn": {
						"type": "string",
						"pattern": "^[0-9]+$",
						"description": "An 18-decimal token amount in wei, as a decimal string."
					},
					"bounty": {
						"type": "string",
						"pattern": "^[0-9]+$",
						"description": "An 18-decimal token amount in wei, as a decimal string."
					},
					"ringer": {
						"type": ["string", "null"],
						"pattern": "^0x[0-9a-f]{40}$"
					}
				}
			},
			"FeedItem": {
				"type": "object",
				"additionalProperties": false,
				"required": ["kind", "who", "amount", "tx", "block", "time"],
				"properties": {
					"kind": {
						"type": "string",
						"enum": ["rang", "claimed", "blessed"]
					},
					"who": {
						"type": "string",
						"pattern": "^0x[0-9a-f]{40}$"
					},
					"amount": {
						"type": "string",
						"pattern": "^[0-9]+$",
						"description": "An 18-decimal token amount in wei, as a decimal string."
					},
					"tx": {
						"type": "string",
						"pattern": "^0x[0-9a-fA-F]{64}$"
					},
					"block": {
						"type": "integer"
					},
					"time": {
						"type": "integer",
						"minimum": 0,
						"description": "Unix seconds."
					}
				}
			},
			"Ringer": {
				"type": "object",
				"additionalProperties": false,
				"required": ["address", "rings", "god"],
				"properties": {
					"address": {
						"type": "string",
						"pattern": "^0x[0-9a-f]{40}$"
					},
					"rings": {
						"type": "integer",
						"minimum": 1
					},
					"god": {
						"type": "boolean"
					}
				}
			},
			"Offering": {
				"type": "object",
				"additionalProperties": false,
				"required": [
					"calls",
					"claims",
					"blessings",
					"streamed",
					"totalNotified",
					"firstBlockTime",
					"owed",
					"claimed",
					"streaming",
					"streamEnds",
					"streamAt",
					"supply",
					"latest",
					"best",
					"days",
					"feed",
					"ringers",
					"highlights",
					"peak"
				],
				"properties": {
					"calls": {
						"type": "integer",
						"description": "distribute() calls so far (bell rings)."
					},
					"claims": {
						"type": "integer"
					},
					"blessings": {
						"type": "integer",
						"description": "Claims the keeper sent for a holder (claimFor)."
					},
					"streamed": {
						"type": "string",
						"pattern": "^[0-9]+$",
						"description": "MUSEGOD that has reached holders (claimed plus owed)."
					},
					"totalNotified": {
						"type": "string",
						"pattern": "^[0-9]+$",
						"description": "All MUSEGOD ever added to the holder stream."
					},
					"firstBlockTime": {
						"type": ["integer", "null"]
					},
					"owed": {
						"type": ["string", "null"],
						"pattern": "^[0-9]+$"
					},
					"claimed": {
						"type": ["string", "null"],
						"pattern": "^[0-9]+$"
					},
					"streaming": {
						"type": ["string", "null"],
						"pattern": "^[0-9]+$",
						"description": "Still to flow in the current 24h stream."
					},
					"streamEnds": {
						"type": ["integer", "null"]
					},
					"streamAt": {
						"type": ["integer", "null"]
					},
					"supply": {
						"type": ["string", "null"],
						"pattern": "^[0-9]+$"
					},
					"latest": {
						"oneOf": [
							{
								"$ref": "#/components/schemas/Receipt"
							},
							{
								"type": "null"
							}
						]
					},
					"best": {
						"oneOf": [
							{
								"$ref": "#/components/schemas/Day"
							},
							{
								"type": "null"
							}
						]
					},
					"days": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/Day"
						},
						"description": "Oldest first, from the first day with a stream."
					},
					"feed": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/FeedItem"
						},
						"description": "Newest first."
					},
					"ringers": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/Ringer"
						},
						"description": "First ring first."
					},
					"priceUsd": {
						"type": ["number", "null"],
						"description": "MUSEGOD in USD from DexScreener, or null when it didn't answer."
					},
					"highlights": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/FeedItem"
						},
						"description": "The biggest claims and blessings since launch, biggest first."
					},
					"peak": {
						"oneOf": [
							{
								"$ref": "#/components/schemas/Receipt"
							},
							{
								"type": "null"
							}
						],
						"description": "The biggest single payout since launch."
					}
				}
			},
			"Rewards": {
				"type": "object",
				"additionalProperties": false,
				"required": ["address", "name", "earned", "balance", "claimed", "claim"],
				"properties": {
					"address": {
						"type": "string",
						"pattern": "^0x[0-9a-fA-F]{40}$"
					},
					"name": {
						"type": ["string", "null"]
					},
					"earned": {
						"type": "string",
						"pattern": "^[0-9]+$",
						"description": "An 18-decimal token amount in wei, as a decimal string."
					},
					"balance": {
						"type": "string",
						"pattern": "^[0-9]+$",
						"description": "An 18-decimal token amount in wei, as a decimal string."
					},
					"claimed": {
						"type": ["string", "null"],
						"pattern": "^[0-9]+$",
						"description": "Every MUSEGOD this wallet has claimed so far, in wei; null when the logs couldn't be read."
					},
					"claim": {
						"$ref": "#/components/schemas/ClaimTxs"
					}
				}
			},
			"Names": {
				"type": "object",
				"additionalProperties": false,
				"required": ["names"],
				"properties": {
					"names": {
						"type": "object",
						"propertyNames": {
							"type": "string",
							"pattern": "^0x[0-9a-f]{40}$"
						},
						"additionalProperties": {
							"$ref": "#/components/schemas/Card"
						},
						"description": "A card per wallet. A wallet whose lookup failed is left out."
					}
				}
			},
			"Card": {
				"type": "object",
				"additionalProperties": false,
				"required": ["name", "image", "banner", "bio", "nft", "score"],
				"properties": {
					"name": {
						"type": ["string", "null"],
						"description": "OpenSea username, else ENS name, else null."
					},
					"image": {
						"type": ["string", "null"],
						"pattern": "^https://",
						"description": "OpenSea profile picture, only from OpenSea's image hosts."
					},
					"score": {
						"type": "integer",
						"minimum": 0,
						"description": "How filled-in the profile is; higher goes first."
					},
					"banner": {
						"type": ["string", "null"],
						"pattern": "^https://",
						"description": "OpenSea banner, only from OpenSea's image hosts."
					},
					"bio": {
						"type": ["string", "null"],
						"maxLength": 140,
						"description": "OpenSea bio as plain text, links removed."
					},
					"nft": {
						"type": ["string", "null"],
						"description": "The NFT the profile wears as its picture, like \"lil nouns #290\"."
					}
				}
			},
			"Holders": {
				"type": "object",
				"additionalProperties": false,
				"required": ["holders", "count", "block"],
				"properties": {
					"holders": {
						"type": "array",
						"description": "Largest first. Wallets only: contracts and burn addresses are left out.",
						"items": {
							"type": "object",
							"additionalProperties": false,
							"required": ["address", "balance"],
							"properties": {
								"address": {
									"type": "string",
									"pattern": "^0x[0-9a-f]{40}$"
								},
								"balance": {
									"type": "string",
									"pattern": "^[0-9]+$"
								}
							}
						}
					},
					"count": {
						"type": "integer",
						"minimum": 0,
						"description": "Addresses holding any MUSEGOD, contracts included, burn addresses left out."
					},
					"block": {
						"type": "integer",
						"description": "The block the balances are as of."
					},
					"balances": {
						"type": "object",
						"propertyNames": {
							"type": "string",
							"pattern": "^0x[0-9a-f]{40}$"
						},
						"additionalProperties": {
							"type": "string",
							"pattern": "^[0-9]+$"
						},
						"description": "With ?a=: the balance of each wallet asked about, 0 when it holds none."
					},
					"ranks": {
						"type": "object",
						"propertyNames": {
							"type": "string",
							"pattern": "^0x[0-9a-f]{40}$"
						},
						"additionalProperties": {
							"type": ["integer", "null"],
							"minimum": 1
						},
						"description": "With ?a=: each wallet's rank among all holders, 1 the biggest bag, null when it holds none."
					}
				}
			},
			"Faithful": {
				"type": "object",
				"additionalProperties": false,
				"required": ["people", "cards", "count"],
				"properties": {
					"people": {
						"type": "array",
						"items": {
							"type": "object",
							"additionalProperties": false,
							"required": ["address", "rings", "balance", "ringer", "holder"],
							"properties": {
								"address": {
									"type": "string",
									"pattern": "^0x[0-9a-f]{40}$"
								},
								"rings": {
									"type": "integer",
									"minimum": 0
								},
								"balance": {
									"type": "string",
									"pattern": "^[0-9]+$"
								},
								"ringer": {
									"type": "boolean"
								},
								"holder": {
									"type": "boolean",
									"description": "One of the 50 largest wallets."
								}
							}
						}
					},
					"cards": {
						"type": "object",
						"propertyNames": {
							"type": "string",
							"pattern": "^0x[0-9a-f]{40}$"
						},
						"additionalProperties": {
							"$ref": "#/components/schemas/Card"
						},
						"description": "A card per wallet whose lookup has finished."
					},
					"count": {
						"type": ["integer", "null"],
						"description": "Addresses holding any MUSEGOD, or null when the holder read failed."
					}
				}
			},
			"Tx": {
				"type": "object",
				"additionalProperties": false,
				"required": ["chainId", "to", "data", "value"],
				"description": "An unsigned transaction to the rewards distributor, ready for any wallet or eth_sendTransaction. Gas is paid in ETH on Robinhood Chain.",
				"properties": {
					"chainId": {
						"type": "integer",
						"enum": [4663],
						"description": "Robinhood Chain."
					},
					"to": {
						"type": "string",
						"pattern": "^0x[0-9a-fA-F]{40}$",
						"description": "The holder rewards distributor."
					},
					"data": {
						"type": "string",
						"pattern": "^0x[0-9a-f]+$",
						"description": "The calldata."
					},
					"value": {
						"type": "string",
						"enum": ["0"],
						"description": "No ETH is sent."
					}
				}
			},
			"ClaimTxs": {
				"type": "object",
				"additionalProperties": false,
				"required": ["ready", "tx", "forTx"],
				"properties": {
					"ready": {
						"type": "boolean",
						"description": "True when there is something to claim (earned > 0)."
					},
					"tx": {
						"$ref": "#/components/schemas/Tx",
						"description": "claim(), sent from the holder's own wallet."
					},
					"forTx": {
						"$ref": "#/components/schemas/Tx",
						"description": "claimFor(address): any wallet may send it and the rewards go to the holder. Not allowed when the holder is a contract."
					}
				}
			},
			"Bell": {
				"type": "object",
				"additionalProperties": false,
				"required": ["ready", "reason", "bountyBps", "tx"],
				"properties": {
					"ready": {
						"type": "boolean",
						"description": "True when distribute() would go through right now."
					},
					"reason": {
						"type": ["string", "null"],
						"description": "Why it would revert, e.g. NothingToDistribute when the bell was rung recently; null when ready."
					},
					"bountyBps": {
						"type": "integer",
						"description": "The caller's tip, in basis points of the buyback."
					},
					"tx": {
						"$ref": "#/components/schemas/Tx"
					}
				}
			},
			"Standing": {
				"type": "object",
				"required": [
					"address",
					"status",
					"reasons",
					"presaleMints",
					"offerings",
					"presaleTotal",
					"publicMints",
					"total",
					"locked",
					"asOf",
					"counts",
					"next"
				],
				"properties": {
					"address": {
						"type": "string"
					},
					"name": {
						"type": "string"
					},
					"status": {
						"type": "string",
						"enum": [
							"chosen",
							"on the list, not locked yet",
							"on the waitlist",
							"not chosen",
							"not on the list yet"
						]
					},
					"reasons": {
						"type": "array",
						"items": {
							"type": "string"
						}
					},
					"presaleMints": {
						"type": "integer",
						"description": "Presale mints before offerings: the stage's 2, or a hand-set limit."
					},
					"offerings": {
						"type": "integer",
						"description": "Altar offerings made, 0 to 3, read live."
					},
					"presaleTotal": {
						"type": "integer",
						"description": "Presale mints with offerings, at most 8."
					},
					"publicMints": {
						"type": "integer"
					},
					"total": {
						"type": "integer"
					},
					"locked": {
						"type": "boolean",
						"description": "True once the list is final."
					},
					"asOf": {
						"type": "string",
						"description": "When the list was last built."
					},
					"counts": {
						"type": "object",
						"required": ["wallets", "mints"],
						"properties": {
							"wallets": {
								"type": "integer"
							},
							"mints": {
								"type": "integer"
							}
						},
						"description": "Wallets on the list and presale mints allowed, as OpenSea shows them."
					},
					"next": {
						"type": "string"
					}
				}
			}
		},
		"headers": {
			"API-Version": {
				"description": "The API version that answered.",
				"schema": {
					"type": "string",
					"example": "1"
				}
			}
		},
		"responses": {
			"BadQuery": {
				"description": "A parameter is missing or malformed (invalid_query).",
				"content": {
					"application/json": {
						"schema": {
							"$ref": "#/components/schemas/Error"
						}
					}
				},
				"headers": {
					"API-Version": {
						"$ref": "#/components/headers/API-Version"
					}
				}
			},
			"NotFound": {
				"description": "The input was valid but matched nothing (not_found).",
				"content": {
					"application/json": {
						"schema": {
							"$ref": "#/components/schemas/Error"
						}
					}
				},
				"headers": {
					"API-Version": {
						"$ref": "#/components/headers/API-Version"
					}
				}
			},
			"RateLimited": {
				"description": "Too many uncached lookups from this IP (rate_limited). See Retry-After.",
				"content": {
					"application/json": {
						"schema": {
							"$ref": "#/components/schemas/Error"
						}
					}
				},
				"headers": {
					"API-Version": {
						"$ref": "#/components/headers/API-Version"
					}
				}
			},
			"Unavailable": {
				"description": "The chain or a data source did not answer (upstream_unavailable). See Retry-After.",
				"content": {
					"application/json": {
						"schema": {
							"$ref": "#/components/schemas/Error"
						}
					}
				},
				"headers": {
					"API-Version": {
						"$ref": "#/components/headers/API-Version"
					}
				}
			}
		}
	},
	"externalDocs": {
		"description": "Docs and guides",
		"url": "https://musegod.org/docs"
	},
	"tags": [
		{
			"name": "Payouts",
			"description": "What the god has paid holders, and the bell that pays them."
		},
		{
			"name": "Wallets",
			"description": "One wallet's rewards, the claim, and names for wallets."
		},
		{
			"name": "Community",
			"description": "Holders and the community wall."
		},
		{
			"name": "Muses",
			"description": "The MUSEGOD Muses presale: a wallet's standing on the list."
		}
	]
}
