Checks the speaking rules β trading is still open, the text is 1β500
characters, a side is settled β and returns the summary to confirm
plus, when publishing is possible, a confirm_token for
POST /opinions. Charges nothing. Requires the write scope;
counts against the read window only.
π¨ Changed 2026-09-15: holding a position is no longer required,
and publishing no longer costs a coin. The opinion list is still a
two-sided debate, so a side must still be settled:
the owner holds a position β the side is their staked side and
side_option_id in the request is ignored;
no position β side_option_id is required; omitting it
returns can_publish: false with reason SIDE_REQUIRED, and an
option id from another topic is a 400.
The side is part of the confirm tokenβs parameter hash β previewing a
different side yields a different token, so a token can never publish
a side other than the one that was previewed.
Personal API key issued per SeeSaw user account:
Authorization: Bearer sspat_β¦. Manage keys on the app API
(/v1/users/me/api-keys). Revocation propagates within ~60 seconds
(validation cache window).
The key's scopes decide what it may do: every key has read; a key
created with ["read","write"] may also use the six write operations
(marked x-required-scope: write in this document). Scopes are fixed at
creation β to get write access, create a new key. A read-only key
calling a write operation gets 403 FORBIDDEN_SCOPE.
Which side this opinion argues. Required when the owner
holds no position on the topic; ignored (the staked side
wins) when they do. Must be an option of this topic.
What the user confirms before POST /opinions. side_option_label is
the side the opinion will be attributed to β the owner's staked side
when they hold a position, otherwise the side they picked via
side_option_id.
Present only when can_publish is false. TOPIC_CLOSED takes
priority; SIDE_REQUIRED means the owner holds no position and
sent no side_option_id; CONTENT_INVALID means empty or over
500 characters.
β οΈ NO_POSITION was removed 2026-09-15 β holding a position is no
longer a precondition for publishing.
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"SeeSaw Developer","colors":{"primary":"#04A363","light":"#0BC97D","dark":"#04A363"},"footer":{"socials":{"website":"https://seesaw.fun"}},"styling":{"latex":false},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"seesaw","hasAudioNarration":false,"actualSubdomain":"seesaw","customDomains":["docs.seesaw.fun"],"gitSource":{"type":"github","owner":"seesawtech","repo":"seesaw-developer-docs","deployBranch":"main","contentDirectory":"","isPrivate":true,"mountPath":"","label":""},"chroma":{"collectionId":"f9af2bf3-38a8-4a14-bb52-4614900390d7","liveDeploymentHistoryId":"6ab4c2fa6db8efdbd0b1fe11","schemaVersion":"v3","latestBuiltDeploymentHistoryId":"6ab4c2fa6db8efdbd0b1fe11"},"entitlements":{"AI_CHAT":{"status":"DISABLED"}},"buildId":"6ab4c2fa6db8efdbd0b1fe11","clientVersion":"0.0.4054","searchFilterCounts":[{"language":"default","tag":"Guides","count":31},{"language":"default","tag":"MCP Server","count":24},{"language":"default","tag":"API Reference","count":19},{"language":"default","tag":"Tools","count":19},{"language":"default","tag":"Endpoints","count":18},{"language":"default","tag":"Read","count":12},{"language":"default","tag":"Me","count":7},{"language":"default","tag":"Write","count":6},{"language":"default","tag":"Topics","count":5},{"language":"default","tag":"Client Setup","count":4}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableContextualStarterQuestions":false,"enableAskAiSearchBar":true},"basePath":"","hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"rbac-expansion","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"full-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"byok-sep-25","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"vite-origin","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-updated-9-15","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-dual-write","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"sso-setup-deadline-banner","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-serve","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":false},"customCss":[],"page":{"pagePath":"api-reference/opinions/preview-publishing-an-opinion-step-1-of-2","slug":"/api-reference/opinions/preview-publishing-an-opinion-step-1-of-2","pageMetadata":{"title":"Preview publishing an opinion (step 1 of 2)","description":"Checks the speaking rules β trading is still open, the text is 1β500\ncharacters, a side is settled β and returns the summary to confirm\nplus, when publishing is possible, a `confirm_token` for\n`POST /opinions`. **Charges nothing.** Requires the `write` scope;\ncounts against the read window only.\n\nπ¨ **Changed 2026-09-15**: holding a position is no longer required,\nand publishing no longer costs a coin. The opinion list is still a\ntwo-sided debate, so a side must still be settled:\n\n- **the owner holds a position** β the side is their staked side and\n `side_option_id` in the request is ignored;\n- **no position** β `side_option_id` is **required**; omitting it\n returns `can_publish: false` with reason `SIDE_REQUIRED`, and an\n option id from another topic is a 400.\n\nThe side is part of the confirm token's parameter hash β previewing a\ndifferent side yields a different token, so a token can never publish\na side other than the one that was previewed.\n","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml post /topics/{id}/opinion-preview","href":"/api-reference/opinions/preview-publishing-an-opinion-step-1-of-2","autogeneratedByOpenApi":true},"description":{"compiledSource":"\"use strict\";\nconst {Fragment: _Fragment, jsx: _jsx, jsxs: _jsxs} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n code: \"code\",\n li: \"li\",\n p: \"p\",\n strong: \"strong\",\n ul: \"ul\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsxs(_Fragment, {\n children: [_jsxs(_components.p, {\n children: [\"Checks the speaking rules β trading is still open, the text is 1β500\\ncharacters, a side is settled β and returns the summary to confirm\\nplus, when publishing is possible, a \", _jsx(_components.code, {\n children: \"confirm_token\"\n }), \" for\\n\", _jsx(_components.code, {\n children: \"POST /opinions\"\n }), \". \", _jsx(_components.strong, {\n children: \"Charges nothing.\"\n }), \" Requires the \", _jsx(_components.code, {\n children: \"write\"\n }), \" scope;\\ncounts against the read window only.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"π¨ \", _jsx(_components.strong, {\n children: \"Changed 2026-09-15\"\n }), \": holding a position is no longer required,\\nand publishing no longer costs a coin. The opinion list is still a\\ntwo-sided debate, so a side must still be settled:\"]\n }), \"\\n\", _jsxs(_components.ul, {\n children: [\"\\n\", _jsxs(_components.li, {\n children: [_jsx(_components.strong, {\n children: \"the owner holds a position\"\n }), \" β the side is their staked side and\\n\", _jsx(_components.code, {\n children: \"side_option_id\"\n }), \" in the request is ignored;\"]\n }), \"\\n\", _jsxs(_components.li, {\n children: [_jsx(_components.strong, {\n children: \"no position\"\n }), \" β \", _jsx(_components.code, {\n children: \"side_option_id\"\n }), \" is \", _jsx(_components.strong, {\n children: \"required\"\n }), \"; omitting it\\nreturns \", _jsx(_components.code, {\n children: \"can_publish: false\"\n }), \" with reason \", _jsx(_components.code, {\n children: \"SIDE_REQUIRED\"\n }), \", and an\\noption id from another topic is a 400.\"]\n }), \"\\n\"]\n }), \"\\n\", _jsx(_components.p, {\n children: \"The side is part of the confirm tokenβs parameter hash β previewing a\\ndifferent side yields a different token, so a token can never publish\\na side other than the one that was previewed.\"\n })]\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){return _jsx(_Fragment,{})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Preview publishing an opinion (step 1 of 2)","description":"Checks the speaking rules β trading is still open, the text is 1β500\ncharacters, a side is settled β and returns the summary to confirm\nplus, when publishing is possible, a `confirm_token` for\n`POST /opinions`. **Charges nothing.** Requires the `write` scope;\ncounts against the read window only.\n\nπ¨ **Changed 2026-09-15**: holding a position is no longer required,\nand publishing no longer costs a coin. The opinion list is still a\ntwo-sided debate, so a side must still be settled:\n\n- **the owner holds a position** β the side is their staked side and\n `side_option_id` in the request is ignored;\n- **no position** β `side_option_id` is **required**; omitting it\n returns `can_publish: false` with reason `SIDE_REQUIRED`, and an\n option id from another topic is a 400.\n\nThe side is part of the confirm token's parameter hash β previewing a\ndifferent side yields a different token, so a token can never publish\na side other than the one that was previewed.\n","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml post /topics/{id}/opinion-preview","href":"/api-reference/opinions/preview-publishing-an-opinion-step-1-of-2","autogeneratedByOpenApi":true}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[],"codeExamples":{}},"jsLinks":[],"openApiReferenceData":{"operation":{"operationId":"previewOpinion","tags":["Opinions"],"summary":"Preview publishing an opinion (step 1 of 2)","x-required-scope":"write","description":"Checks the speaking rules β trading is still open, the text is 1β500\ncharacters, a side is settled β and returns the summary to confirm\nplus, when publishing is possible, a `confirm_token` for\n`POST /opinions`. **Charges nothing.** Requires the `write` scope;\ncounts against the read window only.\n\nπ¨ **Changed 2026-09-15**: holding a position is no longer required,\nand publishing no longer costs a coin. The opinion list is still a\ntwo-sided debate, so a side must still be settled:\n\n- **the owner holds a position** β the side is their staked side and\n `side_option_id` in the request is ignored;\n- **no position** β `side_option_id` is **required**; omitting it\n returns `can_publish: false` with reason `SIDE_REQUIRED`, and an\n option id from another topic is a 400.\n\nThe side is part of the confirm token's parameter hash β previewing a\ndifferent side yields a different token, so a token can never publish\na side other than the one that was previewed.\n","parameters":["895c9568-f141-4c32-9dc2-68bd7d3b8733"],"requestBody":"c1a5454a-ba28-4b79-9c2a-4086d13fbc6f","responses":{"200":"3e486e93-86be-4f0e-8455-afea9e029402","400":"defd62b3-8c8e-458e-9583-66b2247564e2","401":"85528ef8-bc5c-40cb-941a-699f8c5b42ab","403":"889a2273-22a4-4a7e-a29e-ab5d26286a7a","404":"9434a9e9-449a-434f-a2b8-b1b30ae5bb12","429":"11cc5fec-caa8-4da8-b298-320cbb17385a","500":"09882573-b72e-427e-84f6-cc350ced1e07"},"method":"post","type":"path","servers":["8b57a1c3-4bcf-4125-a8ec-f3cb0c2082e6"],"security":["981d7369-b6b3-48d9-be19-e49d959eb5ba"],"path":"/topics/{id}/opinion-preview","baseUrlOptions":["https://api.seesaw.fun/open/v1"],"title":"Preview publishing an opinion (step 1 of 2)","codeSamples":[],"requiredOnlyExamples":false,"requestExampleType":"generated","responseExampleType":"generated"},"dependencies":{"parameters":{"895c9568-f141-4c32-9dc2-68bd7d3b8733":{"name":"id","in":"path","required":true,"description":"Topic UUID.","schema":{"type":"string","format":"uuid","isRequired":true,"description":"Topic UUID.","uniqueKey":"","typeLabel":"string\u003cuuid>"}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["content"],"properties":{"content":{"type":"string","minLength":1,"maxLength":500,"isRequired":true,"uniqueKey":"content","typeLabel":"string"},"reply_to_opinion_id":{"type":"string","format":"uuid","description":"Omit for a top-level opinion.","uniqueKey":"reply_to_opinion_id","typeLabel":"string\u003cuuid>"},"side_option_id":{"type":"string","format":"uuid","description":"Which side this opinion argues. **Required when the owner\nholds no position on the topic**; ignored (the staked side\nwins) when they do. Must be an option of this topic.\n","uniqueKey":"side_option_id","typeLabel":"string\u003cuuid>"}},"uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"content":"\u003cstring>","reply_to_opinion_id":"3c90c3cc-0d44-4b50-8888-8dd25736052a","side_option_id":"3c90c3cc-0d44-4b50-8888-8dd25736052a"}}},"exampleType":"generated"}},"exampleType":"generated"},"responses":{"3e486e93-86be-4f0e-8455-afea9e029402":{"description":"The preview (check `can_publish` before confirming)","content":{"application/json":{"schema":{"type":"object","required":["can_publish","summary"],"properties":{"can_publish":{"type":"boolean","isRequired":true,"uniqueKey":"can_publish","typeLabel":"boolean"},"summary":{"type":"object","description":"What the user confirms before `POST /opinions`. `side_option_label` is\nthe side the opinion will be attributed to β the owner's staked side\nwhen they hold a position, otherwise the side they picked via\n`side_option_id`.\n","required":["topic_title","fee_coins","balance_after","side_option_label","content_preview","reply_to"],"properties":{"topic_title":{"type":"string","isRequired":true,"uniqueKey":"summary.topic_title","typeLabel":"string"},"fee_coins":{"type":"string","description":"What publishing costs. Currently `\"0.0000\"` β publishing is free.","isRequired":true,"uniqueKey":"summary.fee_coins","typeLabel":"string"},"balance_after":{"type":"string","isRequired":true,"uniqueKey":"summary.balance_after","typeLabel":"string"},"side_option_label":{"type":"string","isRequired":true,"uniqueKey":"summary.side_option_label","typeLabel":"string"},"content_preview":{"type":"string","description":"First 140 characters, with an ellipsis when truncated.","isRequired":true,"uniqueKey":"summary.content_preview","typeLabel":"string"},"reply_to":{"oneOf":[{"type":"object","required":["id","excerpt"],"properties":{"id":{"type":"string","format":"uuid","isRequired":true,"uniqueKey":"summary.reply_to.oneOf.0.id","typeLabel":"string\u003cuuid>"},"excerpt":{"type":"string","isRequired":true,"uniqueKey":"summary.reply_to.oneOf.0.excerpt","typeLabel":"string"}},"isRequired":true,"uniqueKey":"summary.reply_to.oneOf.0","typeLabel":"object"},{"type":"null","isRequired":true,"uniqueKey":"summary.reply_to.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"summary.reply_to","typeLabel":"object | null"}},"isRequired":true,"uniqueKey":"summary","typeLabel":"object"},"reason":{"type":"string","enum":["TOPIC_CLOSED","SIDE_REQUIRED","CONTENT_INVALID"],"description":"Present only when `can_publish` is false. `TOPIC_CLOSED` takes\npriority; `SIDE_REQUIRED` means the owner holds no position and\nsent no `side_option_id`; `CONTENT_INVALID` means empty or over\n500 characters.\n\nβ οΈ `NO_POSITION` was removed 2026-09-15 β holding a position is no\nlonger a precondition for publishing.\n","uniqueKey":"reason","typeLabel":"enum\u003cstring>"},"confirm_token":{"type":"string","uniqueKey":"confirm_token","typeLabel":"string"},"expires_at":{"type":"string","format":"date-time","uniqueKey":"expires_at","typeLabel":"string\u003cdate-time>"}},"uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"can_publish":true,"summary":{"topic_title":"\u003cstring>","fee_coins":"\u003cstring>","balance_after":"\u003cstring>","side_option_label":"\u003cstring>","content_preview":"\u003cstring>","reply_to":{"id":"3c90c3cc-0d44-4b50-8888-8dd25736052a","excerpt":"\u003cstring>"}},"reason":"TOPIC_CLOSED","confirm_token":"\u003cstring>","expires_at":"2023-11-07T05:31:56Z"}}},"exampleType":"generated"}},"exampleType":"generated"},"defd62b3-8c8e-458e-9583-66b2247564e2":{"description":"Invalid argument (`details.field` names the offending parameter)","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["UNAUTHORIZED","FORBIDDEN","FORBIDDEN_SCOPE","NOT_FOUND","OPINION_NOT_FOUND","INVALID_ARGUMENT","RATE_LIMITED","SERVICE_DISABLED","INTERNAL","CONFIRM_TOKEN_INVALID","CONFIRM_TOKEN_EXPIRED","CONFIRM_TOKEN_USED","CONFIRM_UNAVAILABLE","MECHANISM_UNSUPPORTED","PREVIEW_UNAVAILABLE","TOPIC_ALREADY_STAKED","TOPIC_CLOSED","TOPIC_QUOTE_EXPIRED","TOPIC_QUOTE_INVALID","TOPIC_MECHANISM_MISMATCH","STAKE_OUT_OF_RANGE","RETRY_IDEMPOTENCY_KEY","OPINION_ALREADY_BACKED","OPINION_BACK_PRICE_MOVED","OPINION_BACK_RETRY","OPINION_BACK_FULL","OPINION_BACK_CHAIN_BROKEN","OPINION_SIDE_REQUIRED","OPINION_SIDE_INVALID","OPINION_TOPIC_CLOSED","OPINION_TOPIC_HIDDEN","OPINIONS_UNAVAILABLE","INSUFFICIENT_BALANCE","CONTENT_INVALID","CONTENT_MODERATED","USER_PUNISHED"],"description":"Stable contract values. `SERVICE_DISABLED` (503) is the\nemergency kill switch; `FORBIDDEN_SCOPE` (403) means the key\nlacks the `write` scope. The `CONFIRM_TOKEN_*` and rule codes\nonly occur on the write surface. `OPINION_SIDE_REQUIRED` /\n`OPINION_SIDE_INVALID` are rare but reachable: `preview_opinion`\nalready answers a missing or invalid side with\n`can_publish: false` (reason `SIDE_REQUIRED`), so they surface\nas a 400 from `publish_opinion` only when the owner's position\nchanged between preview and publish (e.g. no position at\npreview, then an order on the other side before publishing) and\nthe side in the token no longer fits.\n","isRequired":true,"uniqueKey":"error.code","typeLabel":"enum\u003cstring>"},"message":{"type":"string","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"details":{"type":"object","description":"Optional context, e.g. `{\"field\": \"limit\"}`, `{\"retry_after\": 12}` or `{\"required_scope\": \"write\"}`.","additionalProperties":true,"uniqueKey":"error.details","typeLabel":"object"}},"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"error":{"code":"UNAUTHORIZED","message":"\u003cstring>","details":{}}}}},"exampleType":"generated"}},"exampleType":"generated"},"85528ef8-bc5c-40cb-941a-699f8c5b42ab":{"description":"Missing, invalid, revoked, or expired API key (carries `WWW-Authenticate`)","headers":{"WWW-Authenticate":{"schema":{"type":"string"},"description":"Bearer realm=\"seesaw-open-api\""}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["UNAUTHORIZED","FORBIDDEN","FORBIDDEN_SCOPE","NOT_FOUND","OPINION_NOT_FOUND","INVALID_ARGUMENT","RATE_LIMITED","SERVICE_DISABLED","INTERNAL","CONFIRM_TOKEN_INVALID","CONFIRM_TOKEN_EXPIRED","CONFIRM_TOKEN_USED","CONFIRM_UNAVAILABLE","MECHANISM_UNSUPPORTED","PREVIEW_UNAVAILABLE","TOPIC_ALREADY_STAKED","TOPIC_CLOSED","TOPIC_QUOTE_EXPIRED","TOPIC_QUOTE_INVALID","TOPIC_MECHANISM_MISMATCH","STAKE_OUT_OF_RANGE","RETRY_IDEMPOTENCY_KEY","OPINION_ALREADY_BACKED","OPINION_BACK_PRICE_MOVED","OPINION_BACK_RETRY","OPINION_BACK_FULL","OPINION_BACK_CHAIN_BROKEN","OPINION_SIDE_REQUIRED","OPINION_SIDE_INVALID","OPINION_TOPIC_CLOSED","OPINION_TOPIC_HIDDEN","OPINIONS_UNAVAILABLE","INSUFFICIENT_BALANCE","CONTENT_INVALID","CONTENT_MODERATED","USER_PUNISHED"],"description":"Stable contract values. `SERVICE_DISABLED` (503) is the\nemergency kill switch; `FORBIDDEN_SCOPE` (403) means the key\nlacks the `write` scope. The `CONFIRM_TOKEN_*` and rule codes\nonly occur on the write surface. `OPINION_SIDE_REQUIRED` /\n`OPINION_SIDE_INVALID` are rare but reachable: `preview_opinion`\nalready answers a missing or invalid side with\n`can_publish: false` (reason `SIDE_REQUIRED`), so they surface\nas a 400 from `publish_opinion` only when the owner's position\nchanged between preview and publish (e.g. no position at\npreview, then an order on the other side before publishing) and\nthe side in the token no longer fits.\n","isRequired":true,"uniqueKey":"error.code","typeLabel":"enum\u003cstring>"},"message":{"type":"string","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"details":{"type":"object","description":"Optional context, e.g. `{\"field\": \"limit\"}`, `{\"retry_after\": 12}` or `{\"required_scope\": \"write\"}`.","additionalProperties":true,"uniqueKey":"error.details","typeLabel":"object"}},"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"error":{"code":"UNAUTHORIZED","message":"\u003cstring>","details":{}}}}},"exampleType":"generated"}},"exampleType":"generated"},"889a2273-22a4-4a7e-a29e-ab5d26286a7a":{"description":"The key lacks the scope this operation needs β `FORBIDDEN_SCOPE`, with\n`details.required_scope`. Create a key with the `write` scope.\n","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["UNAUTHORIZED","FORBIDDEN","FORBIDDEN_SCOPE","NOT_FOUND","OPINION_NOT_FOUND","INVALID_ARGUMENT","RATE_LIMITED","SERVICE_DISABLED","INTERNAL","CONFIRM_TOKEN_INVALID","CONFIRM_TOKEN_EXPIRED","CONFIRM_TOKEN_USED","CONFIRM_UNAVAILABLE","MECHANISM_UNSUPPORTED","PREVIEW_UNAVAILABLE","TOPIC_ALREADY_STAKED","TOPIC_CLOSED","TOPIC_QUOTE_EXPIRED","TOPIC_QUOTE_INVALID","TOPIC_MECHANISM_MISMATCH","STAKE_OUT_OF_RANGE","RETRY_IDEMPOTENCY_KEY","OPINION_ALREADY_BACKED","OPINION_BACK_PRICE_MOVED","OPINION_BACK_RETRY","OPINION_BACK_FULL","OPINION_BACK_CHAIN_BROKEN","OPINION_SIDE_REQUIRED","OPINION_SIDE_INVALID","OPINION_TOPIC_CLOSED","OPINION_TOPIC_HIDDEN","OPINIONS_UNAVAILABLE","INSUFFICIENT_BALANCE","CONTENT_INVALID","CONTENT_MODERATED","USER_PUNISHED"],"description":"Stable contract values. `SERVICE_DISABLED` (503) is the\nemergency kill switch; `FORBIDDEN_SCOPE` (403) means the key\nlacks the `write` scope. The `CONFIRM_TOKEN_*` and rule codes\nonly occur on the write surface. `OPINION_SIDE_REQUIRED` /\n`OPINION_SIDE_INVALID` are rare but reachable: `preview_opinion`\nalready answers a missing or invalid side with\n`can_publish: false` (reason `SIDE_REQUIRED`), so they surface\nas a 400 from `publish_opinion` only when the owner's position\nchanged between preview and publish (e.g. no position at\npreview, then an order on the other side before publishing) and\nthe side in the token no longer fits.\n","isRequired":true,"uniqueKey":"error.code","typeLabel":"enum\u003cstring>"},"message":{"type":"string","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"details":{"type":"object","description":"Optional context, e.g. `{\"field\": \"limit\"}`, `{\"retry_after\": 12}` or `{\"required_scope\": \"write\"}`.","additionalProperties":true,"uniqueKey":"error.details","typeLabel":"object"}},"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"error":{"code":"UNAUTHORIZED","message":"\u003cstring>","details":{}}}}},"exampleType":"generated"}},"exampleType":"generated"},"9434a9e9-449a-434f-a2b8-b1b30ae5bb12":{"description":"Resource missing, or hidden by privacy/moderation (existence does not leak)","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["UNAUTHORIZED","FORBIDDEN","FORBIDDEN_SCOPE","NOT_FOUND","OPINION_NOT_FOUND","INVALID_ARGUMENT","RATE_LIMITED","SERVICE_DISABLED","INTERNAL","CONFIRM_TOKEN_INVALID","CONFIRM_TOKEN_EXPIRED","CONFIRM_TOKEN_USED","CONFIRM_UNAVAILABLE","MECHANISM_UNSUPPORTED","PREVIEW_UNAVAILABLE","TOPIC_ALREADY_STAKED","TOPIC_CLOSED","TOPIC_QUOTE_EXPIRED","TOPIC_QUOTE_INVALID","TOPIC_MECHANISM_MISMATCH","STAKE_OUT_OF_RANGE","RETRY_IDEMPOTENCY_KEY","OPINION_ALREADY_BACKED","OPINION_BACK_PRICE_MOVED","OPINION_BACK_RETRY","OPINION_BACK_FULL","OPINION_BACK_CHAIN_BROKEN","OPINION_SIDE_REQUIRED","OPINION_SIDE_INVALID","OPINION_TOPIC_CLOSED","OPINION_TOPIC_HIDDEN","OPINIONS_UNAVAILABLE","INSUFFICIENT_BALANCE","CONTENT_INVALID","CONTENT_MODERATED","USER_PUNISHED"],"description":"Stable contract values. `SERVICE_DISABLED` (503) is the\nemergency kill switch; `FORBIDDEN_SCOPE` (403) means the key\nlacks the `write` scope. The `CONFIRM_TOKEN_*` and rule codes\nonly occur on the write surface. `OPINION_SIDE_REQUIRED` /\n`OPINION_SIDE_INVALID` are rare but reachable: `preview_opinion`\nalready answers a missing or invalid side with\n`can_publish: false` (reason `SIDE_REQUIRED`), so they surface\nas a 400 from `publish_opinion` only when the owner's position\nchanged between preview and publish (e.g. no position at\npreview, then an order on the other side before publishing) and\nthe side in the token no longer fits.\n","isRequired":true,"uniqueKey":"error.code","typeLabel":"enum\u003cstring>"},"message":{"type":"string","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"details":{"type":"object","description":"Optional context, e.g. `{\"field\": \"limit\"}`, `{\"retry_after\": 12}` or `{\"required_scope\": \"write\"}`.","additionalProperties":true,"uniqueKey":"error.details","typeLabel":"object"}},"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"error":{"code":"UNAUTHORIZED","message":"\u003cstring>","details":{}}}}},"exampleType":"generated"}},"exampleType":"generated"},"11cc5fec-caa8-4da8-b298-320cbb17385a":{"description":"Rate limit exceeded (see `Retry-After` and `details.retry_after` seconds)","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait before retrying."}},"content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["UNAUTHORIZED","FORBIDDEN","FORBIDDEN_SCOPE","NOT_FOUND","OPINION_NOT_FOUND","INVALID_ARGUMENT","RATE_LIMITED","SERVICE_DISABLED","INTERNAL","CONFIRM_TOKEN_INVALID","CONFIRM_TOKEN_EXPIRED","CONFIRM_TOKEN_USED","CONFIRM_UNAVAILABLE","MECHANISM_UNSUPPORTED","PREVIEW_UNAVAILABLE","TOPIC_ALREADY_STAKED","TOPIC_CLOSED","TOPIC_QUOTE_EXPIRED","TOPIC_QUOTE_INVALID","TOPIC_MECHANISM_MISMATCH","STAKE_OUT_OF_RANGE","RETRY_IDEMPOTENCY_KEY","OPINION_ALREADY_BACKED","OPINION_BACK_PRICE_MOVED","OPINION_BACK_RETRY","OPINION_BACK_FULL","OPINION_BACK_CHAIN_BROKEN","OPINION_SIDE_REQUIRED","OPINION_SIDE_INVALID","OPINION_TOPIC_CLOSED","OPINION_TOPIC_HIDDEN","OPINIONS_UNAVAILABLE","INSUFFICIENT_BALANCE","CONTENT_INVALID","CONTENT_MODERATED","USER_PUNISHED"],"description":"Stable contract values. `SERVICE_DISABLED` (503) is the\nemergency kill switch; `FORBIDDEN_SCOPE` (403) means the key\nlacks the `write` scope. The `CONFIRM_TOKEN_*` and rule codes\nonly occur on the write surface. `OPINION_SIDE_REQUIRED` /\n`OPINION_SIDE_INVALID` are rare but reachable: `preview_opinion`\nalready answers a missing or invalid side with\n`can_publish: false` (reason `SIDE_REQUIRED`), so they surface\nas a 400 from `publish_opinion` only when the owner's position\nchanged between preview and publish (e.g. no position at\npreview, then an order on the other side before publishing) and\nthe side in the token no longer fits.\n","isRequired":true,"uniqueKey":"error.code","typeLabel":"enum\u003cstring>"},"message":{"type":"string","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"details":{"type":"object","description":"Optional context, e.g. `{\"field\": \"limit\"}`, `{\"retry_after\": 12}` or `{\"required_scope\": \"write\"}`.","additionalProperties":true,"uniqueKey":"error.details","typeLabel":"object"}},"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"error":{"code":"UNAUTHORIZED","message":"\u003cstring>","details":{}}}}},"exampleType":"generated"}},"exampleType":"generated"},"09882573-b72e-427e-84f6-cc350ced1e07":{"description":"Internal error (`INTERNAL`, or `CONFIRM_UNAVAILABLE` when token signing is unavailable)","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["UNAUTHORIZED","FORBIDDEN","FORBIDDEN_SCOPE","NOT_FOUND","OPINION_NOT_FOUND","INVALID_ARGUMENT","RATE_LIMITED","SERVICE_DISABLED","INTERNAL","CONFIRM_TOKEN_INVALID","CONFIRM_TOKEN_EXPIRED","CONFIRM_TOKEN_USED","CONFIRM_UNAVAILABLE","MECHANISM_UNSUPPORTED","PREVIEW_UNAVAILABLE","TOPIC_ALREADY_STAKED","TOPIC_CLOSED","TOPIC_QUOTE_EXPIRED","TOPIC_QUOTE_INVALID","TOPIC_MECHANISM_MISMATCH","STAKE_OUT_OF_RANGE","RETRY_IDEMPOTENCY_KEY","OPINION_ALREADY_BACKED","OPINION_BACK_PRICE_MOVED","OPINION_BACK_RETRY","OPINION_BACK_FULL","OPINION_BACK_CHAIN_BROKEN","OPINION_SIDE_REQUIRED","OPINION_SIDE_INVALID","OPINION_TOPIC_CLOSED","OPINION_TOPIC_HIDDEN","OPINIONS_UNAVAILABLE","INSUFFICIENT_BALANCE","CONTENT_INVALID","CONTENT_MODERATED","USER_PUNISHED"],"description":"Stable contract values. `SERVICE_DISABLED` (503) is the\nemergency kill switch; `FORBIDDEN_SCOPE` (403) means the key\nlacks the `write` scope. The `CONFIRM_TOKEN_*` and rule codes\nonly occur on the write surface. `OPINION_SIDE_REQUIRED` /\n`OPINION_SIDE_INVALID` are rare but reachable: `preview_opinion`\nalready answers a missing or invalid side with\n`can_publish: false` (reason `SIDE_REQUIRED`), so they surface\nas a 400 from `publish_opinion` only when the owner's position\nchanged between preview and publish (e.g. no position at\npreview, then an order on the other side before publishing) and\nthe side in the token no longer fits.\n","isRequired":true,"uniqueKey":"error.code","typeLabel":"enum\u003cstring>"},"message":{"type":"string","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"details":{"type":"object","description":"Optional context, e.g. `{\"field\": \"limit\"}`, `{\"retry_after\": 12}` or `{\"required_scope\": \"write\"}`.","additionalProperties":true,"uniqueKey":"error.details","typeLabel":"object"}},"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"error":{"code":"UNAUTHORIZED","message":"\u003cstring>","details":{}}}}},"exampleType":"generated"}},"exampleType":"generated"}},"security":{"981d7369-b6b3-48d9-be19-e49d959eb5ba":{"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"sspat_ personal API key","description":"Personal API key issued per SeeSaw user account:\n`Authorization: Bearer sspat_β¦`. Manage keys on the app API\n(`/v1/users/me/api-keys`). Revocation propagates within ~60 seconds\n(validation cache window).\n\nThe key's **scopes** decide what it may do: every key has `read`; a key\ncreated with `[\"read\",\"write\"]` may also use the six write operations\n(marked `x-required-scope: write` in this document). Scopes are fixed at\ncreation β to get write access, create a new key. A read-only key\ncalling a write operation gets `403 FORBIDDEN_SCOPE`.\n","scopes":[]}}},"servers":{"8b57a1c3-4bcf-4125-a8ec-f3cb0c2082e6":{"url":"https://api.seesaw.fun/open/v1","description":"Production"}},"processedSecurityOptions":[{"schemes":[{"scheme":{"type":"http","scheme":"bearer","bearerFormat":"sspat_ personal API key","description":"Personal API key issued per SeeSaw user account:\n`Authorization: Bearer sspat_β¦`. Manage keys on the app API\n(`/v1/users/me/api-keys`). Revocation propagates within ~60 seconds\n(validation cache window).\n\nThe key's **scopes** decide what it may do: every key has `read`; a key\ncreated with `[\"read\",\"write\"]` may also use the six write operations\n(marked `x-required-scope: write` in this document). Scopes are fixed at\ncreation β to get write access, create a new key. A read-only key\ncalling a write operation gets `403 FORBIDDEN_SCOPE`.\n","scopes":[]},"schema":{"title":"Bearer Auth","type":"string","isRequired":true,"description":"Personal API key issued per SeeSaw user account:\n`Authorization: Bearer sspat_β¦`. Manage keys on the app API\n(`/v1/users/me/api-keys`). Revocation propagates within ~60 seconds\n(validation cache window).\n\nThe key's **scopes** decide what it may do: every key has `read`; a key\ncreated with `[\"read\",\"write\"]` may also use the six write operations\n(marked `x-required-scope: write` in this document). Scopes are fixed at\ncreation β to get write access, create a new key. A read-only key\ncalling a write operation gets `403 FORBIDDEN_SCOPE`.\n","format":"bearer","typeLabel":"string\u003cbearer>","deprecated":false},"schemeName":"apiKey","name":"Authorization"}],"title":"apiKey"}]},"originalFileLocation":"api-reference/openapi.yaml"},"subdomain":"seesaw","siteName":"SeeSaw Developer","title":"Preview publishing an opinion (step 1 of 2) - SeeSaw Developer","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-09-23T20:08:07.111Z","originalFileLocation":"api-reference/opinions/preview-publishing-an-opinion-step-1-of-2.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Checks the speaking rules β trading is still open, the text is 1β500 characters, a side is settled β and returns the summary to confirm plus, when publishing is possible, a confirm_token for POST /opinions. Charges nothing. Requires the write scope; counts against the read window only. π¨ Changed 2026-09-15: holding a position is no longer required, and publishing no longer costs a coin. The opinion list is still a two-sided debate, so a side must still be settled: - the owner holds a position β the side is their staked side and side_option_id in the request is ignored; - no position β side_option_id is required; omitting it returns can_publish: false with reason SIDE_REQUIRED, and an option id from another topic is a 400. The side is part of the confirm token\u0026#x27;s parameter hash β previewing a different side yields a different token, so a token can never publish a side other than the one that was previewed.\">\u003cmeta data-mint-head name=\"application-name\" content=\"SeeSaw Developer\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"https://mintlify.s3.us-west-1.amazonaws.com/seesaw/_generated/favicon/apple-touch-icon.png?v=3\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"https://mintlify.s3.us-west-1.amazonaws.com/seesaw/_generated/favicon/favicon-32x32.png?v=3\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"https://mintlify.s3.us-west-1.amazonaws.com/seesaw/_generated/favicon/favicon-16x16.png?v=3\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"https://mintlify.s3.us-west-1.amazonaws.com/seesaw/_generated/favicon/favicon.ico?v=3\">\u003cmeta data-mint-head property=\"og:title\" content=\"Preview publishing an opinion (step 1 of 2) - SeeSaw Developer\">\u003cmeta data-mint-head property=\"og:description\" content=\"Checks the speaking rules β trading is still open, the text is 1β500 characters, a side is settled β and returns the summary to confirm plus, when publishing is possible, a confirm_token for POST /opinions. Charges nothing. Requires the write scope; counts against the read window only. π¨ Changed 2026-09-15: holding a position is no longer required, and publishing no longer costs a coin. The opinion list is still a two-sided debate, so a side must still be settled: - the owner holds a position β the side is their staked side and side_option_id in the request is ignored; - no position β side_option_id is required; omitting it returns can_publish: false with reason SIDE_REQUIRED, and an option id from another topic is a 400. The side is part of the confirm token\u0026#x27;s parameter hash β previewing a different side yields a different token, so a token can never publish a side other than the one that was previewed.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"SeeSaw Developer\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.seesaw.fun/api-reference/opinions/preview-publishing-an-opinion-step-1-of-2\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://seesaw.mintlify.app/_mintlify/api/og/v2?division=Opinions\u0026amp;title=Preview+publishing+an+opinion+%28step+1+of+2%29\u0026amp;description=Checks+the+speaking+rules+%E2%80%94+trading+is+still+open%2C+the+text+is+1%E2%80%93500%0Acharacters%2C+a+side+is+settled+%E2%80%94+and+returns+the+summary+to+confirm%0Aplus%2C+when+publishing+is\u0026amp;theme=f847ec8af40bd953769b2752\u0026amp;signature=748c501b153fa4505d9e836cb40de6793e172a38f6925bce39dda83499205077\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Preview publishing an opinion (step 1 of 2) - SeeSaw Developer\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Checks the speaking rules β trading is still open, the text is 1β500 characters, a side is settled β and returns the summary to confirm plus, when publishing is possible, a confirm_token for POST /opinions. Charges nothing. Requires the write scope; counts against the read window only. π¨ Changed 2026-09-15: holding a position is no longer required, and publishing no longer costs a coin. The opinion list is still a two-sided debate, so a side must still be settled: - the owner holds a position β the side is their staked side and side_option_id in the request is ignored; - no position β side_option_id is required; omitting it returns can_publish: false with reason SIDE_REQUIRED, and an option id from another topic is a 400. The side is part of the confirm token\u0026#x27;s parameter hash β previewing a different side yields a different token, so a token can never publish a side other than the one that was previewed.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://seesaw.mintlify.app/_mintlify/api/og/v2?division=Opinions\u0026amp;title=Preview+publishing+an+opinion+%28step+1+of+2%29\u0026amp;description=Checks+the+speaking+rules+%E2%80%94+trading+is+still+open%2C+the+text+is+1%E2%80%93500%0Acharacters%2C+a+side+is+settled+%E2%80%94+and+returns+the+summary+to+confirm%0Aplus%2C+when+publishing+is\u0026amp;theme=f847ec8af40bd953769b2752\u0026amp;signature=748c501b153fa4505d9e836cb40de6793e172a38f6925bce39dda83499205077\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"https://mintlify.s3.us-west-1.amazonaws.com/seesaw/_generated/favicon/browserconfig.xml?v=3\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"SeeSaw Developer\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#04A363\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.seesaw.fun/api-reference/opinions/preview-publishing-an-opinion-step-1-of-2\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/api-reference/opinions/preview-publishing-an-opinion-step-1-of-2.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.seesaw.fun/#organization\",\"name\":\"SeeSaw Developer\",\"url\":\"https://docs.seesaw.fun\"},{\"@type\":\"WebSite\",\"@id\":\"https://docs.seesaw.fun#website\",\"name\":\"SeeSaw Developer\",\"url\":\"https://docs.seesaw.fun\",\"publisher\":{\"@id\":\"https://docs.seesaw.fun/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.seesaw.fun/api-reference/opinions/preview-publishing-an-opinion-step-1-of-2#webpage\",\"url\":\"https://docs.seesaw.fun/api-reference/opinions/preview-publishing-an-opinion-step-1-of-2\",\"name\":\"Preview publishing an opinion (step 1 of 2)\",\"description\":\"Checks the speaking rules β trading is still open, the text is 1β500 characters, a side is settled β and returns the summary to confirm plus, when publishing is possible, a confirm_token for POST /opinions. Charges nothing. Requires the write scope; counts against the read window only. π¨ Changed 2026-09-15: holding a position is no longer required, and publishing no longer costs a coin. The opinion list is still a two-sided debate, so a side must still be settled: - the owner holds a position β the side is their staked side and side_option_id in the request is ignored; - no position β side_option_id is required; omitting it returns can_publish: false with reason SIDE_REQUIRED, and an option id from another topic is a 400. The side is part of the confirm token's parameter hash β previewing a different side yields a different token, so a token can never publish a side other than the one that was previewed.\",\"dateModified\":\"2026-09-23T20:08:07.111Z\",\"isPartOf\":{\"@id\":\"https://docs.seesaw.fun#website\"},\"breadcrumb\":{\"@id\":\"https://docs.seesaw.fun/api-reference/opinions/preview-publishing-an-opinion-step-1-of-2#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.seesaw.fun/api-reference/opinions/preview-publishing-an-opinion-step-1-of-2#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Endpoints\",\"item\":\"https://docs.seesaw.fun/api-reference/topics/search-topics-by-keyword\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Preview publishing an opinion (step 1 of 2)\",\"item\":\"https://docs.seesaw.fun/api-reference/opinions/preview-publishing-an-opinion-step-1-of-2\"}]},{\"@type\":[\"TechArticle\",\"APIReference\"],\"@id\":\"https://docs.seesaw.fun/api-reference/opinions/preview-publishing-an-opinion-step-1-of-2#apireference\",\"headline\":\"Preview publishing an opinion (step 1 of 2)\",\"name\":\"Preview publishing an opinion (step 1 of 2)\",\"description\":\"Checks the speaking rules β trading is still open, the text is 1β500 characters, a side is settled β and returns the summary to confirm plus, when publishing is possible, a confirm_token for POST /opinions. Charges nothing. Requires the write scope; counts against the read window only. π¨ Changed 2026-09-15: holding a position is no longer required, and publishing no longer costs a coin. The opinion list is still a two-sided debate, so a side must still be settled: - the owner holds a position β the side is their staked side and side_option_id in the request is ignored; - no position β side_option_id is required; omitting it returns can_publish: false with reason SIDE_REQUIRED, and an option id from another topic is a 400. The side is part of the confirm token's parameter hash β previewing a different side yields a different token, so a token can never publish a side other than the one that was previewed.\",\"url\":\"https://docs.seesaw.fun/api-reference/opinions/preview-publishing-an-opinion-step-1-of-2\",\"mainEntityOfPage\":{\"@id\":\"https://docs.seesaw.fun/api-reference/opinions/preview-publishing-an-opinion-step-1-of-2#webpage\"},\"dateModified\":\"2026-09-23T20:08:07.111Z\",\"publisher\":{\"@id\":\"https://docs.seesaw.fun/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.seesaw.fun#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"SeeSaw Developer\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"SeeSaw Developer","nav":{"global":null,"tabs":[{"tab":"Guides","pages":[{"title":"Guides","href":""}]},{"tab":"API Reference","groups":[{"group":"REST API","pages":[{"title":"REST API","href":"/api-reference/introduction"}]},{"group":"Endpoints","pages":[{"group":"Topics","pages":[{"title":"Search topics by keyword","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /search","href":"/api-reference/topics/search-topics-by-keyword"},{"title":"Browse topics by ranking","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /topics","href":"/api-reference/topics/browse-topics-by-ranking"},{"title":"List the category channels","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /topic-categories","href":"/api-reference/topics/list-the-category-channels"},{"title":"Get one topic in full detail","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /topics/{id}","href":"/api-reference/topics/get-one-topic-in-full-detail"},{"title":"List top-level opinions of a topic","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /topics/{id}/opinions","href":"/api-reference/topics/list-top-level-opinions-of-a-topic"}]},{"group":"Me","pages":[{"title":"Get the key owner's profile and balances","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /me","href":"/api-reference/me/get-the-key-owners-profile-and-balances"},{"title":"List the key owner's positions","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /me/positions","href":"/api-reference/me/list-the-key-owners-positions"},{"title":"List opinions the key owner published","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /me/opinions","href":"/api-reference/me/list-opinions-the-key-owner-published"},{"title":"List opinions the key owner backed","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /me/backed-opinions","href":"/api-reference/me/list-opinions-the-key-owner-backed"},{"title":"List the key owner's watchlist","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /me/watchlist","href":"/api-reference/me/list-the-key-owners-watchlist"},{"title":"List the key owner's coin ledger","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /me/coin-ledger","href":"/api-reference/me/list-the-key-owners-coin-ledger"},{"title":"List the key owner's ruby ledger","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml get /me/ruby-ledger","href":"/api-reference/me/list-the-key-owners-ruby-ledger"}]},{"group":"Trading","pages":[{"title":"Preview an order at the user's stake (step 1 of 2)","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml post /topics/{id}/order-preview","href":"/api-reference/trading/preview-an-order-at-the-users-stake-step-1-of-2"},{"title":"Place the previewed order (step 2 of 2)","description":"Spends the previewed `stake_coins` and opens the position previewed\nby `/topics/{id}/order-preview`. Accepts **only** the\n`confirm_token`; the stake travels inside it, so there is no\n`amount_coins` here. Requires the `write` scope; counts against both\nthe write window (10/min, 200/day) and the read window.\n\nIf the 60-second price lock captured at preview is still live the order\nfills at the previewed units and `price_locked` is `true`. Otherwise it\nfills at the **current market price** β the order is not rejected β\nand `price_locked` is `false`, with `order.units`/`order.price` holding\nthe actual fill next to the confirmed `previewed` values. A lock with\nunder 2 seconds left is treated as expired.\n\n`order.units` is the raw unit count; what the order pays if correct\nis `order.payout_if_correct_rubies` (units Γ the order's ruby\nmultiplier at fill β a guaranteed minimum: if the topic settles while a ruby boost is live, it pays at the higher multiplier). Compare that with\n`previewed.payout_if_correct_rubies` β not with `units`, which\ndiffers from the payout whenever a ruby boost is running.\n\nRetrying with the same token returns the original order and\n`replayed: true`. Note that `price_locked` then describes **this\ncall**, not the original fill (whose lock state is not stored) β to\nreconcile, compare `order` against `previewed`.\n\n`409 STAKE_OUT_OF_RANGE` exists only as a safety mapping: a topic's\nrange cannot change after the preview, so the normal flow never\nreaches it.\n","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml post /orders","href":"/api-reference/trading/place-the-previewed-order-step-2-of-2"}]},{"group":"Opinions","pages":[{"title":"Preview publishing an opinion (step 1 of 2)","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml post /topics/{id}/opinion-preview","href":"/api-reference/opinions/preview-publishing-an-opinion-step-1-of-2"},{"title":"Publish the previewed opinion (step 2 of 2)","description":"Publishes the opinion previewed by `/topics/{id}/opinion-preview` under\nthe owner's name. Accepts **only** the `confirm_token`. Requires the\n`write` scope; counts against both the write window and the read window.\n\nPublishing is **not** idempotent: the token is strictly single-use and\na retry answers `409 CONFIRM_TOKEN_USED`, as does republishing the same\ntext within a minute. Preview again only if the user wants a second post.\n","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml post /opinions","href":"/api-reference/opinions/publish-the-previewed-opinion-step-2-of-2"},{"title":"Preview backing an opinion (step 1 of 2)","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml post /opinions/{id}/back-preview","href":"/api-reference/opinions/preview-backing-an-opinion-step-1-of-2"},{"title":"Back the previewed opinion (step 2 of 2)","deprecated":null,"version":null,"openapi":"/api-reference/openapi.yaml post /backs","href":"/api-reference/opinions/back-the-previewed-opinion-step-2-of-2"}]}]}]}]},"basePath":"","title":"Preview publishing an opinion (step 1 of 2) - SeeSaw Developer","buildId":"dpl_345ReqzYaAjzS9QpeJemsKdb7KDj","deploymentId":"dpl_345ReqzYaAjzS9QpeJemsKdb7KDj","dynamics":["c2aa737661","2b790eda92","b0f84f53ff","a9bb46feb3"],"chunks":["OpenApiReferenceProvider-C8RPHPxo.js","utils-0BK-9q6z.js","cloneDeep-izs3Hzxb.js","_Uint8Array-BVIqJ90l.js","isObjectLike-C8vmzPEN.js","isArray-Dcu1dQ5t.js","utils-DfjzsUJT.js","toString-BX-iyh8G.js","isSymbol-CSH9U2Gm.js","addUniqueKeysToSchema-B4v9psJ4.js","snippetPresets-CpTQv-Ez.js","style-to-object-BY3JEAdk.js","v4-DIgz8zy6.js","ApiExamples-BNP-Fvr0.js","isEqual-BfaiBbcl.js","usePlaygroundInputsStore-BSgu6h1D.js","shallow-Biv1i9Gs.js","getApiPlaygroundDisplay-BoGOXhtF.js","CodeSnippet-DvQoT18_.js","CodeSnippets-Dnpa15xT.js","Page-Buji1S4J.js","getPastedJson-xAIyhUFE.js","icon-S6FBih11.js","MethodPill-SaYeUZyk.js","DuplicateIcon-Cs-p3v2Q.js","createIcon-DjCFEoHq.js","DynamicMarkdown-BcnBltEQ.js","SectionHeading-BUBl0eEF.js","axios-vUo_bRtp.js","Markdown-h4cOoD8d.js","clsx-B2v50t8t.js","lib-DuNMoZ05.js","lib-B8HbPgcC.js","html-void-elements-mCSPHezU.js","dist-DJnKvr0g.js","katex-YMM-FMeD.js","marked.esm-CZWythHk.js","AssistantPageLink-BHrrjR1K.js"]}