{
 "openapi": "3.0.3",
 "info": {
  "title": "Giggles (aura market) API — as observed",
  "version": "2026-09-23",
  "description": "Unofficial. Generated from the reference at /gigglesapi/ by tools/gen_api_specs.py. Operations marked x-verified:false were listed by the operator's catalog or inferred from tolerant readers and have not been confirmed by a live call. The market API is served from api.usegiggles.com; the earlybird wrapper (/api/v1, CORS-open, GET only) from earlybirdgetstheaura.com; auth and direct messages from the app's Supabase project (per-operation server with a {project} variable). The realtime tape is a websocket and is listed under x-websocket, not paths."
 },
 "servers": [
  {
   "url": "https://api.usegiggles.com",
   "description": "market API (/api/v3, /api/user, /api/uploads, /api/v2)"
  },
  {
   "url": "https://earlybirdgetstheaura.com",
   "description": "earlybird wrapper (/api/v1)"
  }
 ],
 "tags": [
  {
   "name": "feed"
  },
  {
   "name": "posts"
  },
  {
   "name": "users"
  },
  {
   "name": "leaderboard"
  },
  {
   "name": "chat"
  },
  {
   "name": "cards"
  },
  {
   "name": "uploads"
  },
  {
   "name": "auth"
  },
  {
   "name": "wallet/tape"
  },
  {
   "name": "earlybird wrapper public"
  },
  {
   "name": "earlybird wrapper token"
  }
 ],
 "paths": {
  "/api/v3/feed": {
   "get": {
    "operationId": "ep-feed",
    "tags": [
     "feed"
    ],
    "summary": "/api/v3/feed",
    "description": "One page of the ranked recommendation feed: ~50 posts with flat market fields and engagement counts.\n\nAuth: user. Standard headers.\n\nNotes:\n- Market numbers in a feed row are as of ranking; they were measured to be fresh (no post ever showed a pot older than the last trade in 8,386 checks).\n- Roughly 200 distinct posts are surfaced in total; deeper paging repeats.\n- Reading three pages every few seconds gives near-certain sight of every fresh slot; polling faster buys nothing.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-feed",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "Page size. Clients use 50; larger values are unverified.",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "cursor",
      "in": "query",
      "required": false,
      "description": "Value of a previous nextCursor. Does not enumerate a stable list (see feed embargo).",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "sort",
      "in": "query",
      "required": false,
      "description": "top is sent by the earlybird wrapper for its top-videos ranking. Effect on ordering is inferred.",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Post object (also returned by users/{id}/posts):",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "success": {
           "type": "boolean",
           "description": ""
          },
          "posts": {
           "type": "array",
           "items": {},
           "description": "Post objects, flat (below)."
          },
          "nextCursor": {
           "type": "integer",
           "description": "Observed constant 52."
          },
          "hasMore": {
           "type": "boolean",
           "description": ""
          },
          "degraded": {
           "type": "boolean",
           "description": "Set when the ranker fell back. Semantics unverified."
          },
          "id": {
           "type": "string",
           "format": "uuid",
           "description": "Post id."
          },
          "user_id": {
           "type": "string",
           "format": "uuid",
           "description": "Creator."
          },
          "username": {
           "type": "string",
           "description": "Creator's handle."
          },
          "user": {
           "type": "object",
           "description": "Creator summary; includes creatorverified."
          },
          "post_description": {
           "type": "string",
           "description": "Caption."
          },
          "created_at": {
           "type": "string",
           "format": "date-time",
           "description": "Creation time. Always ≥ ~15 min before the response (embargo)."
          },
          "hls": {
           "type": "string",
           "format": "uri",
           "description": "HLS manifest (master.m3u8)."
          },
          "media": {
           "type": "string",
           "format": "uri",
           "description": "Media locations."
          },
          "carousel_media": {
           "type": "string",
           "format": "uri",
           "description": "Media locations."
          },
          "video_thumbnail": {
           "type": "string",
           "format": "uri",
           "description": "Media locations."
          },
          "is_photo": {
           "type": "boolean",
           "description": ""
          },
          "hashtag": {
           "type": "string",
           "description": ""
          },
          "source_platform": {
           "type": "string",
           "description": "Origin for imported media. inferred"
          },
          "market_pot": {
           "type": "number",
           "description": "Pot at ranking time."
          },
          "market_volume": {
           "type": "number",
           "description": "Cumulative traded aura."
          },
          "market_holders": {
           "type": "integer",
           "description": ""
          },
          "market_trades": {
           "type": "integer",
           "description": ""
          },
          "share_count": {
           "type": "integer",
           "description": "Engagement counts."
          },
          "comment_count": {
           "type": "integer",
           "description": "Engagement counts."
          },
          "bookmark_count": {
           "type": "integer",
           "description": "Engagement counts."
          },
          "overallengagementscore": {
           "type": "number",
           "description": "Ranker score."
          },
          "like_count": {
           "description": "Present but not populated (always 0/absent) in feed responses."
          },
          "views": {
           "description": "Present but not populated (always 0/absent) in feed responses."
          },
          "completionrate": {
           "description": "Present but not populated (always 0/absent) in feed responses."
          },
          "friend_investors_count": {
           "type": "integer",
           "description": "inferred"
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "expired or missing token."
     },
     "429": {
      "description": "budget exhausted."
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/posts/{postId}/market": {
   "get": {
    "operationId": "ep-post-market",
    "tags": [
     "posts"
    ],
    "summary": "/api/v3/posts/<post-uuid>/market",
    "description": "Current market state of one post. The reference read before any buy.\n\nAuth: user. No body.\n\nNotes:\n- pot < 1 with holders == 0 is the practical definition of an empty post. Posts a previous holder fully exited show pot ≈ 1.5, holders 0, trades ≥ 2.\n- maxInvest depends on the caller; the same post reports different values to different accounts.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-post-market",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     },
     {
      "name": "postId",
      "in": "path",
      "required": true,
      "description": "Post id.",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "pot": {
           "type": "number",
           "description": "Redeemable pool."
          },
          "floor": {
           "type": "number",
           "description": "Seed; 1000 on native posts."
          },
          "level": {
           "type": "number",
           "description": "floor + pot."
          },
          "maxInvest": {
           "type": "number",
           "description": "Largest buy the caller may place now (position cap). Caller-specific."
          },
          "holders": {
           "type": "integer",
           "description": "Accounts with a position."
          },
          "trades": {
           "type": "integer",
           "description": "Total trade count, including trades the tape and /market/trades omit."
          },
          "volume": {
           "type": "number",
           "description": "Cumulative traded aura."
          },
          "status": {
           "type": "string",
           "description": "Lifecycle state. Values unverified."
          },
          "change24h": {
           "type": "number",
           "description": "24-hour level change. Unit (aura vs percent) unverified."
          },
          "creator_fees": {
           "type": "number",
           "description": "Fees accrued to the creator. Also seen as creatorFeesEarned."
          },
          "listed_at": {
           "type": "string",
           "format": "date-time",
           "description": "Listing time. Also seen as listedAt."
          },
          "isImport": {
           "type": "boolean",
           "description": "Imported media. inferred"
          },
          "sell_locked_until": {
           "type": "string",
           "format": "date-time",
           "description": "See sell locks. Also seen as sellLockedUntil."
          },
          "sell_timer_s": {
           "type": "integer",
           "description": "Also seen as sellTimerSeconds."
          }
         }
        }
       }
      }
     },
     "404": {
      "description": "post deleted or unknown. A UUID that is a user, not a post, also 404s."
     },
     "401": {
      "description": "error"
     },
     "429": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/posts/{postId}/market/trades": {
   "get": {
    "operationId": "ep-post-trades",
    "tags": [
     "posts"
    ],
    "summary": "/api/v3/posts/<post-uuid>/market/trades",
    "description": "The most recent trades on a post, newest first. Incomplete by design.\n\nAuth: user.\n\nNotes:\n- Hard cap of 100 rows. A post with market.trades = 2883 returns 100 rows and no way to page.\n- Trades under ~10 aura are omitted (16 rows returned against trades = 156 on one post). Use market.trades for the true count.\n- Never derive the pot from this list; it cannot be reconstructed. Use market or the tape.\n- The list lags the pot by seconds under load.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-post-trades",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     },
     {
      "name": "postId",
      "in": "path",
      "required": true,
      "description": "Post id.",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "Rows requested. Values above 100 clamp to 100.",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "offset",
      "in": "query",
      "required": false,
      "description": "Accepted and ignored. There is no paging.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "page",
      "in": "query",
      "required": false,
      "description": "Accepted and ignored. There is no paging.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "before",
      "in": "query",
      "required": false,
      "description": "Accepted and ignored. There is no paging.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "cursor",
      "in": "query",
      "required": false,
      "description": "Accepted and ignored. There is no paging.",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "postId": {
           "type": "string",
           "format": "uuid",
           "description": ""
          },
          "trades": {
           "type": "array",
           "items": {},
           "description": "Up to 100 rows."
          },
          "trades[].type": {
           "type": "string",
           "description": "buy or sell."
          },
          "trades[].points": {
           "type": "number",
           "description": "Aura in (buy) or out (sell)."
          },
          "trades[].shares": {
           "type": "number",
           "description": ""
          },
          "trades[].userId": {
           "type": "string",
           "format": "uuid",
           "description": ""
          },
          "trades[].username": {
           "type": "string",
           "description": "May contain zero-width characters; strip U+200B before comparing."
          },
          "trades[].photoUrl": {
           "type": "string",
           "format": "uri",
           "description": ""
          },
          "trades[].at": {
           "type": "string",
           "format": "date-time",
           "description": "Both string and numeric (seconds or milliseconds) forms have been handled by clients; parse defensively."
          }
         }
        }
       }
      }
     },
     "404": {
      "description": "deleted post."
     },
     "401": {
      "description": "error"
     },
     "429": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/posts/{postId}/invest": {
   "post": {
    "operationId": "ep-post-invest",
    "tags": [
     "posts"
    ],
    "summary": "/api/v3/posts/<post-uuid>/invest",
    "description": "Buy into a post. Adds points to the pot and mints shares to the caller.\n\nAuth: owner. Body: JSON.\n\nNotes:\n- A rejected buy is rejected whole. To buy the maximum, re-issue with points = maxPoints.\n- Round trip is ~0.8 s mean; 1–14 % of calls exceed 2 s depending on platform load.\n- Buys are accepted before a post is listed in the feed.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-post-invest",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     },
     {
      "name": "postId",
      "in": "path",
      "required": true,
      "description": "Post id.",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "pot": {
           "type": "number",
           "description": "Pot after the buy. Equals points exactly when the pot was empty before."
          },
          "shares": {
           "type": "number",
           "description": "Shares minted to the caller."
          },
          "others": {
           "description": "Further fields exist; clients read only the two above. inferred"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "POSITION_CAP: points exceeds the cap. maxPoints is the amount that would be accepted; 0 means none. | points above maxInvest or above spendable balance."
     },
     "404": {
      "description": "Deleted post."
     },
     "401": {
      "description": "Token expired; budget exhausted (the buy did not happen)."
     },
     "429": {
      "description": "Token expired; budget exhausted (the buy did not happen)."
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "points": {
          "type": "number",
          "description": "Amount to invest. Must be ≤ the caller's maxInvest and ≤ aura_spendable."
         }
        },
        "required": [
         "points"
        ]
       }
      }
     }
    },
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/posts/{postId}/redeem": {
   "post": {
    "operationId": "ep-post-redeem",
    "tags": [
     "posts"
    ],
    "summary": "/api/v3/posts/<post-uuid>/redeem",
    "description": "Sell the caller's entire position in a post.\n\nAuth: owner. Body: JSON.\n\nNotes:\n- All-or-nothing. Two accounts redeeming the same post in sequence each receive their fraction of the level at the moment of their call; order does not change the group total.\n- Round trip ≈ 0.36 s in a burst of sequential redeems.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-post-redeem",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     },
     {
      "name": "postId",
      "in": "path",
      "required": true,
      "description": "Post id.",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "postId": {
           "type": "string",
           "format": "uuid",
           "description": ""
          },
          "redeemed": {
           "type": "number",
           "description": "Aura paid out, net of fees."
          },
          "shares": {
           "type": "number",
           "description": "Shares burned."
          },
          "realizedPnl": {
           "type": "number",
           "description": "redeemed − invested."
          },
          "creatorFee": {
           "type": "number",
           "description": "Fee paid to the creator (≈ 0.1 %)."
          },
          "pot": {
           "type": "number",
           "description": "Pot after the redeem."
          },
          "level": {
           "type": "number",
           "description": "Level after the redeem."
          },
          "counter": {
           "description": "Meaning unverified."
          },
          "spendable": {
           "type": "number",
           "description": "Caller's liquid balance after the redeem."
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "A sell-lock refusal (see locks) has an unverified shape."
     },
     "429": {
      "description": "A sell-lock refusal (see locks) has an unverified shape."
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "amount": {
          "type": "number",
          "description": "Ignored. Clients send 1. Any value redeems 100 % of shares."
         }
        },
        "required": [
         "amount"
        ]
       }
      }
     }
    },
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/users/search": {
   "get": {
    "operationId": "ep-users-search",
    "tags": [
     "users"
    ],
    "summary": "/api/v3/users/search",
    "description": "Username search. Returns up to 20 users.\n\nAuth: user.\n\nNotes:\n- The only way to resolve a username to an id without a local table; the leaderboard covers only ranked users.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-users-search",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     },
     {
      "name": "q",
      "in": "query",
      "required": false,
      "description": "Name prefix or fragment. URL-encode.",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "success": {
           "type": "boolean",
           "description": ""
          },
          "count": {
           "type": "integer",
           "description": "≤ 20."
          },
          "users[].id": {
           "type": "string",
           "format": "uuid",
           "description": ""
          },
          "users[].username": {
           "type": "string",
           "description": "Do an exact, case-insensitive match client-side; results are fuzzy."
          },
          "users[].display_name": {
           "type": "string",
           "description": ""
          },
          "users[].bio": {
           "type": "string",
           "description": ""
          },
          "users[].profile_photo": {
           "type": "string",
           "description": ""
          },
          "users[].created_at": {
           "type": "string",
           "description": ""
          },
          "users[].creator_verified": {
           "type": "boolean",
           "description": "inferred from a tolerant reader."
          },
          "users[].creator_verified_gold": {
           "type": "boolean",
           "description": "inferred from a tolerant reader."
          },
          "users[].has_wallet": {
           "type": "boolean",
           "description": "inferred from a tolerant reader."
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "error"
     },
     "429": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/users/{userId}/profile": {
   "get": {
    "operationId": "ep-users-profile",
    "tags": [
     "users"
    ],
    "summary": "/api/v3/users/<user-uuid>/profile",
    "description": "Public profile of any user, including balances.\n\nAuth: user. Works for any id, including the caller's own.\n\nNotes:\n- Cheapest balance read: one call, two numbers.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-users-profile",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     },
     {
      "name": "userId",
      "in": "path",
      "required": true,
      "description": "User id (sub).",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "user": {
           "type": "object",
           "description": "Wrapper object; all fields below are inside it."
          },
          "user.username": {
           "type": "string",
           "description": ""
          },
          "user.display_name": {
           "type": "string",
           "description": ""
          },
          "user.bio": {
           "type": "string",
           "description": ""
          },
          "user.college": {
           "type": "string",
           "description": ""
          },
          "user.profile_photo": {
           "type": "string",
           "description": ""
          },
          "user.social_links": {
           "type": "array",
           "items": {},
           "description": ""
          },
          "user.aura": {
           "type": "number",
           "description": "Total net worth: spendable plus the value of open positions. Matches the leaderboard's networth. Stable under heavy trading."
          },
          "user.aura_spendable": {
           "type": "number",
           "description": "Liquid balance. Holdings = aura − aura_spendable."
          },
          "user.creatorverified": {
           "type": "boolean",
           "description": "Verification badges."
          },
          "user.creatorverifiedgold": {
           "type": "boolean",
           "description": "Verification badges."
          },
          "user.followers_count": {
           "type": "integer",
           "description": "inferred (read by the earlybird connect page)."
          },
          "user.leaderboard_rank": {
           "type": "integer",
           "description": "inferred (read by the earlybird connect page)."
          },
          "user.follower_count": {
           "type": "integer",
           "description": "inferred (read by the wrapper)."
          },
          "user.following_count": {
           "type": "integer",
           "description": "inferred (read by the wrapper)."
          },
          "user.activity_public": {
           "type": "integer",
           "description": "inferred (read by the wrapper)."
          },
          "user.graduation_year": {
           "description": "Passed through by the profile updater. inferred"
          },
          "user.profile_color": {
           "description": "Passed through by the profile updater. inferred"
          }
         }
        }
       }
      }
     },
     "404": {
      "description": "unknown user (a post UUID also 404s here)."
     },
     "401": {
      "description": "error"
     },
     "429": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/users/{userId}/stats": {
   "get": {
    "operationId": "ep-users-stats",
    "tags": [
     "users"
    ],
    "summary": "/api/v3/users/<user-uuid>/stats",
    "description": "Trading statistics for any user.\n\nAuth: user.\n\nNotes:\n- Not a substitute for transactions; it carries totals only.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-users-stats",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     },
     {
      "name": "userId",
      "in": "path",
      "required": true,
      "description": "User id.",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "netWorth": {
           "type": "number",
           "description": ""
          },
          "rank": {
           "type": "integer",
           "description": "Leaderboard rank."
          },
          "totalVolume": {
           "type": "number",
           "description": ""
          },
          "tradeCount": {
           "type": "integer",
           "description": ""
          },
          "creatorVerified": {
           "type": "boolean",
           "description": ""
          },
          "networth": {
           "type": "number",
           "description": "inferred from the wrapper's reader; may be absent."
          },
          "realizedPnl": {
           "type": "number",
           "description": "inferred from the wrapper's reader; may be absent."
          },
          "winRate": {
           "type": "number",
           "description": "inferred from the wrapper's reader; may be absent."
          }
         }
        }
       }
      }
     },
     "404": {
      "description": "error"
     },
     "401": {
      "description": "error"
     },
     "429": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/users/{userId}/posts": {
   "get": {
    "operationId": "ep-users-posts",
    "tags": [
     "users"
    ],
    "summary": "/api/v3/users/<user-uuid>/posts",
    "description": "A creator's live posts with flat market fields, newest first.\n\nAuth: user.\n\nNotes:\n- Live posts only. Deleted posts disappear.\n- A post appears here from the moment of insertion — roughly 15 minutes before the feed shows it. This is the earliest public surface for a new post, and the only one before listing.\n- No paging observed; the count returned for prolific creators is unverified.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-users-posts",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     },
     {
      "name": "userId",
      "in": "path",
      "required": true,
      "description": "Creator's user id.",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "posts": {
           "type": "array",
           "items": {},
           "description": "Flat post objects: id, created_at, hashtag, is_photo, media, carousel_media, post_description, market_pot, market_volume, market_holders, market_trades. Same shape as feed rows; market (nested) is absent."
          }
         }
        }
       }
      }
     },
     "404": {
      "description": "error"
     },
     "401": {
      "description": "error"
     },
     "429": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/users/{userId}/transactions": {
   "get": {
    "operationId": "ep-users-transactions",
    "tags": [
     "users"
    ],
    "summary": "/api/v3/users/<user-uuid>/transactions",
    "description": "A user's trade ledger, newest first. Readable for any user.\n\nAuth: user.\n\nNotes:\n- Any account's trading is public through this route; a user's own history is not private on this platform.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-users-transactions",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     },
     {
      "name": "userId",
      "in": "path",
      "required": true,
      "description": "User id.",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "Clients use 100.",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "cursor",
      "in": "query",
      "required": false,
      "description": "Paging cursor. inferred from the owner form below.",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "transactions": {
           "type": "array",
           "items": {},
           "description": "Ledger rows. (A bare array and a data key have also been tolerated by readers.)"
          },
          "transactions[].id": {
           "type": "string",
           "description": "Stable row id; use for de-duplication."
          },
          "transactions[].type": {
           "type": "string",
           "description": "buy / sell (casing varies; compare case-insensitively)."
          },
          "transactions[].postId": {
           "type": "string",
           "format": "uuid",
           "description": "Also seen as post_id."
          },
          "transactions[].points": {
           "type": "number",
           "description": ""
          },
          "transactions[].realizedPnl": {
           "type": "number",
           "description": "On sells."
          },
          "transactions[].createdAt": {
           "type": "string",
           "format": "date-time",
           "description": "Also seen as created_at."
          }
         }
        }
       }
      }
     },
     "404": {
      "description": "error"
     },
     "401": {
      "description": "error"
     },
     "429": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/transactions": {
   "get": {
    "operationId": "ep-transactions",
    "tags": [
     "users"
    ],
    "summary": "/api/v3/transactions",
    "description": "The caller's own full trade ledger. Same rows as the user-scoped route.\n\nAuth: owner.\n\nNotes:\n- The cheapest source of an account's own history.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-transactions",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "Clients use 100.",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "cursor",
      "in": "query",
      "required": false,
      "description": "Empty for the first page.",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "As users/{id}/transactions."
     },
     "401": {
      "description": "error"
     },
     "429": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/users/{userId}/portfolio": {
   "get": {
    "operationId": "ep-users-portfolio",
    "tags": [
     "users"
    ],
    "summary": "/api/v3/users/<user-uuid>/portfolio",
    "description": "Open and closed positions of any user, 50 per page.\n\nAuth: user. Public for any id.\n\nNotes:\n- Page until hasMore is false or nextOffset repeats; clients cap at 6–30 pages.\n- value lags a fresh buy by a moment; spendable drops instantly. Net-worth computed as holdings + spendable can dip transiently.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-users-portfolio",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     },
     {
      "name": "userId",
      "in": "path",
      "required": true,
      "description": "User id.",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     },
     {
      "name": "offset",
      "in": "query",
      "required": false,
      "description": "From a previous nextOffset. Omit for page one.",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "positions": {
           "type": "array",
           "items": {},
           "description": "Open positions."
          },
          "positions[].postId": {
           "type": "string",
           "format": "uuid",
           "description": ""
          },
          "positions[].invested": {
           "type": "number",
           "description": "Cost basis."
          },
          "positions[].value": {
           "type": "number",
           "description": "Mark: frac × level."
          },
          "positions[].shares": {
           "type": "number",
           "description": ""
          },
          "positions[].unrealizedPnl": {
           "type": "number",
           "description": ""
          },
          "positions[].unrealizedPnlPct": {
           "type": "number",
           "description": ""
          },
          "positions[].realizedPnl": {
           "type": "number",
           "description": ""
          },
          "positions[].totalPnl": {
           "type": "number",
           "description": ""
          },
          "positions[].creatorId": {
           "type": "string",
           "format": "uuid",
           "description": "Also recoverable from the thumbnail path /posts/thumbnails/<creator>/…."
          },
          "positions[].postDeleted": {
           "type": "boolean",
           "description": "Post deleted; redeem still refunds."
          },
          "positions[].isImport": {
           "type": "boolean",
           "description": ""
          },
          "positions[].description": {
           "type": "string",
           "description": ""
          },
          "positions[].thumbnail": {
           "type": "string",
           "description": ""
          },
          "closedPositions": {
           "type": "array",
           "items": {},
           "description": "Rows: openedAt, closedAt, invested, returned, realizedPnl, trades."
          },
          "hasMore": {
           "type": "boolean",
           "description": ""
          },
          "nextOffset": {
           "type": "integer",
           "description": ""
          },
          "current": {
           "type": "object",
           "description": "netWorth, holdings, spendable. netWorth intermittently returns holdings only; holdings + spendable is reliable, and profile.aura is steadier still during rapid trading."
          },
          "pnl24h": {
           "type": "object",
           "description": "pnl, percentChange, rank."
          },
          "totalPnl": {
           "type": "number",
           "description": "Lifetime."
          },
          "totalPnlPercent": {
           "type": "number",
           "description": "Lifetime."
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "error"
     },
     "429": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/portfolio": {
   "get": {
    "operationId": "ep-portfolio",
    "tags": [
     "users"
    ],
    "summary": "/api/v3/portfolio",
    "description": "The caller's own portfolio. Same shape and paging as the user-scoped route.\n\nAuth: owner.\n\nNotes:\n- Exercised continuously by client code; not listed in the operator's catalog. Prefer this route for one's own account.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-portfolio",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     },
     {
      "name": "offset",
      "in": "query",
      "required": false,
      "description": "From nextOffset.",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "As users/{id}/portfolio."
     },
     "401": {
      "description": "error"
     },
     "429": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/users/{userId}/cards": {
   "get": {
    "operationId": "ep-users-cards",
    "tags": [
     "users"
    ],
    "summary": "/api/v3/users/<user-uuid>/cards",
    "description": "Any user's card collection.\n\nAuth: user.\n\nNotes:\n- Read-only view; trade composition uses trade-with.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-users-cards",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     },
     {
      "name": "userId",
      "in": "path",
      "required": true,
      "description": "User id.",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Card objects; field names as in trade-with theirs.cards (inferred)."
     },
     "404": {
      "description": "error"
     },
     "401": {
      "description": "error"
     },
     "429": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/user/profile": {
   "put": {
    "operationId": "ep-user-profile-put",
    "tags": [
     "users"
    ],
    "summary": "/api/user/profile",
    "description": "Update the caller's profile. Full replacement: send every field.\n\nAuth: owner. Body: JSON. Not under /api/v3.\n\nNotes:\n- Fetch profile first and pass every existing field through, or fields are wiped.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-user-profile-put",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "200 with success: true or a body without error. inferred"
     },
     "401": {
      "description": "error"
     },
     "429": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "username": {
          "type": "string",
          "description": "Current values, re-sent."
         },
         "display_name": {
          "type": "string",
          "description": "Current values, re-sent."
         },
         "bio": {
          "type": "string",
          "description": "Current values, re-sent."
         },
         "college": {
          "type": "string",
          "description": "Current values, re-sent."
         },
         "social_links": {
          "type": "array",
          "items": {},
          "description": "Current value; [] if none."
         },
         "profile_photo": {
          "type": "string",
          "format": "uri",
          "description": "Public URL from presign. GIF allowed."
         },
         "email": {
          "type": "string",
          "description": "one of email/phone_number. Required; taken from the JWT's email / phone claim."
         },
         "phone_number": {
          "type": "string",
          "description": "one of email/phone_number. Required; taken from the JWT's email / phone claim."
         },
         "graduation_year": {
          "description": "Optional pass-through."
         },
         "profile_color": {
          "description": "Optional pass-through."
         }
        },
        "required": [
         "username",
         "display_name",
         "bio",
         "college",
         "social_links",
         "profile_photo"
        ]
       }
      }
     }
    },
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/leaderboard": {
   "get": {
    "operationId": "ep-leaderboard",
    "tags": [
     "leaderboard"
    ],
    "summary": "/api/v3/leaderboard",
    "description": "Net-worth ranking, 50 per page, with the caller's own row.\n\nAuth: user.\n\nNotes:\n- Networth decays to 0 by roughly rank 3,000–6,000; the top few thousand rows are effectively the whole money supply.\n- Non-trade changes in networth are usually mark-to-market of open positions, not transfers.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-leaderboard",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     },
     {
      "name": "period",
      "in": "query",
      "required": false,
      "description": "24h is verified. 7d, 30d, all are forwarded by the wrapper; acceptance upstream is inferred. Default is the all-time ranking.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "cursor",
      "in": "query",
      "required": false,
      "description": "From nextCursor. Walks stop at about page 120 (6,000 rows).",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "success": {
           "type": "boolean",
           "description": ""
          },
          "period": {
           "type": "string",
           "description": ""
          },
          "leaderboard": {
           "type": "array",
           "items": {},
           "description": "50 rows."
          },
          "leaderboard[].userId": {
           "type": "string",
           "format": "uuid",
           "description": ""
          },
          "leaderboard[].username": {
           "type": "string",
           "description": ""
          },
          "leaderboard[].displayName": {
           "type": "string",
           "description": ""
          },
          "leaderboard[].photoUrl": {
           "type": "string",
           "description": ""
          },
          "leaderboard[].verified": {
           "type": "boolean",
           "description": ""
          },
          "leaderboard[].verifiedGold": {
           "type": "boolean",
           "description": ""
          },
          "leaderboard[].networth": {
           "type": "number",
           "description": "Total wealth including open positions (equals profile.aura)."
          },
          "leaderboard[].rank": {
           "type": "number",
           "description": "Present on period rankings. inferred"
          },
          "leaderboard[].delta": {
           "type": "number",
           "description": "Present on period rankings. inferred"
          },
          "leaderboard[].percentChange": {
           "type": "number",
           "description": "Present on period rankings. inferred"
          },
          "me": {
           "type": "object",
           "description": "The caller's row (userId, username, …)."
          },
          "hasMore": {
           "type": "boolean",
           "description": ""
          },
          "nextCursor": {
           "type": "boolean",
           "description": ""
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "(a full walk on one token has hit 429)."
     },
     "429": {
      "description": "(a full walk on one token has hit 429)."
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/chat": {
   "get": {
    "operationId": "ep-chat-get",
    "tags": [
     "chat"
    ],
    "summary": "/api/v3/chat",
    "description": "The global chat room.\n\nAuth: user.\n\nNotes:\n- /chat/global does not exist; this route is the global room.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-chat-get",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     },
     {
      "name": "cursor",
      "in": "query",
      "required": false,
      "description": "From nextCursor. inferred",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "success": {
           "type": "boolean",
           "description": ""
          },
          "chatterCount": {
           "type": "integer",
           "description": "≈ 13k observed."
          },
          "messages": {
           "type": "array",
           "items": {},
           "description": ""
          },
          "messages[].id": {
           "type": "string",
           "description": "kind: user observed."
          },
          "messages[].kind": {
           "type": "string",
           "description": "kind: user observed."
          },
          "messages[].authorUserId": {
           "description": "Author."
          },
          "messages[].username": {
           "description": "Author."
          },
          "messages[].avatarUrl": {
           "description": "Author."
          },
          "messages[].authorGold": {
           "description": "Author."
          },
          "messages[].clan": {
           "description": "Author."
          },
          "messages[].text": {
           "description": "Content."
          },
          "messages[].gifUrl": {
           "description": "Content."
          },
          "messages[].replyTo": {
           "description": "Content."
          },
          "messages[].reactions": {
           "description": ""
          },
          "messages[].reactionCount": {
           "description": ""
          },
          "messages[].meta": {
           "description": ""
          },
          "messages[].gameId": {
           "description": ""
          },
          "messages[].createdAt": {
           "description": ""
          },
          "hasMore": {
           "description": ""
          },
          "nextCursor": {
           "description": ""
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "error"
     },
     "429": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   },
   "post": {
    "operationId": "ep-chat-post",
    "tags": [
     "chat"
    ],
    "summary": "/api/v3/chat",
    "description": "Post a message to the global chat.\n\nAuth: owner. Body: JSON.\n\nNotes:\n- Not gated on creator verification; an unverified account posted successfully.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-chat-post",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "{\"success\": true, \"message\": {…}} with the created message object."
     },
     "401": {
      "description": "error"
     },
     "429": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "text": {
          "type": "string",
          "description": "Message text."
         }
        },
        "required": [
         "text"
        ]
       }
      }
     }
    },
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/rest/v1/chat_participants": {
   "get": {
    "operationId": "ep-dm-participants",
    "tags": [
     "chat"
    ],
    "summary": "{SB}/rest/v1/chat_participants",
    "description": "The caller's conversation list: one row per chat the caller belongs to.\n\nAuth: user JWT + apikey.\n\nNotes:\n- The other party of a chat is not on this row. Resolve it from chat_messages (a sender ≠ self) or from chats.last_message_by. For an empty chat the other party is unknowable.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-dm-participants",
    "parameters": [
     {
      "name": "user_id",
      "in": "query",
      "required": false,
      "description": "eq.<user-uuid> — the caller's own id (RLS returns nothing else).",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     },
     {
      "name": "is_hidden",
      "in": "query",
      "required": false,
      "description": "eq.false to exclude hidden chats.",
      "schema": {
       "type": "boolean"
      }
     },
     {
      "name": "select",
      "in": "query",
      "required": false,
      "description": "Column list, e.g. chat_id,unread_count,last_read_at.",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "chat_id": {
           "type": "string",
           "format": "uuid",
           "description": ""
          },
          "user_id": {
           "type": "string",
           "format": "uuid",
           "description": ""
          },
          "joined_at": {
           "type": "string",
           "format": "date-time",
           "description": ""
          },
          "last_read_at": {
           "type": "string",
           "format": "date-time",
           "description": ""
          },
          "unread_count": {
           "type": "integer",
           "description": ""
          },
          "is_muted": {
           "type": "boolean",
           "description": ""
          },
          "is_pinned": {
           "type": "boolean",
           "description": ""
          },
          "is_hidden": {
           "type": "boolean",
           "description": ""
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "bad or expired JWT; an empty array when RLS excludes everything."
     }
    },
    "servers": [
     {
      "url": "https://{project}.supabase.co",
      "description": "the Supabase project behind the app (ref is embedded in the client)",
      "variables": {
       "project": {
        "default": "PROJECT-REF"
       }
      }
     }
    ],
    "security": [
     {
      "bearerAuth": [],
      "publishableKey": []
     }
    ]
   },
   "patch": {
    "operationId": "ep-dm-participant-patch",
    "tags": [
     "chat"
    ],
    "summary": "{SB}/rest/v1/chat_participants",
    "description": "Mark a chat read, or hide it. Only the caller's own row is writable.\n\nAuth: user JWT + apikey. Prefer: return=minimal.\n\nNotes:\n- Hiding is per participant; the other party's view is unchanged.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-dm-participant-patch",
    "parameters": [
     {
      "name": "Prefer",
      "in": "header",
      "required": false,
      "description": "PostgREST response preference",
      "schema": {
       "type": "string",
       "default": "return=minimal"
      }
     },
     {
      "name": "chat_id",
      "in": "query",
      "required": false,
      "description": "eq. both, or chat_id=in.(…) for bulk.",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     },
     {
      "name": "user_id",
      "in": "query",
      "required": false,
      "description": "eq. both, or chat_id=in.(…) for bulk.",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "204 (or 200) with no body."
     },
     "401": {
      "description": "A filter matching another user's row updates nothing and returns success."
     }
    },
    "servers": [
     {
      "url": "https://{project}.supabase.co",
      "description": "the Supabase project behind the app (ref is embedded in the client)",
      "variables": {
       "project": {
        "default": "PROJECT-REF"
       }
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "last_read_at": {
          "type": "string",
          "format": "date-time",
          "description": "Mark read: now, with unread_count: 0."
         },
         "is_hidden": {
          "type": "boolean",
          "description": "true hides the chat from the caller's list. There is no delete."
         }
        },
        "required": [
         "last_read_at",
         "is_hidden"
        ]
       }
      }
     }
    },
    "security": [
     {
      "bearerAuth": [],
      "publishableKey": []
     }
    ]
   }
  },
  "/rest/v1/chats": {
   "get": {
    "operationId": "ep-dm-chats",
    "tags": [
     "chat"
    ],
    "summary": "{SB}/rest/v1/chats",
    "description": "Chat headers: last message preview and timestamps.\n\nAuth: user JWT + apikey.\n\nNotes:\n- A sending client updates this row itself after inserting a message (PATCH chats?id=eq.<chat-uuid> with the four last_message* columns, Prefer: return=minimal); the server does not maintain it.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-dm-chats",
    "parameters": [
     {
      "name": "id",
      "in": "query",
      "required": false,
      "description": "in.(a,b,c); clients batch 40 ids per call.",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     },
     {
      "name": "select",
      "in": "query",
      "required": false,
      "description": "e.g. id,last_message,last_message_time,last_message_by.",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "id": {
           "type": "string",
           "format": "uuid",
           "description": ""
          },
          "created_at": {
           "type": "string",
           "format": "date-time",
           "description": ""
          },
          "last_message_time": {
           "type": "string",
           "format": "date-time",
           "description": ""
          },
          "last_message": {
           "type": "string",
           "description": "Preview text."
          },
          "last_message_by": {
           "type": "string",
           "format": "uuid",
           "description": ""
          },
          "last_message_id": {
           "type": "string",
           "format": "uuid",
           "description": ""
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://{project}.supabase.co",
      "description": "the Supabase project behind the app (ref is embedded in the client)",
      "variables": {
       "project": {
        "default": "PROJECT-REF"
       }
      }
     }
    ],
    "security": [
     {
      "bearerAuth": [],
      "publishableKey": []
     }
    ]
   }
  },
  "/rest/v1/chat_messages": {
   "get": {
    "operationId": "ep-dm-messages",
    "tags": [
     "chat"
    ],
    "summary": "{SB}/rest/v1/chat_messages",
    "description": "Messages in a chat.\n\nAuth: user JWT + apikey.\n\nNotes:\n- Mark as read by patching the caller's participant row (below).",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-dm-messages",
    "parameters": [
     {
      "name": "chat_id",
      "in": "query",
      "required": false,
      "description": "eq.<chat-uuid> or in.(…).",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     },
     {
      "name": "user_id",
      "in": "query",
      "required": false,
      "description": "neq.<user-uuid> to find the other party.",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     },
     {
      "name": "order",
      "in": "query",
      "required": false,
      "description": "created_at.desc.",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "id": {
           "type": "string",
           "format": "uuid",
           "description": ""
          },
          "chat_id": {
           "type": "string",
           "format": "uuid",
           "description": ""
          },
          "user_id": {
           "type": "string",
           "format": "uuid",
           "description": ""
          },
          "text": {
           "type": "string",
           "description": "A clan invite is the text clan_invite:<id> plus a row in clan_invites."
          },
          "message_type": {
           "type": "string",
           "description": "text, media, post, gift, market_event, system."
          },
          "media": {
           "description": ""
          },
          "post_id": {
           "description": ""
          },
          "reply_to_message_id": {
           "description": ""
          },
          "reactions": {
           "description": ""
          },
          "meta": {
           "description": ""
          },
          "read_by": {
           "description": ""
          },
          "client_id": {
           "type": "string",
           "format": "uuid",
           "description": "Client-generated idempotency id."
          },
          "created_at": {
           "type": "string",
           "format": "date-time",
           "description": ""
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://{project}.supabase.co",
      "description": "the Supabase project behind the app (ref is embedded in the client)",
      "variables": {
       "project": {
        "default": "PROJECT-REF"
       }
      }
     }
    ],
    "security": [
     {
      "bearerAuth": [],
      "publishableKey": []
     }
    ]
   },
   "post": {
    "operationId": "ep-dm-send",
    "tags": [
     "chat"
    ],
    "summary": "{SB}/rest/v1/chat_messages",
    "description": "Send a direct message.\n\nAuth: user JWT + apikey. Headers: Content-Type: application/json, Prefer: return=representation to receive the inserted row.\n\nNotes:\n- Creating a new chat (rows in chats and two chat_participants) is unverified; only sending into existing chats has been exercised.\n- After the insert, patch chats so the conversation list preview updates (see chats).",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-dm-send",
    "parameters": [
     {
      "name": "Prefer",
      "in": "header",
      "required": false,
      "description": "PostgREST response preference",
      "schema": {
       "type": "string",
       "default": "return=representation"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "201 with a one-element array containing the row (under return=representation)."
     },
     "401": {
      "description": "from RLS when the caller is not a participant."
     },
     "403": {
      "description": "from RLS when the caller is not a participant."
     },
     "4XX": {
      "description": "from RLS when the caller is not a participant."
     }
    },
    "servers": [
     {
      "url": "https://{project}.supabase.co",
      "description": "the Supabase project behind the app (ref is embedded in the client)",
      "variables": {
       "project": {
        "default": "PROJECT-REF"
       }
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "chat_id": {
          "type": "string",
          "format": "uuid",
          "description": "Existing chat the caller belongs to."
         },
         "user_id": {
          "type": "string",
          "format": "uuid",
          "description": "The caller's own id."
         },
         "text": {
          "type": "string",
          "description": ""
         },
         "message_type": {
          "type": "string",
          "description": "text."
         },
         "client_id": {
          "type": "string",
          "format": "uuid",
          "description": "Fresh UUID v4."
         },
         "meta": {
          "type": "object",
          "description": "Optional; e.g. {\"trade_offer_id\": …} to attach a card offer."
         }
        },
        "required": [
         "chat_id",
         "user_id",
         "text",
         "message_type",
         "client_id"
        ]
       }
      }
     }
    },
    "security": [
     {
      "bearerAuth": [],
      "publishableKey": []
     }
    ]
   }
  },
  "/api/v3/cards/trade-with/{userId}": {
   "get": {
    "operationId": "ep-cards-trade-with",
    "tags": [
     "cards"
    ],
    "summary": "/api/v3/cards/trade-with/<user-uuid>",
    "description": "Everything needed to compose an offer to one user: both inventories, spendable aura, limits.\n\nAuth: owner.\n\nNotes:\n- The authoritative composer; use it instead of /cards/inventory before creating an offer.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-cards-trade-with",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     },
     {
      "name": "userId",
      "in": "path",
      "required": true,
      "description": "Counterparty user id.",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "other": {
           "type": "object",
           "description": "Counterparty summary."
          },
          "mine.cards": {
           "type": "array",
           "items": {},
           "description": "Caller's tradeable items."
          },
          "mine.cosmetics": {
           "type": "array",
           "items": {},
           "description": "Caller's tradeable items."
          },
          "mine.spendableAura": {
           "type": "number",
           "description": ""
          },
          "theirs.cards": {
           "type": "array",
           "items": {},
           "description": "Counterparty's cards."
          },
          "cards[].cardId": {
           "type": "string",
           "description": "Catalog id."
          },
          "cards[].userCardId": {
           "type": "string",
           "format": "uuid",
           "description": "The instance id used in offers."
          },
          "cards[].name": {
           "description": ""
          },
          "cards[].tier": {
           "description": ""
          },
          "cards[].editionNo": {
           "description": ""
          },
          "cards[].imageUrl": {
           "description": ""
          },
          "cards[].animationUrl": {
           "description": ""
          },
          "cards[].instaSellAura": {
           "type": "number",
           "description": "Instant-sell value."
          },
          "cards[].tradeable": {
           "type": "boolean",
           "description": "Reliable here (not in inventory)."
          },
          "cards[].lockedReason": {
           "type": "boolean",
           "description": "Reliable here (not in inventory)."
          },
          "limits.maxItemsPerSide": {
           "type": "integer",
           "description": "6."
          },
          "limits.openOutgoingMax": {
           "type": "integer",
           "description": "1000."
          },
          "limits.offersToThisUserMaxPerDay": {
           "type": "integer",
           "description": "25."
          },
          "limits.auraFeePct": {
           "type": "number",
           "description": "1."
          }
         }
        }
       }
      }
     },
     "404": {
      "description": "unknown user; 401, 429."
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/cards/trades": {
   "post": {
    "operationId": "ep-cards-trades-create",
    "tags": [
     "cards"
    ],
    "summary": "/api/v3/cards/trades",
    "description": "Create a trade offer.\n\nAuth: owner. Body: JSON.\n\nNotes:\n- Validation order is empty_side → no_card_in_trade → Invalid items.\n- Aura gift: offer [one card] + N aura, request 1 aura. The recipient nets ≈ N minus the 1 % fee plus a card.\n- Gift counts and cooldowns are not exposed.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-cards-trades-create",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "description": ""
          },
          "offerId": {
           "type": "string",
           "format": "uuid",
           "description": ""
          },
          "expiresAt": {
           "type": "string",
           "format": "date-time",
           "description": "≈ 7 days."
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "empty_side: A side has no items and 0 aura. | no_card_in_trade: Neither side contains a card. Pure-aura transfers are refused. | Invalid items: Wrong item key or id. Checked after the two above."
     },
     "401": {
      "description": "error"
     },
     "429": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "receiverId": {
          "type": "string",
          "format": "uuid",
          "description": "Counterparty."
         },
         "offeredItems": {
          "type": "array",
          "items": {},
          "description": "[{\"itemType\":\"card\",\"id\":\"<userCardId>\"}]. itemType is lowercase card (also sticker); id holds the userCardId."
         },
         "requestedItems": {
          "type": "array",
          "items": {},
          "description": "Same shape, from theirs.cards."
         },
         "offeredAura": {
          "type": "number",
          "description": ""
         },
         "requestedAura": {
          "type": "number",
          "description": ""
         }
        },
        "required": [
         "receiverId",
         "offeredItems",
         "requestedItems",
         "offeredAura",
         "requestedAura"
        ]
       }
      }
     }
    },
    "security": [
     {
      "bearerAuth": []
     }
    ]
   },
   "get": {
    "operationId": "ep-cards-trades-list",
    "tags": [
     "cards"
    ],
    "summary": "/api/v3/cards/trades",
    "description": "The caller's offers by status.\n\nAuth: owner.\n\nNotes:\n- incoming lists offers made to the caller.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-cards-trades-list",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     },
     {
      "name": "status",
      "in": "query",
      "required": false,
      "description": "pending, accepted, incoming.",
      "schema": {
       "type": "string",
       "enum": [
        "pending",
        "accepted",
        "incoming"
       ]
      }
     }
    ],
    "responses": {
     "200": {
      "description": "{\"offers\": [ … ]}; offer fields inferred."
     },
     "401": {
      "description": "error"
     },
     "429": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/cards/trades/{offerId}/{action}": {
   "post": {
    "operationId": "ep-cards-trades-respond",
    "tags": [
     "cards"
    ],
    "summary": "/api/v3/cards/trades/<offer-uuid>/{accept|decline|cancel}",
    "description": "Resolve an offer. accept and decline by the receiver; cancel by the sender.\n\nAuth: owner. No body.\n\nNotes:\n- Accepting settles both sides atomically, including aura, less the 1 % fee.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-cards-trades-respond",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     },
     {
      "name": "offerId",
      "in": "path",
      "required": true,
      "description": "From offerId on creation or from the list.",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     },
     {
      "name": "action",
      "in": "path",
      "required": true,
      "description": "accept, decline, cancel.",
      "schema": {
       "type": "string",
       "enum": [
        "accept",
        "decline",
        "cancel"
       ]
      }
     }
    ],
    "responses": {
     "200": {
      "description": "{\"ok\": true, …}. inferred"
     },
     "4XX": {
      "description": "when the caller is the wrong party or the offer expired. Shapes unverified."
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "requestBody": {
     "required": false,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {}
       }
      }
     }
    },
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/cards/inventory": {
   "get": {
    "operationId": "ep-cards-inventory",
    "tags": [
     "cards"
    ],
    "summary": "[unverified] /api/v3/cards/inventory",
    "description": "NOT VERIFIED: inferred. The caller's own cards.\n\nAuth: owner.\n\nNotes:\n- Listed in the operator's catalog without a live mark.",
    "x-verified": false,
    "x-confidence": "inferred",
    "x-auth": "token",
    "x-doc-anchor": "ep-cards-inventory",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Card objects. The tradeable flag here is unreliable; use trade-with."
     },
     "401": {
      "description": "error"
     },
     "429": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/cards/catalog": {
   "get": {
    "operationId": "ep-cards-catalog",
    "tags": [
     "cards"
    ],
    "summary": "[unverified] /api/v3/cards/catalog",
    "description": "NOT VERIFIED: inferred. The card catalog: 57 cards across 6 tiers (including \"contraband\").\n\nAuth: user.\n\nNotes:\n- Counts as of 2026-09-20.",
    "x-verified": false,
    "x-confidence": "inferred",
    "x-auth": "token",
    "x-doc-anchor": "ep-cards-catalog",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Catalog entries; field names unverified."
     },
     "401": {
      "description": "error"
     },
     "429": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/cards/boxes": {
   "get": {
    "operationId": "ep-cards-boxes",
    "tags": [
     "cards"
    ],
    "summary": "[unverified] /api/v3/cards/boxes",
    "description": "NOT VERIFIED: inferred. Loot-box state for the caller.\n\nAuth: owner.\n\nNotes:\n- Box prices: 1★ = 50 aura … 5★ = 2500 aura. The purchase/open route is unverified.",
    "x-verified": false,
    "x-confidence": "inferred",
    "x-auth": "token",
    "x-doc-anchor": "ep-cards-boxes",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "spendable": {
           "type": "number",
           "description": ""
          },
          "keyBalance": {
           "type": "integer",
           "description": ""
          },
          "pendingBoxes": {
           "type": "array",
           "items": {},
           "description": ""
          }
         }
        }
       }
      }
     },
     "401": {
      "description": "error"
     },
     "429": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/cards/market/auctions": {
   "get": {
    "operationId": "ep-cards-market",
    "tags": [
     "cards"
    ],
    "summary": "[unverified] /api/v3/cards/market/auctions · /api/v3/cards/market/lots",
    "description": "NOT VERIFIED: inferred. The card marketplace: auctions and fixed-price lots.\n\nAuth: user.\n\nNotes:\n- Listed by the operator's catalog; never exercised by client code.",
    "x-verified": false,
    "x-confidence": "inferred",
    "x-auth": "token",
    "x-doc-anchor": "ep-cards-market",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "unverified."
     },
     "401": {
      "description": "error"
     },
     "429": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/cards/market/lots": {
   "get": {
    "operationId": "ep-cards-market-2",
    "tags": [
     "cards"
    ],
    "summary": "[unverified] /api/v3/cards/market/auctions · /api/v3/cards/market/lots",
    "description": "NOT VERIFIED: inferred. The card marketplace: auctions and fixed-price lots.\n\nAuth: user.\n\nNotes:\n- Listed by the operator's catalog; never exercised by client code.",
    "x-verified": false,
    "x-confidence": "inferred",
    "x-auth": "token",
    "x-doc-anchor": "ep-cards-market",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "unverified."
     },
     "401": {
      "description": "error"
     },
     "429": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v3/rewards/daily": {
   "get": {
    "operationId": "ep-rewards-daily",
    "tags": [
     "cards"
    ],
    "summary": "[unverified] /api/v3/rewards/daily",
    "description": "NOT VERIFIED: inferred. Daily reward state. Resets at 16:00 UTC.\n\nAuth: owner.\n\nNotes:\n- Whether a claim is a separate POST is unverified.",
    "x-verified": false,
    "x-confidence": "inferred",
    "x-auth": "token",
    "x-doc-anchor": "ep-rewards-daily",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "unverified."
     },
     "401": {
      "description": "error"
     },
     "429": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/uploads/presign": {
   "post": {
    "operationId": "ep-uploads-presign",
    "tags": [
     "uploads"
    ],
    "summary": "/api/uploads/presign",
    "description": "Obtain a presigned PUT URL for an image.\n\nAuth: owner. Body: JSON. Not under /api/v3.\n\nNotes:\n- The earlybird tool caps uploads at 8 MB client-side; the platform's limit is unverified.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "ep-uploads-presign",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "uploadUrl": {
           "type": "string",
           "format": "uri",
           "description": "Presigned PUT (Cloudflare R2)."
          },
          "key": {
           "type": "string",
           "description": "Object key."
          },
          "publicUrl": {
           "type": "string",
           "format": "uri",
           "description": "The URL to store on the profile."
          },
          "expiresIn": {
           "type": "integer",
           "description": "300."
          },
          "requiredHeaders": {
           "type": "object",
           "description": "Headers the PUT must carry (at least Content-Type)."
          }
         }
        }
       }
      }
     },
     "4XX": {
      "description": "for an unsupported type or size; shape unverified. 401."
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "kind": {
          "type": "string",
          "enum": [
           "profile-image",
           "post-media"
          ],
          "description": "profile-image or post-media."
         },
         "contentType": {
          "type": "string",
          "description": "image/jpeg, image/png, image/gif, image/webp."
         },
         "contentLength": {
          "type": "integer",
          "description": "Exact byte length of the upload."
         }
        },
        "required": [
         "kind",
         "contentType",
         "contentLength"
        ]
       }
      }
     }
    },
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/": {
   "put": {
    "operationId": "ep-uploads-put",
    "tags": [
     "uploads"
    ],
    "summary": "{uploadUrl}",
    "description": "Upload the raw bytes to the presigned URL.\n\nNo bearer token: the URL is self-authorising. Headers: exactly requiredHeaders from presign. Body: the file bytes.\n\nNotes:\n- Then set profile_photo to publicUrl with PUT /api/user/profile.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "none",
    "x-doc-anchor": "ep-uploads-put",
    "parameters": [],
    "responses": {
     "200": {
      "description": "200, 201 or 204, empty body."
     },
     "403": {
      "description": "after expiresIn or on a header/length mismatch."
     }
    },
    "servers": [
     {
      "url": "{uploadUrl}",
      "variables": {
       "uploadUrl": {
        "default": "https://presigned.example/upload"
       }
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/octet-stream": {
       "schema": {
        "type": "string",
        "format": "binary"
       }
      }
     }
    },
    "security": []
   }
  },
  "/auth/v1/otp": {
   "post": {
    "operationId": "ep-auth-otp",
    "tags": [
     "auth"
    ],
    "summary": "{SB}/auth/v1/otp",
    "description": "Email a one-time code (or magic link) to an address.\n\nHeaders: apikey, Authorization: Bearer <publishable-key>, Content-Type: application/json.\n\nNotes:\n- Rate-limited by GoTrue per address; limits unverified.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "publishable",
    "x-doc-anchor": "ep-auth-otp",
    "parameters": [],
    "responses": {
     "200": {
      "description": "200, empty object."
     },
     "4XX": {
      "description": "with msg / error_description / error; a message containing captcha means the project demands a captcha token this call did not supply."
     }
    },
    "servers": [
     {
      "url": "https://{project}.supabase.co",
      "description": "the Supabase project behind the app (ref is embedded in the client)",
      "variables": {
       "project": {
        "default": "PROJECT-REF"
       }
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "email": {
          "type": "string",
          "description": ""
         },
         "create_user": {
          "type": "boolean",
          "description": "false: existing accounts only. true: unverified whether the resulting account is provisioned."
         }
        },
        "required": [
         "email",
         "create_user"
        ]
       }
      }
     }
    },
    "security": [
     {
      "publishableKey": []
     }
    ]
   }
  },
  "/auth/v1/verify": {
   "post": {
    "operationId": "ep-auth-verify",
    "tags": [
     "auth"
    ],
    "summary": "{SB}/auth/v1/verify",
    "description": "Exchange the emailed code for a session.\n\nHeaders: apikey, Content-Type: application/json.\n\nNotes:\n- Codes are single-use and short-lived.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "publishable",
    "x-doc-anchor": "ep-auth-verify",
    "parameters": [],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "access_token": {
           "description": ""
          },
          "refresh_token": {
           "type": "string",
           "description": ""
          },
          "expires_in": {
           "description": "Standard GoTrue session fields. inferred"
          },
          "token_type": {
           "description": "Standard GoTrue session fields. inferred"
          },
          "user": {
           "description": "Standard GoTrue session fields. inferred"
          }
         }
        }
       }
      }
     },
     "4XX": {
      "description": "with msg / error_description: invalid or expired code."
     }
    },
    "servers": [
     {
      "url": "https://{project}.supabase.co",
      "description": "the Supabase project behind the app (ref is embedded in the client)",
      "variables": {
       "project": {
        "default": "PROJECT-REF"
       }
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "type": {
          "type": "string",
          "description": "email."
         },
         "email": {
          "type": "string",
          "description": ""
         },
         "token": {
          "type": "string",
          "description": "The 6-digit code."
         }
        },
        "required": [
         "type",
         "email",
         "token"
        ]
       }
      }
     }
    },
    "security": [
     {
      "publishableKey": []
     }
    ]
   }
  },
  "/auth/v1/authorize": {
   "get": {
    "operationId": "ep-auth-authorize",
    "tags": [
     "auth"
    ],
    "summary": "{SB}/auth/v1/authorize",
    "description": "Start an OAuth login in a browser.\n\nBrowser navigation; no headers.\n\nNotes:\n- The fragment is not sent to the redirect host; read it from the address bar or page script.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "none",
    "x-doc-anchor": "ep-auth-authorize",
    "parameters": [
     {
      "name": "provider",
      "in": "query",
      "required": false,
      "description": "google, apple.",
      "schema": {
       "type": "string",
       "enum": [
        "google",
        "apple"
       ]
      }
     },
     {
      "name": "redirect_to",
      "in": "query",
      "required": false,
      "description": "Must be on the project's allow-list.",
      "schema": {
       "type": "string",
       "format": "uri"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "302 chain ending at redirect_to#access_token=…&refresh_token=…."
     }
    },
    "servers": [
     {
      "url": "https://{project}.supabase.co",
      "description": "the Supabase project behind the app (ref is embedded in the client)",
      "variables": {
       "project": {
        "default": "PROJECT-REF"
       }
      }
     }
    ],
    "security": []
   }
  },
  "/auth/v1/token": {
   "post": {
    "operationId": "ep-auth-refresh",
    "tags": [
     "auth"
    ],
    "summary": "{SB}/auth/v1/token?grant_type=refresh_token",
    "description": "Rotate a session.\n\nHeaders: apikey, Authorization: Bearer <publishable-key>, Content-Type: application/json.\n\nNotes:\n- Persist the new pair before using it. Keep one writer per account.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "publishable",
    "x-doc-anchor": "ep-auth-refresh",
    "parameters": [
     {
      "name": "grant_type",
      "in": "query",
      "required": true,
      "description": "refresh_token.",
      "schema": {
       "type": "string",
       "default": "refresh_token"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "access_token, refresh_token (new), expires_in, user. See refresh tokens."
     },
     "4XX": {
      "description": "with error_description: token already used, revoked, or malformed."
     }
    },
    "servers": [
     {
      "url": "https://{project}.supabase.co",
      "description": "the Supabase project behind the app (ref is embedded in the client)",
      "variables": {
       "project": {
        "default": "PROJECT-REF"
       }
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "refresh_token": {
          "type": "string",
          "description": "The current refresh token."
         }
        },
        "required": [
         "refresh_token"
        ]
       }
      }
     }
    },
    "security": [
     {
      "publishableKey": []
     }
    ]
   }
  },
  "/api/v2/wallet": {
   "get": {
    "operationId": "ep-wallet",
    "tags": [
     "wallet/tape"
    ],
    "summary": "[unverified] /api/v2/wallet",
    "description": "NOT VERIFIED: inferred. Wallet capability probe.\n\nAuth: owner.\n\nNotes:\n- The only v2 route referenced. /api/v3/wallet and /api/v3/balance do not exist.",
    "x-verified": false,
    "x-confidence": "inferred",
    "x-auth": "token",
    "x-doc-anchor": "ep-wallet",
    "parameters": [
     {
      "name": "User-Agent",
      "in": "header",
      "required": false,
      "description": "the Android client's agent; browsers cannot set it",
      "schema": {
       "type": "string",
       "default": "okhttp/4.12.0"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Includes hasWallet: false: aura is not backed by a payment wallet and is not withdrawable."
     },
     "401": {
      "description": "error"
     }
    },
    "servers": [
     {
      "url": "https://api.usegiggles.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v1": {
   "get": {
    "operationId": "w-index",
    "tags": [
     "earlybird wrapper public"
    ],
    "summary": "/ (index)",
    "description": "the endpoint index (also /api/v1/health); no auth.\n\nNo auth. Served from the wrapper's own database.\n\nNotes:\n- the endpoint index (also /api/v1/health); no auth.\n- Cached 10 s.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "none",
    "x-doc-anchor": "wrapper-public",
    "parameters": [],
    "responses": {
     "200": {
      "description": "the endpoint index (also /api/v1/health); no auth.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "description": ""
          },
          "endpoints": {
           "description": ""
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Bad parameter (unknown period/by, q too short, non-UUID id, non-integer limit)."
     },
     "404": {
      "description": "No such endpoint; see GET /api/v1."
     },
     "502": {
      "description": "Upstream unreachable or returned an unexpected status."
     }
    },
    "servers": [
     {
      "url": "https://earlybirdgetstheaura.com"
     }
    ],
    "security": []
   }
  },
  "/api/v1/public/chain": {
   "get": {
    "operationId": "w-public-chain",
    "tags": [
     "earlybird wrapper public"
    ],
    "summary": "/public/chain",
    "description": "the aurachain: a hash-chained, append-only ledger of closed trades, fully pseudonymised.\n\nNo auth. Served from the wrapper's own database.\n\nNotes:\n- the aurachain: a hash-chained, append-only ledger of closed trades, fully pseudonymised.\n- Cached 10 s.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "none",
    "x-doc-anchor": "wrapper-public",
    "parameters": [
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "1–200",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "before",
      "in": "query",
      "required": false,
      "description": "(seq)",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "after",
      "in": "query",
      "required": false,
      "description": "(seq)",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "user",
      "in": "query",
      "required": false,
      "description": "(pseudonym)",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "the aurachain: a hash-chained, append-only ledger of closed trades, fully pseudonymised.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "description": ""
          },
          "rows": {
           "type": "array",
           "items": {},
           "description": "items: […]"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Bad parameter (unknown period/by, q too short, non-UUID id, non-integer limit)."
     },
     "404": {
      "description": "No such endpoint; see GET /api/v1."
     },
     "502": {
      "description": "Upstream unreachable or returned an unexpected status."
     }
    },
    "servers": [
     {
      "url": "https://earlybirdgetstheaura.com"
     }
    ],
    "security": []
   }
  },
  "/api/v1/public/chain/stats": {
   "get": {
    "operationId": "w-public-chain-stats",
    "tags": [
     "earlybird wrapper public"
    ],
    "summary": "/public/chain/stats",
    "description": "Ledger totals, win rate, head hash.\n\nNo auth. Served from the wrapper's own database.\n\nNotes:\n- Ledger totals, win rate, head hash.\n- Cached 10 s.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "none",
    "x-doc-anchor": "wrapper-public",
    "parameters": [],
    "responses": {
     "200": {
      "description": "Ledger totals, win rate, head hash."
     },
     "400": {
      "description": "Bad parameter (unknown period/by, q too short, non-UUID id, non-integer limit)."
     },
     "404": {
      "description": "No such endpoint; see GET /api/v1."
     },
     "502": {
      "description": "Upstream unreachable or returned an unexpected status."
     }
    },
    "servers": [
     {
      "url": "https://earlybirdgetstheaura.com"
     }
    ],
    "security": []
   }
  },
  "/api/v1/public/chain/verify": {
   "get": {
    "operationId": "w-public-chain-verify",
    "tags": [
     "earlybird wrapper public"
    ],
    "summary": "/public/chain/verify",
    "description": "recomputes every hash from genesis.\n\nNo auth. Served from the wrapper's own database.\n\nNotes:\n- or {ok:false, broken_at} — recomputes every hash from genesis.\n- Cached 10 s.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "none",
    "x-doc-anchor": "wrapper-public",
    "parameters": [],
    "responses": {
     "200": {
      "description": "or {ok:false, broken_at} — recomputes every hash from genesis.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "description": ""
          },
          "checked": {
           "description": ""
          },
          "head": {
           "description": ""
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Bad parameter (unknown period/by, q too short, non-UUID id, non-integer limit)."
     },
     "404": {
      "description": "No such endpoint; see GET /api/v1."
     },
     "502": {
      "description": "Upstream unreachable or returned an unexpected status."
     }
    },
    "servers": [
     {
      "url": "https://earlybirdgetstheaura.com"
     }
    ],
    "security": []
   }
  },
  "/api/v1/public/chain/users": {
   "get": {
    "operationId": "w-public-chain-users",
    "tags": [
     "earlybird wrapper public"
    ],
    "summary": "/public/chain/users",
    "description": "Pseudonymised accounts on the chain.\n\nNo auth. Served from the wrapper's own database.\n\nNotes:\n- Pseudonymised accounts on the chain.\n- Cached 10 s.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "none",
    "x-doc-anchor": "wrapper-public",
    "parameters": [
     {
      "name": "q",
      "in": "query",
      "required": true,
      "description": "(pseudonym prefix), limit",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Pseudonymised accounts on the chain."
     },
     "400": {
      "description": "Bad parameter (unknown period/by, q too short, non-UUID id, non-integer limit)."
     },
     "404": {
      "description": "No such endpoint; see GET /api/v1."
     },
     "502": {
      "description": "Upstream unreachable or returned an unexpected status."
     }
    },
    "servers": [
     {
      "url": "https://earlybirdgetstheaura.com"
     }
    ],
    "security": []
   }
  },
  "/api/v1/public/live/history": {
   "get": {
    "operationId": "w-public-live-history",
    "tags": [
     "earlybird wrapper public"
    ],
    "summary": "/public/live/history",
    "description": "closed live-pool rounds, totals only: no post ids, no names.\n\nNo auth. Served from the wrapper's own database.\n\nNotes:\n- closed live-pool rounds, totals only: no post ids, no names.\n- Cached 10 s.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "none",
    "x-doc-anchor": "wrapper-public",
    "parameters": [
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "1–500 (default 50)",
      "schema": {
       "type": "integer",
       "default": 50
      }
     }
    ],
    "responses": {
     "200": {
      "description": "closed live-pool rounds, totals only: no post ids, no names.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "description": ""
          },
          "count": {
           "description": ""
          },
          "rounds": {
           "type": "array",
           "items": {},
           "description": "items: [{round, members, invested, returned, pnl, pnlPct, enteredAt, exitedAt, minutes}]"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Bad parameter (unknown period/by, q too short, non-UUID id, non-integer limit)."
     },
     "404": {
      "description": "No such endpoint; see GET /api/v1."
     },
     "502": {
      "description": "Upstream unreachable or returned an unexpected status."
     }
    },
    "servers": [
     {
      "url": "https://earlybirdgetstheaura.com"
     }
    ],
    "security": []
   }
  },
  "/api/v1/public/posts/{postId}/trajectory": {
   "get": {
    "operationId": "w-public-posts-trajectory",
    "tags": [
     "earlybird wrapper public"
    ],
    "summary": "/public/posts/{postId}/trajectory",
    "description": "logged snapshots; rows younger than 1 h are never served; ≤ 2000 rows.\n\nNo auth. Served from the wrapper's own database.\n\nNotes:\n- logged snapshots; rows younger than 1 h are never served; ≤ 2000 rows.\n- Cached 10 s.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "none",
    "x-doc-anchor": "wrapper-public",
    "parameters": [
     {
      "name": "postId",
      "in": "path",
      "required": true,
      "description": "post id",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "logged snapshots; rows younger than 1 h are never served; ≤ 2000 rows.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "description": ""
          },
          "post": {
           "description": ""
          },
          "count": {
           "description": ""
          },
          "intervalSeconds": {
           "description": "value 30"
          },
          "minAgeSeconds": {
           "description": "value 3600"
          },
          "points": {
           "type": "array",
           "items": {},
           "description": "items: [{ts, pot, volume, holders, trades}]"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Bad parameter (unknown period/by, q too short, non-UUID id, non-integer limit)."
     },
     "404": {
      "description": "No such endpoint; see GET /api/v1."
     },
     "502": {
      "description": "Upstream unreachable or returned an unexpected status."
     }
    },
    "servers": [
     {
      "url": "https://earlybirdgetstheaura.com"
     }
    ],
    "security": []
   }
  },
  "/api/v1/public/pulse": {
   "get": {
    "operationId": "w-public-pulse",
    "tags": [
     "earlybird wrapper public"
    ],
    "summary": "/public/pulse",
    "description": "\n\nNo auth. Served from the wrapper's own database.\n\nNotes:\n- Cached 10 s.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "none",
    "x-doc-anchor": "wrapper-public",
    "parameters": [
     {
      "name": "hours",
      "in": "query",
      "required": false,
      "description": "1–336 (default 24)",
      "schema": {
       "type": "integer",
       "default": 24
      }
     },
     {
      "name": "keys",
      "in": "query",
      "required": false,
      "description": "comma list",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "description": ""
          },
          "hours": {
           "description": ""
          },
          "since": {
           "description": ""
          },
          "series": {
           "type": "object",
           "description": "items: {key: [[ts, value], …]}"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Bad parameter (unknown period/by, q too short, non-UUID id, non-integer limit)."
     },
     "404": {
      "description": "No such endpoint; see GET /api/v1."
     },
     "502": {
      "description": "Upstream unreachable or returned an unexpected status."
     },
     "503": {
      "description": "/public/pulse on a host with no sampler data."
     }
    },
    "servers": [
     {
      "url": "https://earlybirdgetstheaura.com"
     }
    ],
    "security": []
   }
  },
  "/api/v1/public/pulse/keys": {
   "get": {
    "operationId": "w-public-pulse-keys",
    "tags": [
     "earlybird wrapper public"
    ],
    "summary": "/public/pulse/keys",
    "description": "\n\nNo auth. Served from the wrapper's own database.\n\nNotes:\n- Cached 10 s.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "none",
    "x-doc-anchor": "wrapper-public",
    "parameters": [],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "description": ""
          },
          "count": {
           "description": ""
          },
          "keys": {
           "type": "array",
           "items": {},
           "description": "items: []"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Bad parameter (unknown period/by, q too short, non-UUID id, non-integer limit)."
     },
     "404": {
      "description": "No such endpoint; see GET /api/v1."
     },
     "502": {
      "description": "Upstream unreachable or returned an unexpected status."
     }
    },
    "servers": [
     {
      "url": "https://earlybirdgetstheaura.com"
     }
    ],
    "security": []
   }
  },
  "/api/v1/public/stats": {
   "get": {
    "operationId": "w-public-stats",
    "tags": [
     "earlybird wrapper public"
    ],
    "summary": "/public/stats",
    "description": "\n\nNo auth. Served from the wrapper's own database.\n\nNotes:\n- Cached 10 s.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "none",
    "x-doc-anchor": "wrapper-public",
    "parameters": [],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "description": ""
          },
          "ledger": {
           "type": "object",
           "description": "items: {…}"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Bad parameter (unknown period/by, q too short, non-UUID id, non-integer limit)."
     },
     "404": {
      "description": "No such endpoint; see GET /api/v1."
     },
     "502": {
      "description": "Upstream unreachable or returned an unexpected status."
     }
    },
    "servers": [
     {
      "url": "https://earlybirdgetstheaura.com"
     }
    ],
    "security": []
   }
  },
  "/api/v1/public/dataset": {
   "get": {
    "operationId": "w-public-dataset",
    "tags": [
     "earlybird wrapper public"
    ],
    "summary": "/public/dataset",
    "description": "Pointer to the CC-BY-4.0 giggles-market dataset: .\n\nNo auth. Served from the wrapper's own database.\n\nNotes:\n- Pointer to the CC-BY-4.0 giggles-market dataset: .\n- Cached 10 s.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "none",
    "x-doc-anchor": "wrapper-public",
    "parameters": [],
    "responses": {
     "200": {
      "description": "Pointer to the CC-BY-4.0 giggles-market dataset: .",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "description": ""
          },
          "name": {
           "description": ""
          },
          "license": {
           "description": ""
          },
          "tables": {
           "type": "array",
           "items": {},
           "description": "items: []"
          },
          "note": {
           "description": ""
          },
          "url": {
           "description": ""
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Bad parameter (unknown period/by, q too short, non-UUID id, non-integer limit)."
     },
     "404": {
      "description": "No such endpoint; see GET /api/v1."
     },
     "502": {
      "description": "Upstream unreachable or returned an unexpected status."
     }
    },
    "servers": [
     {
      "url": "https://earlybirdgetstheaura.com"
     }
    ],
    "security": []
   }
  },
  "/api/v1/leaderboard": {
   "get": {
    "operationId": "w-leaderboard",
    "tags": [
     "earlybird wrapper token"
    ],
    "summary": "/leaderboard",
    "description": "\n\nAuth: the caller's own Giggles JWT as a bearer token (?token= also works for a quick test). Validated locally, forwarded unchanged, never stored or logged.\n\nNotes:\n- Shared across callers by path: market 12 s, search 30 s, portfolio 30 s, profile/stats/posts 45 s, leaderboard 45 s, other 20 s. N callers asking the same public question cost one upstream call.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "wrapper-token",
    "parameters": [
     {
      "name": "period",
      "in": "query",
      "required": false,
      "description": "24h (default) | 7d | 30d | all",
      "schema": {
       "type": "string",
       "enum": [
        "24h",
        "7d",
        "30d",
        "all"
       ],
       "default": "24h"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "description": ""
          },
          "period": {
           "description": ""
          },
          "count": {
           "description": ""
          },
          "leaderboard": {
           "type": "array",
           "items": {},
           "description": "items: [{rank, userId, username, displayName, networth, delta, percentChange, verified}]"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Bad parameter (unknown period/by, q too short, non-UUID id, non-integer limit)."
     },
     "401": {
      "description": "Missing, malformed or expired bearer token (message says which)."
     },
     "403": {
      "description": "Upstream rejected the token (passed through)."
     },
     "404": {
      "description": "No such endpoint; see GET /api/v1."
     },
     "429": {
      "description": "Upstream rate limit (passed through; it is the caller's budget)."
     },
     "502": {
      "description": "Upstream unreachable or returned an unexpected status."
     }
    },
    "servers": [
     {
      "url": "https://earlybirdgetstheaura.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v1/top-videos": {
   "get": {
    "operationId": "w-top-videos",
    "tags": [
     "earlybird wrapper token"
    ],
    "summary": "/top-videos",
    "description": "Ranks one page of the top feed (≤ 50 posts), not history. Aliases /top_videos, /top.\n\nAuth: the caller's own Giggles JWT as a bearer token (?token= also works for a quick test). Validated locally, forwarded unchanged, never stored or logged.\n\nNotes:\n- Ranks one page of the top feed (≤ 50 posts), not history. Aliases /top_videos, /top.\n- Aliases: /top_videos, /top\n- post shape: {id, userId, username, description, media, thumbnail, createdAt, market{pot, volume, holders, trades}, engagement{shares, comments, bookmarks, score}}.\n- Shared across callers by path: market 12 s, search 30 s, portfolio 30 s, profile/stats/posts 45 s, leaderboard 45 s, other 20 s. N callers asking the same public question cost one upstream call.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "wrapper-token",
    "parameters": [
     {
      "name": "by",
      "in": "query",
      "required": false,
      "description": "volume (default) | pot | holders | trades | shares | comments | bookmarks | engagement",
      "schema": {
       "type": "string",
       "enum": [
        "volume",
        "pot",
        "holders",
        "trades",
        "shares",
        "comments",
        "bookmarks",
        "engagement"
       ],
       "default": "volume"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "1–50 (default 20)",
      "schema": {
       "type": "integer",
       "default": 20
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Ranks one page of the top feed (≤ 50 posts), not history. Aliases /top_videos, /top.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "description": ""
          },
          "rankedBy": {
           "description": ""
          },
          "count": {
           "description": ""
          },
          "videos": {
           "type": "array",
           "items": {},
           "description": "items: [{rank, …post}]"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Bad parameter (unknown period/by, q too short, non-UUID id, non-integer limit)."
     },
     "401": {
      "description": "Missing, malformed or expired bearer token (message says which)."
     },
     "403": {
      "description": "Upstream rejected the token (passed through)."
     },
     "404": {
      "description": "No such endpoint; see GET /api/v1."
     },
     "429": {
      "description": "Upstream rate limit (passed through; it is the caller's budget)."
     },
     "502": {
      "description": "Upstream unreachable or returned an unexpected status."
     }
    },
    "servers": [
     {
      "url": "https://earlybirdgetstheaura.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v1/search": {
   "get": {
    "operationId": "w-search",
    "tags": [
     "earlybird wrapper token"
    ],
    "summary": "/search",
    "description": "Aliases /members, /member-search.\n\nAuth: the caller's own Giggles JWT as a bearer token (?token= also works for a quick test). Validated locally, forwarded unchanged, never stored or logged.\n\nNotes:\n- Aliases /members, /member-search.\n- Aliases: /members, /member-search\n- Shared across callers by path: market 12 s, search 30 s, portfolio 30 s, profile/stats/posts 45 s, leaderboard 45 s, other 20 s. N callers asking the same public question cost one upstream call.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "wrapper-token",
    "parameters": [
     {
      "name": "q",
      "in": "query",
      "required": true,
      "description": "≥ 2 chars",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Aliases /members, /member-search.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "description": ""
          },
          "query": {
           "description": ""
          },
          "count": {
           "description": ""
          },
          "members": {
           "type": "array",
           "items": {},
           "description": "items: [{id, username, displayName, bio, photo, verified, hasWallet}]"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Bad parameter (unknown period/by, q too short, non-UUID id, non-integer limit)."
     },
     "401": {
      "description": "Missing, malformed or expired bearer token (message says which)."
     },
     "403": {
      "description": "Upstream rejected the token (passed through)."
     },
     "404": {
      "description": "No such endpoint; see GET /api/v1."
     },
     "429": {
      "description": "Upstream rate limit (passed through; it is the caller's budget)."
     },
     "502": {
      "description": "Upstream unreachable or returned an unexpected status."
     }
    },
    "servers": [
     {
      "url": "https://earlybirdgetstheaura.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v1/feed": {
   "get": {
    "operationId": "w-feed",
    "tags": [
     "earlybird wrapper token"
    ],
    "summary": "/feed",
    "description": "one feed page in the app's own order.\n\nAuth: the caller's own Giggles JWT as a bearer token (?token= also works for a quick test). Validated locally, forwarded unchanged, never stored or logged.\n\nNotes:\n- one feed page in the app's own order.\n- post shape: {id, userId, username, description, media, thumbnail, createdAt, market{pot, volume, holders, trades}, engagement{shares, comments, bookmarks, score}}.\n- Shared across callers by path: market 12 s, search 30 s, portfolio 30 s, profile/stats/posts 45 s, leaderboard 45 s, other 20 s. N callers asking the same public question cost one upstream call.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "wrapper-token",
    "parameters": [
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "1–50",
      "schema": {
       "type": "integer"
      }
     },
     {
      "name": "cursor",
      "in": "query",
      "required": false,
      "description": "from nextCursor",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "one feed page in the app's own order.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "description": ""
          },
          "count": {
           "description": ""
          },
          "nextCursor": {
           "description": ""
          },
          "posts": {
           "type": "array",
           "items": {},
           "description": "items: […post]"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Bad parameter (unknown period/by, q too short, non-UUID id, non-integer limit)."
     },
     "401": {
      "description": "Missing, malformed or expired bearer token (message says which)."
     },
     "403": {
      "description": "Upstream rejected the token (passed through)."
     },
     "404": {
      "description": "No such endpoint; see GET /api/v1."
     },
     "429": {
      "description": "Upstream rate limit (passed through; it is the caller's budget)."
     },
     "502": {
      "description": "Upstream unreachable or returned an unexpected status."
     }
    },
    "servers": [
     {
      "url": "https://earlybirdgetstheaura.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v1/posts/{postId}/market": {
   "get": {
    "operationId": "w-posts-market",
    "tags": [
     "earlybird wrapper token"
    ],
    "summary": "/posts/{postId}/market",
    "description": "\n\nAuth: the caller's own Giggles JWT as a bearer token (?token= also works for a quick test). Validated locally, forwarded unchanged, never stored or logged.\n\nNotes:\n- Shared across callers by path: market 12 s, search 30 s, portfolio 30 s, profile/stats/posts 45 s, leaderboard 45 s, other 20 s. N callers asking the same public question cost one upstream call.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "wrapper-token",
    "parameters": [
     {
      "name": "postId",
      "in": "path",
      "required": true,
      "description": "post id",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "description": ""
          },
          "post": {
           "description": ""
          },
          "market": {
           "type": "object",
           "description": "items: {pot, floor, level, maxInvest, holders, trades, volume, status, change24h, creatorFees, listedAt, isImport, sellLockedUntil, sellTimerSeconds}"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Bad parameter (unknown period/by, q too short, non-UUID id, non-integer limit)."
     },
     "401": {
      "description": "Missing, malformed or expired bearer token (message says which)."
     },
     "403": {
      "description": "Upstream rejected the token (passed through)."
     },
     "404": {
      "description": "No such endpoint; see GET /api/v1."
     },
     "429": {
      "description": "Upstream rate limit (passed through; it is the caller's budget)."
     },
     "502": {
      "description": "Upstream unreachable or returned an unexpected status."
     }
    },
    "servers": [
     {
      "url": "https://earlybirdgetstheaura.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v1/posts/{postId}/trades": {
   "get": {
    "operationId": "w-posts-trades",
    "tags": [
     "earlybird wrapper token"
    ],
    "summary": "/posts/{postId}/trades",
    "description": "Upstream cap of 100 and the sub-10 omission apply.\n\nAuth: the caller's own Giggles JWT as a bearer token (?token= also works for a quick test). Validated locally, forwarded unchanged, never stored or logged.\n\nNotes:\n- Upstream cap of 100 and the sub-10 omission apply.\n- Shared across callers by path: market 12 s, search 30 s, portfolio 30 s, profile/stats/posts 45 s, leaderboard 45 s, other 20 s. N callers asking the same public question cost one upstream call.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "wrapper-token",
    "parameters": [
     {
      "name": "postId",
      "in": "path",
      "required": true,
      "description": "post id",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "description": "1–100",
      "schema": {
       "type": "integer"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Upstream cap of 100 and the sub-10 omission apply.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "description": ""
          },
          "post": {
           "description": ""
          },
          "count": {
           "description": ""
          },
          "note": {
           "description": ""
          },
          "trades": {
           "type": "array",
           "items": {},
           "description": "items: [{at, type, userId, username, points, shares}]"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Bad parameter (unknown period/by, q too short, non-UUID id, non-integer limit)."
     },
     "401": {
      "description": "Missing, malformed or expired bearer token (message says which)."
     },
     "403": {
      "description": "Upstream rejected the token (passed through)."
     },
     "404": {
      "description": "No such endpoint; see GET /api/v1."
     },
     "429": {
      "description": "Upstream rate limit (passed through; it is the caller's budget)."
     },
     "502": {
      "description": "Upstream unreachable or returned an unexpected status."
     }
    },
    "servers": [
     {
      "url": "https://earlybirdgetstheaura.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v1/users/{userId}/profile": {
   "get": {
    "operationId": "w-users-profile",
    "tags": [
     "earlybird wrapper token"
    ],
    "summary": "/users/{userId}/profile",
    "description": "\n\nAuth: the caller's own Giggles JWT as a bearer token (?token= also works for a quick test). Validated locally, forwarded unchanged, never stored or logged.\n\nNotes:\n- Shared across callers by path: market 12 s, search 30 s, portfolio 30 s, profile/stats/posts 45 s, leaderboard 45 s, other 20 s. N callers asking the same public question cost one upstream call.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "wrapper-token",
    "parameters": [
     {
      "name": "userId",
      "in": "path",
      "required": true,
      "description": "user id",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "description": ""
          },
          "user": {
           "type": "object",
           "description": "items: {id, username, displayName, bio, photo, aura, auraSpendable, verified, followers, following, activityPublic}"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Bad parameter (unknown period/by, q too short, non-UUID id, non-integer limit)."
     },
     "401": {
      "description": "Missing, malformed or expired bearer token (message says which)."
     },
     "403": {
      "description": "Upstream rejected the token (passed through)."
     },
     "404": {
      "description": "No such endpoint; see GET /api/v1."
     },
     "429": {
      "description": "Upstream rate limit (passed through; it is the caller's budget)."
     },
     "502": {
      "description": "Upstream unreachable or returned an unexpected status."
     }
    },
    "servers": [
     {
      "url": "https://earlybirdgetstheaura.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v1/users/{userId}/stats": {
   "get": {
    "operationId": "w-users-stats",
    "tags": [
     "earlybird wrapper token"
    ],
    "summary": "/users/{userId}/stats",
    "description": "\n\nAuth: the caller's own Giggles JWT as a bearer token (?token= also works for a quick test). Validated locally, forwarded unchanged, never stored or logged.\n\nNotes:\n- Shared across callers by path: market 12 s, search 30 s, portfolio 30 s, profile/stats/posts 45 s, leaderboard 45 s, other 20 s. N callers asking the same public question cost one upstream call.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "wrapper-token",
    "parameters": [
     {
      "name": "userId",
      "in": "path",
      "required": true,
      "description": "user id",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "description": ""
          },
          "user": {
           "description": ""
          },
          "stats": {
           "type": "object",
           "description": "items: {rank, tradeCount, networth, realizedPnl, winRate}"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Bad parameter (unknown period/by, q too short, non-UUID id, non-integer limit)."
     },
     "401": {
      "description": "Missing, malformed or expired bearer token (message says which)."
     },
     "403": {
      "description": "Upstream rejected the token (passed through)."
     },
     "404": {
      "description": "No such endpoint; see GET /api/v1."
     },
     "429": {
      "description": "Upstream rate limit (passed through; it is the caller's budget)."
     },
     "502": {
      "description": "Upstream unreachable or returned an unexpected status."
     }
    },
    "servers": [
     {
      "url": "https://earlybirdgetstheaura.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  },
  "/api/v1/users/{userId}/posts": {
   "get": {
    "operationId": "w-users-posts",
    "tags": [
     "earlybird wrapper token"
    ],
    "summary": "/users/{userId}/posts",
    "description": "the creator's live posts, newest first; the earliest public surface for a new post.\n\nAuth: the caller's own Giggles JWT as a bearer token (?token= also works for a quick test). Validated locally, forwarded unchanged, never stored or logged.\n\nNotes:\n- the creator's live posts, newest first; the earliest public surface for a new post.\n- post shape: {id, userId, username, description, media, thumbnail, createdAt, market{pot, volume, holders, trades}, engagement{shares, comments, bookmarks, score}}.\n- Shared across callers by path: market 12 s, search 30 s, portfolio 30 s, profile/stats/posts 45 s, leaderboard 45 s, other 20 s. N callers asking the same public question cost one upstream call.",
    "x-verified": true,
    "x-confidence": "verified",
    "x-auth": "token",
    "x-doc-anchor": "wrapper-token",
    "parameters": [
     {
      "name": "userId",
      "in": "path",
      "required": true,
      "description": "user id",
      "schema": {
       "type": "string",
       "format": "uuid"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "the creator's live posts, newest first; the earliest public surface for a new post.",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "ok": {
           "type": "boolean",
           "description": ""
          },
          "user": {
           "description": ""
          },
          "count": {
           "description": ""
          },
          "posts": {
           "type": "array",
           "items": {},
           "description": "items: […post]"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Bad parameter (unknown period/by, q too short, non-UUID id, non-integer limit)."
     },
     "401": {
      "description": "Missing, malformed or expired bearer token (message says which)."
     },
     "403": {
      "description": "Upstream rejected the token (passed through)."
     },
     "404": {
      "description": "No such endpoint; see GET /api/v1."
     },
     "429": {
      "description": "Upstream rate limit (passed through; it is the caller's budget)."
     },
     "502": {
      "description": "Upstream unreachable or returned an unexpected status."
     }
    },
    "servers": [
     {
      "url": "https://earlybirdgetstheaura.com"
     }
    ],
    "security": [
     {
      "bearerAuth": []
     }
    ]
   }
  }
 },
 "components": {
  "securitySchemes": {
   "bearerAuth": {
    "type": "http",
    "scheme": "bearer",
    "bearerFormat": "JWT",
    "description": "A live Supabase access token (the app session's JWT)."
   },
   "publishableKey": {
    "type": "apiKey",
    "in": "header",
    "name": "apikey",
    "description": "The Supabase project's publishable (anon) key; not a secret, grants nothing alone."
   }
  }
 },
 "x-websocket": [
  {
   "name": "realtime trade tape",
   "summary": "Push stream of trades over Centrifugo.",
   "notes": [
    "~10-aura publish floor; sells lack username; duplicates occur.",
    "No post-creation events. Empty posts are invisible on the stream."
   ],
   "x-verified": false,
   "x-doc-anchor": "ep-tape"
  }
 ]
}
