{
  "openapi": "3.1.0",
  "info": {
    "title": "CREATORCAP API",
    "version": "1.1.0",
    "description": "Public API behind creatorcap.app. Same-origin for browsers; errors are {error, message, reasons?}. Human guide: docs/API.md. Operations marked x-internal are for the indexer and ops only."
  },
  "servers": [
    {
      "url": "https://creatorcap.app"
    }
  ],
  "tags": [
    {
      "name": "meta"
    },
    {
      "name": "receipts"
    },
    {
      "name": "waitlist"
    },
    {
      "name": "invites"
    },
    {
      "name": "claims"
    },
    {
      "name": "internal"
    }
  ],
  "paths": {
    "/api/config": {
      "get": {
        "summary": "Public project config and open/closed flags",
        "tags": [
          "meta"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Config"
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        }
      }
    },
    "/api/stats": {
      "get": {
        "summary": "Live homepage counters (edge cached 30 s)",
        "tags": [
          "meta"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Stats"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "public, max-age=N (edge cached)"
              },
              "CC-Cache": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "hit",
                    "miss"
                  ]
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "503": {
            "$ref": "#/components/responses/Closed"
          }
        }
      }
    },
    "/api/handles": {
      "get": {
        "summary": "Type-ahead handle search: up to 8 prefix matches (edge cached 30 s; 60 cache misses/min per IP)",
        "tags": [
          "receipts"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "maxItems": 8,
                  "items": {
                    "$ref": "#/components/schemas/HandleHit"
                  }
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "public, max-age=N (edge cached)"
              },
              "CC-Cache": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "hit",
                    "miss"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Closed"
          }
        },
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Handle prefix, 1-15 of [A-Za-z0-9_], optional leading @, case-insensitive",
            "schema": {
              "type": "string",
              "pattern": "^@?[A-Za-z0-9_]{1,15}$"
            }
          }
        ]
      }
    },
    "/api/handles/{handle}": {
      "get": {
        "summary": "Receipts for one handle",
        "tags": [
          "receipts"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Profile"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "503": {
            "$ref": "#/components/responses/Closed"
          }
        },
        "parameters": [
          {
            "name": "handle",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_]{1,15}$"
            }
          }
        ]
      }
    },
    "/api/leaderboard": {
      "get": {
        "summary": "30-day caller leaderboard (handles with 5+ scored calls; edge cached 60 s)",
        "tags": [
          "receipts"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Leaderboard"
                }
              }
            },
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "public, max-age=N (edge cached)"
              },
              "CC-Cache": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "hit",
                    "miss"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "503": {
            "$ref": "#/components/responses/Closed"
          }
        },
        "parameters": [
          {
            "name": "by",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "hit_rate",
                "median_peak",
                "calls"
              ],
              "default": "hit_rate"
            }
          }
        ]
      }
    },
    "/api/waitlist": {
      "post": {
        "summary": "Join the waitlist (10 per hour per IP)",
        "tags": [
          "waitlist"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WaitlistPosition"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/CrossOrigin"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Closed"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WaitlistJoin"
              }
            }
          }
        }
      }
    },
    "/api/waitlist/count": {
      "get": {
        "summary": "Waitlist size",
        "tags": [
          "waitlist"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "total"
                  ],
                  "properties": {
                    "total": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "503": {
            "$ref": "#/components/responses/Closed"
          }
        }
      }
    },
    "/api/invite/status": {
      "get": {
        "summary": "Whether membership (invites, sign-in) is open, with reason codes",
        "tags": [
          "invites"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenStatus"
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          }
        }
      }
    },
    "/api/invite/check": {
      "post": {
        "summary": "Check an invite code without reserving it (20 per 10 min per IP)",
        "tags": [
          "invites"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InviteCheck"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/CrossOrigin"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Closed"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CodeBody"
              }
            }
          }
        }
      }
    },
    "/api/invite/redeem/start": {
      "post": {
        "summary": "Reserve a code for 10 min and start X sign-in (20 per 10 min per IP)",
        "tags": [
          "invites"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthorizeStart"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/CrossOrigin"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Closed"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RedeemBody"
              }
            }
          }
        }
      }
    },
    "/api/invite/callback": {
      "get": {
        "summary": "X OAuth redirect target for invites and sign-in. Redirects to /welcome (sets nocap_s) or /invite?error=<code>",
        "tags": [
          "invites"
        ],
        "responses": {
          "302": {
            "description": "Redirect",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                }
              },
              "Set-Cookie": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "503": {
            "$ref": "#/components/responses/Closed"
          }
        },
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "code",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-redirect": true
      }
    },
    "/api/login/start": {
      "post": {
        "summary": "Existing member sign-in on a new device (10 per 10 min per IP)",
        "tags": [
          "invites"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthorizeStart"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/CrossOrigin"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Closed"
          }
        }
      }
    },
    "/api/me": {
      "get": {
        "summary": "The signed-in member, their allowance and minted codes",
        "tags": [
          "invites"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "503": {
            "$ref": "#/components/responses/Closed"
          }
        },
        "security": [
          {
            "memberCookie": []
          }
        ]
      }
    },
    "/api/invites": {
      "post": {
        "summary": "Mint one invite code (INVITE_DAILY_LIMIT per rolling 24 h)",
        "tags": [
          "invites"
        ],
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MintedInvite"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/CrossOrigin"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Closed"
          }
        },
        "security": [
          {
            "memberCookie": []
          }
        ]
      }
    },
    "/api/invites/leaderboard": {
      "get": {
        "summary": "Top inviters by codes used in 30 days",
        "tags": [
          "invites"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InviteLeaderboard"
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "503": {
            "$ref": "#/components/responses/Closed"
          }
        }
      }
    },
    "/api/logout": {
      "post": {
        "summary": "Sign out everywhere (bumps session_gen) and clear the cookie",
        "tags": [
          "invites"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/CrossOrigin"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          }
        }
      }
    },
    "/api/claim/status": {
      "get": {
        "summary": "Whether X handle claims are open, with reason codes",
        "tags": [
          "claims"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenStatus"
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          }
        }
      }
    },
    "/api/claim/start": {
      "post": {
        "summary": "Start an X claim for a wallet (10 per 10 min per IP)",
        "tags": [
          "claims"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClaimStart"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/CrossOrigin"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Closed"
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WalletBody"
              }
            }
          }
        }
      }
    },
    "/api/claim/callback": {
      "get": {
        "summary": "X OAuth redirect target for claims. Redirects to /claim?session=<id> or /claim?error=<code>",
        "tags": [
          "claims"
        ],
        "responses": {
          "302": {
            "description": "Redirect",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "503": {
            "$ref": "#/components/responses/Closed"
          }
        },
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "code",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-redirect": true
      }
    },
    "/api/claim/attestation": {
      "get": {
        "summary": "The signed claim attestation for a finished session",
        "tags": [
          "claims"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Attestation"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "410": {
            "$ref": "#/components/responses/Gone"
          },
          "503": {
            "$ref": "#/components/responses/Closed"
          }
        },
        "parameters": [
          {
            "name": "session",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9]{24}$"
            }
          }
        ]
      }
    },
    "/api/ingest/posts": {
      "post": {
        "summary": "Indexer: upsert a handle and its posts; extracts calls",
        "tags": [
          "internal"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "posts": {
                      "type": "integer"
                    },
                    "calls": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "503": {
            "$ref": "#/components/responses/Closed"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IngestPosts"
              }
            }
          }
        },
        "x-internal": true
      }
    },
    "/api/ingest/scores": {
      "post": {
        "summary": "Indexer: write call scores and Clocked facts (max 500 rows)",
        "tags": [
          "internal"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "scores": {
                      "type": "integer"
                    },
                    "clocked": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "503": {
            "$ref": "#/components/responses/Closed"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IngestScores"
              }
            }
          }
        },
        "x-internal": true
      }
    },
    "/api/ingest/claimed": {
      "post": {
        "summary": "Indexer: record a confirmed on-chain claim",
        "tags": [
          "internal"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "415": {
            "$ref": "#/components/responses/UnsupportedMediaType"
          },
          "503": {
            "$ref": "#/components/responses/Closed"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "x_user_id",
                  "wallet"
                ],
                "properties": {
                  "x_user_id": {
                    "$ref": "#/components/schemas/XUserId"
                  },
                  "wallet": {
                    "$ref": "#/components/schemas/Pubkey"
                  },
                  "vault_pda": {
                    "$ref": "#/components/schemas/Pubkey"
                  }
                }
              }
            }
          }
        },
        "x-internal": true
      }
    },
    "/api/admin/invites/genesis": {
      "post": {
        "summary": "Admin: mint 1-100 genesis codes",
        "tags": [
          "internal"
        ],
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "codes": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/InviteCode"
                      }
                    },
                    "expiresAt": {
                      "$ref": "#/components/schemas/Ms"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "503": {
            "$ref": "#/components/responses/Closed"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "count"
                ],
                "properties": {
                  "count": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 100
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 200
                  }
                }
              }
            }
          }
        },
        "x-internal": true
      }
    },
    "/api/admin/members/revoke": {
      "post": {
        "summary": "Admin: revoke a member (sessions and open codes)",
        "tags": [
          "internal"
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "codesExpired": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "405": {
            "$ref": "#/components/responses/MethodNotAllowed"
          },
          "503": {
            "$ref": "#/components/responses/Closed"
          }
        },
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "x_user_id"
                ],
                "properties": {
                  "x_user_id": {
                    "$ref": "#/components/schemas/XUserId"
                  }
                }
              }
            }
          }
        },
        "x-internal": true
      }
    }
  },
  "components": {
    "schemas": {
      "Ms": {
        "type": "integer",
        "description": "Unix epoch milliseconds"
      },
      "Pubkey": {
        "type": "string",
        "pattern": "^[1-9A-HJ-NP-Za-km-z]{32,44}$",
        "description": "base58 Solana address"
      },
      "XUserId": {
        "type": "string",
        "pattern": "^\\d{1,20}$"
      },
      "InviteCode": {
        "type": "string",
        "pattern": "^NOCAP-[2-9A-HJKMNP-Z]{4}-[2-9A-HJKMNP-Z]{4}$"
      },
      "Error": {
        "type": "object",
        "required": [
          "error",
          "message"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Stable machine code (see docs/API.md)"
          },
          "message": {
            "type": "string"
          },
          "reasons": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "code",
                "message"
              ],
              "properties": {
                "code": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          },
          "invitesLeft": {
            "type": "integer"
          },
          "nextInviteAt": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "Ok": {
        "type": "object",
        "required": [
          "ok"
        ],
        "properties": {
          "ok": {
            "const": true
          }
        }
      },
      "Config": {
        "type": "object",
        "required": [
          "project",
          "ticker",
          "invitesOpen",
          "claimsOpen"
        ],
        "additionalProperties": false,
        "properties": {
          "project": {
            "const": "CREATORCAP"
          },
          "ticker": {
            "const": "NOCAP"
          },
          "invitesOpen": {
            "type": "boolean"
          },
          "claimsOpen": {
            "type": "boolean"
          }
        }
      },
      "Stats": {
        "type": "object",
        "required": [
          "waitlist",
          "handles",
          "scoredCalls",
          "members",
          "invitesUsed24h",
          "claimedHandles",
          "asOf"
        ],
        "properties": {
          "waitlist": {
            "type": "integer"
          },
          "handles": {
            "type": "integer",
            "description": "Indexed handles, not opted out"
          },
          "scoredCalls": {
            "type": "integer"
          },
          "members": {
            "type": "integer",
            "description": "Members not revoked"
          },
          "invitesUsed24h": {
            "type": "integer"
          },
          "claimedHandles": {
            "type": "integer"
          },
          "asOf": {
            "$ref": "#/components/schemas/Ms"
          }
        }
      },
      "HandleHit": {
        "type": "object",
        "required": [
          "handle",
          "displayName",
          "calls",
          "hitRate"
        ],
        "properties": {
          "handle": {
            "type": "string"
          },
          "displayName": {
            "type": [
              "string",
              "null"
            ]
          },
          "calls": {
            "type": "integer"
          },
          "hitRate": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1
          }
        }
      },
      "Profile": {
        "type": "object",
        "required": [
          "handle",
          "displayName",
          "claimed",
          "summary",
          "calls",
          "clocked",
          "caps"
        ],
        "properties": {
          "handle": {
            "type": "string"
          },
          "displayName": {
            "type": [
              "string",
              "null"
            ]
          },
          "claimed": {
            "type": "boolean"
          },
          "summary": {
            "type": "object",
            "properties": {
              "calls": {
                "type": "integer"
              },
              "scored": {
                "type": "integer"
              },
              "hitRate": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "medianPeakPct": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "clocked": {
                "type": "integer"
              }
            }
          },
          "calls": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "type": "object",
              "properties": {
                "post_id": {
                  "type": "string"
                },
                "mint": {
                  "type": "string"
                },
                "posted_at": {
                  "$ref": "#/components/schemas/Ms"
                },
                "source": {
                  "type": "string"
                },
                "price_at_post": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "peak_pct": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "low24h_pct": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "minutes_to_peak": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "hit": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "clocked": {
                  "type": "integer"
                }
              }
            }
          },
          "clocked": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "type": "object",
              "properties": {
                "post_id": {
                  "type": "string"
                },
                "mint": {
                  "type": "string"
                },
                "kind": {
                  "enum": [
                    "bought_before",
                    "sold_after"
                  ]
                },
                "sig": {
                  "type": "string"
                },
                "text": {
                  "type": "string"
                },
                "disputed": {
                  "type": "integer"
                }
              }
            }
          },
          "caps": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "mint": {
                  "type": "string"
                },
                "pool": {
                  "type": "string"
                },
                "quote_mint": {
                  "type": "string"
                },
                "created_at": {
                  "$ref": "#/components/schemas/Ms"
                },
                "migrated_at": {
                  "type": [
                    "integer",
                    "null"
                  ]
                }
              }
            }
          }
        }
      },
      "Leaderboard": {
        "type": "object",
        "required": [
          "by",
          "windowDays",
          "rows"
        ],
        "properties": {
          "by": {
            "enum": [
              "hit_rate",
              "median_peak",
              "calls"
            ]
          },
          "windowDays": {
            "const": 30
          },
          "rows": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "type": "object",
              "properties": {
                "handle": {
                  "type": "string"
                },
                "calls": {
                  "type": "integer"
                },
                "scored": {
                  "type": "integer"
                },
                "hit_rate": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "median_peak_pct": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "clocked": {
                  "type": "integer"
                },
                "display_name": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "spark": {
                  "type": "array",
                  "maxItems": 12,
                  "items": {
                    "type": "number"
                  },
                  "description": "Last 12 scored peak % values in posting order"
                }
              }
            }
          }
        }
      },
      "WaitlistJoin": {
        "type": "object",
        "required": [
          "handle"
        ],
        "properties": {
          "handle": {
            "type": "string",
            "pattern": "^@?[A-Za-z0-9_]{1,15}$"
          }
        }
      },
      "WaitlistPosition": {
        "type": "object",
        "required": [
          "position",
          "total",
          "already"
        ],
        "properties": {
          "position": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "already": {
            "type": "boolean",
            "description": "true when the handle was already on the list"
          }
        }
      },
      "OpenStatus": {
        "type": "object",
        "required": [
          "open",
          "reasons"
        ],
        "properties": {
          "open": {
            "type": "boolean"
          },
          "reasons": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "CodeBody": {
        "type": "object",
        "required": [
          "code"
        ],
        "properties": {
          "code": {
            "type": "string",
            "maxLength": 40
          }
        }
      },
      "InviteCheck": {
        "type": "object",
        "required": [
          "valid",
          "reason"
        ],
        "properties": {
          "valid": {
            "type": "boolean"
          },
          "reason": {
            "enum": [
              "ok",
              "unknown",
              "used",
              "expired"
            ]
          }
        }
      },
      "RedeemBody": {
        "type": "object",
        "required": [
          "code",
          "wallet"
        ],
        "properties": {
          "code": {
            "type": "string",
            "maxLength": 40
          },
          "wallet": {
            "$ref": "#/components/schemas/Pubkey"
          }
        }
      },
      "WalletBody": {
        "type": "object",
        "required": [
          "wallet"
        ],
        "properties": {
          "wallet": {
            "$ref": "#/components/schemas/Pubkey"
          }
        }
      },
      "AuthorizeStart": {
        "type": "object",
        "required": [
          "authorizeUrl",
          "expiresAt"
        ],
        "properties": {
          "authorizeUrl": {
            "type": "string",
            "format": "uri"
          },
          "expiresAt": {
            "$ref": "#/components/schemas/Ms"
          }
        }
      },
      "ClaimStart": {
        "type": "object",
        "required": [
          "authorizeUrl",
          "session",
          "expiresAt"
        ],
        "properties": {
          "authorizeUrl": {
            "type": "string",
            "format": "uri"
          },
          "session": {
            "type": "string"
          },
          "expiresAt": {
            "$ref": "#/components/schemas/Ms"
          }
        }
      },
      "Attestation": {
        "type": "object",
        "properties": {
          "xUserId": {
            "$ref": "#/components/schemas/XUserId"
          },
          "wallet": {
            "$ref": "#/components/schemas/Pubkey"
          },
          "expiry": {
            "type": "integer",
            "description": "Unix seconds"
          },
          "message": {
            "type": "string",
            "description": "base64"
          },
          "signature": {
            "type": "string",
            "description": "base58 Ed25519"
          },
          "handle": {
            "type": "string"
          },
          "attestor": {
            "$ref": "#/components/schemas/Pubkey"
          },
          "programId": {
            "$ref": "#/components/schemas/Pubkey"
          }
        }
      },
      "Me": {
        "type": "object",
        "properties": {
          "handle": {
            "type": "string"
          },
          "wallet": {
            "$ref": "#/components/schemas/Pubkey"
          },
          "joinedAt": {
            "$ref": "#/components/schemas/Ms"
          },
          "invitedBy": {
            "type": [
              "string",
              "null"
            ]
          },
          "inviteDepth": {
            "type": "integer"
          },
          "invitesLeft": {
            "type": "integer"
          },
          "nextInviteAt": {
            "type": [
              "integer",
              "null"
            ]
          },
          "codes": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "type": "object",
              "properties": {
                "prefix": {
                  "type": "string"
                },
                "createdAt": {
                  "$ref": "#/components/schemas/Ms"
                },
                "expiresAt": {
                  "$ref": "#/components/schemas/Ms"
                },
                "status": {
                  "enum": [
                    "open",
                    "used",
                    "expired"
                  ]
                },
                "usedByHandle": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          }
        }
      },
      "MintedInvite": {
        "type": "object",
        "properties": {
          "code": {
            "$ref": "#/components/schemas/InviteCode"
          },
          "shareUrl": {
            "type": "string",
            "format": "uri"
          },
          "expiresAt": {
            "$ref": "#/components/schemas/Ms"
          },
          "invitesLeft": {
            "type": "integer"
          },
          "nextInviteAt": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "InviteLeaderboard": {
        "type": "object",
        "properties": {
          "windowDays": {
            "const": 30
          },
          "rows": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "type": "object",
              "properties": {
                "handle": {
                  "type": "string"
                },
                "count": {
                  "type": "integer"
                }
              }
            }
          }
        }
      },
      "IngestPosts": {
        "type": "object",
        "required": [
          "x_user_id",
          "handle",
          "posts"
        ],
        "properties": {
          "x_user_id": {
            "$ref": "#/components/schemas/XUserId"
          },
          "handle": {
            "type": "string"
          },
          "display_name": {
            "type": "string"
          },
          "posts": {
            "type": "array",
            "maxItems": 200,
            "items": {
              "type": "object",
              "required": [
                "id",
                "text",
                "posted_at"
              ],
              "properties": {
                "id": {
                  "$ref": "#/components/schemas/XUserId"
                },
                "text": {
                  "type": "string"
                },
                "posted_at": {
                  "$ref": "#/components/schemas/Ms"
                }
              }
            }
          }
        }
      },
      "IngestScores": {
        "type": "object",
        "properties": {
          "scores": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "post_id",
                "mint",
                "price_at_post"
              ],
              "properties": {
                "post_id": {
                  "type": "string"
                },
                "mint": {
                  "$ref": "#/components/schemas/Pubkey"
                },
                "price_at_post": {
                  "type": "number",
                  "exclusiveMinimum": 0
                },
                "peak_pct": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "low24h_pct": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "minutes_to_peak": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "hit": {
                  "type": "boolean"
                }
              }
            }
          },
          "clocked": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "post_id",
                "mint",
                "wallet",
                "kind",
                "sig",
                "text"
              ],
              "properties": {
                "post_id": {
                  "type": "string"
                },
                "mint": {
                  "$ref": "#/components/schemas/Pubkey"
                },
                "wallet": {
                  "$ref": "#/components/schemas/Pubkey"
                },
                "kind": {
                  "enum": [
                    "bought_before",
                    "sold_after"
                  ]
                },
                "sig": {
                  "type": "string"
                },
                "minutes": {
                  "type": "number"
                },
                "share": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "text": {
                  "type": "string",
                  "maxLength": 200
                }
              }
            }
          }
        },
        "description": "scores + clocked together: at most 500 rows"
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Invalid input (bad_json, bad_handle, bad_query, bad_wallet, bad_code, ...)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "not_signed_in / unauthorized",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "CrossOrigin": {
        "description": "cross_origin: non-GET from another Origin",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "not_found / unknown_handle / invite_unknown / no_attestation / unknown_member",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "MethodNotAllowed": {
        "description": "method_not_allowed (Allow header lists methods)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Conflict": {
        "description": "invite_used / invite_reserved",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Gone": {
        "description": "invite_expired / attestation_expired",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooLarge": {
        "description": "body_too_large",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "UnsupportedMediaType": {
        "description": "bad_content_type: send application/json",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "rate_limited / invite_limit",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Internal": {
        "description": "internal",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Closed": {
        "description": "db_unbound / members_closed / claims_closed, with reasons[]",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "INGEST_TOKEN for /api/ingest/*, ADMIN_TOKEN for /api/admin/* (32+ chars)"
      },
      "memberCookie": {
        "type": "apiKey",
        "in": "cookie",
        "name": "nocap_s",
        "description": "HttpOnly signed member session set by /api/invite/callback"
      }
    }
  }
}