# Sunday Squares > Sports pools with friends. A Square is a grid on one game (10 by 10, or 5 by 5) where players claim boxes and the drawn numbers decide who wins each period. A Pool is Survivor or Losers: one team a week across a football season, and a wrong pick is a strike. People play at sundaysquares.com. An agent reads the public pages freely and, once a person connects it or hands it a key, plays and hosts as that person. ## What Sunday Squares is - A host creates a game as a draft, which costs nothing, then publishes it with Hosting Credits to open it to players. Players claim boxes on a Square or take entries in a Pool and make their picks. Sunday Squares keeps score and keeps the list of who has paid. - Money never moves through Sunday Squares. Players pay the host and the host pays winners on Venmo, PayPal, or Cash App: we show a pay button with the amount filled in, and the host marks each one paid. Buying Hosting Credits answers a Stripe checkout page that only a person can finish. No action here completes a payment, and no agent can. - Fourway, the tournament format, has no agent actions yet. Its pages read like any other. ## Acting on Sunday Squares Three doors, one set of actions behind them. Pick the door by where you run. ### Conversational agents: MCP People connecting an AI app can follow the setup and permissions guide at `https://sundaysquares.com/ai`. Connect to `https://sundaysquares.com/mcp` (Streamable HTTP, POST only, no session to keep). The endpoint is OAuth only: an API key sent there is refused. - Discovery: `https://sundaysquares.com/.well-known/oauth-protected-resource/mcp` names the authorization server, and `https://sundaysquares.com/.well-known/oauth-authorization-server` lists its endpoints (issuer `https://sundaysquares.com/api/auth`; authorize, token, register, and JWKS under it at `/oauth2/authorize`, `/oauth2/token`, `/oauth2/register`, and `/jwks`). A first POST without a token answers 401 with a `WWW-Authenticate` header that points at the first document. - Registration: either a client id metadata document (your `client_id` is an HTTPS URL to a JSON document you host) or dynamic registration at `/oauth2/register`. PKCE is required either way. - Linking an account: the authorization code flow. The person signs in to Sunday Squares, reads a consent screen that says what the connection may do in plain words (one sentence per scope, listed below), and allows or denies. A consent that covers the scopes you asked for is remembered, so later authorizations skip the screen. - Tokens: an access token lasts one hour. Ask for `offline_access` beside the game scopes to get a refresh token, good for 30 days and rotated on each use; without it the person has to authorize again every hour. - Tools: one tool per action below, named as the action is named, with the action's input and output schemas and hints (read-only, destructive, idempotent). A success answers the output as `structuredContent`; a refusal answers `isError` with the error envelope (`code`, `message`, `details`, `retryable`) under `_meta.error` and in the text block. - Retries: send `_meta.idempotencyKey` on an idempotent mutation to name your own key. Without one, the same call with the same input within the same five-minute window replays the first answer with `_meta.replayed: true`. ### Direct HTTP: the REST API The spec is `https://sundaysquares.com/openapi.json` (OpenAPI 3.1, generated from the same registry: every action as one operation whose `operationId` is the action's name, with request and response schemas, error codes, and examples). - Auth: a signed-in person creates a key on their profile page (`https://sundaysquares.com/dashboard/profile`) and gives it to you. Send it as `x-api-key`. The key acts as that person under `/api/v1` and nowhere else: it never reaches account settings, other keys, or admin routes. Limit: 120 requests per minute per key, then `429 RATE_LIMITED`. - Envelope: every answer is `{"ok": true, "data": ...}` or `{"ok": false, "error": {"code", "message", "details", "retryable"}}`. Read `error.code`, not the message; the codes and their statuses are listed below. Validation fails with `400 VALIDATION_ERROR` and `details.fieldErrors` keyed by field. - Retries: send an `Idempotency-Key` header (any string up to 255 characters) on an idempotent mutation. The same key with the same input replays the first answer; the same key with different input answers `409 IDEMPOTENCY_KEY_REUSED`; a key whose first call is still running answers `409 RESOURCE_CONFLICT`. A record lasts 24 hours. - Tracing: action responses carry an `x-request-id` header. Send your own (letters, digits, `.`, `_`, `:`, `-`, up to 128 characters) and it comes back; quote it when you report a problem. Requests refused by the API-key middleware do not carry this header. - Pages: `find_games` and `list_my_games` take `cursor` and `limit` and answer `items`, `nextCursor`, and `total`. Pass `nextCursor` back until none comes. `list_square_members` and `list_pool_entries` return the full roster in `members` and `entries`, respectively, without pagination. - Private games: a password-protected Square or Pool takes its `password` inline (a query parameter on the read, a body field on the join or the first claim). Players already in the game need none. ### Inside the browser: WebMCP Every page of sundaysquares.com registers the actions below as tools on `document.modelContext` when the browser provides it, for an agent that lives inside the browser. They ride the signed-in session, so no key or token is needed, and they call the same REST v1 routes. Every page offers the actions that need no game (`find_games`, `list_my_games`, `create_square`, `get_pool_creation_options`, `create_pool`, `list_credit_bundles`, `get_credit_balance`, `create_credit_checkout`); a Square's page adds the Square's actions with that Square filled in, and a Pool's page adds the Pool's the same way. On a Square's page, `claim_square_box` also takes a `boxNumber` as the grid shows it, in place of a `boxId`. ## What an agent can do ### Vocabulary - `league` is one of `NFL`, `NCAAF`, `NBA`, `NCAAB`, `WNBA`, `NCAAW`, `MLB`, `NHL`, `NWSL`, `WPHL`. A Square goes on one game in any of them; a Pool goes on a whole `NFL` or `NCAAF` season. - A Pool's `format` is `SURVIVOR` or `LOSERS`. - A game you host or play is in one `lifecycle` state: `draft`, `upcoming`, `active`, `completed`. - Ids are opaque strings; a Pool entry is an entry in the path and `memberId` in the action input. ### Games - `find_games`, `GET /api/v1/games`. Find games to host a Square on. Anyone may call it; a key or token needs `games:read`. A read; repeat it freely. - `list_my_games`, `GET /api/v1/me/games`. List the games you host or play. Signed in, with `hosting:read`. A read; repeat it freely. ### Squares - `get_square`, `GET /api/v1/squares/{squareId}`. Read a Square. Signed in or not; a signed-in caller's key or token needs `games:read`. A read; repeat it freely. - `create_square`, `POST /api/v1/squares`. Create a draft Square. Signed in, with `squares:write`. Idempotent: a retry with the same key replays the first answer and runs nothing twice. - `update_square`, `PATCH /api/v1/squares/{squareId}`. Edit a Square's settings. Signed in, with `squares:write`. Not idempotent: a retry runs it again. - `publish_square`, `POST /api/v1/squares/{squareId}/publish`. Publish a Square. Signed in, with `squares:write`. Idempotent: a retry with the same key replays the first answer and runs nothing twice. - `delete_square`, `DELETE /api/v1/squares/{squareId}`. Delete a draft Square. Signed in, with `squares:write`. Not idempotent: a retry runs it again. - `claim_square_box`, `POST /api/v1/squares/{squareId}/boxes/{boxId}/claim`. Claim a box on a Square. Signed in, with `squares:write`. Idempotent: a retry with the same key replays the first answer and runs nothing twice. ### Pools - `get_pool`, `GET /api/v1/pools/{poolId}`. Read a Pool. Signed in or not; a signed-in caller's key or token needs `games:read`. A read; repeat it freely. - `get_pool_creation_options`, `GET /api/v1/pools/options`. Read the available Pool leagues, seasons, and weeks. Signed in or not; a signed-in caller's key or token needs `games:read`. A read; repeat it freely. - `create_pool`, `POST /api/v1/pools`. Create a draft Pool. Signed in, with `pools:write`. Idempotent: a retry with the same key replays the first answer and runs nothing twice. - `update_pool`, `PATCH /api/v1/pools/{poolId}`. Edit a Pool's settings. Signed in, with `pools:write`. Not idempotent: a retry runs it again. - `publish_pool`, `POST /api/v1/pools/{poolId}/publish`. Publish a Pool. Signed in, with `pools:write`. Idempotent: a retry with the same key replays the first answer and runs nothing twice. - `delete_pool`, `DELETE /api/v1/pools/{poolId}`. Delete a draft Pool. Signed in, with `pools:write`. Not idempotent: a retry runs it again. - `join_pool`, `POST /api/v1/pools/{poolId}/entries`. Join a Pool. Signed in, with `pools:write`. Idempotent: a retry with the same key replays the first answer and runs nothing twice. - `set_pool_pick`, `PUT /api/v1/pools/{poolId}/entries/{entryId}/pick`. Set an entry's pick for a week. Signed in, with `pools:write`. Idempotent: a retry with the same key replays the first answer and runs nothing twice. - `clear_pool_pick`, `DELETE /api/v1/pools/{poolId}/entries/{entryId}/pick`. Clear an entry's pick for a week. Signed in, with `pools:write`. Idempotent: a retry with the same key replays the first answer and runs nothing twice. ### Members - `list_square_members`, `GET /api/v1/squares/{squareId}/members`. List a Square's members. Signed in, with `members:read`. A read; repeat it freely. - `list_pool_entries`, `GET /api/v1/pools/{poolId}/entries`. List a Pool's entries. Signed in, with `members:read`. A read; repeat it freely. - `update_square_member_payment`, `PATCH /api/v1/squares/{squareId}/members/{memberId}/payment`. Record a member's boxes as paid or unpaid. Signed in, with `members:write`. Idempotent: a retry with the same key replays the first answer and runs nothing twice. - `update_pool_entry_payment`, `PATCH /api/v1/pools/{poolId}/entries/{entryId}/payment`. Record an entry as paid or unpaid. Signed in, with `members:write`. Idempotent: a retry with the same key replays the first answer and runs nothing twice. ### Credits - `list_credit_bundles`, `GET /api/v1/credits/bundles`. List the Hosting Credits bundles on sale. Anyone may call it; a key or token needs `credits:read`. A read; repeat it freely. - `get_credit_balance`, `GET /api/v1/credits/balance`. Check your Hosting Credits. Signed in, with `credits:read`. A read; repeat it freely. - `create_credit_checkout`, `POST /api/v1/credits/checkout`. Start a Hosting Credits purchase. Signed in, with `credits:checkout`. Not idempotent: a retry runs it again. ### Error codes Every refusal carries one of these in `error.code`; the status follows the code, and `retryable` says whether the same call may succeed later. | Code | Status | Retryable | Meaning | | --- | --- | --- | --- | | `VALIDATION_ERROR` | 400 | no | Something in the request is not right. | | `UNAUTHENTICATED` | 401 | no | You need to sign in for that. | | `FORBIDDEN` | 403 | no | You are not allowed to do that. | | `NOT_FOUND` | 404 | no | We could not find that. | | `GAME_NOT_FOUND` | 404 | no | We could not find that game. | | `GAME_NOT_ELIGIBLE` | 409 | no | That game is not open for this. | | `RESOURCE_CONFLICT` | 409 | yes | That request is still in progress. Try again shortly. | | `HOSTING_CREDITS_REQUIRED` | 402 | no | You need more Hosting Credits for that. | | `ALREADY_PUBLISHED` | 409 | no | This game is already published. | | `NOT_PUBLISHABLE` | 409 | no | This game is not ready to publish. | | `PICK_LOCKED` | 409 | no | That pick is locked. | | `BOX_UNAVAILABLE` | 409 | no | That box is taken. Pick another. | | `ENTRY_LIMIT_REACHED` | 409 | no | You have reached the entry limit for this Pool. | | `POOL_NOT_OPEN` | 409 | no | This Pool is not open for entries. | | `PASSWORD_REQUIRED` | 403 | no | This game needs a password. | | `LEAGUE_NOT_AVAILABLE` | 403 | no | That league is not available right now. | | `IDEMPOTENCY_KEY_REUSED` | 409 | no | That idempotency key was already used with a different request. | | `RATE_LIMITED` | 429 | yes | Too many requests. Slow down and try again. | | `INTERNAL_ERROR` | 500 | yes | That didn't go through on our end. Try again in a minute. | ### Scopes A key or a connection holds some of these; an action refuses with `FORBIDDEN`, naming the scope, when the credential lacks one it needs. - `games:read`: See games on the schedule and the Squares and Pools you can view. - `hosting:read`: See the games you host and play in. - `squares:write`: Create, publish, edit, and delete your Squares, and claim boxes for you. - `pools:write`: Create, publish, edit, and delete your Pools, join Pools, and make your picks. - `members:read`: See the players in the games you host and who has paid. - `members:write`: Mark players paid in the games you host. - `credits:read`: See how many Hosting Credits you have. - `credits:checkout`: Start a Hosting Credits checkout for you to finish. - `offline_access` (MCP only): Stay connected for 30 days at a time instead of asking you again every hour. ## The four flows Each step names the action; its REST operation is the one beside it above, and the MCP and WebMCP tools carry the action's name. 1. **Find a game.** `find_games` with a `league` and, if you like, a `search` for a team name lists upcoming games in kickoff order; a game's `id` is what `create_square` asks for. Public game pages live under `/games`. Before offering Pool choices, call `get_pool_creation_options`: it returns the caller's enabled leagues, live season week windows, and which weeks can start a Pool. Offer only leagues and seasons returned there, not every value in the static input schema. A Pool takes a `league`, a `seasonYear`, and scheduled start/end weeks; the end week must be at or after the start. 2. **Host a Square or a Pool.** `get_credit_balance` first: how many credits the person holds, and what publishing costs. `create_square` (or `create_pool`) makes a draft, which costs nothing and is visible only to its host; the answer carries the game, its `publicUrl`, and its `manageUrl`. `update_square` (`update_pool`) changes the settings: the name, description, handles, instructions, and password at any time, and the price and payout fields until it's published, when they lock. `publish_square` (`publish_pool`) spends the Hosting Credits and opens it to players; the answer carries `publicUrl` and `creditsRemaining`. A shortfall refuses with `HOSTING_CREDITS_REQUIRED` and `details.creditsRequired`, `creditsAvailable`, and `creditsUrl`; nothing is charged, and flow 4 is the next step. Later, `list_square_members` (`list_pool_entries`) shows who has paid, and `update_square_member_payment` (`update_pool_entry_payment`) records the mark the host made. 3. **Play.** In a Pool: `get_pool` reads it and returns `picks` for players and the host. Use it to answer what someone picked for a week: each pick includes its `week`, `poolMemberId`, and chosen `team`; match `poolMemberId` to the returned `members`. Players see all their own entries' picks, including before kickoff, plus other entries' revealed picks. The host sees all picks; visitors without an entry receive no picks. `join_pool` takes one or more entries and answers them with the Pool as a player sees it, `set_pool_pick` sets an entry's pick for a week (a `teamId` in a `gameId` that week), and `clear_pool_pick` takes it back; a pick locks at its game's kickoff. On a Square: `get_square` lists the boxes and their states (a visitor to a public Square gets enough to choose an open box), then `claim_square_box` takes one; a first claim joins the person as a player. 4. **Buy Hosting Credits.** `list_credit_bundles` lists what is on sale, with each bundle's credits and its price in dollars; it needs no sign-in, so an agent can price hosting first. `create_credit_checkout` with a `bundleId` from that list answers a Stripe `checkoutUrl`. Hand it to the person; they open it, they pay, and the credits land on their account. Pass a site-relative `returnTo` (a game's `publicUrl` path, say) and Stripe sends them back there afterwards, paid or not; without one they land on the credits pages. ## Pages worth reading - https://sundaysquares.com/how-squares-work: the grid, the drawn numbers, and the payouts - https://sundaysquares.com/how-pools-work: Survivor and Losers rules - https://sundaysquares.com/how-fourways-work: the tournament pick format - https://sundaysquares.com/games: public games by league and season - https://sundaysquares.com/credits: what publishing costs, and the bundles - https://sundaysquares.com/faq: the questions hosts and players ask A host can change a Pool entry price after publishing while no other player has an entry. A changed price clears existing entry payment marks: free entries become paid, and positive-price entries become unpaid.