Reconcile a portfolio's holdings to an absolute desired state
“Make Allocait match what I hold”: the primary agent write (ADR-019).
Pass the FULL desired set of non-cash holdings; anything currently
held that is not listed is closed. Every change routes through the
same RPC the app UI uses (position_create_with_cash/
position_update_with_cash/position_close), so cash is always settled
and affordability always enforced server-side. This records the change
in Allocait only. It moves no money and places no order.
Idempotent: calling this again with the same desired state changes
nothing (symbols already matching are reported under unchanged,
with no write performed). Atomic: the whole batch is one transaction;
if any single change would fail (e.g. insufficient cash), the
entire call fails and nothing from it is applied. A row whose share
count is unchanged needs no price, and a price on it is never used
for anything.
Shorts. A negative shares is a short. It is recorded only on a
portfolio with allow_shorts on and never for a holding in the
Options class (including a row that moves a short into Options);
otherwise the call is INVALID_INPUT, carries symbols, and writes
nothing. A short sale credits cash, and closing or reducing a short
debits it at the row’s current_price, so a cover that cash cannot
pay is INSUFFICIENT_CASH. A close of a held short (a shares: 0
row, or a held symbol you drop in replace mode) is that cover, and
needs its price like any close. Short proceeds are credited to cash
within the call, so one call can fund a buy with a short sale. A
repeat of the same desired state, shorts included, changes nothing.
Closing a symbol you drop from the desired state needs a price it
does not otherwise carry. price_overrides must carry one for every
held symbol absent from positions: its proceeds are booked at
that price. A held symbol about to close with no price in the map
fails with a 400 whose body carries missing_symbols. Allocait
never books a close at its own stored quote (ADR-036). An option you
leave out needs its price too, even with a mark: the mark may be
months old, and a close books cash at the price named. One case needs
none: an option past its expiry closes at 0 with no cash and records
one action (ADR-147).
Options and marks. A row may carry mark (an option’s per-share
premium; see DesiredPosition). Left out or null, the stored mark
stays. A row whose only change is mark is reported under updated,
needs no price, moves no cash and records no action. A mark on a
holding that ends outside the Options class is INVALID_INPUT, carries
symbols and writes nothing (ADR-147). An expired option is closed
with {symbol, shares: 0, price: 0}, after the person says what
happened; the row needs price: 0 because the share count changes.
Moves. A row may carry moves (1, 2, 3, -1, -2 or -3; see
DesiredPosition). Left out or null, the stored value stays, so the
state you read from GET /positions can go back unchanged. A row
whose only change is moves is reported under updated, needs no
price and records no action. Moves other than 1 on cash or on an
Options-class holding (including a row that moves a fund into
Options) is INVALID_INPUT and nothing is written (ADR-142).
Symbols. Every symbol is trimmed and uppercased before it is
matched, so alfa updates the held ALFA instead of opening a
second holding, and the close-price check uses the same rule. After
that each must be a ticker of 1 to 21 characters of A-Z, 0-9, . or
- starting with a letter or digit (ALFA, ALFA.B, or an option
symbol such as ECHO281215P00050000), or a slash pair
(ALFA/BRAV). Anything else is a 400 with “Each position needs a valid
symbol: a ticker (ALFA, ALFA.B) or a pair (ALFA/BRAV)”, and the database
refuses it with error_code: INVALID_INPUT if it gets that far.
Nothing is written.
Requires the API key to have the write permission.
POST
/
portfolios
/
{id}
/
positions
/
apply
Reconcile a portfolio's holdings to an absolute desired state
Raw API key shown once when created in the Allocait app (Settings > API keys). Send it verbatim in this header; the server stores and compares only its SHA-256 hash. Allocait generates every key: alc_ followed by 64 lowercase hex characters. Keys issued before 2026-10-10 are 64 hex characters with no prefix and keep working. A key may carry an expiry (90 days or 1 year, or never). A malformed, unknown, revoked or expired key gets 401 with no further detail, and a malformed one is refused before any lookup. Keys cannot be created through this API.
Symbol-to-price map. Keys are ticker symbols, matched
case-insensitively (normalised to uppercase); values are numbers,
0 or more. 0 is a real price, for example an option that expired
worthless. On the agent surface, a symbol you leave out of this map is
never priced from Allocait's own data (ADR-036); it comes back
null, or the call is refused, depending on the endpoint. Two
exceptions are the user's own numbers, not Allocait's: a held option
with a mark is priced at its mark, so you may leave it out, and a
price you pass for it replaces the mark for that call only and is
never stored; an option past its expiry counts 0 whatever you pass
(ADR-147).
Holdings taken to 0 shares. Closing a short is a cover, which debits cash; read the sign of the change in shares, not the word, to tell which way cash moved.
Holdings left with no asset class. No band judges them until they have one.
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"Allocait","description":"Allocait checks a portfolio against the owner's own allocation rules. This site documents the REST API agents call and how to connect Claude, ChatGPT and Codex to it.","colors":{"primary":"#0b0b0b","light":"#ffc629","dark":"#0b0b0b"},"logo":{"light":"https://mintcdn.com/allocait/N9X8GrpLUSiZAKIm/logo/light.svg?fit=max\u0026auto=format\u0026n=N9X8GrpLUSiZAKIm\u0026q=85\u0026s=37c68a7ac5dc3550d24f484060530ea6","dark":"https://mintcdn.com/allocait/N9X8GrpLUSiZAKIm/logo/dark.svg?fit=max\u0026auto=format\u0026n=N9X8GrpLUSiZAKIm\u0026q=85\u0026s=64ac292a0a27672b4ff7d318734b0a5a","href":"https://allocait.app"},"favicon":"https://mintcdn.com/allocait/N9X8GrpLUSiZAKIm/logo/favicon.svg?fit=max\u0026auto=format\u0026n=N9X8GrpLUSiZAKIm\u0026q=85\u0026s=ff094ad764f4dfe37dbf99bdd5c9aaf4","api":{"playground":{"display":"simple"}},"appearance":{"default":"dark"},"background":{"color":{"light":"#ffffff","dark":"#09090b"}},"navbar":{"links":[{"href":"mailto:support@allocait.app","label":"Support"}],"primary":{"type":"button","label":"allocait.app","href":"https://allocait.app"}},"footer":{"links":[{"header":"Product","items":[{"label":"allocait.app","href":"https://allocait.app"},{"label":"Support","href":"mailto:support@allocait.app"}]}]},"icons":{"library":"tabler"},"contextual":{"options":["copy","view","chatgpt","claude","mcp","cursor","vscode"],"display":"header"},"styling":{"latex":true},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"allocait","hasAudioNarration":false,"actualSubdomain":"allocait","customDomains":["docs.allocait.app"],"gitSource":{"type":"github","owner":"allocait-app","repo":"docs","deployBranch":"main","contentDirectory":"","isPrivate":true,"mountPath":"","label":""},"entitlements":{"AI_CHAT":{"status":"DISABLED"},"WEB_MCP":{"status":"DISABLED"},"NARRATION":{"status":"DISABLED"}},"buildId":"6af158c3927786a3649023cd","clientVersion":"0.0.4102","searchFilterCounts":[{"language":"default","tag":"API reference","count":95},{"language":"default","tag":"Endpoints","count":84},{"language":"default","tag":"Concepts","count":24},{"language":"default","tag":"Claude \u0026 MCP","count":23},{"language":"default","tag":"Rules","count":22},{"language":"default","tag":"Guides","count":13},{"language":"default","tag":"Tools","count":12},{"language":"default","tag":"Inputs","count":12},{"language":"default","tag":"ChatGPT \u0026 Codex","count":10},{"language":"default","tag":"Watchlists","count":9}]},"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":"custom-rbac-roles","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":"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":"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":"editor-metadata","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},{"name":"live-agents","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":false},"customCss":[{"filePath":"style.css","content":"img.nav-logo{height:1rem!important;width:auto!important}.callout[data-callout-type=\"note\"],.callout[data-callout-type=\"info\"],.callout[data-callout-type=\"tip\"]{background-color:rgb(255 198 41 / 0.2)!important;border-color:rgb(255 198 41 / 0.6)!important}.callout[data-callout-type=\"note\"] *,.callout[data-callout-type=\"info\"] *,.callout[data-callout-type=\"tip\"] *{color:#0b0b0b!important}.callout[data-callout-type=\"note\"] svg,.callout[data-callout-type=\"info\"] svg,.callout[data-callout-type=\"tip\"] svg{fill:#0b0b0b!important}html.dark .callout[data-callout-type=\"note\"],html.dark .callout[data-callout-type=\"info\"],html.dark .callout[data-callout-type=\"tip\"]{background-color:rgb(255 198 41 / 0.1)!important;border-color:rgb(255 198 41 / 0.35)!important}html.dark .callout[data-callout-type=\"note\"] *,html.dark .callout[data-callout-type=\"info\"] *,html.dark .callout[data-callout-type=\"tip\"] *{color:#ffc629!important}html.dark .callout[data-callout-type=\"note\"] svg,html.dark .callout[data-callout-type=\"info\"] svg,html.dark .callout[data-callout-type=\"tip\"] svg{fill:#ffc629!important}"}],"page":{"pagePath":"api-reference/holdings-and-cash/reconcile-a-portfolios-holdings-to-an-absolute-desired-state","slug":"/api-reference/holdings-and-cash/reconcile-a-portfolios-holdings-to-an-absolute-desired-state","pageMetadata":{"title":"Reconcile a portfolio's holdings to an absolute desired state","description":"\"Make Allocait match what I hold\": the primary agent write (ADR-019).\nPass the FULL desired set of non-cash holdings; anything currently\nheld that is not listed is closed. Every change routes through the\nsame RPC the app UI uses (position_create_with_cash/\nposition_update_with_cash/position_close), so cash is always settled\nand affordability always enforced server-side. This records the change\nin Allocait only. It moves no money and places no order.\n\nIdempotent: calling this again with the same desired state changes\nnothing (symbols already matching are reported under `unchanged`,\nwith no write performed). Atomic: the whole batch is one transaction;\nif any single change would fail (e.g. insufficient cash), the\nentire call fails and nothing from it is applied. A row whose share\ncount is unchanged needs no price, and a price on it is never used\nfor anything.\n\n**Shorts.** A negative `shares` is a short. It is recorded only on a\nportfolio with `allow_shorts` on and never for a holding in the\nOptions class (including a row that moves a short into Options);\notherwise the call is `INVALID_INPUT`, carries `symbols`, and writes\nnothing. A short sale credits cash, and closing or reducing a short\ndebits it at the row's `current_price`, so a cover that cash cannot\npay is `INSUFFICIENT_CASH`. A close of a held short (a `shares: 0`\nrow, or a held symbol you drop in replace mode) is that cover, and\nneeds its price like any close. Short proceeds are credited to cash\nwithin the call, so one call can fund a buy with a short sale. A\nrepeat of the same desired state, shorts included, changes nothing.\n\nClosing a symbol you drop from the desired state needs a price it\ndoes not otherwise carry. `price_overrides` must carry one for every\nheld symbol absent from `positions`: its proceeds are booked at\nthat price. A held symbol about to close with no price in the map\nfails with a 400 whose body carries `missing_symbols`. Allocait\nnever books a close at its own stored quote (ADR-036). An option you\nleave out needs its price too, even with a mark: the mark may be\nmonths old, and a close books cash at the price named. One case needs\nnone: an option past its expiry closes at 0 with no cash and records\none action (ADR-147).\n\n**Options and marks.** A row may carry `mark` (an option's per-share\npremium; see `DesiredPosition`). Left out or null, the stored mark\nstays. A row whose only change is `mark` is reported under `updated`,\nneeds no price, moves no cash and records no action. A mark on a\nholding that ends outside the Options class is `INVALID_INPUT`, carries\n`symbols` and writes nothing (ADR-147). An expired option is closed\nwith `{symbol, shares: 0, price: 0}`, after the person says what\nhappened; the row needs `price: 0` because the share count changes.\n\n**Moves.** A row may carry `moves` (1, 2, 3, -1, -2 or -3; see\n`DesiredPosition`). Left out or null, the stored value stays, so the\nstate you read from `GET /positions` can go back unchanged. A row\nwhose only change is `moves` is reported under `updated`, needs no\nprice and records no action. Moves other than 1 on cash or on an\nOptions-class holding (including a row that moves a fund into\nOptions) is `INVALID_INPUT` and nothing is written (ADR-142).\n\n**Symbols.** Every symbol is trimmed and uppercased before it is\nmatched, so ` alfa ` updates the held `ALFA` instead of opening a\nsecond holding, and the close-price check uses the same rule. After\nthat each must be a ticker of 1 to 21 characters of A-Z, 0-9, `.` or\n`-` starting with a letter or digit (`ALFA`, `ALFA.B`, or an option\nsymbol such as `ECHO281215P00050000`), or a slash pair\n(`ALFA/BRAV`). Anything else is a 400 with \"Each position needs a valid\nsymbol: a ticker (ALFA, ALFA.B) or a pair (ALFA/BRAV)\", and the database\nrefuses it with `error_code: INVALID_INPUT` if it gets that far.\nNothing is written.\n\n**Requires the API key to have the `write` permission.**\n","deprecated":null,"version":null,"openapi":"/openapi.yaml post /portfolios/{id}/positions/apply","href":"/api-reference/holdings-and-cash/reconcile-a-portfolios-holdings-to-an-absolute-desired-state","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 p: \"p\",\n strong: \"strong\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsxs(_Fragment, {\n children: [_jsx(_components.p, {\n children: \"“Make Allocait match what I hold”: the primary agent write (ADR-019).\\nPass the FULL desired set of non-cash holdings; anything currently\\nheld that is not listed is closed. Every change routes through the\\nsame RPC the app UI uses (position_create_with_cash/\\nposition_update_with_cash/position_close), so cash is always settled\\nand affordability always enforced server-side. This records the change\\nin Allocait only. It moves no money and places no order.\"\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"Idempotent: calling this again with the same desired state changes\\nnothing (symbols already matching are reported under \", _jsx(_components.code, {\n children: \"unchanged\"\n }), \",\\nwith no write performed). Atomic: the whole batch is one transaction;\\nif any single change would fail (e.g. insufficient cash), the\\nentire call fails and nothing from it is applied. A row whose share\\ncount is unchanged needs no price, and a price on it is never used\\nfor anything.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [_jsx(_components.strong, {\n children: \"Shorts.\"\n }), \" A negative \", _jsx(_components.code, {\n children: \"shares\"\n }), \" is a short. It is recorded only on a\\nportfolio with \", _jsx(_components.code, {\n children: \"allow_shorts\"\n }), \" on and never for a holding in the\\nOptions class (including a row that moves a short into Options);\\notherwise the call is \", _jsx(_components.code, {\n children: \"INVALID_INPUT\"\n }), \", carries \", _jsx(_components.code, {\n children: \"symbols\"\n }), \", and writes\\nnothing. A short sale credits cash, and closing or reducing a short\\ndebits it at the row’s \", _jsx(_components.code, {\n children: \"current_price\"\n }), \", so a cover that cash cannot\\npay is \", _jsx(_components.code, {\n children: \"INSUFFICIENT_CASH\"\n }), \". A close of a held short (a \", _jsx(_components.code, {\n children: \"shares: 0\"\n }), \"\\nrow, or a held symbol you drop in replace mode) is that cover, and\\nneeds its price like any close. Short proceeds are credited to cash\\nwithin the call, so one call can fund a buy with a short sale. A\\nrepeat of the same desired state, shorts included, changes nothing.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"Closing a symbol you drop from the desired state needs a price it\\ndoes not otherwise carry. \", _jsx(_components.code, {\n children: \"price_overrides\"\n }), \" must carry one for every\\nheld symbol absent from \", _jsx(_components.code, {\n children: \"positions\"\n }), \": its proceeds are booked at\\nthat price. A held symbol about to close with no price in the map\\nfails with a 400 whose body carries \", _jsx(_components.code, {\n children: \"missing_symbols\"\n }), \". Allocait\\nnever books a close at its own stored quote (ADR-036). An option you\\nleave out needs its price too, even with a mark: the mark may be\\nmonths old, and a close books cash at the price named. One case needs\\nnone: an option past its expiry closes at 0 with no cash and records\\none action (ADR-147).\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [_jsx(_components.strong, {\n children: \"Options and marks.\"\n }), \" A row may carry \", _jsx(_components.code, {\n children: \"mark\"\n }), \" (an option’s per-share\\npremium; see \", _jsx(_components.code, {\n children: \"DesiredPosition\"\n }), \"). Left out or null, the stored mark\\nstays. A row whose only change is \", _jsx(_components.code, {\n children: \"mark\"\n }), \" is reported under \", _jsx(_components.code, {\n children: \"updated\"\n }), \",\\nneeds no price, moves no cash and records no action. A mark on a\\nholding that ends outside the Options class is \", _jsx(_components.code, {\n children: \"INVALID_INPUT\"\n }), \", carries\\n\", _jsx(_components.code, {\n children: \"symbols\"\n }), \" and writes nothing (ADR-147). An expired option is closed\\nwith \", _jsx(_components.code, {\n children: \"{symbol, shares: 0, price: 0}\"\n }), \", after the person says what\\nhappened; the row needs \", _jsx(_components.code, {\n children: \"price: 0\"\n }), \" because the share count changes.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [_jsx(_components.strong, {\n children: \"Moves.\"\n }), \" A row may carry \", _jsx(_components.code, {\n children: \"moves\"\n }), \" (1, 2, 3, -1, -2 or -3; see\\n\", _jsx(_components.code, {\n children: \"DesiredPosition\"\n }), \"). Left out or null, the stored value stays, so the\\nstate you read from \", _jsx(_components.code, {\n children: \"GET /positions\"\n }), \" can go back unchanged. A row\\nwhose only change is \", _jsx(_components.code, {\n children: \"moves\"\n }), \" is reported under \", _jsx(_components.code, {\n children: \"updated\"\n }), \", needs no\\nprice and records no action. Moves other than 1 on cash or on an\\nOptions-class holding (including a row that moves a fund into\\nOptions) is \", _jsx(_components.code, {\n children: \"INVALID_INPUT\"\n }), \" and nothing is written (ADR-142).\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [_jsx(_components.strong, {\n children: \"Symbols.\"\n }), \" Every symbol is trimmed and uppercased before it is\\nmatched, so \", _jsx(_components.code, {\n children: \"alfa\"\n }), \" updates the held \", _jsx(_components.code, {\n children: \"ALFA\"\n }), \" instead of opening a\\nsecond holding, and the close-price check uses the same rule. After\\nthat each must be a ticker of 1 to 21 characters of A-Z, 0-9, \", _jsx(_components.code, {\n children: \".\"\n }), \" or\\n\", _jsx(_components.code, {\n children: \"-\"\n }), \" starting with a letter or digit (\", _jsx(_components.code, {\n children: \"ALFA\"\n }), \", \", _jsx(_components.code, {\n children: \"ALFA.B\"\n }), \", or an option\\nsymbol such as \", _jsx(_components.code, {\n children: \"ECHO281215P00050000\"\n }), \"), or a slash pair\\n(\", _jsx(_components.code, {\n children: \"ALFA/BRAV\"\n }), \"). Anything else is a 400 with “Each position needs a valid\\nsymbol: a ticker (ALFA, ALFA.B) or a pair (ALFA/BRAV)”, and the database\\nrefuses it with \", _jsx(_components.code, {\n children: \"error_code: INVALID_INPUT\"\n }), \" if it gets that far.\\nNothing is written.\"]\n }), \"\\n\", _jsx(_components.p, {\n children: _jsxs(_components.strong, {\n children: [\"Requires the API key to have the \", _jsx(_components.code, {\n children: \"write\"\n }), \" permission.\"]\n })\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":"Reconcile a portfolio's holdings to an absolute desired state","description":"\"Make Allocait match what I hold\": the primary agent write (ADR-019).\nPass the FULL desired set of non-cash holdings; anything currently\nheld that is not listed is closed. Every change routes through the\nsame RPC the app UI uses (position_create_with_cash/\nposition_update_with_cash/position_close), so cash is always settled\nand affordability always enforced server-side. This records the change\nin Allocait only. It moves no money and places no order.\n\nIdempotent: calling this again with the same desired state changes\nnothing (symbols already matching are reported under `unchanged`,\nwith no write performed). Atomic: the whole batch is one transaction;\nif any single change would fail (e.g. insufficient cash), the\nentire call fails and nothing from it is applied. A row whose share\ncount is unchanged needs no price, and a price on it is never used\nfor anything.\n\n**Shorts.** A negative `shares` is a short. It is recorded only on a\nportfolio with `allow_shorts` on and never for a holding in the\nOptions class (including a row that moves a short into Options);\notherwise the call is `INVALID_INPUT`, carries `symbols`, and writes\nnothing. A short sale credits cash, and closing or reducing a short\ndebits it at the row's `current_price`, so a cover that cash cannot\npay is `INSUFFICIENT_CASH`. A close of a held short (a `shares: 0`\nrow, or a held symbol you drop in replace mode) is that cover, and\nneeds its price like any close. Short proceeds are credited to cash\nwithin the call, so one call can fund a buy with a short sale. A\nrepeat of the same desired state, shorts included, changes nothing.\n\nClosing a symbol you drop from the desired state needs a price it\ndoes not otherwise carry. `price_overrides` must carry one for every\nheld symbol absent from `positions`: its proceeds are booked at\nthat price. A held symbol about to close with no price in the map\nfails with a 400 whose body carries `missing_symbols`. Allocait\nnever books a close at its own stored quote (ADR-036). An option you\nleave out needs its price too, even with a mark: the mark may be\nmonths old, and a close books cash at the price named. One case needs\nnone: an option past its expiry closes at 0 with no cash and records\none action (ADR-147).\n\n**Options and marks.** A row may carry `mark` (an option's per-share\npremium; see `DesiredPosition`). Left out or null, the stored mark\nstays. A row whose only change is `mark` is reported under `updated`,\nneeds no price, moves no cash and records no action. A mark on a\nholding that ends outside the Options class is `INVALID_INPUT`, carries\n`symbols` and writes nothing (ADR-147). An expired option is closed\nwith `{symbol, shares: 0, price: 0}`, after the person says what\nhappened; the row needs `price: 0` because the share count changes.\n\n**Moves.** A row may carry `moves` (1, 2, 3, -1, -2 or -3; see\n`DesiredPosition`). Left out or null, the stored value stays, so the\nstate you read from `GET /positions` can go back unchanged. A row\nwhose only change is `moves` is reported under `updated`, needs no\nprice and records no action. Moves other than 1 on cash or on an\nOptions-class holding (including a row that moves a fund into\nOptions) is `INVALID_INPUT` and nothing is written (ADR-142).\n\n**Symbols.** Every symbol is trimmed and uppercased before it is\nmatched, so ` alfa ` updates the held `ALFA` instead of opening a\nsecond holding, and the close-price check uses the same rule. After\nthat each must be a ticker of 1 to 21 characters of A-Z, 0-9, `.` or\n`-` starting with a letter or digit (`ALFA`, `ALFA.B`, or an option\nsymbol such as `ECHO281215P00050000`), or a slash pair\n(`ALFA/BRAV`). Anything else is a 400 with \"Each position needs a valid\nsymbol: a ticker (ALFA, ALFA.B) or a pair (ALFA/BRAV)\", and the database\nrefuses it with `error_code: INVALID_INPUT` if it gets that far.\nNothing is written.\n\n**Requires the API key to have the `write` permission.**\n","deprecated":null,"version":null,"openapi":"/openapi.yaml post /portfolios/{id}/positions/apply","href":"/api-reference/holdings-and-cash/reconcile-a-portfolios-holdings-to-an-absolute-desired-state","autogeneratedByOpenApi":true}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[],"codeExamples":{}},"jsLinks":[],"openApiReferenceData":{"operation":{"tags":["Holdings and cash"],"operationId":"applyPositions","summary":"Reconcile a portfolio's holdings to an absolute desired state","description":"\"Make Allocait match what I hold\": the primary agent write (ADR-019).\nPass the FULL desired set of non-cash holdings; anything currently\nheld that is not listed is closed. Every change routes through the\nsame RPC the app UI uses (position_create_with_cash/\nposition_update_with_cash/position_close), so cash is always settled\nand affordability always enforced server-side. This records the change\nin Allocait only. It moves no money and places no order.\n\nIdempotent: calling this again with the same desired state changes\nnothing (symbols already matching are reported under `unchanged`,\nwith no write performed). Atomic: the whole batch is one transaction;\nif any single change would fail (e.g. insufficient cash), the\nentire call fails and nothing from it is applied. A row whose share\ncount is unchanged needs no price, and a price on it is never used\nfor anything.\n\n**Shorts.** A negative `shares` is a short. It is recorded only on a\nportfolio with `allow_shorts` on and never for a holding in the\nOptions class (including a row that moves a short into Options);\notherwise the call is `INVALID_INPUT`, carries `symbols`, and writes\nnothing. A short sale credits cash, and closing or reducing a short\ndebits it at the row's `current_price`, so a cover that cash cannot\npay is `INSUFFICIENT_CASH`. A close of a held short (a `shares: 0`\nrow, or a held symbol you drop in replace mode) is that cover, and\nneeds its price like any close. Short proceeds are credited to cash\nwithin the call, so one call can fund a buy with a short sale. A\nrepeat of the same desired state, shorts included, changes nothing.\n\nClosing a symbol you drop from the desired state needs a price it\ndoes not otherwise carry. `price_overrides` must carry one for every\nheld symbol absent from `positions`: its proceeds are booked at\nthat price. A held symbol about to close with no price in the map\nfails with a 400 whose body carries `missing_symbols`. Allocait\nnever books a close at its own stored quote (ADR-036). An option you\nleave out needs its price too, even with a mark: the mark may be\nmonths old, and a close books cash at the price named. One case needs\nnone: an option past its expiry closes at 0 with no cash and records\none action (ADR-147).\n\n**Options and marks.** A row may carry `mark` (an option's per-share\npremium; see `DesiredPosition`). Left out or null, the stored mark\nstays. A row whose only change is `mark` is reported under `updated`,\nneeds no price, moves no cash and records no action. A mark on a\nholding that ends outside the Options class is `INVALID_INPUT`, carries\n`symbols` and writes nothing (ADR-147). An expired option is closed\nwith `{symbol, shares: 0, price: 0}`, after the person says what\nhappened; the row needs `price: 0` because the share count changes.\n\n**Moves.** A row may carry `moves` (1, 2, 3, -1, -2 or -3; see\n`DesiredPosition`). Left out or null, the stored value stays, so the\nstate you read from `GET /positions` can go back unchanged. A row\nwhose only change is `moves` is reported under `updated`, needs no\nprice and records no action. Moves other than 1 on cash or on an\nOptions-class holding (including a row that moves a fund into\nOptions) is `INVALID_INPUT` and nothing is written (ADR-142).\n\n**Symbols.** Every symbol is trimmed and uppercased before it is\nmatched, so ` alfa ` updates the held `ALFA` instead of opening a\nsecond holding, and the close-price check uses the same rule. After\nthat each must be a ticker of 1 to 21 characters of A-Z, 0-9, `.` or\n`-` starting with a letter or digit (`ALFA`, `ALFA.B`, or an option\nsymbol such as `ECHO281215P00050000`), or a slash pair\n(`ALFA/BRAV`). Anything else is a 400 with \"Each position needs a valid\nsymbol: a ticker (ALFA, ALFA.B) or a pair (ALFA/BRAV)\", and the database\nrefuses it with `error_code: INVALID_INPUT` if it gets that far.\nNothing is written.\n\n**Requires the API key to have the `write` permission.**\n","security":["9317449d-ab5b-4e81-b6f1-0d90131137bf"],"parameters":["87cf117f-5c80-4731-89ef-8e7ed957fedf"],"requestBody":"ff93e506-f19c-420e-94dd-b963ec81aa6c","responses":{"200":"01ea5198-7576-4714-975d-43c37660a60f","400":"c53beb43-8c22-4637-8f5c-bf027d43aede","401":"1151d27b-2160-42f8-b6ca-8b5481a11d24","403":"33ae3146-326d-4ee6-a009-05a07d0d0c41","404":"bc5fb155-7d89-46ac-a728-be02fb39f0b9","409":"6350442f-c077-44fb-a39b-3eb23a72a06b","429":"a645206f-59b1-4f2a-91eb-ae1878d073b4","500":"dc847161-7c4a-4004-a58b-ae2ef187f448"},"method":"post","type":"path","servers":["13120e45-325e-41e1-99fd-a11d6f409d02"],"path":"/portfolios/{id}/positions/apply","baseUrlOptions":["https://allocait.app/api/v1"],"title":"Reconcile a portfolio's holdings to an absolute desired state","codeSamples":[],"requiredOnlyExamples":false,"requestExampleType":"generated","responseExampleType":"generated"},"dependencies":{"parameters":{"87cf117f-5c80-4731-89ef-8e7ed957fedf":{"name":"id","in":"path","required":true,"description":"portfolio_id","schema":{"type":"string","format":"uuid","isRequired":true,"description":"portfolio_id","uniqueKey":"","typeLabel":"string\u003cuuid>"}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["positions"],"properties":{"positions":{"type":"array","description":"The full desired set of non-cash holdings.","items":{"type":"object","required":["symbol","shares"],"properties":{"symbol":{"type":"string","example":"ALFA","description":"Trimmed and uppercased before use. A ticker of 1 to 21 characters of A-Z, 0-9, `.` or `-` starting with a letter or digit (an option symbol is 21 at most), or a slash pair such as `ALFA/BRAV`.","isRequired":true,"uniqueKey":"positions.items.[INDEX].symbol","typeLabel":"string"},"shares":{"type":"number","description":"The new total. Negative is a short, recorded only on a portfolio with `allow_shorts` on and never in the Options class; otherwise the call is `INVALID_INPUT` and nothing is written (ADR-153). 0 closes the holding. For a holding filed under the Options system asset class, recorded as contracts × 100 at the per-share premium (ADR-056).","isRequired":true,"uniqueKey":"positions.items.[INDEX].shares","typeLabel":"number"},"current_price":{"type":"number","minimum":0,"description":"Price per share for the cash difference. Required when `shares` changes; not needed on an unchanged row.","uniqueKey":"positions.items.[INDEX].current_price","typeLabel":"number"},"asset_class_id":{"oneOf":[{"type":"string","format":"uuid","description":"Class to file the holding under, applied even when `shares` is\nunchanged. Omit to keep the current class. A new symbol sent\nwithout one is stored with no class and listed in\n`unclassified`; there is no default.\n","uniqueKey":"positions.items.[INDEX].asset_class_id.oneOf.0","typeLabel":"string\u003cuuid>"},{"type":"null","format":"uuid","description":"Class to file the holding under, applied even when `shares` is\nunchanged. Omit to keep the current class. A new symbol sent\nwithout one is stored with no class and listed in\n`unclassified`; there is no default.\n","uniqueKey":"positions.items.[INDEX].asset_class_id.oneOf.1","typeLabel":"null"}],"uniqueKey":"positions.items.[INDEX].asset_class_id","typeLabel":"string\u003cuuid> | null"},"moves":{"oneOf":[{"type":"integer","enum":[1,2,3,-1,-2,-3,null],"description":"Times its index a fund moves; negative is inverse. Omit or send\nnull to keep the stored value (1 for a new holding), so the state\nyou read from `GET /positions` can go back unchanged. Set it only\nwhen the person says the fund is inverse or leveraged. Applies to\nstocks and funds, not to cash or an Options-class holding. A row\nwhose only change is `moves` needs no price and records no\naction (ADR-142).\n","uniqueKey":"positions.items.[INDEX].moves.oneOf.0","typeLabel":"enum\u003cinteger>"},{"type":"null","enum":[1,2,3,-1,-2,-3,null],"description":"Times its index a fund moves; negative is inverse. Omit or send\nnull to keep the stored value (1 for a new holding), so the state\nyou read from `GET /positions` can go back unchanged. Set it only\nwhen the person says the fund is inverse or leveraged. Applies to\nstocks and funds, not to cash or an Options-class holding. A row\nwhose only change is `moves` needs no price and records no\naction (ADR-142).\n","uniqueKey":"positions.items.[INDEX].moves.oneOf.1","typeLabel":"null"}],"uniqueKey":"positions.items.[INDEX].moves","typeLabel":"enum\u003cinteger> | null"},"mark":{"oneOf":[{"type":"number","minimum":0,"maximum":1000000000,"description":"An option's per-share premium, as the person's broker quotes it\n(2.35 for a $235 contract). Only for a holding that ends in the\nOptions class; on any other holding the call is `INVALID_INPUT`\n(`A mark applies to options only.`). Left out or null, the stored\nmark stays, so the state you read from `GET /positions` can go back\nunchanged. When the share count changes and `mark` is left out, the\nmark becomes the row's price. A row whose only change is `mark`\nneeds no price, moves no cash and records no action. Allocait never\nguesses a mark (ADR-147).\n","uniqueKey":"positions.items.[INDEX].mark.oneOf.0","typeLabel":"number"},{"type":"null","minimum":0,"maximum":1000000000,"description":"An option's per-share premium, as the person's broker quotes it\n(2.35 for a $235 contract). Only for a holding that ends in the\nOptions class; on any other holding the call is `INVALID_INPUT`\n(`A mark applies to options only.`). Left out or null, the stored\nmark stays, so the state you read from `GET /positions` can go back\nunchanged. When the share count changes and `mark` is left out, the\nmark becomes the row's price. A row whose only change is `mark`\nneeds no price, moves no cash and records no action. Allocait never\nguesses a mark (ADR-147).\n","uniqueKey":"positions.items.[INDEX].mark.oneOf.1","typeLabel":"null"}],"uniqueKey":"positions.items.[INDEX].mark","typeLabel":"number | null"},"sizing":{"type":"string","enum":["MANUAL","MINIMUM","BALANCED","MAXIMUM"],"description":"The holding's sizing mode, as set in the app's position wizard.","uniqueKey":"positions.items.[INDEX].sizing","typeLabel":"enum\u003cstring>"},"comment":{"type":"string","description":"Why, recorded on this row's action.","uniqueKey":"positions.items.[INDEX].comment","typeLabel":"string"}},"uniqueKey":"positions.items.[INDEX]","typeLabel":"object"},"isRequired":true,"uniqueKey":"positions","typeLabel":"object[]"},"price_overrides":{"type":"object","description":"Symbol-to-price map. Keys are ticker symbols, matched\ncase-insensitively (normalised to uppercase); values are numbers,\n0 or more. 0 is a real price, for example an option that expired\nworthless. On the agent surface, a symbol you leave out of this map is\nnever priced from Allocait's own data (ADR-036); it comes back\nnull, or the call is refused, depending on the endpoint. Two\nexceptions are the user's own numbers, not Allocait's: a held option\nwith a `mark` is priced at its mark, so you may leave it out, and a\nprice you pass for it replaces the mark for that call only and is\nnever stored; an option past its expiry counts 0 whatever you pass\n(ADR-147).\n","additionalProperties":{"type":"number","minimum":0,"uniqueKey":"price_overrides.additionalProperties","typeLabel":"number"},"example":{"ALFA":181.5,"ECHO":98.1,"GOLF":62.3},"uniqueKey":"price_overrides","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"example":{"positions":[{"symbol":"ALFA","shares":250,"asset_class_id":"d238ab31-0a5f-4a33-99e3-b828f2d4c316","current_price":181.5},{"symbol":"BRAV","shares":200,"asset_class_id":"768714f9-2f74-4712-ae2a-a8cff76267b0","current_price":94.2},{"symbol":"GOLF","shares":300,"asset_class_id":"a5abe5cf-9a5c-42e4-ad3c-f50ba87186fd","current_price":62.3}]},"examples":{"Example":{"title":"Example","value":{"positions":[{"symbol":"ALFA","shares":250,"asset_class_id":"d238ab31-0a5f-4a33-99e3-b828f2d4c316","current_price":181.5},{"symbol":"BRAV","shares":200,"asset_class_id":"768714f9-2f74-4712-ae2a-a8cff76267b0","current_price":94.2},{"symbol":"GOLF","shares":300,"asset_class_id":"a5abe5cf-9a5c-42e4-ad3c-f50ba87186fd","current_price":62.3}]}}},"exampleType":"examples"}},"exampleType":"examples"},"responses":{"01ea5198-7576-4714-975d-43c37660a60f":{"description":"Reconciliation result","headers":{"X-RateLimit-Limit":{"description":"The per-minute request budget, always `60`. Sent on every response that reaches a route and on the per-minute `429`.","schema":{"type":"integer","example":60}},"X-RateLimit-Remaining":{"description":"Requests left in the current one-minute window, after this one. The daily count is not reported until it is reached.","schema":{"type":"integer","example":41}}},"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","uniqueKey":"success","typeLabel":"boolean"},"portfolio_id":{"type":"string","format":"uuid","uniqueKey":"portfolio_id","typeLabel":"string\u003cuuid>"},"created":{"type":"array","items":{"type":"object","uniqueKey":"created.items.[INDEX]","typeLabel":"object"},"uniqueKey":"created","typeLabel":"object[]"},"updated":{"type":"array","items":{"type":"object","uniqueKey":"updated.items.[INDEX]","typeLabel":"object"},"description":"Share changes, plus class, sizing and Moves changes on an unchanged\nshare count as `{symbol, shares, asset_class_id, sizing}`.\n","uniqueKey":"updated","typeLabel":"object[]"},"closed":{"type":"array","items":{"type":"object","uniqueKey":"closed.items.[INDEX]","typeLabel":"object"},"description":"Holdings taken to 0 shares. Closing a short is a cover, which debits cash; read the sign of the change in `shares`, not the word, to tell which way cash moved.","uniqueKey":"closed","typeLabel":"object[]"},"unchanged":{"type":"array","items":{"type":"object","uniqueKey":"unchanged.items.[INDEX]","typeLabel":"object"},"description":"Symbols already matching the desired state, shares and class. No RPC call was made for these.","uniqueKey":"unchanged","typeLabel":"object[]"},"action_ids":{"type":"array","items":{"type":"string","format":"uuid","uniqueKey":"action_ids.items.[INDEX]","typeLabel":"string\u003cuuid>"},"description":"The actions this call recorded, for `PATCH /actions/{id}` to add a comment later.","uniqueKey":"action_ids","typeLabel":"string\u003cuuid>[]"},"unclassified":{"type":"array","items":{"type":"string","uniqueKey":"unclassified.items.[INDEX]","typeLabel":"string"},"description":"Holdings left with no asset class. No band judges them until they have one.","uniqueKey":"unclassified","typeLabel":"string[]"}},"uniqueKey":"","typeLabel":"object"},"example":{"success":true,"portfolio_id":"919bbfb9-c2e7-4c4c-9d6d-d20f83330245","unchanged":[],"mode":"replace","created":[{"symbol":"ALFA","shares":250,"result":{"success":true,"shares":250,"cash_impact":-45375,"position_id":"3471983e-66bf-4b25-8cf6-5fd797a675e5","new_cash_balance":54625}},{"symbol":"BRAV","shares":200,"result":{"success":true,"shares":200,"cash_impact":-18840,"position_id":"b82535b6-a44d-4aad-b495-893a594e6a91","new_cash_balance":35785}},{"symbol":"GOLF","shares":300,"result":{"success":true,"shares":300,"cash_impact":-18690,"position_id":"60e31b17-793b-4efd-af47-30b29a3910e8","new_cash_balance":17095}}],"updated":[],"closed":[],"action_ids":["6e3373d2-c862-4d52-8ef4-54a73ada4845","44df1d50-b26f-49d8-9934-3aae7d35abc9","9155bf83-aaa4-4b7c-99a0-3e0ebc23da80"],"unclassified":[]},"examples":{"Example":{"title":"Example","value":{"success":true,"portfolio_id":"919bbfb9-c2e7-4c4c-9d6d-d20f83330245","unchanged":[],"mode":"replace","created":[{"symbol":"ALFA","shares":250,"result":{"success":true,"shares":250,"cash_impact":-45375,"position_id":"3471983e-66bf-4b25-8cf6-5fd797a675e5","new_cash_balance":54625}},{"symbol":"BRAV","shares":200,"result":{"success":true,"shares":200,"cash_impact":-18840,"position_id":"b82535b6-a44d-4aad-b495-893a594e6a91","new_cash_balance":35785}},{"symbol":"GOLF","shares":300,"result":{"success":true,"shares":300,"cash_impact":-18690,"position_id":"60e31b17-793b-4efd-af47-30b29a3910e8","new_cash_balance":17095}}],"updated":[],"closed":[],"action_ids":["6e3373d2-c862-4d52-8ef4-54a73ada4845","44df1d50-b26f-49d8-9934-3aae7d35abc9","9155bf83-aaa4-4b7c-99a0-3e0ebc23da80"],"unclassified":[]}}},"exampleType":"examples"}},"exampleType":"examples"},"c53beb43-8c22-4637-8f5c-bf027d43aede":{"description":"Missing/invalid `positions`, a symbol that is not a valid ticker or pair (`error_code: INVALID_INPUT`; see Symbols above), a duplicate symbol after trimming and uppercasing, CASH included as a symbol, a negative `shares` the portfolio does not allow (see Shorts above; `symbols` lists the rows), a `moves` outside the six values or set on cash or an Options-class holding (see Moves above), a `mark` that is not a number from 0 to 1000000000 or sits on a holding outside the Options class (see Options and marks above), a change worth 100000000000000 or more, or `price_overrides` missing a price for a symbol being closed (`PRICES_INCOMPLETE`; `missing_symbols` lists them; an unexpired option left out is among them, even with a mark)","content":{"application/json":{"schema":{"oneOf":[{"type":"object","description":"Uniform error envelope returned by every non-2xx response. `error` is\na human-readable message, safe to show the user. A refusal also\ncarries `error_code`, a stable string from the catalog in the info\nblock's Error codes section, to branch on; the HTTP status follows\nthe code (ADR-134). Only `401`, the no-plan `403`, the `403` for a\nkey without `read` and the `429` for the request budget have none.\nA `500` carries `request_id`. Extra fields by code:\nTIER_LIMIT_EXCEEDED carries `limit_type`, `current_count` and\n`max_allowed`, so a caller can branch on how many it has versus the\ncap without a second call. Its `error` text names no plan. It covers\nthe portfolio and scenario limits and the account caps: 20 webhook endpoints, 50\nwatchlists, 200 symbols on one watchlist and 50 custom symbols on one\nmonitor. Don't retry it. RULES_INVALID (`POST\n/scenarios/{id}/rules`) carries `issues`. ALREADY_EXISTS carries\n`existing_id`, plus `duplicate_ids` where older duplicates exist.\nCONFIRMATION_REQUIRED carries `would_remove`; from `POST /setup/apply` it\ncarries a fresh preview instead (`digest`, `file`, `items` and the rest of\n`SetupPreview`). INVALID_INPUT from the setup routes carries `issues`, and\nIN_USE from `POST /setup/receipts/{id}/undo` carries `later_receipt_id`.\nNOT_FOUND on a name, and\nINVALID_INPUT on a CHOICE value, carry `valid_values`. INVALID_INPUT on a\nmonitor whose level input is not a number type carries `input_id`.\nIN_USE on an input whose type would change while a monitor uses it\ncarries `monitors`. IN_USE on `PATCH /portfolios/{id}` that turns\nAllow shorts off while a short is held carries `short_count` and\n`symbols`. INVALID_INPUT on the holdings writes (`POST\n/positions/{symbol}`, `POST /portfolios/{id}/positions/apply`) that\nrefuses a negative `shares`, a `moves` other than 1 on an Options\nholding, or a `mark` on a holding outside the Options class, carries\n`symbols`, the holdings it refused. (`symbols` also appears on `WOULD_CLOSE_POSITIONS`, MCP\nonly, where it lists the holdings a full list left out.)\n","required":["error"],"example":{"error":"Portfolio not found or access denied","error_code":"NOT_FOUND"},"properties":{"error":{"type":"string","description":"Human-readable error message.","isRequired":true,"uniqueKey":"oneOf.0.error","typeLabel":"string"},"error_code":{"type":"string","enum":["INVALID_INPUT","NOT_FOUND","ALREADY_EXISTS","IN_USE","CONFIRMATION_REQUIRED","PRICES_INCOMPLETE","PRICES_INVALID","WOULD_CLOSE_POSITIONS","INSUFFICIENT_CASH","RULES_INVALID","TARGET_OUT_OF_BAND","TIER_LIMIT_EXCEEDED","FEATURE_NOT_AVAILABLE","IDEMPOTENCY_KEY_CONFLICT","DAILY_LIMIT_EXCEEDED","READ_ONLY_KEY","OAUTH_WRITE_NOT_GRANTED","TOOL_REMOVED","INTERNAL_ERROR"],"description":"Stable machine-readable code from the closed catalog. Present on every refusal a route produces; absent on `401`, the no-plan `403`, the `403` for a key without `read`, and the `429` for the request budget.","uniqueKey":"oneOf.0.error_code","typeLabel":"enum\u003cstring>"},"request_id":{"type":"string","format":"uuid","description":"Present on a `500` (`INTERNAL_ERROR`). The same id is in Allocait's logs next to the real cause; quote it when you report the failure.","uniqueKey":"oneOf.0.request_id","typeLabel":"string\u003cuuid>"},"issues":{"type":"array","items":{"oneOf":[{"type":"object","description":"One problem or note found while reading rules text. A file\ncarrying any `error`-level issue is never applied; `POST\n/scenarios/{id}/rules` 400s with `error_code: RULES_INVALID` and\nthe full `issues` list instead. A file with only `warning`-level\nissues still applies; those ride along on the 200 response.\n","required":["level","message"],"properties":{"level":{"type":"string","enum":["error","warning"],"isRequired":true,"uniqueKey":"oneOf.0.issues.items.[INDEX].oneOf.0.level","typeLabel":"enum\u003cstring>"},"message":{"type":"string","isRequired":true,"uniqueKey":"oneOf.0.issues.items.[INDEX].oneOf.0.message","typeLabel":"string"},"class":{"type":"string","description":"The asset class name this issue is about, when it is about one class rather than the whole file.","uniqueKey":"oneOf.0.issues.items.[INDEX].oneOf.0.class","typeLabel":"string"}},"uniqueKey":"oneOf.0.issues.items.[INDEX].oneOf.0","typeLabel":"object"},{"type":"object","description":"One thing the file's text got wrong (`error`) or one note about it (`warning`, such as a web address in a description).","required":["level","where","message"],"properties":{"level":{"type":"string","enum":["error","warning"],"isRequired":true,"uniqueKey":"oneOf.0.issues.items.[INDEX].oneOf.1.level","typeLabel":"enum\u003cstring>"},"where":{"oneOf":[{"type":"string","description":"Where in the file, or null for the file as a whole.","isRequired":true,"uniqueKey":"oneOf.0.issues.items.[INDEX].oneOf.1.where.oneOf.0","typeLabel":"string"},{"type":"null","description":"Where in the file, or null for the file as a whole.","isRequired":true,"uniqueKey":"oneOf.0.issues.items.[INDEX].oneOf.1.where.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"oneOf.0.issues.items.[INDEX].oneOf.1.where","typeLabel":"string | null"},"message":{"type":"string","isRequired":true,"uniqueKey":"oneOf.0.issues.items.[INDEX].oneOf.1.message","typeLabel":"string"},"line":{"type":"integer","description":"The line of the file the issue is on, when it is known.","uniqueKey":"oneOf.0.issues.items.[INDEX].oneOf.1.line","typeLabel":"integer"}},"uniqueKey":"oneOf.0.issues.items.[INDEX].oneOf.1","typeLabel":"object"}],"uniqueKey":"oneOf.0.issues.items.[INDEX]","typeLabel":"object"},"description":"Every error and warning found while reading the rules text, when error_code is RULES_INVALID. From the setup routes (`POST /setup/preview`, `POST /setup/apply`) it is `SetupIssue` rows, on INVALID_INPUT.","uniqueKey":"oneOf.0.issues","typeLabel":"object[]"},"later_receipt_id":{"type":"string","format":"uuid","description":"The newer import of the same setup file that blocks this undo. Present on IN_USE from `POST /setup/receipts/{id}/undo`; undo that receipt first.","uniqueKey":"oneOf.0.later_receipt_id","typeLabel":"string\u003cuuid>"},"limit_type":{"type":"string","description":"Which cap was hit, e.g. \"portfolios\", \"scenarios\", \"webhook_endpoints\", \"watchlists\", \"asset_classes\", \"input_entries\" or \"input_entries_per_input\". Present only when error_code is TIER_LIMIT_EXCEEDED.","uniqueKey":"oneOf.0.limit_type","typeLabel":"string"},"current_count":{"type":"number","description":"How many of limit_type the caller has right now. Present only when error_code is TIER_LIMIT_EXCEEDED.","uniqueKey":"oneOf.0.current_count","typeLabel":"number"},"max_allowed":{"type":"number","description":"The cap for limit_type on this account. Present only when error_code is TIER_LIMIT_EXCEEDED.","uniqueKey":"oneOf.0.max_allowed","typeLabel":"number"},"existing_id":{"type":"string","format":"uuid","description":"The record that already has this name. Present only when error_code is ALREADY_EXISTS; change it with that id instead of creating another.","uniqueKey":"oneOf.0.existing_id","typeLabel":"string\u003cuuid>"},"duplicate_ids":{"type":"array","items":{"type":"string","format":"uuid","uniqueKey":"oneOf.0.duplicate_ids.items.[INDEX]","typeLabel":"string\u003cuuid>"},"description":"Other records with the same name, created before duplicates were refused. Present only on ALREADY_EXISTS, and only when such records exist.","uniqueKey":"oneOf.0.duplicate_ids","typeLabel":"string\u003cuuid>[]"},"would_remove":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string","uniqueKey":"oneOf.0.would_remove.additionalProperties.items.[INDEX]","typeLabel":"string"},"uniqueKey":"oneOf.0.would_remove.additionalProperties","typeLabel":"string[]"},"description":"What the call would drop, keyed by list field, for example the `items` that a watchlist save would remove. Present only when error_code is CONFIRMATION_REQUIRED; resend with those members in `remove`.","uniqueKey":"oneOf.0.would_remove","typeLabel":"object"},"valid_values":{"type":"array","items":{"type":"string","uniqueKey":"oneOf.0.valid_values.items.[INDEX]","typeLabel":"string"},"description":"The names or options the field accepts. Present on NOT_FOUND for a name, and on INVALID_INPUT for a CHOICE value.","uniqueKey":"oneOf.0.valid_values","typeLabel":"string[]"},"input_id":{"type":"string","format":"uuid","description":"The level input that is not a NUMBER, DECIMAL or PERCENTAGE input. Present on INVALID_INPUT from a monitor create or update (`POST /monitors`, `PATCH /monitors/{id}`, MCP `save_monitor`); point the monitor at a number-type input instead.","uniqueKey":"oneOf.0.input_id","typeLabel":"string\u003cuuid>"},"short_count":{"type":"integer","description":"How many shorts the portfolio holds. Present on IN_USE from `PATCH /portfolios/{id}` (MCP `save_portfolio`) that turns Allow shorts off while a short is held; close them first.","uniqueKey":"oneOf.0.short_count","typeLabel":"integer"},"symbols":{"type":"array","items":{"type":"string","uniqueKey":"oneOf.0.symbols.items.[INDEX]","typeLabel":"string"},"description":"Holdings the call names. On IN_USE from `PATCH /portfolios/{id}`, the shorts still held (up to 20, by symbol). On INVALID_INPUT from the holdings writes, the holdings whose negative `shares` was refused (Allow shorts is off, or the holding is in Options), or whose `moves` other than 1 was refused (the holding is in Options). On WOULD_CLOSE_POSITIONS (MCP only), the holdings a full list left out.","uniqueKey":"oneOf.0.symbols","typeLabel":"string[]"},"monitors":{"type":"array","description":"The monitors that use this input as a level. Present on IN_USE from an input update (`PATCH /inputs/{id}`, MCP `save_input`) that would change `data_type` to TEXT or CHOICE; move those monitors to another input first.","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","uniqueKey":"oneOf.0.monitors.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","uniqueKey":"oneOf.0.monitors.items.[INDEX].name","typeLabel":"string"},"portfolio_id":{"type":"string","format":"uuid","uniqueKey":"oneOf.0.monitors.items.[INDEX].portfolio_id","typeLabel":"string\u003cuuid>"}},"uniqueKey":"oneOf.0.monitors.items.[INDEX]","typeLabel":"object"},"uniqueKey":"oneOf.0.monitors","typeLabel":"object[]"}},"uniqueKey":"oneOf.0","typeLabel":"object"},{"type":"object","description":"The 400 body when a `prices` or `price_overrides` map does not cover\nevery symbol an endpoint requires a price for. `missing_symbols` is\npresent whenever the refusal is a coverage gap (a hole in the map);\nit is absent when the map was missing, malformed, or carried a\nnegative value, since there is nothing to list in that case.\nA held option with a mark, and an expired option, are never listed\nin `missing_symbols`: `missing_symbols` is the exact list, even where\nthe message says \"every non-cash position\" (ADR-147).\n","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable message, e.g. \"prices must cover every non-cash position in the portfolio\".","isRequired":true,"uniqueKey":"oneOf.1.error","typeLabel":"string"},"error_code":{"type":"string","enum":["PRICES_INCOMPLETE","PRICES_INVALID"],"description":"`PRICES_INCOMPLETE` for a missing map or a hole in it (with `missing_symbols` when it is a hole), `PRICES_INVALID` for a map of the wrong shape or with a negative or non-numeric value.","uniqueKey":"oneOf.1.error_code","typeLabel":"enum\u003cstring>"},"missing_symbols":{"type":"array","items":{"type":"string","uniqueKey":"oneOf.1.missing_symbols.items.[INDEX]","typeLabel":"string"},"description":"Held symbols absent from the map, sorted. Fetch prices for exactly these and call again.","uniqueKey":"oneOf.1.missing_symbols","typeLabel":"string[]"}},"uniqueKey":"oneOf.1","typeLabel":"object"}],"uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"error":"Portfolio not found or access denied","error_code":"NOT_FOUND"}}},"exampleType":"generated"}},"exampleType":"generated"},"1151d27b-2160-42f8-b6ca-8b5481a11d24":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"type":"object","description":"Uniform error envelope returned by every non-2xx response. `error` is\na human-readable message, safe to show the user. A refusal also\ncarries `error_code`, a stable string from the catalog in the info\nblock's Error codes section, to branch on; the HTTP status follows\nthe code (ADR-134). Only `401`, the no-plan `403`, the `403` for a\nkey without `read` and the `429` for the request budget have none.\nA `500` carries `request_id`. Extra fields by code:\nTIER_LIMIT_EXCEEDED carries `limit_type`, `current_count` and\n`max_allowed`, so a caller can branch on how many it has versus the\ncap without a second call. Its `error` text names no plan. It covers\nthe portfolio and scenario limits and the account caps: 20 webhook endpoints, 50\nwatchlists, 200 symbols on one watchlist and 50 custom symbols on one\nmonitor. Don't retry it. RULES_INVALID (`POST\n/scenarios/{id}/rules`) carries `issues`. ALREADY_EXISTS carries\n`existing_id`, plus `duplicate_ids` where older duplicates exist.\nCONFIRMATION_REQUIRED carries `would_remove`; from `POST /setup/apply` it\ncarries a fresh preview instead (`digest`, `file`, `items` and the rest of\n`SetupPreview`). INVALID_INPUT from the setup routes carries `issues`, and\nIN_USE from `POST /setup/receipts/{id}/undo` carries `later_receipt_id`.\nNOT_FOUND on a name, and\nINVALID_INPUT on a CHOICE value, carry `valid_values`. INVALID_INPUT on a\nmonitor whose level input is not a number type carries `input_id`.\nIN_USE on an input whose type would change while a monitor uses it\ncarries `monitors`. IN_USE on `PATCH /portfolios/{id}` that turns\nAllow shorts off while a short is held carries `short_count` and\n`symbols`. INVALID_INPUT on the holdings writes (`POST\n/positions/{symbol}`, `POST /portfolios/{id}/positions/apply`) that\nrefuses a negative `shares`, a `moves` other than 1 on an Options\nholding, or a `mark` on a holding outside the Options class, carries\n`symbols`, the holdings it refused. (`symbols` also appears on `WOULD_CLOSE_POSITIONS`, MCP\nonly, where it lists the holdings a full list left out.)\n","required":["error"],"example":{"error":"Portfolio not found or access denied","error_code":"NOT_FOUND"},"properties":{"error":{"type":"string","description":"Human-readable error message.","isRequired":true,"uniqueKey":"error","typeLabel":"string"},"error_code":{"type":"string","enum":["INVALID_INPUT","NOT_FOUND","ALREADY_EXISTS","IN_USE","CONFIRMATION_REQUIRED","PRICES_INCOMPLETE","PRICES_INVALID","WOULD_CLOSE_POSITIONS","INSUFFICIENT_CASH","RULES_INVALID","TARGET_OUT_OF_BAND","TIER_LIMIT_EXCEEDED","FEATURE_NOT_AVAILABLE","IDEMPOTENCY_KEY_CONFLICT","DAILY_LIMIT_EXCEEDED","READ_ONLY_KEY","OAUTH_WRITE_NOT_GRANTED","TOOL_REMOVED","INTERNAL_ERROR"],"description":"Stable machine-readable code from the closed catalog. Present on every refusal a route produces; absent on `401`, the no-plan `403`, the `403` for a key without `read`, and the `429` for the request budget.","uniqueKey":"error_code","typeLabel":"enum\u003cstring>"},"request_id":{"type":"string","format":"uuid","description":"Present on a `500` (`INTERNAL_ERROR`). The same id is in Allocait's logs next to the real cause; quote it when you report the failure.","uniqueKey":"request_id","typeLabel":"string\u003cuuid>"},"issues":{"type":"array","items":{"oneOf":[{"type":"object","description":"One problem or note found while reading rules text. A file\ncarrying any `error`-level issue is never applied; `POST\n/scenarios/{id}/rules` 400s with `error_code: RULES_INVALID` and\nthe full `issues` list instead. A file with only `warning`-level\nissues still applies; those ride along on the 200 response.\n","required":["level","message"],"properties":{"level":{"type":"string","enum":["error","warning"],"isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.0.level","typeLabel":"enum\u003cstring>"},"message":{"type":"string","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.0.message","typeLabel":"string"},"class":{"type":"string","description":"The asset class name this issue is about, when it is about one class rather than the whole file.","uniqueKey":"issues.items.[INDEX].oneOf.0.class","typeLabel":"string"}},"uniqueKey":"issues.items.[INDEX].oneOf.0","typeLabel":"object"},{"type":"object","description":"One thing the file's text got wrong (`error`) or one note about it (`warning`, such as a web address in a description).","required":["level","where","message"],"properties":{"level":{"type":"string","enum":["error","warning"],"isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.level","typeLabel":"enum\u003cstring>"},"where":{"oneOf":[{"type":"string","description":"Where in the file, or null for the file as a whole.","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.where.oneOf.0","typeLabel":"string"},{"type":"null","description":"Where in the file, or null for the file as a whole.","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.where.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.where","typeLabel":"string | null"},"message":{"type":"string","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.message","typeLabel":"string"},"line":{"type":"integer","description":"The line of the file the issue is on, when it is known.","uniqueKey":"issues.items.[INDEX].oneOf.1.line","typeLabel":"integer"}},"uniqueKey":"issues.items.[INDEX].oneOf.1","typeLabel":"object"}],"uniqueKey":"issues.items.[INDEX]","typeLabel":"object"},"description":"Every error and warning found while reading the rules text, when error_code is RULES_INVALID. From the setup routes (`POST /setup/preview`, `POST /setup/apply`) it is `SetupIssue` rows, on INVALID_INPUT.","uniqueKey":"issues","typeLabel":"object[]"},"later_receipt_id":{"type":"string","format":"uuid","description":"The newer import of the same setup file that blocks this undo. Present on IN_USE from `POST /setup/receipts/{id}/undo`; undo that receipt first.","uniqueKey":"later_receipt_id","typeLabel":"string\u003cuuid>"},"limit_type":{"type":"string","description":"Which cap was hit, e.g. \"portfolios\", \"scenarios\", \"webhook_endpoints\", \"watchlists\", \"asset_classes\", \"input_entries\" or \"input_entries_per_input\". Present only when error_code is TIER_LIMIT_EXCEEDED.","uniqueKey":"limit_type","typeLabel":"string"},"current_count":{"type":"number","description":"How many of limit_type the caller has right now. Present only when error_code is TIER_LIMIT_EXCEEDED.","uniqueKey":"current_count","typeLabel":"number"},"max_allowed":{"type":"number","description":"The cap for limit_type on this account. Present only when error_code is TIER_LIMIT_EXCEEDED.","uniqueKey":"max_allowed","typeLabel":"number"},"existing_id":{"type":"string","format":"uuid","description":"The record that already has this name. Present only when error_code is ALREADY_EXISTS; change it with that id instead of creating another.","uniqueKey":"existing_id","typeLabel":"string\u003cuuid>"},"duplicate_ids":{"type":"array","items":{"type":"string","format":"uuid","uniqueKey":"duplicate_ids.items.[INDEX]","typeLabel":"string\u003cuuid>"},"description":"Other records with the same name, created before duplicates were refused. Present only on ALREADY_EXISTS, and only when such records exist.","uniqueKey":"duplicate_ids","typeLabel":"string\u003cuuid>[]"},"would_remove":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string","uniqueKey":"would_remove.additionalProperties.items.[INDEX]","typeLabel":"string"},"uniqueKey":"would_remove.additionalProperties","typeLabel":"string[]"},"description":"What the call would drop, keyed by list field, for example the `items` that a watchlist save would remove. Present only when error_code is CONFIRMATION_REQUIRED; resend with those members in `remove`.","uniqueKey":"would_remove","typeLabel":"object"},"valid_values":{"type":"array","items":{"type":"string","uniqueKey":"valid_values.items.[INDEX]","typeLabel":"string"},"description":"The names or options the field accepts. Present on NOT_FOUND for a name, and on INVALID_INPUT for a CHOICE value.","uniqueKey":"valid_values","typeLabel":"string[]"},"input_id":{"type":"string","format":"uuid","description":"The level input that is not a NUMBER, DECIMAL or PERCENTAGE input. Present on INVALID_INPUT from a monitor create or update (`POST /monitors`, `PATCH /monitors/{id}`, MCP `save_monitor`); point the monitor at a number-type input instead.","uniqueKey":"input_id","typeLabel":"string\u003cuuid>"},"short_count":{"type":"integer","description":"How many shorts the portfolio holds. Present on IN_USE from `PATCH /portfolios/{id}` (MCP `save_portfolio`) that turns Allow shorts off while a short is held; close them first.","uniqueKey":"short_count","typeLabel":"integer"},"symbols":{"type":"array","items":{"type":"string","uniqueKey":"symbols.items.[INDEX]","typeLabel":"string"},"description":"Holdings the call names. On IN_USE from `PATCH /portfolios/{id}`, the shorts still held (up to 20, by symbol). On INVALID_INPUT from the holdings writes, the holdings whose negative `shares` was refused (Allow shorts is off, or the holding is in Options), or whose `moves` other than 1 was refused (the holding is in Options). On WOULD_CLOSE_POSITIONS (MCP only), the holdings a full list left out.","uniqueKey":"symbols","typeLabel":"string[]"},"monitors":{"type":"array","description":"The monitors that use this input as a level. Present on IN_USE from an input update (`PATCH /inputs/{id}`, MCP `save_input`) that would change `data_type` to TEXT or CHOICE; move those monitors to another input first.","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","uniqueKey":"monitors.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","uniqueKey":"monitors.items.[INDEX].name","typeLabel":"string"},"portfolio_id":{"type":"string","format":"uuid","uniqueKey":"monitors.items.[INDEX].portfolio_id","typeLabel":"string\u003cuuid>"}},"uniqueKey":"monitors.items.[INDEX]","typeLabel":"object"},"uniqueKey":"monitors","typeLabel":"object[]"}},"uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"error":"Portfolio not found or access denied","error_code":"NOT_FOUND"}}},"exampleType":"generated"}},"exampleType":"generated"},"33ae3146-326d-4ee6-a009-05a07d0d0c41":{"description":"Key lacks `write` permission (`READ_ONLY_KEY`; a Core account's keys read only), or API access needs an active plan","content":{"application/json":{"schema":{"type":"object","description":"Uniform error envelope returned by every non-2xx response. `error` is\na human-readable message, safe to show the user. A refusal also\ncarries `error_code`, a stable string from the catalog in the info\nblock's Error codes section, to branch on; the HTTP status follows\nthe code (ADR-134). Only `401`, the no-plan `403`, the `403` for a\nkey without `read` and the `429` for the request budget have none.\nA `500` carries `request_id`. Extra fields by code:\nTIER_LIMIT_EXCEEDED carries `limit_type`, `current_count` and\n`max_allowed`, so a caller can branch on how many it has versus the\ncap without a second call. Its `error` text names no plan. It covers\nthe portfolio and scenario limits and the account caps: 20 webhook endpoints, 50\nwatchlists, 200 symbols on one watchlist and 50 custom symbols on one\nmonitor. Don't retry it. RULES_INVALID (`POST\n/scenarios/{id}/rules`) carries `issues`. ALREADY_EXISTS carries\n`existing_id`, plus `duplicate_ids` where older duplicates exist.\nCONFIRMATION_REQUIRED carries `would_remove`; from `POST /setup/apply` it\ncarries a fresh preview instead (`digest`, `file`, `items` and the rest of\n`SetupPreview`). INVALID_INPUT from the setup routes carries `issues`, and\nIN_USE from `POST /setup/receipts/{id}/undo` carries `later_receipt_id`.\nNOT_FOUND on a name, and\nINVALID_INPUT on a CHOICE value, carry `valid_values`. INVALID_INPUT on a\nmonitor whose level input is not a number type carries `input_id`.\nIN_USE on an input whose type would change while a monitor uses it\ncarries `monitors`. IN_USE on `PATCH /portfolios/{id}` that turns\nAllow shorts off while a short is held carries `short_count` and\n`symbols`. INVALID_INPUT on the holdings writes (`POST\n/positions/{symbol}`, `POST /portfolios/{id}/positions/apply`) that\nrefuses a negative `shares`, a `moves` other than 1 on an Options\nholding, or a `mark` on a holding outside the Options class, carries\n`symbols`, the holdings it refused. (`symbols` also appears on `WOULD_CLOSE_POSITIONS`, MCP\nonly, where it lists the holdings a full list left out.)\n","required":["error"],"example":{"error":"Portfolio not found or access denied","error_code":"NOT_FOUND"},"properties":{"error":{"type":"string","description":"Human-readable error message.","isRequired":true,"uniqueKey":"error","typeLabel":"string"},"error_code":{"type":"string","enum":["INVALID_INPUT","NOT_FOUND","ALREADY_EXISTS","IN_USE","CONFIRMATION_REQUIRED","PRICES_INCOMPLETE","PRICES_INVALID","WOULD_CLOSE_POSITIONS","INSUFFICIENT_CASH","RULES_INVALID","TARGET_OUT_OF_BAND","TIER_LIMIT_EXCEEDED","FEATURE_NOT_AVAILABLE","IDEMPOTENCY_KEY_CONFLICT","DAILY_LIMIT_EXCEEDED","READ_ONLY_KEY","OAUTH_WRITE_NOT_GRANTED","TOOL_REMOVED","INTERNAL_ERROR"],"description":"Stable machine-readable code from the closed catalog. Present on every refusal a route produces; absent on `401`, the no-plan `403`, the `403` for a key without `read`, and the `429` for the request budget.","uniqueKey":"error_code","typeLabel":"enum\u003cstring>"},"request_id":{"type":"string","format":"uuid","description":"Present on a `500` (`INTERNAL_ERROR`). The same id is in Allocait's logs next to the real cause; quote it when you report the failure.","uniqueKey":"request_id","typeLabel":"string\u003cuuid>"},"issues":{"type":"array","items":{"oneOf":[{"type":"object","description":"One problem or note found while reading rules text. A file\ncarrying any `error`-level issue is never applied; `POST\n/scenarios/{id}/rules` 400s with `error_code: RULES_INVALID` and\nthe full `issues` list instead. A file with only `warning`-level\nissues still applies; those ride along on the 200 response.\n","required":["level","message"],"properties":{"level":{"type":"string","enum":["error","warning"],"isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.0.level","typeLabel":"enum\u003cstring>"},"message":{"type":"string","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.0.message","typeLabel":"string"},"class":{"type":"string","description":"The asset class name this issue is about, when it is about one class rather than the whole file.","uniqueKey":"issues.items.[INDEX].oneOf.0.class","typeLabel":"string"}},"uniqueKey":"issues.items.[INDEX].oneOf.0","typeLabel":"object"},{"type":"object","description":"One thing the file's text got wrong (`error`) or one note about it (`warning`, such as a web address in a description).","required":["level","where","message"],"properties":{"level":{"type":"string","enum":["error","warning"],"isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.level","typeLabel":"enum\u003cstring>"},"where":{"oneOf":[{"type":"string","description":"Where in the file, or null for the file as a whole.","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.where.oneOf.0","typeLabel":"string"},{"type":"null","description":"Where in the file, or null for the file as a whole.","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.where.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.where","typeLabel":"string | null"},"message":{"type":"string","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.message","typeLabel":"string"},"line":{"type":"integer","description":"The line of the file the issue is on, when it is known.","uniqueKey":"issues.items.[INDEX].oneOf.1.line","typeLabel":"integer"}},"uniqueKey":"issues.items.[INDEX].oneOf.1","typeLabel":"object"}],"uniqueKey":"issues.items.[INDEX]","typeLabel":"object"},"description":"Every error and warning found while reading the rules text, when error_code is RULES_INVALID. From the setup routes (`POST /setup/preview`, `POST /setup/apply`) it is `SetupIssue` rows, on INVALID_INPUT.","uniqueKey":"issues","typeLabel":"object[]"},"later_receipt_id":{"type":"string","format":"uuid","description":"The newer import of the same setup file that blocks this undo. Present on IN_USE from `POST /setup/receipts/{id}/undo`; undo that receipt first.","uniqueKey":"later_receipt_id","typeLabel":"string\u003cuuid>"},"limit_type":{"type":"string","description":"Which cap was hit, e.g. \"portfolios\", \"scenarios\", \"webhook_endpoints\", \"watchlists\", \"asset_classes\", \"input_entries\" or \"input_entries_per_input\". Present only when error_code is TIER_LIMIT_EXCEEDED.","uniqueKey":"limit_type","typeLabel":"string"},"current_count":{"type":"number","description":"How many of limit_type the caller has right now. Present only when error_code is TIER_LIMIT_EXCEEDED.","uniqueKey":"current_count","typeLabel":"number"},"max_allowed":{"type":"number","description":"The cap for limit_type on this account. Present only when error_code is TIER_LIMIT_EXCEEDED.","uniqueKey":"max_allowed","typeLabel":"number"},"existing_id":{"type":"string","format":"uuid","description":"The record that already has this name. Present only when error_code is ALREADY_EXISTS; change it with that id instead of creating another.","uniqueKey":"existing_id","typeLabel":"string\u003cuuid>"},"duplicate_ids":{"type":"array","items":{"type":"string","format":"uuid","uniqueKey":"duplicate_ids.items.[INDEX]","typeLabel":"string\u003cuuid>"},"description":"Other records with the same name, created before duplicates were refused. Present only on ALREADY_EXISTS, and only when such records exist.","uniqueKey":"duplicate_ids","typeLabel":"string\u003cuuid>[]"},"would_remove":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string","uniqueKey":"would_remove.additionalProperties.items.[INDEX]","typeLabel":"string"},"uniqueKey":"would_remove.additionalProperties","typeLabel":"string[]"},"description":"What the call would drop, keyed by list field, for example the `items` that a watchlist save would remove. Present only when error_code is CONFIRMATION_REQUIRED; resend with those members in `remove`.","uniqueKey":"would_remove","typeLabel":"object"},"valid_values":{"type":"array","items":{"type":"string","uniqueKey":"valid_values.items.[INDEX]","typeLabel":"string"},"description":"The names or options the field accepts. Present on NOT_FOUND for a name, and on INVALID_INPUT for a CHOICE value.","uniqueKey":"valid_values","typeLabel":"string[]"},"input_id":{"type":"string","format":"uuid","description":"The level input that is not a NUMBER, DECIMAL or PERCENTAGE input. Present on INVALID_INPUT from a monitor create or update (`POST /monitors`, `PATCH /monitors/{id}`, MCP `save_monitor`); point the monitor at a number-type input instead.","uniqueKey":"input_id","typeLabel":"string\u003cuuid>"},"short_count":{"type":"integer","description":"How many shorts the portfolio holds. Present on IN_USE from `PATCH /portfolios/{id}` (MCP `save_portfolio`) that turns Allow shorts off while a short is held; close them first.","uniqueKey":"short_count","typeLabel":"integer"},"symbols":{"type":"array","items":{"type":"string","uniqueKey":"symbols.items.[INDEX]","typeLabel":"string"},"description":"Holdings the call names. On IN_USE from `PATCH /portfolios/{id}`, the shorts still held (up to 20, by symbol). On INVALID_INPUT from the holdings writes, the holdings whose negative `shares` was refused (Allow shorts is off, or the holding is in Options), or whose `moves` other than 1 was refused (the holding is in Options). On WOULD_CLOSE_POSITIONS (MCP only), the holdings a full list left out.","uniqueKey":"symbols","typeLabel":"string[]"},"monitors":{"type":"array","description":"The monitors that use this input as a level. Present on IN_USE from an input update (`PATCH /inputs/{id}`, MCP `save_input`) that would change `data_type` to TEXT or CHOICE; move those monitors to another input first.","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","uniqueKey":"monitors.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","uniqueKey":"monitors.items.[INDEX].name","typeLabel":"string"},"portfolio_id":{"type":"string","format":"uuid","uniqueKey":"monitors.items.[INDEX].portfolio_id","typeLabel":"string\u003cuuid>"}},"uniqueKey":"monitors.items.[INDEX]","typeLabel":"object"},"uniqueKey":"monitors","typeLabel":"object[]"}},"uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"error":"Portfolio not found or access denied","error_code":"NOT_FOUND"}}},"exampleType":"generated"}},"exampleType":"generated"},"bc5fb155-7d89-46ac-a728-be02fb39f0b9":{"description":"A portfolio that is missing or not yours, or an `asset_class_id` that is neither one of this user's classes nor a built-in class (`NOT_FOUND`; the message is \"One or more asset_class_id values were not found for this user or the built-in classes\")","content":{"application/json":{"schema":{"type":"object","description":"Uniform error envelope returned by every non-2xx response. `error` is\na human-readable message, safe to show the user. A refusal also\ncarries `error_code`, a stable string from the catalog in the info\nblock's Error codes section, to branch on; the HTTP status follows\nthe code (ADR-134). Only `401`, the no-plan `403`, the `403` for a\nkey without `read` and the `429` for the request budget have none.\nA `500` carries `request_id`. Extra fields by code:\nTIER_LIMIT_EXCEEDED carries `limit_type`, `current_count` and\n`max_allowed`, so a caller can branch on how many it has versus the\ncap without a second call. Its `error` text names no plan. It covers\nthe portfolio and scenario limits and the account caps: 20 webhook endpoints, 50\nwatchlists, 200 symbols on one watchlist and 50 custom symbols on one\nmonitor. Don't retry it. RULES_INVALID (`POST\n/scenarios/{id}/rules`) carries `issues`. ALREADY_EXISTS carries\n`existing_id`, plus `duplicate_ids` where older duplicates exist.\nCONFIRMATION_REQUIRED carries `would_remove`; from `POST /setup/apply` it\ncarries a fresh preview instead (`digest`, `file`, `items` and the rest of\n`SetupPreview`). INVALID_INPUT from the setup routes carries `issues`, and\nIN_USE from `POST /setup/receipts/{id}/undo` carries `later_receipt_id`.\nNOT_FOUND on a name, and\nINVALID_INPUT on a CHOICE value, carry `valid_values`. INVALID_INPUT on a\nmonitor whose level input is not a number type carries `input_id`.\nIN_USE on an input whose type would change while a monitor uses it\ncarries `monitors`. IN_USE on `PATCH /portfolios/{id}` that turns\nAllow shorts off while a short is held carries `short_count` and\n`symbols`. INVALID_INPUT on the holdings writes (`POST\n/positions/{symbol}`, `POST /portfolios/{id}/positions/apply`) that\nrefuses a negative `shares`, a `moves` other than 1 on an Options\nholding, or a `mark` on a holding outside the Options class, carries\n`symbols`, the holdings it refused. (`symbols` also appears on `WOULD_CLOSE_POSITIONS`, MCP\nonly, where it lists the holdings a full list left out.)\n","required":["error"],"example":{"error":"Portfolio not found or access denied","error_code":"NOT_FOUND"},"properties":{"error":{"type":"string","description":"Human-readable error message.","isRequired":true,"uniqueKey":"error","typeLabel":"string"},"error_code":{"type":"string","enum":["INVALID_INPUT","NOT_FOUND","ALREADY_EXISTS","IN_USE","CONFIRMATION_REQUIRED","PRICES_INCOMPLETE","PRICES_INVALID","WOULD_CLOSE_POSITIONS","INSUFFICIENT_CASH","RULES_INVALID","TARGET_OUT_OF_BAND","TIER_LIMIT_EXCEEDED","FEATURE_NOT_AVAILABLE","IDEMPOTENCY_KEY_CONFLICT","DAILY_LIMIT_EXCEEDED","READ_ONLY_KEY","OAUTH_WRITE_NOT_GRANTED","TOOL_REMOVED","INTERNAL_ERROR"],"description":"Stable machine-readable code from the closed catalog. Present on every refusal a route produces; absent on `401`, the no-plan `403`, the `403` for a key without `read`, and the `429` for the request budget.","uniqueKey":"error_code","typeLabel":"enum\u003cstring>"},"request_id":{"type":"string","format":"uuid","description":"Present on a `500` (`INTERNAL_ERROR`). The same id is in Allocait's logs next to the real cause; quote it when you report the failure.","uniqueKey":"request_id","typeLabel":"string\u003cuuid>"},"issues":{"type":"array","items":{"oneOf":[{"type":"object","description":"One problem or note found while reading rules text. A file\ncarrying any `error`-level issue is never applied; `POST\n/scenarios/{id}/rules` 400s with `error_code: RULES_INVALID` and\nthe full `issues` list instead. A file with only `warning`-level\nissues still applies; those ride along on the 200 response.\n","required":["level","message"],"properties":{"level":{"type":"string","enum":["error","warning"],"isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.0.level","typeLabel":"enum\u003cstring>"},"message":{"type":"string","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.0.message","typeLabel":"string"},"class":{"type":"string","description":"The asset class name this issue is about, when it is about one class rather than the whole file.","uniqueKey":"issues.items.[INDEX].oneOf.0.class","typeLabel":"string"}},"uniqueKey":"issues.items.[INDEX].oneOf.0","typeLabel":"object"},{"type":"object","description":"One thing the file's text got wrong (`error`) or one note about it (`warning`, such as a web address in a description).","required":["level","where","message"],"properties":{"level":{"type":"string","enum":["error","warning"],"isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.level","typeLabel":"enum\u003cstring>"},"where":{"oneOf":[{"type":"string","description":"Where in the file, or null for the file as a whole.","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.where.oneOf.0","typeLabel":"string"},{"type":"null","description":"Where in the file, or null for the file as a whole.","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.where.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.where","typeLabel":"string | null"},"message":{"type":"string","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.message","typeLabel":"string"},"line":{"type":"integer","description":"The line of the file the issue is on, when it is known.","uniqueKey":"issues.items.[INDEX].oneOf.1.line","typeLabel":"integer"}},"uniqueKey":"issues.items.[INDEX].oneOf.1","typeLabel":"object"}],"uniqueKey":"issues.items.[INDEX]","typeLabel":"object"},"description":"Every error and warning found while reading the rules text, when error_code is RULES_INVALID. From the setup routes (`POST /setup/preview`, `POST /setup/apply`) it is `SetupIssue` rows, on INVALID_INPUT.","uniqueKey":"issues","typeLabel":"object[]"},"later_receipt_id":{"type":"string","format":"uuid","description":"The newer import of the same setup file that blocks this undo. Present on IN_USE from `POST /setup/receipts/{id}/undo`; undo that receipt first.","uniqueKey":"later_receipt_id","typeLabel":"string\u003cuuid>"},"limit_type":{"type":"string","description":"Which cap was hit, e.g. \"portfolios\", \"scenarios\", \"webhook_endpoints\", \"watchlists\", \"asset_classes\", \"input_entries\" or \"input_entries_per_input\". Present only when error_code is TIER_LIMIT_EXCEEDED.","uniqueKey":"limit_type","typeLabel":"string"},"current_count":{"type":"number","description":"How many of limit_type the caller has right now. Present only when error_code is TIER_LIMIT_EXCEEDED.","uniqueKey":"current_count","typeLabel":"number"},"max_allowed":{"type":"number","description":"The cap for limit_type on this account. Present only when error_code is TIER_LIMIT_EXCEEDED.","uniqueKey":"max_allowed","typeLabel":"number"},"existing_id":{"type":"string","format":"uuid","description":"The record that already has this name. Present only when error_code is ALREADY_EXISTS; change it with that id instead of creating another.","uniqueKey":"existing_id","typeLabel":"string\u003cuuid>"},"duplicate_ids":{"type":"array","items":{"type":"string","format":"uuid","uniqueKey":"duplicate_ids.items.[INDEX]","typeLabel":"string\u003cuuid>"},"description":"Other records with the same name, created before duplicates were refused. Present only on ALREADY_EXISTS, and only when such records exist.","uniqueKey":"duplicate_ids","typeLabel":"string\u003cuuid>[]"},"would_remove":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string","uniqueKey":"would_remove.additionalProperties.items.[INDEX]","typeLabel":"string"},"uniqueKey":"would_remove.additionalProperties","typeLabel":"string[]"},"description":"What the call would drop, keyed by list field, for example the `items` that a watchlist save would remove. Present only when error_code is CONFIRMATION_REQUIRED; resend with those members in `remove`.","uniqueKey":"would_remove","typeLabel":"object"},"valid_values":{"type":"array","items":{"type":"string","uniqueKey":"valid_values.items.[INDEX]","typeLabel":"string"},"description":"The names or options the field accepts. Present on NOT_FOUND for a name, and on INVALID_INPUT for a CHOICE value.","uniqueKey":"valid_values","typeLabel":"string[]"},"input_id":{"type":"string","format":"uuid","description":"The level input that is not a NUMBER, DECIMAL or PERCENTAGE input. Present on INVALID_INPUT from a monitor create or update (`POST /monitors`, `PATCH /monitors/{id}`, MCP `save_monitor`); point the monitor at a number-type input instead.","uniqueKey":"input_id","typeLabel":"string\u003cuuid>"},"short_count":{"type":"integer","description":"How many shorts the portfolio holds. Present on IN_USE from `PATCH /portfolios/{id}` (MCP `save_portfolio`) that turns Allow shorts off while a short is held; close them first.","uniqueKey":"short_count","typeLabel":"integer"},"symbols":{"type":"array","items":{"type":"string","uniqueKey":"symbols.items.[INDEX]","typeLabel":"string"},"description":"Holdings the call names. On IN_USE from `PATCH /portfolios/{id}`, the shorts still held (up to 20, by symbol). On INVALID_INPUT from the holdings writes, the holdings whose negative `shares` was refused (Allow shorts is off, or the holding is in Options), or whose `moves` other than 1 was refused (the holding is in Options). On WOULD_CLOSE_POSITIONS (MCP only), the holdings a full list left out.","uniqueKey":"symbols","typeLabel":"string[]"},"monitors":{"type":"array","description":"The monitors that use this input as a level. Present on IN_USE from an input update (`PATCH /inputs/{id}`, MCP `save_input`) that would change `data_type` to TEXT or CHOICE; move those monitors to another input first.","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","uniqueKey":"monitors.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","uniqueKey":"monitors.items.[INDEX].name","typeLabel":"string"},"portfolio_id":{"type":"string","format":"uuid","uniqueKey":"monitors.items.[INDEX].portfolio_id","typeLabel":"string\u003cuuid>"}},"uniqueKey":"monitors.items.[INDEX]","typeLabel":"object"},"uniqueKey":"monitors","typeLabel":"object[]"}},"uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"error":"Portfolio not found or access denied","error_code":"NOT_FOUND"}}},"exampleType":"generated"}},"exampleType":"generated"},"6350442f-c077-44fb-a39b-3eb23a72a06b":{"description":"A step that could not be afforded, a buy or a cover (`INSUFFICIENT_CASH`)","content":{"application/json":{"schema":{"type":"object","description":"Uniform error envelope returned by every non-2xx response. `error` is\na human-readable message, safe to show the user. A refusal also\ncarries `error_code`, a stable string from the catalog in the info\nblock's Error codes section, to branch on; the HTTP status follows\nthe code (ADR-134). Only `401`, the no-plan `403`, the `403` for a\nkey without `read` and the `429` for the request budget have none.\nA `500` carries `request_id`. Extra fields by code:\nTIER_LIMIT_EXCEEDED carries `limit_type`, `current_count` and\n`max_allowed`, so a caller can branch on how many it has versus the\ncap without a second call. Its `error` text names no plan. It covers\nthe portfolio and scenario limits and the account caps: 20 webhook endpoints, 50\nwatchlists, 200 symbols on one watchlist and 50 custom symbols on one\nmonitor. Don't retry it. RULES_INVALID (`POST\n/scenarios/{id}/rules`) carries `issues`. ALREADY_EXISTS carries\n`existing_id`, plus `duplicate_ids` where older duplicates exist.\nCONFIRMATION_REQUIRED carries `would_remove`; from `POST /setup/apply` it\ncarries a fresh preview instead (`digest`, `file`, `items` and the rest of\n`SetupPreview`). INVALID_INPUT from the setup routes carries `issues`, and\nIN_USE from `POST /setup/receipts/{id}/undo` carries `later_receipt_id`.\nNOT_FOUND on a name, and\nINVALID_INPUT on a CHOICE value, carry `valid_values`. INVALID_INPUT on a\nmonitor whose level input is not a number type carries `input_id`.\nIN_USE on an input whose type would change while a monitor uses it\ncarries `monitors`. IN_USE on `PATCH /portfolios/{id}` that turns\nAllow shorts off while a short is held carries `short_count` and\n`symbols`. INVALID_INPUT on the holdings writes (`POST\n/positions/{symbol}`, `POST /portfolios/{id}/positions/apply`) that\nrefuses a negative `shares`, a `moves` other than 1 on an Options\nholding, or a `mark` on a holding outside the Options class, carries\n`symbols`, the holdings it refused. (`symbols` also appears on `WOULD_CLOSE_POSITIONS`, MCP\nonly, where it lists the holdings a full list left out.)\n","required":["error"],"example":{"error":"Portfolio not found or access denied","error_code":"NOT_FOUND"},"properties":{"error":{"type":"string","description":"Human-readable error message.","isRequired":true,"uniqueKey":"error","typeLabel":"string"},"error_code":{"type":"string","enum":["INVALID_INPUT","NOT_FOUND","ALREADY_EXISTS","IN_USE","CONFIRMATION_REQUIRED","PRICES_INCOMPLETE","PRICES_INVALID","WOULD_CLOSE_POSITIONS","INSUFFICIENT_CASH","RULES_INVALID","TARGET_OUT_OF_BAND","TIER_LIMIT_EXCEEDED","FEATURE_NOT_AVAILABLE","IDEMPOTENCY_KEY_CONFLICT","DAILY_LIMIT_EXCEEDED","READ_ONLY_KEY","OAUTH_WRITE_NOT_GRANTED","TOOL_REMOVED","INTERNAL_ERROR"],"description":"Stable machine-readable code from the closed catalog. Present on every refusal a route produces; absent on `401`, the no-plan `403`, the `403` for a key without `read`, and the `429` for the request budget.","uniqueKey":"error_code","typeLabel":"enum\u003cstring>"},"request_id":{"type":"string","format":"uuid","description":"Present on a `500` (`INTERNAL_ERROR`). The same id is in Allocait's logs next to the real cause; quote it when you report the failure.","uniqueKey":"request_id","typeLabel":"string\u003cuuid>"},"issues":{"type":"array","items":{"oneOf":[{"type":"object","description":"One problem or note found while reading rules text. A file\ncarrying any `error`-level issue is never applied; `POST\n/scenarios/{id}/rules` 400s with `error_code: RULES_INVALID` and\nthe full `issues` list instead. A file with only `warning`-level\nissues still applies; those ride along on the 200 response.\n","required":["level","message"],"properties":{"level":{"type":"string","enum":["error","warning"],"isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.0.level","typeLabel":"enum\u003cstring>"},"message":{"type":"string","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.0.message","typeLabel":"string"},"class":{"type":"string","description":"The asset class name this issue is about, when it is about one class rather than the whole file.","uniqueKey":"issues.items.[INDEX].oneOf.0.class","typeLabel":"string"}},"uniqueKey":"issues.items.[INDEX].oneOf.0","typeLabel":"object"},{"type":"object","description":"One thing the file's text got wrong (`error`) or one note about it (`warning`, such as a web address in a description).","required":["level","where","message"],"properties":{"level":{"type":"string","enum":["error","warning"],"isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.level","typeLabel":"enum\u003cstring>"},"where":{"oneOf":[{"type":"string","description":"Where in the file, or null for the file as a whole.","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.where.oneOf.0","typeLabel":"string"},{"type":"null","description":"Where in the file, or null for the file as a whole.","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.where.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.where","typeLabel":"string | null"},"message":{"type":"string","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.message","typeLabel":"string"},"line":{"type":"integer","description":"The line of the file the issue is on, when it is known.","uniqueKey":"issues.items.[INDEX].oneOf.1.line","typeLabel":"integer"}},"uniqueKey":"issues.items.[INDEX].oneOf.1","typeLabel":"object"}],"uniqueKey":"issues.items.[INDEX]","typeLabel":"object"},"description":"Every error and warning found while reading the rules text, when error_code is RULES_INVALID. From the setup routes (`POST /setup/preview`, `POST /setup/apply`) it is `SetupIssue` rows, on INVALID_INPUT.","uniqueKey":"issues","typeLabel":"object[]"},"later_receipt_id":{"type":"string","format":"uuid","description":"The newer import of the same setup file that blocks this undo. Present on IN_USE from `POST /setup/receipts/{id}/undo`; undo that receipt first.","uniqueKey":"later_receipt_id","typeLabel":"string\u003cuuid>"},"limit_type":{"type":"string","description":"Which cap was hit, e.g. \"portfolios\", \"scenarios\", \"webhook_endpoints\", \"watchlists\", \"asset_classes\", \"input_entries\" or \"input_entries_per_input\". Present only when error_code is TIER_LIMIT_EXCEEDED.","uniqueKey":"limit_type","typeLabel":"string"},"current_count":{"type":"number","description":"How many of limit_type the caller has right now. Present only when error_code is TIER_LIMIT_EXCEEDED.","uniqueKey":"current_count","typeLabel":"number"},"max_allowed":{"type":"number","description":"The cap for limit_type on this account. Present only when error_code is TIER_LIMIT_EXCEEDED.","uniqueKey":"max_allowed","typeLabel":"number"},"existing_id":{"type":"string","format":"uuid","description":"The record that already has this name. Present only when error_code is ALREADY_EXISTS; change it with that id instead of creating another.","uniqueKey":"existing_id","typeLabel":"string\u003cuuid>"},"duplicate_ids":{"type":"array","items":{"type":"string","format":"uuid","uniqueKey":"duplicate_ids.items.[INDEX]","typeLabel":"string\u003cuuid>"},"description":"Other records with the same name, created before duplicates were refused. Present only on ALREADY_EXISTS, and only when such records exist.","uniqueKey":"duplicate_ids","typeLabel":"string\u003cuuid>[]"},"would_remove":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string","uniqueKey":"would_remove.additionalProperties.items.[INDEX]","typeLabel":"string"},"uniqueKey":"would_remove.additionalProperties","typeLabel":"string[]"},"description":"What the call would drop, keyed by list field, for example the `items` that a watchlist save would remove. Present only when error_code is CONFIRMATION_REQUIRED; resend with those members in `remove`.","uniqueKey":"would_remove","typeLabel":"object"},"valid_values":{"type":"array","items":{"type":"string","uniqueKey":"valid_values.items.[INDEX]","typeLabel":"string"},"description":"The names or options the field accepts. Present on NOT_FOUND for a name, and on INVALID_INPUT for a CHOICE value.","uniqueKey":"valid_values","typeLabel":"string[]"},"input_id":{"type":"string","format":"uuid","description":"The level input that is not a NUMBER, DECIMAL or PERCENTAGE input. Present on INVALID_INPUT from a monitor create or update (`POST /monitors`, `PATCH /monitors/{id}`, MCP `save_monitor`); point the monitor at a number-type input instead.","uniqueKey":"input_id","typeLabel":"string\u003cuuid>"},"short_count":{"type":"integer","description":"How many shorts the portfolio holds. Present on IN_USE from `PATCH /portfolios/{id}` (MCP `save_portfolio`) that turns Allow shorts off while a short is held; close them first.","uniqueKey":"short_count","typeLabel":"integer"},"symbols":{"type":"array","items":{"type":"string","uniqueKey":"symbols.items.[INDEX]","typeLabel":"string"},"description":"Holdings the call names. On IN_USE from `PATCH /portfolios/{id}`, the shorts still held (up to 20, by symbol). On INVALID_INPUT from the holdings writes, the holdings whose negative `shares` was refused (Allow shorts is off, or the holding is in Options), or whose `moves` other than 1 was refused (the holding is in Options). On WOULD_CLOSE_POSITIONS (MCP only), the holdings a full list left out.","uniqueKey":"symbols","typeLabel":"string[]"},"monitors":{"type":"array","description":"The monitors that use this input as a level. Present on IN_USE from an input update (`PATCH /inputs/{id}`, MCP `save_input`) that would change `data_type` to TEXT or CHOICE; move those monitors to another input first.","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","uniqueKey":"monitors.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","uniqueKey":"monitors.items.[INDEX].name","typeLabel":"string"},"portfolio_id":{"type":"string","format":"uuid","uniqueKey":"monitors.items.[INDEX].portfolio_id","typeLabel":"string\u003cuuid>"}},"uniqueKey":"monitors.items.[INDEX]","typeLabel":"object"},"uniqueKey":"monitors","typeLabel":"object[]"}},"uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"error":"Portfolio not found or access denied","error_code":"NOT_FOUND"}}},"exampleType":"generated"}},"exampleType":"generated"},"a645206f-59b1-4f2a-91eb-ae1878d073b4":{"description":"Rate limit exceeded: maximum 60 requests per minute per account,\nor 10,000 per UTC day (see the info block's Rate limits section).\nThe per-minute refusal carries `Retry-After: 60` and the\n`X-RateLimit-*` headers; the daily refusal carries `Retry-After`\nset to the seconds until 00:00 UTC and no `X-RateLimit-*` headers.\nNeither has an `error_code`. `POST /support-requests` and\n`POST /agent-feedback` also return `429` with `error_code:\nDAILY_LIMIT_EXCEEDED` past 20 filed per rolling 24 hours; that one\ncarries the `X-RateLimit-*` headers and no `Retry-After`.\n","headers":{"Retry-After":{"description":"Seconds to wait before retrying. Sent only on the request budget's own `429`s, never on a per-route cap (`DAILY_LIMIT_EXCEEDED`) or any other status.","schema":{"type":"integer","example":60}},"X-RateLimit-Limit":{"description":"The per-minute request budget, always `60`. Sent on every response that reaches a route and on the per-minute `429`.","schema":{"type":"integer","example":60}},"X-RateLimit-Remaining":{"description":"Requests left in the current one-minute window, after this one. The daily count is not reported until it is reached.","schema":{"type":"integer","example":41}}},"content":{"application/json":{"schema":{"type":"object","description":"Uniform error envelope returned by every non-2xx response. `error` is\na human-readable message, safe to show the user. A refusal also\ncarries `error_code`, a stable string from the catalog in the info\nblock's Error codes section, to branch on; the HTTP status follows\nthe code (ADR-134). Only `401`, the no-plan `403`, the `403` for a\nkey without `read` and the `429` for the request budget have none.\nA `500` carries `request_id`. Extra fields by code:\nTIER_LIMIT_EXCEEDED carries `limit_type`, `current_count` and\n`max_allowed`, so a caller can branch on how many it has versus the\ncap without a second call. Its `error` text names no plan. It covers\nthe portfolio and scenario limits and the account caps: 20 webhook endpoints, 50\nwatchlists, 200 symbols on one watchlist and 50 custom symbols on one\nmonitor. Don't retry it. RULES_INVALID (`POST\n/scenarios/{id}/rules`) carries `issues`. ALREADY_EXISTS carries\n`existing_id`, plus `duplicate_ids` where older duplicates exist.\nCONFIRMATION_REQUIRED carries `would_remove`; from `POST /setup/apply` it\ncarries a fresh preview instead (`digest`, `file`, `items` and the rest of\n`SetupPreview`). INVALID_INPUT from the setup routes carries `issues`, and\nIN_USE from `POST /setup/receipts/{id}/undo` carries `later_receipt_id`.\nNOT_FOUND on a name, and\nINVALID_INPUT on a CHOICE value, carry `valid_values`. INVALID_INPUT on a\nmonitor whose level input is not a number type carries `input_id`.\nIN_USE on an input whose type would change while a monitor uses it\ncarries `monitors`. IN_USE on `PATCH /portfolios/{id}` that turns\nAllow shorts off while a short is held carries `short_count` and\n`symbols`. INVALID_INPUT on the holdings writes (`POST\n/positions/{symbol}`, `POST /portfolios/{id}/positions/apply`) that\nrefuses a negative `shares`, a `moves` other than 1 on an Options\nholding, or a `mark` on a holding outside the Options class, carries\n`symbols`, the holdings it refused. (`symbols` also appears on `WOULD_CLOSE_POSITIONS`, MCP\nonly, where it lists the holdings a full list left out.)\n","required":["error"],"example":{"error":"Portfolio not found or access denied","error_code":"NOT_FOUND"},"properties":{"error":{"type":"string","description":"Human-readable error message.","isRequired":true,"uniqueKey":"error","typeLabel":"string"},"error_code":{"type":"string","enum":["INVALID_INPUT","NOT_FOUND","ALREADY_EXISTS","IN_USE","CONFIRMATION_REQUIRED","PRICES_INCOMPLETE","PRICES_INVALID","WOULD_CLOSE_POSITIONS","INSUFFICIENT_CASH","RULES_INVALID","TARGET_OUT_OF_BAND","TIER_LIMIT_EXCEEDED","FEATURE_NOT_AVAILABLE","IDEMPOTENCY_KEY_CONFLICT","DAILY_LIMIT_EXCEEDED","READ_ONLY_KEY","OAUTH_WRITE_NOT_GRANTED","TOOL_REMOVED","INTERNAL_ERROR"],"description":"Stable machine-readable code from the closed catalog. Present on every refusal a route produces; absent on `401`, the no-plan `403`, the `403` for a key without `read`, and the `429` for the request budget.","uniqueKey":"error_code","typeLabel":"enum\u003cstring>"},"request_id":{"type":"string","format":"uuid","description":"Present on a `500` (`INTERNAL_ERROR`). The same id is in Allocait's logs next to the real cause; quote it when you report the failure.","uniqueKey":"request_id","typeLabel":"string\u003cuuid>"},"issues":{"type":"array","items":{"oneOf":[{"type":"object","description":"One problem or note found while reading rules text. A file\ncarrying any `error`-level issue is never applied; `POST\n/scenarios/{id}/rules` 400s with `error_code: RULES_INVALID` and\nthe full `issues` list instead. A file with only `warning`-level\nissues still applies; those ride along on the 200 response.\n","required":["level","message"],"properties":{"level":{"type":"string","enum":["error","warning"],"isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.0.level","typeLabel":"enum\u003cstring>"},"message":{"type":"string","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.0.message","typeLabel":"string"},"class":{"type":"string","description":"The asset class name this issue is about, when it is about one class rather than the whole file.","uniqueKey":"issues.items.[INDEX].oneOf.0.class","typeLabel":"string"}},"uniqueKey":"issues.items.[INDEX].oneOf.0","typeLabel":"object"},{"type":"object","description":"One thing the file's text got wrong (`error`) or one note about it (`warning`, such as a web address in a description).","required":["level","where","message"],"properties":{"level":{"type":"string","enum":["error","warning"],"isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.level","typeLabel":"enum\u003cstring>"},"where":{"oneOf":[{"type":"string","description":"Where in the file, or null for the file as a whole.","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.where.oneOf.0","typeLabel":"string"},{"type":"null","description":"Where in the file, or null for the file as a whole.","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.where.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.where","typeLabel":"string | null"},"message":{"type":"string","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.message","typeLabel":"string"},"line":{"type":"integer","description":"The line of the file the issue is on, when it is known.","uniqueKey":"issues.items.[INDEX].oneOf.1.line","typeLabel":"integer"}},"uniqueKey":"issues.items.[INDEX].oneOf.1","typeLabel":"object"}],"uniqueKey":"issues.items.[INDEX]","typeLabel":"object"},"description":"Every error and warning found while reading the rules text, when error_code is RULES_INVALID. From the setup routes (`POST /setup/preview`, `POST /setup/apply`) it is `SetupIssue` rows, on INVALID_INPUT.","uniqueKey":"issues","typeLabel":"object[]"},"later_receipt_id":{"type":"string","format":"uuid","description":"The newer import of the same setup file that blocks this undo. Present on IN_USE from `POST /setup/receipts/{id}/undo`; undo that receipt first.","uniqueKey":"later_receipt_id","typeLabel":"string\u003cuuid>"},"limit_type":{"type":"string","description":"Which cap was hit, e.g. \"portfolios\", \"scenarios\", \"webhook_endpoints\", \"watchlists\", \"asset_classes\", \"input_entries\" or \"input_entries_per_input\". Present only when error_code is TIER_LIMIT_EXCEEDED.","uniqueKey":"limit_type","typeLabel":"string"},"current_count":{"type":"number","description":"How many of limit_type the caller has right now. Present only when error_code is TIER_LIMIT_EXCEEDED.","uniqueKey":"current_count","typeLabel":"number"},"max_allowed":{"type":"number","description":"The cap for limit_type on this account. Present only when error_code is TIER_LIMIT_EXCEEDED.","uniqueKey":"max_allowed","typeLabel":"number"},"existing_id":{"type":"string","format":"uuid","description":"The record that already has this name. Present only when error_code is ALREADY_EXISTS; change it with that id instead of creating another.","uniqueKey":"existing_id","typeLabel":"string\u003cuuid>"},"duplicate_ids":{"type":"array","items":{"type":"string","format":"uuid","uniqueKey":"duplicate_ids.items.[INDEX]","typeLabel":"string\u003cuuid>"},"description":"Other records with the same name, created before duplicates were refused. Present only on ALREADY_EXISTS, and only when such records exist.","uniqueKey":"duplicate_ids","typeLabel":"string\u003cuuid>[]"},"would_remove":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string","uniqueKey":"would_remove.additionalProperties.items.[INDEX]","typeLabel":"string"},"uniqueKey":"would_remove.additionalProperties","typeLabel":"string[]"},"description":"What the call would drop, keyed by list field, for example the `items` that a watchlist save would remove. Present only when error_code is CONFIRMATION_REQUIRED; resend with those members in `remove`.","uniqueKey":"would_remove","typeLabel":"object"},"valid_values":{"type":"array","items":{"type":"string","uniqueKey":"valid_values.items.[INDEX]","typeLabel":"string"},"description":"The names or options the field accepts. Present on NOT_FOUND for a name, and on INVALID_INPUT for a CHOICE value.","uniqueKey":"valid_values","typeLabel":"string[]"},"input_id":{"type":"string","format":"uuid","description":"The level input that is not a NUMBER, DECIMAL or PERCENTAGE input. Present on INVALID_INPUT from a monitor create or update (`POST /monitors`, `PATCH /monitors/{id}`, MCP `save_monitor`); point the monitor at a number-type input instead.","uniqueKey":"input_id","typeLabel":"string\u003cuuid>"},"short_count":{"type":"integer","description":"How many shorts the portfolio holds. Present on IN_USE from `PATCH /portfolios/{id}` (MCP `save_portfolio`) that turns Allow shorts off while a short is held; close them first.","uniqueKey":"short_count","typeLabel":"integer"},"symbols":{"type":"array","items":{"type":"string","uniqueKey":"symbols.items.[INDEX]","typeLabel":"string"},"description":"Holdings the call names. On IN_USE from `PATCH /portfolios/{id}`, the shorts still held (up to 20, by symbol). On INVALID_INPUT from the holdings writes, the holdings whose negative `shares` was refused (Allow shorts is off, or the holding is in Options), or whose `moves` other than 1 was refused (the holding is in Options). On WOULD_CLOSE_POSITIONS (MCP only), the holdings a full list left out.","uniqueKey":"symbols","typeLabel":"string[]"},"monitors":{"type":"array","description":"The monitors that use this input as a level. Present on IN_USE from an input update (`PATCH /inputs/{id}`, MCP `save_input`) that would change `data_type` to TEXT or CHOICE; move those monitors to another input first.","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","uniqueKey":"monitors.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","uniqueKey":"monitors.items.[INDEX].name","typeLabel":"string"},"portfolio_id":{"type":"string","format":"uuid","uniqueKey":"monitors.items.[INDEX].portfolio_id","typeLabel":"string\u003cuuid>"}},"uniqueKey":"monitors.items.[INDEX]","typeLabel":"object"},"uniqueKey":"monitors","typeLabel":"object[]"}},"uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"error":"Portfolio not found or access denied","error_code":"NOT_FOUND"}}},"exampleType":"generated"}},"exampleType":"generated"},"dc847161-7c4a-4004-a58b-ae2ef187f448":{"description":"Unexpected server error. `error_code` is `INTERNAL_ERROR` and\n`request_id` names this failure: quote it in `POST /agent-feedback`.\nThe body never carries a schema, column or constraint name.\n","content":{"application/json":{"schema":{"type":"object","description":"Uniform error envelope returned by every non-2xx response. `error` is\na human-readable message, safe to show the user. A refusal also\ncarries `error_code`, a stable string from the catalog in the info\nblock's Error codes section, to branch on; the HTTP status follows\nthe code (ADR-134). Only `401`, the no-plan `403`, the `403` for a\nkey without `read` and the `429` for the request budget have none.\nA `500` carries `request_id`. Extra fields by code:\nTIER_LIMIT_EXCEEDED carries `limit_type`, `current_count` and\n`max_allowed`, so a caller can branch on how many it has versus the\ncap without a second call. Its `error` text names no plan. It covers\nthe portfolio and scenario limits and the account caps: 20 webhook endpoints, 50\nwatchlists, 200 symbols on one watchlist and 50 custom symbols on one\nmonitor. Don't retry it. RULES_INVALID (`POST\n/scenarios/{id}/rules`) carries `issues`. ALREADY_EXISTS carries\n`existing_id`, plus `duplicate_ids` where older duplicates exist.\nCONFIRMATION_REQUIRED carries `would_remove`; from `POST /setup/apply` it\ncarries a fresh preview instead (`digest`, `file`, `items` and the rest of\n`SetupPreview`). INVALID_INPUT from the setup routes carries `issues`, and\nIN_USE from `POST /setup/receipts/{id}/undo` carries `later_receipt_id`.\nNOT_FOUND on a name, and\nINVALID_INPUT on a CHOICE value, carry `valid_values`. INVALID_INPUT on a\nmonitor whose level input is not a number type carries `input_id`.\nIN_USE on an input whose type would change while a monitor uses it\ncarries `monitors`. IN_USE on `PATCH /portfolios/{id}` that turns\nAllow shorts off while a short is held carries `short_count` and\n`symbols`. INVALID_INPUT on the holdings writes (`POST\n/positions/{symbol}`, `POST /portfolios/{id}/positions/apply`) that\nrefuses a negative `shares`, a `moves` other than 1 on an Options\nholding, or a `mark` on a holding outside the Options class, carries\n`symbols`, the holdings it refused. (`symbols` also appears on `WOULD_CLOSE_POSITIONS`, MCP\nonly, where it lists the holdings a full list left out.)\n","required":["error"],"example":{"error":"Portfolio not found or access denied","error_code":"NOT_FOUND"},"properties":{"error":{"type":"string","description":"Human-readable error message.","isRequired":true,"uniqueKey":"error","typeLabel":"string"},"error_code":{"type":"string","enum":["INVALID_INPUT","NOT_FOUND","ALREADY_EXISTS","IN_USE","CONFIRMATION_REQUIRED","PRICES_INCOMPLETE","PRICES_INVALID","WOULD_CLOSE_POSITIONS","INSUFFICIENT_CASH","RULES_INVALID","TARGET_OUT_OF_BAND","TIER_LIMIT_EXCEEDED","FEATURE_NOT_AVAILABLE","IDEMPOTENCY_KEY_CONFLICT","DAILY_LIMIT_EXCEEDED","READ_ONLY_KEY","OAUTH_WRITE_NOT_GRANTED","TOOL_REMOVED","INTERNAL_ERROR"],"description":"Stable machine-readable code from the closed catalog. Present on every refusal a route produces; absent on `401`, the no-plan `403`, the `403` for a key without `read`, and the `429` for the request budget.","uniqueKey":"error_code","typeLabel":"enum\u003cstring>"},"request_id":{"type":"string","format":"uuid","description":"Present on a `500` (`INTERNAL_ERROR`). The same id is in Allocait's logs next to the real cause; quote it when you report the failure.","uniqueKey":"request_id","typeLabel":"string\u003cuuid>"},"issues":{"type":"array","items":{"oneOf":[{"type":"object","description":"One problem or note found while reading rules text. A file\ncarrying any `error`-level issue is never applied; `POST\n/scenarios/{id}/rules` 400s with `error_code: RULES_INVALID` and\nthe full `issues` list instead. A file with only `warning`-level\nissues still applies; those ride along on the 200 response.\n","required":["level","message"],"properties":{"level":{"type":"string","enum":["error","warning"],"isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.0.level","typeLabel":"enum\u003cstring>"},"message":{"type":"string","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.0.message","typeLabel":"string"},"class":{"type":"string","description":"The asset class name this issue is about, when it is about one class rather than the whole file.","uniqueKey":"issues.items.[INDEX].oneOf.0.class","typeLabel":"string"}},"uniqueKey":"issues.items.[INDEX].oneOf.0","typeLabel":"object"},{"type":"object","description":"One thing the file's text got wrong (`error`) or one note about it (`warning`, such as a web address in a description).","required":["level","where","message"],"properties":{"level":{"type":"string","enum":["error","warning"],"isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.level","typeLabel":"enum\u003cstring>"},"where":{"oneOf":[{"type":"string","description":"Where in the file, or null for the file as a whole.","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.where.oneOf.0","typeLabel":"string"},{"type":"null","description":"Where in the file, or null for the file as a whole.","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.where.oneOf.1","typeLabel":"null"}],"isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.where","typeLabel":"string | null"},"message":{"type":"string","isRequired":true,"uniqueKey":"issues.items.[INDEX].oneOf.1.message","typeLabel":"string"},"line":{"type":"integer","description":"The line of the file the issue is on, when it is known.","uniqueKey":"issues.items.[INDEX].oneOf.1.line","typeLabel":"integer"}},"uniqueKey":"issues.items.[INDEX].oneOf.1","typeLabel":"object"}],"uniqueKey":"issues.items.[INDEX]","typeLabel":"object"},"description":"Every error and warning found while reading the rules text, when error_code is RULES_INVALID. From the setup routes (`POST /setup/preview`, `POST /setup/apply`) it is `SetupIssue` rows, on INVALID_INPUT.","uniqueKey":"issues","typeLabel":"object[]"},"later_receipt_id":{"type":"string","format":"uuid","description":"The newer import of the same setup file that blocks this undo. Present on IN_USE from `POST /setup/receipts/{id}/undo`; undo that receipt first.","uniqueKey":"later_receipt_id","typeLabel":"string\u003cuuid>"},"limit_type":{"type":"string","description":"Which cap was hit, e.g. \"portfolios\", \"scenarios\", \"webhook_endpoints\", \"watchlists\", \"asset_classes\", \"input_entries\" or \"input_entries_per_input\". Present only when error_code is TIER_LIMIT_EXCEEDED.","uniqueKey":"limit_type","typeLabel":"string"},"current_count":{"type":"number","description":"How many of limit_type the caller has right now. Present only when error_code is TIER_LIMIT_EXCEEDED.","uniqueKey":"current_count","typeLabel":"number"},"max_allowed":{"type":"number","description":"The cap for limit_type on this account. Present only when error_code is TIER_LIMIT_EXCEEDED.","uniqueKey":"max_allowed","typeLabel":"number"},"existing_id":{"type":"string","format":"uuid","description":"The record that already has this name. Present only when error_code is ALREADY_EXISTS; change it with that id instead of creating another.","uniqueKey":"existing_id","typeLabel":"string\u003cuuid>"},"duplicate_ids":{"type":"array","items":{"type":"string","format":"uuid","uniqueKey":"duplicate_ids.items.[INDEX]","typeLabel":"string\u003cuuid>"},"description":"Other records with the same name, created before duplicates were refused. Present only on ALREADY_EXISTS, and only when such records exist.","uniqueKey":"duplicate_ids","typeLabel":"string\u003cuuid>[]"},"would_remove":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string","uniqueKey":"would_remove.additionalProperties.items.[INDEX]","typeLabel":"string"},"uniqueKey":"would_remove.additionalProperties","typeLabel":"string[]"},"description":"What the call would drop, keyed by list field, for example the `items` that a watchlist save would remove. Present only when error_code is CONFIRMATION_REQUIRED; resend with those members in `remove`.","uniqueKey":"would_remove","typeLabel":"object"},"valid_values":{"type":"array","items":{"type":"string","uniqueKey":"valid_values.items.[INDEX]","typeLabel":"string"},"description":"The names or options the field accepts. Present on NOT_FOUND for a name, and on INVALID_INPUT for a CHOICE value.","uniqueKey":"valid_values","typeLabel":"string[]"},"input_id":{"type":"string","format":"uuid","description":"The level input that is not a NUMBER, DECIMAL or PERCENTAGE input. Present on INVALID_INPUT from a monitor create or update (`POST /monitors`, `PATCH /monitors/{id}`, MCP `save_monitor`); point the monitor at a number-type input instead.","uniqueKey":"input_id","typeLabel":"string\u003cuuid>"},"short_count":{"type":"integer","description":"How many shorts the portfolio holds. Present on IN_USE from `PATCH /portfolios/{id}` (MCP `save_portfolio`) that turns Allow shorts off while a short is held; close them first.","uniqueKey":"short_count","typeLabel":"integer"},"symbols":{"type":"array","items":{"type":"string","uniqueKey":"symbols.items.[INDEX]","typeLabel":"string"},"description":"Holdings the call names. On IN_USE from `PATCH /portfolios/{id}`, the shorts still held (up to 20, by symbol). On INVALID_INPUT from the holdings writes, the holdings whose negative `shares` was refused (Allow shorts is off, or the holding is in Options), or whose `moves` other than 1 was refused (the holding is in Options). On WOULD_CLOSE_POSITIONS (MCP only), the holdings a full list left out.","uniqueKey":"symbols","typeLabel":"string[]"},"monitors":{"type":"array","description":"The monitors that use this input as a level. Present on IN_USE from an input update (`PATCH /inputs/{id}`, MCP `save_input`) that would change `data_type` to TEXT or CHOICE; move those monitors to another input first.","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","uniqueKey":"monitors.items.[INDEX].id","typeLabel":"string\u003cuuid>"},"name":{"type":"string","uniqueKey":"monitors.items.[INDEX].name","typeLabel":"string"},"portfolio_id":{"type":"string","format":"uuid","uniqueKey":"monitors.items.[INDEX].portfolio_id","typeLabel":"string\u003cuuid>"}},"uniqueKey":"monitors.items.[INDEX]","typeLabel":"object"},"uniqueKey":"monitors","typeLabel":"object[]"}},"uniqueKey":"","typeLabel":"object"},"example":{"error":"Internal error","error_code":"INTERNAL_ERROR","request_id":"5b1e7c52-3f4a-4d0e-9a55-2d5cbe6f0a11"},"examples":{"Example":{"title":"Example","value":{"error":"Internal error","error_code":"INTERNAL_ERROR","request_id":"5b1e7c52-3f4a-4d0e-9a55-2d5cbe6f0a11"}}},"exampleType":"examples"}},"exampleType":"examples"}},"security":{"9317449d-ab5b-4e81-b6f1-0d90131137bf":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Raw API key shown once when created in the Allocait app (Settings > API keys). Send it verbatim in this header; the server stores and compares only its SHA-256 hash. Allocait generates every key: `alc_` followed by 64 lowercase hex characters. Keys issued before 2026-10-10 are 64 hex characters with no prefix and keep working. A key may carry an expiry (90 days or 1 year, or never). A malformed, unknown, revoked or expired key gets `401` with no further detail, and a malformed one is refused before any lookup. Keys cannot be created through this API.","scopes":[],"schemeName":"ApiKeyAuth"}}},"servers":{"13120e45-325e-41e1-99fd-a11d6f409d02":{"url":"https://allocait.app/api/v1","description":"Hosted Allocait API"}},"processedSecurityOptions":[{"schemes":[{"scheme":{"type":"apiKey","in":"header","name":"x-api-key","description":"Raw API key shown once when created in the Allocait app (Settings > API keys). Send it verbatim in this header; the server stores and compares only its SHA-256 hash. Allocait generates every key: `alc_` followed by 64 lowercase hex characters. Keys issued before 2026-10-10 are 64 hex characters with no prefix and keep working. A key may carry an expiry (90 days or 1 year, or never). A malformed, unknown, revoked or expired key gets `401` with no further detail, and a malformed one is refused before any lookup. Keys cannot be created through this API.","scopes":[],"schemeName":"ApiKeyAuth"},"schema":{"title":"x-api-key","type":"string","description":"Raw API key shown once when created in the Allocait app (Settings > API keys). Send it verbatim in this header; the server stores and compares only its SHA-256 hash. Allocait generates every key: `alc_` followed by 64 lowercase hex characters. Keys issued before 2026-10-10 are 64 hex characters with no prefix and keep working. A key may carry an expiry (90 days or 1 year, or never). A malformed, unknown, revoked or expired key gets `401` with no further detail, and a malformed one is refused before any lookup. Keys cannot be created through this API.","isRequired":true,"typeLabel":"string","deprecated":false},"schemeName":"ApiKeyAuth","name":"x-api-key"}],"title":"ApiKeyAuth"}]},"originalFileLocation":"openapi.yaml"},"subdomain":"allocait","siteName":"Allocait","title":"Reconcile a portfolio's holdings to an absolute desired state - Allocait","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-11-09T05:23:21.647Z","originalFileLocation":"api-reference/holdings-and-cash/reconcile-a-portfolios-holdings-to-an-absolute-desired-state.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=\"dark\"}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=\"\u0026quot;Make Allocait match what I hold\u0026quot;: the primary agent write (ADR-019). Pass the FULL desired set of non-cash holdings; anything currently held that is not listed is closed. Every change routes through the same RPC the app UI uses (position_create_with_cash/ position_update_with_cash/position_close), so cash is always settled and affordability always enforced server-side. This records the change in Allocait only. It moves no money and places no order. Idempotent: calling this again with the same desired state changes nothing (symbols already matching are reported under unchanged, with no write performed). Atomic: the whole batch is one transaction; if any single change would fail (e.g. insufficient cash), the entire call fails and nothing from it is applied. A row whose share count is unchanged needs no price, and a price on it is never used for anything. Shorts. A negative shares is a short. It is recorded only on a portfolio with allow_shorts on and never for a holding in the Options class (including a row that moves a short into Options); otherwise the call is INVALID_INPUT, carries symbols, and writes nothing. A short sale credits cash, and closing or reducing a short debits it at the row\u0026#x27;s current_price, so a cover that cash cannot pay is INSUFFICIENT_CASH. A close of a held short (a shares: 0 row, or a held symbol you drop in replace mode) is that cover, and needs its price like any close. Short proceeds are credited to cash within the call, so one call can fund a buy with a short sale. A repeat of the same desired state, shorts included, changes nothing. Closing a symbol you drop from the desired state needs a price it does not otherwise carry. price_overrides must carry one for every held symbol absent from positions: its proceeds are booked at that price. A held symbol about to close with no price in the map fails with a 400 whose body carries missing_symbols. Allocait never books a close at its own stored quote (ADR-036). An option you leave out needs its price too, even with a mark: the mark may be months old, and a close books cash at the price named. One case needs none: an option past its expiry closes at 0 with no cash and records one action (ADR-147). Options and marks. A row may carry mark (an option\u0026#x27;s per-share premium; see DesiredPosition). Left out or null, the stored mark stays. A row whose only change is mark is reported under updated, needs no price, moves no cash and records no action. A mark on a holding that ends outside the Options class is INVALID_INPUT, carries symbols and writes nothing (ADR-147). An expired option is closed with {symbol, shares: 0, price: 0}, after the person says what happened; the row needs price: 0 because the share count changes. Moves. A row may carry moves (1, 2, 3, -1, -2 or -3; see DesiredPosition). Left out or null, the stored value stays, so the state you read from GET /positions can go back unchanged. A row whose only change is moves is reported under updated, needs no price and records no action. Moves other than 1 on cash or on an Options-class holding (including a row that moves a fund into Options) is INVALID_INPUT and nothing is written (ADR-142). Symbols. Every symbol is trimmed and uppercased before it is matched, so alfa updates the held ALFA instead of opening a second holding, and the close-price check uses the same rule. After that each must be a ticker of 1 to 21 characters of A-Z, 0-9, . or - starting with a letter or digit (ALFA, ALFA.B, or an option symbol such as ECHO281215P00050000), or a slash pair (ALFA/BRAV). Anything else is a 400 with \u0026quot;Each position needs a valid symbol: a ticker (ALFA, ALFA.B) or a pair (ALFA/BRAV)\u0026quot;, and the database refuses it with error_code: INVALID_INPUT if it gets that far. Nothing is written. Requires the API key to have the write permission.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Allocait\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/allocait/8WDpZ9h0WNasoRjb/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/allocait/8WDpZ9h0WNasoRjb/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/allocait/8WDpZ9h0WNasoRjb/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/allocait/8WDpZ9h0WNasoRjb/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/allocait/8WDpZ9h0WNasoRjb/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/allocait/8WDpZ9h0WNasoRjb/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/allocait/8WDpZ9h0WNasoRjb/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/allocait/8WDpZ9h0WNasoRjb/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/allocait/8WDpZ9h0WNasoRjb/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Reconcile a portfolio\u0026#x27;s holdings to an absolute desired state - Allocait\">\u003cmeta data-mint-head property=\"og:description\" content=\"\u0026quot;Make Allocait match what I hold\u0026quot;: the primary agent write (ADR-019). Pass the FULL desired set of non-cash holdings; anything currently held that is not listed is closed. Every change routes through the same RPC the app UI uses (position_create_with_cash/ position_update_with_cash/position_close), so cash is always settled and affordability always enforced server-side. This records the change in Allocait only. It moves no money and places no order. Idempotent: calling this again with the same desired state changes nothing (symbols already matching are reported under unchanged, with no write performed). Atomic: the whole batch is one transaction; if any single change would fail (e.g. insufficient cash), the entire call fails and nothing from it is applied. A row whose share count is unchanged needs no price, and a price on it is never used for anything. Shorts. A negative shares is a short. It is recorded only on a portfolio with allow_shorts on and never for a holding in the Options class (including a row that moves a short into Options); otherwise the call is INVALID_INPUT, carries symbols, and writes nothing. A short sale credits cash, and closing or reducing a short debits it at the row\u0026#x27;s current_price, so a cover that cash cannot pay is INSUFFICIENT_CASH. A close of a held short (a shares: 0 row, or a held symbol you drop in replace mode) is that cover, and needs its price like any close. Short proceeds are credited to cash within the call, so one call can fund a buy with a short sale. A repeat of the same desired state, shorts included, changes nothing. Closing a symbol you drop from the desired state needs a price it does not otherwise carry. price_overrides must carry one for every held symbol absent from positions: its proceeds are booked at that price. A held symbol about to close with no price in the map fails with a 400 whose body carries missing_symbols. Allocait never books a close at its own stored quote (ADR-036). An option you leave out needs its price too, even with a mark: the mark may be months old, and a close books cash at the price named. One case needs none: an option past its expiry closes at 0 with no cash and records one action (ADR-147). Options and marks. A row may carry mark (an option\u0026#x27;s per-share premium; see DesiredPosition). Left out or null, the stored mark stays. A row whose only change is mark is reported under updated, needs no price, moves no cash and records no action. A mark on a holding that ends outside the Options class is INVALID_INPUT, carries symbols and writes nothing (ADR-147). An expired option is closed with {symbol, shares: 0, price: 0}, after the person says what happened; the row needs price: 0 because the share count changes. Moves. A row may carry moves (1, 2, 3, -1, -2 or -3; see DesiredPosition). Left out or null, the stored value stays, so the state you read from GET /positions can go back unchanged. A row whose only change is moves is reported under updated, needs no price and records no action. Moves other than 1 on cash or on an Options-class holding (including a row that moves a fund into Options) is INVALID_INPUT and nothing is written (ADR-142). Symbols. Every symbol is trimmed and uppercased before it is matched, so alfa updates the held ALFA instead of opening a second holding, and the close-price check uses the same rule. After that each must be a ticker of 1 to 21 characters of A-Z, 0-9, . or - starting with a letter or digit (ALFA, ALFA.B, or an option symbol such as ECHO281215P00050000), or a slash pair (ALFA/BRAV). Anything else is a 400 with \u0026quot;Each position needs a valid symbol: a ticker (ALFA, ALFA.B) or a pair (ALFA/BRAV)\u0026quot;, and the database refuses it with error_code: INVALID_INPUT if it gets that far. Nothing is written. Requires the API key to have the write permission.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Allocait\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.allocait.app/api-reference/holdings-and-cash/reconcile-a-portfolios-holdings-to-an-absolute-desired-state\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://allocait.mintlify.site/_mintlify/api/og/v2?division=Holdings+and+cash\u0026amp;title=Reconcile+a+portfolio%27s+holdings+to+an+absolute+desired+state\u0026amp;description=%22Make+Allocait+match+what+I+hold%22%3A+the+primary+agent+write+%28ADR-019%29.%0APass+the+FULL+desired+set+of+non-cash+holdings%3B+anything+currently%0Aheld+that+is+not+listed\u0026amp;theme=f4ffca4ae92cd96f3d29a15e\u0026amp;signature=dd1ab476d731e051c48226d5965d85a08b602f24d94aabeb3e12a2ecaa701a33\">\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=\"Reconcile a portfolio\u0026#x27;s holdings to an absolute desired state - Allocait\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"\u0026quot;Make Allocait match what I hold\u0026quot;: the primary agent write (ADR-019). Pass the FULL desired set of non-cash holdings; anything currently held that is not listed is closed. Every change routes through the same RPC the app UI uses (position_create_with_cash/ position_update_with_cash/position_close), so cash is always settled and affordability always enforced server-side. This records the change in Allocait only. It moves no money and places no order. Idempotent: calling this again with the same desired state changes nothing (symbols already matching are reported under unchanged, with no write performed). Atomic: the whole batch is one transaction; if any single change would fail (e.g. insufficient cash), the entire call fails and nothing from it is applied. A row whose share count is unchanged needs no price, and a price on it is never used for anything. Shorts. A negative shares is a short. It is recorded only on a portfolio with allow_shorts on and never for a holding in the Options class (including a row that moves a short into Options); otherwise the call is INVALID_INPUT, carries symbols, and writes nothing. A short sale credits cash, and closing or reducing a short debits it at the row\u0026#x27;s current_price, so a cover that cash cannot pay is INSUFFICIENT_CASH. A close of a held short (a shares: 0 row, or a held symbol you drop in replace mode) is that cover, and needs its price like any close. Short proceeds are credited to cash within the call, so one call can fund a buy with a short sale. A repeat of the same desired state, shorts included, changes nothing. Closing a symbol you drop from the desired state needs a price it does not otherwise carry. price_overrides must carry one for every held symbol absent from positions: its proceeds are booked at that price. A held symbol about to close with no price in the map fails with a 400 whose body carries missing_symbols. Allocait never books a close at its own stored quote (ADR-036). An option you leave out needs its price too, even with a mark: the mark may be months old, and a close books cash at the price named. One case needs none: an option past its expiry closes at 0 with no cash and records one action (ADR-147). Options and marks. A row may carry mark (an option\u0026#x27;s per-share premium; see DesiredPosition). Left out or null, the stored mark stays. A row whose only change is mark is reported under updated, needs no price, moves no cash and records no action. A mark on a holding that ends outside the Options class is INVALID_INPUT, carries symbols and writes nothing (ADR-147). An expired option is closed with {symbol, shares: 0, price: 0}, after the person says what happened; the row needs price: 0 because the share count changes. Moves. A row may carry moves (1, 2, 3, -1, -2 or -3; see DesiredPosition). Left out or null, the stored value stays, so the state you read from GET /positions can go back unchanged. A row whose only change is moves is reported under updated, needs no price and records no action. Moves other than 1 on cash or on an Options-class holding (including a row that moves a fund into Options) is INVALID_INPUT and nothing is written (ADR-142). Symbols. Every symbol is trimmed and uppercased before it is matched, so alfa updates the held ALFA instead of opening a second holding, and the close-price check uses the same rule. After that each must be a ticker of 1 to 21 characters of A-Z, 0-9, . or - starting with a letter or digit (ALFA, ALFA.B, or an option symbol such as ECHO281215P00050000), or a slash pair (ALFA/BRAV). Anything else is a 400 with \u0026quot;Each position needs a valid symbol: a ticker (ALFA, ALFA.B) or a pair (ALFA/BRAV)\u0026quot;, and the database refuses it with error_code: INVALID_INPUT if it gets that far. Nothing is written. Requires the API key to have the write permission.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://allocait.mintlify.site/_mintlify/api/og/v2?division=Holdings+and+cash\u0026amp;title=Reconcile+a+portfolio%27s+holdings+to+an+absolute+desired+state\u0026amp;description=%22Make+Allocait+match+what+I+hold%22%3A+the+primary+agent+write+%28ADR-019%29.%0APass+the+FULL+desired+set+of+non-cash+holdings%3B+anything+currently%0Aheld+that+is+not+listed\u0026amp;theme=f4ffca4ae92cd96f3d29a15e\u0026amp;signature=dd1ab476d731e051c48226d5965d85a08b602f24d94aabeb3e12a2ecaa701a33\">\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=\"/mintlify-assets/_mintlify/favicons/allocait/8WDpZ9h0WNasoRjb/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Allocait\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#0b0b0b\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.allocait.app/api-reference/holdings-and-cash/reconcile-a-portfolios-holdings-to-an-absolute-desired-state\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/api-reference/holdings-and-cash/reconcile-a-portfolios-holdings-to-an-absolute-desired-state.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.allocait.app/#organization\",\"name\":\"Allocait\",\"url\":\"https://docs.allocait.app\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/allocait/N9X8GrpLUSiZAKIm/logo/light.svg?fit=max\u0026auto=format\u0026n=N9X8GrpLUSiZAKIm\u0026q=85\u0026s=37c68a7ac5dc3550d24f484060530ea6\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.allocait.app#website\",\"name\":\"Allocait\",\"url\":\"https://docs.allocait.app\",\"publisher\":{\"@id\":\"https://docs.allocait.app/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.allocait.app/api-reference/holdings-and-cash/reconcile-a-portfolios-holdings-to-an-absolute-desired-state#webpage\",\"url\":\"https://docs.allocait.app/api-reference/holdings-and-cash/reconcile-a-portfolios-holdings-to-an-absolute-desired-state\",\"name\":\"Reconcile a portfolio's holdings to an absolute desired state\",\"description\":\"\\\"Make Allocait match what I hold\\\": the primary agent write (ADR-019). Pass the FULL desired set of non-cash holdings; anything currently held that is not listed is closed. Every change routes through the same RPC the app UI uses (position_create_with_cash/ position_update_with_cash/position_close), so cash is always settled and affordability always enforced server-side. This records the change in Allocait only. It moves no money and places no order. Idempotent: calling this again with the same desired state changes nothing (symbols already matching are reported under unchanged, with no write performed). Atomic: the whole batch is one transaction; if any single change would fail (e.g. insufficient cash), the entire call fails and nothing from it is applied. A row whose share count is unchanged needs no price, and a price on it is never used for anything. Shorts. A negative shares is a short. It is recorded only on a portfolio with allow_shorts on and never for a holding in the Options class (including a row that moves a short into Options); otherwise the call is INVALID_INPUT, carries symbols, and writes nothing. A short sale credits cash, and closing or reducing a short debits it at the row's current_price, so a cover that cash cannot pay is INSUFFICIENT_CASH. A close of a held short (a shares: 0 row, or a held symbol you drop in replace mode) is that cover, and needs its price like any close. Short proceeds are credited to cash within the call, so one call can fund a buy with a short sale. A repeat of the same desired state, shorts included, changes nothing. Closing a symbol you drop from the desired state needs a price it does not otherwise carry. price_overrides must carry one for every held symbol absent from positions: its proceeds are booked at that price. A held symbol about to close with no price in the map fails with a 400 whose body carries missing_symbols. Allocait never books a close at its own stored quote (ADR-036). An option you leave out needs its price too, even with a mark: the mark may be months old, and a close books cash at the price named. One case needs none: an option past its expiry closes at 0 with no cash and records one action (ADR-147). Options and marks. A row may carry mark (an option's per-share premium; see DesiredPosition). Left out or null, the stored mark stays. A row whose only change is mark is reported under updated, needs no price, moves no cash and records no action. A mark on a holding that ends outside the Options class is INVALID_INPUT, carries symbols and writes nothing (ADR-147). An expired option is closed with {symbol, shares: 0, price: 0}, after the person says what happened; the row needs price: 0 because the share count changes. Moves. A row may carry moves (1, 2, 3, -1, -2 or -3; see DesiredPosition). Left out or null, the stored value stays, so the state you read from GET /positions can go back unchanged. A row whose only change is moves is reported under updated, needs no price and records no action. Moves other than 1 on cash or on an Options-class holding (including a row that moves a fund into Options) is INVALID_INPUT and nothing is written (ADR-142). Symbols. Every symbol is trimmed and uppercased before it is matched, so alfa updates the held ALFA instead of opening a second holding, and the close-price check uses the same rule. After that each must be a ticker of 1 to 21 characters of A-Z, 0-9, . or - starting with a letter or digit (ALFA, ALFA.B, or an option symbol such as ECHO281215P00050000), or a slash pair (ALFA/BRAV). Anything else is a 400 with \\\"Each position needs a valid symbol: a ticker (ALFA, ALFA.B) or a pair (ALFA/BRAV)\\\", and the database refuses it with error_code: INVALID_INPUT if it gets that far. Nothing is written. Requires the API key to have the write permission.\",\"dateModified\":\"2026-11-09T05:23:21.647Z\",\"isPartOf\":{\"@id\":\"https://docs.allocait.app#website\"},\"breadcrumb\":{\"@id\":\"https://docs.allocait.app/api-reference/holdings-and-cash/reconcile-a-portfolios-holdings-to-an-absolute-desired-state#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.allocait.app/api-reference/holdings-and-cash/reconcile-a-portfolios-holdings-to-an-absolute-desired-state#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Endpoints\",\"item\":\"https://docs.allocait.app/api-reference/checks/check-compliance-with-your-prices\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Holdings and cash\",\"item\":\"https://docs.allocait.app/api-reference/holdings-and-cash/list-positions\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Reconcile a portfolio's holdings to an absolute desired state\",\"item\":\"https://docs.allocait.app/api-reference/holdings-and-cash/reconcile-a-portfolios-holdings-to-an-absolute-desired-state\"}]},{\"@type\":[\"TechArticle\",\"APIReference\"],\"@id\":\"https://docs.allocait.app/api-reference/holdings-and-cash/reconcile-a-portfolios-holdings-to-an-absolute-desired-state#apireference\",\"headline\":\"Reconcile a portfolio's holdings to an absolute desired state\",\"name\":\"Reconcile a portfolio's holdings to an absolute desired state\",\"description\":\"\\\"Make Allocait match what I hold\\\": the primary agent write (ADR-019). Pass the FULL desired set of non-cash holdings; anything currently held that is not listed is closed. Every change routes through the same RPC the app UI uses (position_create_with_cash/ position_update_with_cash/position_close), so cash is always settled and affordability always enforced server-side. This records the change in Allocait only. It moves no money and places no order. Idempotent: calling this again with the same desired state changes nothing (symbols already matching are reported under unchanged, with no write performed). Atomic: the whole batch is one transaction; if any single change would fail (e.g. insufficient cash), the entire call fails and nothing from it is applied. A row whose share count is unchanged needs no price, and a price on it is never used for anything. Shorts. A negative shares is a short. It is recorded only on a portfolio with allow_shorts on and never for a holding in the Options class (including a row that moves a short into Options); otherwise the call is INVALID_INPUT, carries symbols, and writes nothing. A short sale credits cash, and closing or reducing a short debits it at the row's current_price, so a cover that cash cannot pay is INSUFFICIENT_CASH. A close of a held short (a shares: 0 row, or a held symbol you drop in replace mode) is that cover, and needs its price like any close. Short proceeds are credited to cash within the call, so one call can fund a buy with a short sale. A repeat of the same desired state, shorts included, changes nothing. Closing a symbol you drop from the desired state needs a price it does not otherwise carry. price_overrides must carry one for every held symbol absent from positions: its proceeds are booked at that price. A held symbol about to close with no price in the map fails with a 400 whose body carries missing_symbols. Allocait never books a close at its own stored quote (ADR-036). An option you leave out needs its price too, even with a mark: the mark may be months old, and a close books cash at the price named. One case needs none: an option past its expiry closes at 0 with no cash and records one action (ADR-147). Options and marks. A row may carry mark (an option's per-share premium; see DesiredPosition). Left out or null, the stored mark stays. A row whose only change is mark is reported under updated, needs no price, moves no cash and records no action. A mark on a holding that ends outside the Options class is INVALID_INPUT, carries symbols and writes nothing (ADR-147). An expired option is closed with {symbol, shares: 0, price: 0}, after the person says what happened; the row needs price: 0 because the share count changes. Moves. A row may carry moves (1, 2, 3, -1, -2 or -3; see DesiredPosition). Left out or null, the stored value stays, so the state you read from GET /positions can go back unchanged. A row whose only change is moves is reported under updated, needs no price and records no action. Moves other than 1 on cash or on an Options-class holding (including a row that moves a fund into Options) is INVALID_INPUT and nothing is written (ADR-142). Symbols. Every symbol is trimmed and uppercased before it is matched, so alfa updates the held ALFA instead of opening a second holding, and the close-price check uses the same rule. After that each must be a ticker of 1 to 21 characters of A-Z, 0-9, . or - starting with a letter or digit (ALFA, ALFA.B, or an option symbol such as ECHO281215P00050000), or a slash pair (ALFA/BRAV). Anything else is a 400 with \\\"Each position needs a valid symbol: a ticker (ALFA, ALFA.B) or a pair (ALFA/BRAV)\\\", and the database refuses it with error_code: INVALID_INPUT if it gets that far. Nothing is written. Requires the API key to have the write permission.\",\"url\":\"https://docs.allocait.app/api-reference/holdings-and-cash/reconcile-a-portfolios-holdings-to-an-absolute-desired-state\",\"mainEntityOfPage\":{\"@id\":\"https://docs.allocait.app/api-reference/holdings-and-cash/reconcile-a-portfolios-holdings-to-an-absolute-desired-state#webpage\"},\"dateModified\":\"2026-11-09T05:23:21.647Z\",\"publisher\":{\"@id\":\"https://docs.allocait.app/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.allocait.app#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Allocait\",\"publisher\":{\"@type\":\"Organization\",\"name\":\"Allocait\"},\"provider\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","agentPointerHref":"/api-reference/holdings-and-cash/reconcile-a-portfolios-holdings-to-an-absolute-desired-state.md","siteName":"Allocait","nav":{"global":null,"tabs":[{"tab":"Get started","pages":[{"title":"Get started","href":""}]},{"tab":"Concepts","pages":[{"title":"Concepts","href":"/concepts/allocation-bands"}]},{"tab":"Claude \u0026 MCP","pages":[{"title":"Claude \u0026 MCP","href":"/claude/overview"}]},{"tab":"ChatGPT \u0026 Codex","pages":[{"title":"ChatGPT \u0026 Codex","href":"/chatgpt/connect"}]},{"tab":"Guides","pages":[{"title":"Guides","href":"/guides/write-your-rules"}]},{"tab":"API reference","groups":[{"group":"Start","pages":[{"title":"API overview","sidebarTitle":"Overview","href":"/api/introduction"},{"title":"Endpoints by task","sidebarTitle":"By task","href":"/api/endpoints-by-task"},{"title":"API quickstart","sidebarTitle":"Quickstart","href":"/api/quickstart"},{"title":"Authentication","sidebarTitle":"Authentication","href":"/api/authentication"}]},{"group":"Conventions","pages":[{"title":"Conventions","sidebarTitle":"Conventions","href":"/api/conventions"},{"title":"Rate limits","sidebarTitle":"Rate limits","href":"/api/rate-limits"},{"title":"Idempotency","sidebarTitle":"Idempotency","href":"/api/idempotency"}]},{"group":"Errors","pages":[{"title":"API errors","sidebarTitle":"Errors","href":"/api/errors"},{"title":"Error codes","sidebarTitle":"Error codes","href":"/api/error-codes"}]},{"group":"Webhooks","pages":[{"title":"Webhook events","sidebarTitle":"Webhook events","href":"/api/webhook-events"},{"title":"Verify deliveries","sidebarTitle":"Verify deliveries","href":"/api/webhook-verification"}]},{"group":"Endpoints","pages":[{"group":"Checks","pages":[{"title":"Check compliance with your prices","deprecated":null,"version":null,"openapi":"/openapi.yaml post /compliance/check","href":"/api-reference/checks/check-compliance-with-your-prices"},{"title":"Pre-flight check of a proposed allocation plan","deprecated":null,"version":null,"openapi":"/openapi.yaml post /validate","href":"/api-reference/checks/pre-flight-check-of-a-proposed-allocation-plan"},{"title":"Check a proposed change against the rules","deprecated":null,"version":null,"openapi":"/openapi.yaml post /trades/check","href":"/api-reference/checks/check-a-proposed-change-against-the-rules"},{"title":"Preview rebalance amounts with your prices","deprecated":null,"version":null,"openapi":"/openapi.yaml post /rebalance/preview","href":"/api-reference/checks/preview-rebalance-amounts-with-your-prices"}]},{"group":"Portfolios","pages":[{"title":"Get portfolio summary","deprecated":null,"version":null,"openapi":"/openapi.yaml get /portfolio/summary","href":"/api-reference/portfolios/get-portfolio-summary"},{"title":"List portfolios","deprecated":null,"version":null,"openapi":"/openapi.yaml get /portfolios","href":"/api-reference/portfolios/list-portfolios"},{"title":"Create a portfolio with an initial cash position","deprecated":null,"version":null,"openapi":"/openapi.yaml post /portfolios","href":"/api-reference/portfolios/create-a-portfolio-with-an-initial-cash-position"},{"title":"Delete a portfolio","deprecated":null,"version":null,"openapi":"/openapi.yaml delete /portfolios/{id}","href":"/api-reference/portfolios/delete-a-portfolio"},{"title":"Change a portfolio's name, description, active scenario or Allow shorts","deprecated":null,"version":null,"openapi":"/openapi.yaml patch /portfolios/{id}","href":"/api-reference/portfolios/change-a-portfolios-name-description-active-scenario-or-allow-shorts"},{"title":"Get a portfolio snapshot (unpriced)","deprecated":null,"version":null,"openapi":"/openapi.yaml get /portfolio/snapshot","href":"/api-reference/portfolios/get-a-portfolio-snapshot-unpriced"},{"title":"Get a portfolio snapshot with your prices","deprecated":null,"version":null,"openapi":"/openapi.yaml post /portfolio/snapshot","href":"/api-reference/portfolios/get-a-portfolio-snapshot-with-your-prices"}]},{"group":"Holdings and cash","pages":[{"title":"List positions","deprecated":null,"version":null,"openapi":"/openapi.yaml get /positions","href":"/api-reference/holdings-and-cash/list-positions"},{"title":"Record one holding's new total shares","deprecated":null,"version":null,"openapi":"/openapi.yaml post /positions/{symbol}","href":"/api-reference/holdings-and-cash/record-one-holdings-new-total-shares"},{"title":"Calculate a target share count for a position","description":"Pure calculation, no portfolio ownership involved: pass in current\nholding, price, portfolio value, and a sizing mode and get back the\ntarget share count, its cash impact, and whether it is affordable.\nMINIMUM, BALANCED and MAXIMUM size one holding to the bottom,\nmidpoint or top of the position size range you pass as\n`size_min_pct` / `size_max_pct` (the rule's `size_*` fields from\n`GET /position-rules`), not to the asset class allocation band.\nNothing is written; call `/portfolios/:id/positions/apply` to act on\nthe result.\n\n`sizing_mode` is `MANUAL` unless you send another. `MANUAL` with a\n`manual_shares` sizes to that count. `MANUAL` without one starts\nfrom the size held now (`current_shares`), so `basis_points` moves\nthe holding by that many basis points of `portfolio_value`, up or\ndown. `basis_points` is also added to the target of `MINIMUM`,\n`BALANCED` and `MAXIMUM`. The target is held between 0 and 100\npercent of `portfolio_value`, not inside the size range: when it\nlands outside, `compliance_status` is `BELOW_MIN` or `ABOVE_MAX`\nand the share count is still the one asked for.\n\n`direction` is `long` (the default) or `short`. For a short, pass\nthe short size range of the asset class as `size_min_pct` /\n`size_max_pct` (the rule's `short_size_*` fields from `GET\n/position-rules`), because this calculation reads no rule. Pass a\nnegative `current_shares` for a short you hold and a positive\n`manual_shares`. The share count, value and percent come back\nnegative for a short, and `compliance_status` compares the size\nwithout its sign with the range.\n\n`min_position_pct` / `max_position_pct` are deprecated aliases for\n`size_min_pct` / `size_max_pct` and mean the same thing here. When\nboth spellings are sent, `size_*` wins. `price` and `cash` are the\nnames MCP's `calculate_position_size` uses; `current_price` and\n`current_cash` are older names for the same fields, read only when\nthe new name is absent.\n","deprecated":null,"version":null,"openapi":"/openapi.yaml post /positions/size","href":"/api-reference/holdings-and-cash/calculate-a-target-share-count-for-a-position"},{"title":"Reconcile a portfolio's holdings to an absolute desired state","deprecated":null,"version":null,"openapi":"/openapi.yaml post /portfolios/{id}/positions/apply","href":"/api-reference/holdings-and-cash/reconcile-a-portfolios-holdings-to-an-absolute-desired-state"},{"title":"Deposit or withdraw cash from a portfolio","description":"Recorded as an action with `symbol: \"CASH\"`; `GET /actions` lists it.\nThe response names the action and the new cash balance only.\nA withdrawal larger than the cash balance is refused with `409` and\n`error_code: INSUFFICIENT_CASH`. The check runs on the server under a\nlock. This records the change in Allocait only. It moves no money and\nplaces no order.\n\nPass `idempotency_key` to make a retry safe: calling again with the\nsame key and the same `direction`/`amount`/`comment` returns the\noriginal result (`idempotent_replay: true`) instead of applying the\nmovement twice. The same key with a different body is refused with\n`409` and `error_code: IDEMPOTENCY_KEY_CONFLICT`. Omit it for a call\nyou know will only ever be made once.\n\n**Requires the API key to have the `write` permission.**\n","deprecated":null,"version":null,"openapi":"/openapi.yaml post /portfolios/{id}/cash-movements","href":"/api-reference/holdings-and-cash/deposit-or-withdraw-cash-from-a-portfolio"}]},{"group":"Watchlists","pages":[{"title":"List watchlists","deprecated":null,"version":null,"openapi":"/openapi.yaml get /watchlists","href":"/api-reference/watchlists/list-watchlists"},{"title":"Create a watchlist","deprecated":null,"version":null,"openapi":"/openapi.yaml post /watchlists","href":"/api-reference/watchlists/create-a-watchlist"},{"title":"Change a watchlist's name and symbols in one call","deprecated":null,"version":null,"openapi":"/openapi.yaml put /watchlists/{id}","href":"/api-reference/watchlists/change-a-watchlists-name-and-symbols-in-one-call"},{"title":"Delete a watchlist","deprecated":null,"version":null,"openapi":"/openapi.yaml delete /watchlists/{id}","href":"/api-reference/watchlists/delete-a-watchlist"},{"title":"Rename a watchlist","deprecated":null,"version":null,"openapi":"/openapi.yaml patch /watchlists/{id}","href":"/api-reference/watchlists/rename-a-watchlist"},{"title":"List items in a watchlist","deprecated":null,"version":null,"openapi":"/openapi.yaml get /watchlists/{id}/items","href":"/api-reference/watchlists/list-items-in-a-watchlist"},{"title":"Add a symbol to a watchlist","deprecated":null,"version":null,"openapi":"/openapi.yaml post /watchlists/{id}/items","href":"/api-reference/watchlists/add-a-symbol-to-a-watchlist"},{"title":"Reorder a watchlist's items","deprecated":null,"version":null,"openapi":"/openapi.yaml patch /watchlists/{id}/items","href":"/api-reference/watchlists/reorder-a-watchlists-items"},{"title":"Remove a symbol from a watchlist","deprecated":null,"version":null,"openapi":"/openapi.yaml delete /watchlists/{id}/items/{item_id}","href":"/api-reference/watchlists/remove-a-symbol-from-a-watchlist"}]},{"group":"Inputs","pages":[{"title":"List input (tracked-value) definitions","deprecated":null,"version":null,"openapi":"/openapi.yaml get /inputs","href":"/api-reference/inputs/list-input-tracked-value-definitions"},{"title":"Create an Input (tracked-value) definition","deprecated":null,"version":null,"openapi":"/openapi.yaml post /inputs","href":"/api-reference/inputs/create-an-input-tracked-value-definition"},{"title":"Change an Input definition and its options in one call","deprecated":null,"version":null,"openapi":"/openapi.yaml put /inputs/{id}","href":"/api-reference/inputs/change-an-input-definition-and-its-options-in-one-call"},{"title":"Delete an Input definition","deprecated":null,"version":null,"openapi":"/openapi.yaml delete /inputs/{id}","href":"/api-reference/inputs/delete-an-input-definition"},{"title":"Update an Input definition","deprecated":null,"version":null,"openapi":"/openapi.yaml patch /inputs/{id}","href":"/api-reference/inputs/update-an-input-definition"},{"title":"List recorded values for one input","deprecated":null,"version":null,"openapi":"/openapi.yaml get /inputs/{id}/entries","href":"/api-reference/inputs/list-recorded-values-for-one-input"},{"title":"List dropdown options for one CHOICE input","deprecated":null,"version":null,"openapi":"/openapi.yaml get /inputs/{id}/options","href":"/api-reference/inputs/list-dropdown-options-for-one-choice-input"},{"title":"Add a dropdown option","deprecated":null,"version":null,"openapi":"/openapi.yaml post /inputs/{id}/options","href":"/api-reference/inputs/add-a-dropdown-option"},{"title":"Remove a dropdown option","deprecated":null,"version":null,"openapi":"/openapi.yaml delete /inputs/{id}/options/{option_id}","href":"/api-reference/inputs/remove-a-dropdown-option"},{"title":"Rename or reorder a dropdown option","deprecated":null,"version":null,"openapi":"/openapi.yaml patch /inputs/{id}/options/{option_id}","href":"/api-reference/inputs/rename-or-reorder-a-dropdown-option"},{"title":"Record (upsert) one or more custom input values","deprecated":null,"version":null,"openapi":"/openapi.yaml post /inputs/entries","href":"/api-reference/inputs/record-upsert-one-or-more-custom-input-values"},{"title":"Delete one or more recorded input values","deprecated":null,"version":null,"openapi":"/openapi.yaml delete /inputs/entries","href":"/api-reference/inputs/delete-one-or-more-recorded-input-values"}]},{"group":"Playbooks","pages":[{"title":"List playbooks","deprecated":null,"version":null,"openapi":"/openapi.yaml get /playbooks","href":"/api-reference/playbooks/list-playbooks"},{"title":"Create a playbook","deprecated":null,"version":null,"openapi":"/openapi.yaml post /playbooks","href":"/api-reference/playbooks/create-a-playbook"},{"title":"Resolve a playbook's symbols and its input values for a date","deprecated":null,"version":null,"openapi":"/openapi.yaml get /playbooks/{id}","href":"/api-reference/playbooks/resolve-a-playbooks-symbols-and-its-input-values-for-a-date"},{"title":"Delete a playbook","deprecated":null,"version":null,"openapi":"/openapi.yaml delete /playbooks/{id}","href":"/api-reference/playbooks/delete-a-playbook"},{"title":"Update a playbook","deprecated":null,"version":null,"openapi":"/openapi.yaml patch /playbooks/{id}","href":"/api-reference/playbooks/update-a-playbook"}]},{"group":"Monitors","pages":[{"title":"List condition monitors and their recorded levels","deprecated":null,"version":null,"openapi":"/openapi.yaml get /monitors","href":"/api-reference/monitors/list-condition-monitors-and-their-recorded-levels"},{"title":"Create a condition monitor","deprecated":null,"version":null,"openapi":"/openapi.yaml post /monitors","href":"/api-reference/monitors/create-a-condition-monitor"},{"title":"Delete a condition monitor","deprecated":null,"version":null,"openapi":"/openapi.yaml delete /monitors/{id}","href":"/api-reference/monitors/delete-a-condition-monitor"},{"title":"Update a condition monitor","deprecated":null,"version":null,"openapi":"/openapi.yaml patch /monitors/{id}","href":"/api-reference/monitors/update-a-condition-monitor"}]},{"group":"Rules","pages":[{"title":"List scenarios","deprecated":null,"version":null,"openapi":"/openapi.yaml get /scenarios","href":"/api-reference/rules/list-scenarios"},{"title":"Create a scenario","deprecated":null,"version":null,"openapi":"/openapi.yaml post /scenarios","href":"/api-reference/rules/create-a-scenario"},{"title":"Delete a scenario","deprecated":null,"version":null,"openapi":"/openapi.yaml delete /scenarios/{id}","href":"/api-reference/rules/delete-a-scenario"},{"title":"Rename a scenario and/or replace its description","deprecated":null,"version":null,"openapi":"/openapi.yaml patch /scenarios/{id}","href":"/api-reference/rules/rename-a-scenario-andor-replace-its-description"},{"title":"List asset classes","deprecated":null,"version":null,"openapi":"/openapi.yaml get /asset-classes","href":"/api-reference/rules/list-asset-classes"},{"title":"Create a custom asset class","deprecated":null,"version":null,"openapi":"/openapi.yaml post /asset-classes","href":"/api-reference/rules/create-a-custom-asset-class"},{"title":"Delete a custom asset class","deprecated":null,"version":null,"openapi":"/openapi.yaml delete /asset-classes/{id}","href":"/api-reference/rules/delete-a-custom-asset-class"},{"title":"Rename an asset class and/or replace its description","deprecated":null,"version":null,"openapi":"/openapi.yaml patch /asset-classes/{id}","href":"/api-reference/rules/rename-an-asset-class-andor-replace-its-description"},{"title":"List position rules (allocation band and position size per asset class)","deprecated":null,"version":null,"openapi":"/openapi.yaml get /position-rules","href":"/api-reference/rules/list-position-rules-allocation-band-and-position-size-per-asset-class"},{"title":"Set one asset class's rule in a scenario","deprecated":null,"version":null,"openapi":"/openapi.yaml post /scenarios/{id}/position-rules","href":"/api-reference/rules/set-one-asset-classs-rule-in-a-scenario"},{"title":"Reconcile a scenario's position rules to an absolute desired state","deprecated":null,"version":null,"openapi":"/openapi.yaml post /scenarios/{id}/position-rules/apply","href":"/api-reference/rules/reconcile-a-scenarios-position-rules-to-an-absolute-desired-state"},{"title":"Get a scenario's rules as rules text","deprecated":null,"version":null,"openapi":"/openapi.yaml get /scenarios/{id}/rules","href":"/api-reference/rules/get-a-scenarios-rules-as-rules-text"},{"title":"Import rules text into a scenario's rules, or preview it with dry_run","deprecated":null,"version":null,"openapi":"/openapi.yaml post /scenarios/{id}/rules","href":"/api-reference/rules/import-rules-text-into-a-scenarios-rules-or-preview-it-with-dry_run"},{"title":"List rules restore points, newest first","deprecated":null,"version":null,"openapi":"/openapi.yaml get /rules/restore-points","href":"/api-reference/rules/list-rules-restore-points-newest-first"},{"title":"Undo an import or an earlier restore","deprecated":null,"version":null,"openapi":"/openapi.yaml post /rules/restore-points/{id}/restore","href":"/api-reference/rules/undo-an-import-or-an-earlier-restore"},{"title":"Get the rules text format spec, ready-to-import examples, and copy/paste AI prompts","deprecated":null,"version":null,"openapi":"/openapi.yaml get /rules/spec","href":"/api-reference/rules/get-the-rules-text-format-spec-ready-to-import-examples-and-copypaste-ai-prompts"}]},{"group":"Actions","pages":[{"title":"List a portfolio's actions","deprecated":null,"version":null,"openapi":"/openapi.yaml get /actions","href":"/api-reference/actions/list-a-portfolios-actions"},{"title":"Delete one or more actions","deprecated":null,"version":null,"openapi":"/openapi.yaml delete /actions","href":"/api-reference/actions/delete-one-or-more-actions"},{"title":"Edit an action's comment","deprecated":null,"version":null,"openapi":"/openapi.yaml patch /actions/{id}","href":"/api-reference/actions/edit-an-actions-comment"}]},{"group":"Support and feedback","pages":[{"title":"List your support requests, newest first","deprecated":null,"version":null,"openapi":"/openapi.yaml get /support-requests","href":"/api-reference/support-and-feedback/list-your-support-requests-newest-first"},{"title":"File a support request","deprecated":null,"version":null,"openapi":"/openapi.yaml post /support-requests","href":"/api-reference/support-and-feedback/file-a-support-request"},{"title":"List your agent feedback, newest first","deprecated":null,"version":null,"openapi":"/openapi.yaml get /agent-feedback","href":"/api-reference/support-and-feedback/list-your-agent-feedback-newest-first"},{"title":"Report feedback about the agent surface itself","deprecated":null,"version":null,"openapi":"/openapi.yaml post /agent-feedback","href":"/api-reference/support-and-feedback/report-feedback-about-the-agent-surface-itself"}]},{"group":"Charts","pages":[{"title":"List saved charts for a portfolio","deprecated":null,"version":null,"openapi":"/openapi.yaml get /charts","href":"/api-reference/charts/list-saved-charts-for-a-portfolio"},{"title":"Create a saved chart","deprecated":null,"version":null,"openapi":"/openapi.yaml post /charts","href":"/api-reference/charts/create-a-saved-chart"},{"title":"Delete a saved chart","deprecated":null,"version":null,"openapi":"/openapi.yaml delete /charts/{id}","href":"/api-reference/charts/delete-a-saved-chart"},{"title":"Update a saved chart","deprecated":null,"version":null,"openapi":"/openapi.yaml patch /charts/{id}","href":"/api-reference/charts/update-a-saved-chart"}]},{"group":"Preferences","pages":[{"title":"Get what the app opens to, and the theme","deprecated":null,"version":null,"openapi":"/openapi.yaml get /preferences","href":"/api-reference/preferences/get-what-the-app-opens-to-and-the-theme"},{"title":"Set which portfolio/watchlist/chart/playbook/monitor the app shows, or the theme","deprecated":null,"version":null,"openapi":"/openapi.yaml post /preferences","href":"/api-reference/preferences/set-which-portfoliowatchlistchartplaybookmonitor-the-app-shows-or-the-theme"}]},{"group":"Webhooks","pages":[{"title":"List webhook endpoints","deprecated":null,"version":null,"openapi":"/openapi.yaml get /webhooks","href":"/api-reference/webhooks/list-webhook-endpoints"},{"title":"Register a webhook endpoint","deprecated":null,"version":null,"openapi":"/openapi.yaml post /webhooks","href":"/api-reference/webhooks/register-a-webhook-endpoint"},{"title":"Delete a webhook endpoint","deprecated":null,"version":null,"openapi":"/openapi.yaml delete /webhooks/{id}","href":"/api-reference/webhooks/delete-a-webhook-endpoint"},{"title":"Update a webhook endpoint","deprecated":null,"version":null,"openapi":"/openapi.yaml patch /webhooks/{id}","href":"/api-reference/webhooks/update-a-webhook-endpoint"}]},{"group":"Setup file","pages":[{"title":"Export your watchlists, Inputs, playbooks, monitors and saved charts as one JSON document","deprecated":null,"version":null,"openapi":"/openapi.yaml get /setup/export","href":"/api-reference/setup-file/export-your-watchlists-inputs-playbooks-monitors-and-saved-charts-as-one-json-document"},{"title":"Preview an ALLOCAIT.md setup file","deprecated":null,"version":null,"openapi":"/openapi.yaml post /setup/preview","href":"/api-reference/setup-file/preview-an-allocaitmd-setup-file"},{"title":"Apply a previewed ALLOCAIT.md setup file","deprecated":null,"version":null,"openapi":"/openapi.yaml post /setup/apply","href":"/api-reference/setup-file/apply-a-previewed-allocaitmd-setup-file"},{"title":"List the account's setup file receipts","deprecated":null,"version":null,"openapi":"/openapi.yaml get /setup/receipts","href":"/api-reference/setup-file/list-the-accounts-setup-file-receipts"},{"title":"Undo an applied setup file, or preview the undo","deprecated":null,"version":null,"openapi":"/openapi.yaml post /setup/receipts/{id}/undo","href":"/api-reference/setup-file/undo-an-applied-setup-file-or-preview-the-undo"}]}]}]},{"tab":"Support \u0026 trust","pages":[{"title":"Support \u0026 trust","href":"/support"}]}]},"basePath":"","title":"Reconcile a portfolio's holdings to an absolute desired state - Allocait","buildId":"dpl_596ePT575z4qnAFksq1biRW97aLY","deploymentId":"dpl_596ePT575z4qnAFksq1biRW97aLY","dynamics":["c2aa737661","2b790eda92","b0f84f53ff","a9bb46feb3"],"chunks":["OpenApiReferenceProvider-C9Wu0MeN.js","utils-jWgu-6Qf.js","cloneDeep-izs3Hzxb.js","_Uint8Array-BVIqJ90l.js","isObjectLike-C8vmzPEN.js","isArray-Dcu1dQ5t.js","utils-DBD8f9qn.js","toString-BX-iyh8G.js","isSymbol-CSH9U2Gm.js","addUniqueKeysToSchema-BGuX1ui5.js","snippetPresets-Evy63nQB.js","style-to-object-BY3JEAdk.js","v4-DIgz8zy6.js","conditional-requirements-CFM0id0Q.js","ApiExamples-BQHO1lnV.js","isEqual-BfaiBbcl.js","usePlaygroundInputsStore-B6C8m-t_.js","shallow-BG4cT0fH.js","getApiPlaygroundDisplay-CgEPrE-D.js","CodeSnippet-CU8JyZ9N.js","CodeSnippets-BnxHU32T.js","Page-D6S0Yh6q.js","getPastedJson-CwiYOonh.js","icon-DYDpWmDf.js","MethodPill-C-J-l7_c.js","DuplicateIcon-B6ARwg9w.js","createIcon-Dsv4MtDq.js","DynamicMarkdown-CtasenyM.js","SectionHeading-YNcgYwjB.js","axios-vUo_bRtp.js","Markdown-CwLx6PAg.js","clsx-C3kuh_dz.js","lib-DuNMoZ05.js","lib-B8HbPgcC.js","html-void-elements-mCSPHezU.js","dist-CmKHfscc.js","katex-YMM-FMeD.js","marked.esm-CZWythHk.js","AssistantPageLink-U3mI6Ljh.js"]}