{"openapi":"3.1.0","info":{"title":"Trade Me AI Platform","description":"Anvil - Trade Me's AI platform: Specialist AI Assistant and generalist AI integrations\n\nOpenAPI spec available at `/openapi.json`","version":"0.1.0"},"paths":{"/health":{"get":{"summary":"Health","description":"Health check endpoint.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/assistant/trademe/conversations":{"get":{"tags":["v1"],"summary":"List Conversations","operationId":"list_conversations_api_v1_assistant_trademe_conversations_get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Per Page"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["v1"],"summary":"Create Conversation","operationId":"create_conversation_api_v1_assistant_trademe_conversations_post","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"is-in-staff-group","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Is-In-Staff-Group"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateConversationBody"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/assistant/trademe/conversations/{conversation_id}":{"get":{"tags":["v1"],"summary":"Get Conversation","operationId":"get_conversation_api_v1_assistant_trademe_conversations__conversation_id__get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["v1"],"summary":"Update Conversation","operationId":"update_conversation_api_v1_assistant_trademe_conversations__conversation_id__patch","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateConversationBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["v1"],"summary":"Delete Conversation","operationId":"delete_conversation_api_v1_assistant_trademe_conversations__conversation_id__delete","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/assistant/trademe/conversations/{conversation_id}/participants/{target_member_id}":{"patch":{"tags":["v1"],"summary":"Upsert Participant","description":"Invite a member or update their role.\n\nSelf-action (target == caller): only an invited member may change their own role,\nand only to 'member' (accepting the invitation).\nOther-action (target != caller): caller must be an owner. Any role is allowed.","operationId":"upsert_participant_api_v1_assistant_trademe_conversations__conversation_id__participants__target_member_id__patch","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"target_member_id","in":"path","required":true,"schema":{"type":"string","title":"Target Member Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertParticipantBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["v1"],"summary":"Remove Participant","description":"Remove a participant (leave, decline, or kick).\n\nSelf-action (target == caller): any participant may leave/decline. Owners may not leave\nif they are the only owner.\nOther-action (target != caller): caller must be an owner.","operationId":"remove_participant_api_v1_assistant_trademe_conversations__conversation_id__participants__target_member_id__delete","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"target_member_id","in":"path","required":true,"schema":{"type":"string","title":"Target Member Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/assistant/trademe/conversations/{conversation_id}/messages":{"get":{"tags":["v1"],"summary":"List Messages","description":"List messages for a conversation with block downgrade for client capabilities.\n\nWhy Accept-Nodes is a required header (not optional, not a query param):\n- Required: forces clients to declare capabilities; no silent degraded experiences.\n- Header: content negotiation is a header concern (HTTP Accept-* convention); no\n  URL encoding issues; FastAPI parses comma-separated values natively.\n- Same semantics as POST /rpc initialize acceptNodes — different encoding per call site.\n\nBlocks are returned already hydrated — the streaming markdown parser populates\nall snapshot fields at parse time.  The REST endpoint applies only the block\ndowngrade step (no artifact fetching).","operationId":"list_messages_api_v1_assistant_trademe_conversations__conversation_id__messages_get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"after_seq","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"title":"After Seq"}},{"name":"before_seq","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"title":"Before Seq"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":100,"title":"Limit"}},{"name":"order","in":"query","required":false,"schema":{"enum":["asc","desc"],"type":"string","default":"desc","title":"Order"}},{"name":"accept-nodes","in":"header","required":true,"schema":{"type":"array","items":{"type":"string"},"description":"Required. Comma-separated list of supported node types and versions in type:version format. e.g. 'plain_text:1, map:1'. Mirrors the capabilities declaration sent on POST /rpc initialize. Server downgrades nodes to the highest mutually supported version; nodes with no fallback are omitted. Using a required header follows HTTP content negotiation conventions (Accept, Accept-Language) — this is what Accept-* headers exist for.","title":"Accept-Nodes"},"description":"Required. Comma-separated list of supported node types and versions in type:version format. e.g. 'plain_text:1, map:1'. Mirrors the capabilities declaration sent on POST /rpc initialize. Server downgrades nodes to the highest mutually supported version; nodes with no fallback are omitted. Using a required header follows HTTP content negotiation conventions (Accept, Accept-Language) — this is what Accept-* headers exist for."},{"name":"X-Anvil-Node-Capabilities","in":"header","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Optional. Protocol capabilities in type:version form — the same header the SSE subscribe request accepts. Today only 'provisional_blocks:2' changes this response: a client that declares it receives each message's blocks in stored order; every other client (including one declaring 'provisional_blocks:1') receives a message's provisional blocks first, the order it was shown them live. Omitted = provisional-first (and, for a message with no provisional block, exactly the stored order).","title":"X-Anvil-Node-Capabilities"},"description":"Optional. Protocol capabilities in type:version form — the same header the SSE subscribe request accepts. Today only 'provisional_blocks:2' changes this response: a client that declares it receives each message's blocks in stored order; every other client (including one declaring 'provisional_blocks:1') receives a message's provisional blocks first, the order it was shown them live. Omitted = provisional-first (and, for a message with no provisional block, exactly the stored order)."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/assistant/trademe/conversations/{conversation_id}/rpc":{"get":{"tags":["v1"],"summary":"Rpc Subscribe","description":"Subscribe to live events for a conversation via SSE with JSON-RPC notification framing.\n\nUnified endpoint (Streamable HTTP style): both GET (subscribe) and POST (send)\nshare the same URL, enabling session affinity through proxies like Fastly Fanout.\n\nRequires ``Anvil-Session-Id`` header obtained from ``POST /rpc initialize``.\n\nSSE frame format:\n    id:    per-stream unique ID (Tier 1 replay; non-digit format bypasses Tier 2)\n    event: message (fixed -- dispatch moves to JSON-RPC method in data body)\n    data:  {\"jsonrpc\":\"2.0\",\"method\":\"<slash/namespaced>\",\"params\":{...}}","operationId":"rpc_subscribe_api_v1_assistant_trademe_conversations__conversation_id__rpc_get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"pending","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"title":"Pending"}},{"name":"Anvil-Session-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Anvil-Session-Id"}},{"name":"Last-Event-ID","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last-Event-Id"}},{"name":"X-Anvil-Node-Capabilities","in":"header","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Optional. Same type:version vocabulary as the session's initialize accept_nodes, parsed by the same function — union'd with the session's declared capabilities for this connection. Lets a client opt into 'artifact_updates:1' on a reconnect without re-running initialize. Omitted = the session's initialize-time capabilities only (existing behaviour, unchanged).","title":"X-Anvil-Node-Capabilities"},"description":"Optional. Same type:version vocabulary as the session's initialize accept_nodes, parsed by the same function — union'd with the session's declared capabilities for this connection. Lets a client opt into 'artifact_updates:1' on a reconnect without re-running initialize. Omitted = the session's initialize-time capabilities only (existing behaviour, unchanged)."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Server-sent events stream","content":{"application/json":{"schema":{}},"text/event-stream":{"schema":{"oneOf":[{"$ref":"#/components/schemas/MessageCreatedNotification"},{"$ref":"#/components/schemas/MessageOpenedNotification"},{"$ref":"#/components/schemas/MessageBlockNotification"},{"$ref":"#/components/schemas/MessageProvisionalBlockNotification"},{"$ref":"#/components/schemas/MessageBlockAdoptedNotification"},{"$ref":"#/components/schemas/MessageCompleteNotification"},{"$ref":"#/components/schemas/MessageRemovedNotification"},{"$ref":"#/components/schemas/MessageRejectedNotification"},{"$ref":"#/components/schemas/AgentTypingNotification"},{"$ref":"#/components/schemas/AgentThinkingNotification"},{"$ref":"#/components/schemas/TaskSetNotification"},{"$ref":"#/components/schemas/TaskProgressNotification"},{"$ref":"#/components/schemas/TaskClearNotification"},{"$ref":"#/components/schemas/ArtifactUpdatedNotification"}],"discriminator":{"propertyName":"method","mapping":{"message/created":"#/components/schemas/MessageCreatedNotification","message/streamOpened":"#/components/schemas/MessageOpenedNotification","message/block":"#/components/schemas/MessageBlockNotification","message/provisionalBlock":"#/components/schemas/MessageProvisionalBlockNotification","message/blockAdopted":"#/components/schemas/MessageBlockAdoptedNotification","message/streamComplete":"#/components/schemas/MessageCompleteNotification","message/removed":"#/components/schemas/MessageRemovedNotification","message/rejected":"#/components/schemas/MessageRejectedNotification","agent/typing":"#/components/schemas/AgentTypingNotification","agent/thinking":"#/components/schemas/AgentThinkingNotification","task/set":"#/components/schemas/TaskSetNotification","task/progress":"#/components/schemas/TaskProgressNotification","task/clear":"#/components/schemas/TaskClearNotification","artifact/updated":"#/components/schemas/ArtifactUpdatedNotification"}}}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["v1"],"summary":"Rpc Delete Session","description":"Terminate a session explicitly (client-initiated).\n\nDeletes the session record and event buffer.\nAfter this call, GET /rpc with the same ``Anvil-Session-Id`` will return 404.","operationId":"rpc_delete_session_api_v1_assistant_trademe_conversations__conversation_id__rpc_delete","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"Anvil-Session-Id","in":"header","required":true,"schema":{"type":"string","title":"Anvil-Session-Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["v1"],"summary":"Rpc Dispatch","description":"Dispatch JSON-RPC 2.0 requests for the unified /rpc POST endpoint.\n\nSupported methods:\n    initialize              — MCP-aligned session initialisation (no session header needed)\n    notifications/initialized — client acknowledgement after initialize (requires session header)\n    message/send            — send a user message (session header optional for backward compat)\n\nError envelope (camelCase via AssistantGatewayMixin):\n\n    {\"jsonrpc\": \"2.0\", \"id\": <id>, \"error\": {\n        \"code\": <int>, \"message\": <str>,\n        \"data\": {\"category\", \"kind\", \"retryable\",\n                 \"resetAt\", \"retryAfter\", \"userMessage\"}}}\n\nSee ``docs/source/domains/assistant-gateway/concepts/error-contract.md`` for\nfull field semantics, per-category ``data.kind`` registry, and rendering\nguidance. The rationale lives in Decision 006\n(``docs/source/domains/assistant-gateway/decisions/decisions-006-error-taxonomy.md``).\n\nError codes — five canonical categories plus the JSON-RPC envelope error:\n\n    -32600  Invalid Request — a structurally valid JSON-RPC envelope that is\n            not a usable request (unknown method, request missing its id, or\n            params/blocks that fail the per-method model). Bare shape, no\n            ``data`` block; not one of the five canonical categories. An\n            unparseable body is rejected as HTTP 422 *before* this handler\n            (we do not emit -32700); unknown method is folded here rather\n            than -32601 (see Decision 006 §\"What carries a JSON-RPC envelope\").\n    -32602  validation   — input shape, length, file size, structural limit\n            breach; see Decision 003 and Decision 006.\n    -32001  quota        — token or turn quota exhausted; carries\n            ``data.resetAt``; see Decision 005 and Decision 006.\n    -32002  safety       — code-of-conduct or content-policy violation;\n            see Decision 006.\n    -32003  rate_limit   — application-level concurrency or per-turn caps\n            (``concurrent_sessions`` per-member session cap at initialize,\n            ``concurrent_conversations``, ``tool_calls_per_turn``,\n            ``task_agents_per_input``). Distinct from Cloud Armor\n            transport-level rate limits.\n    -32603  internal     — unanticipated server failure (incl. the\n            process/fleet-wide session backstop); assistant state\n            transactionally reverts so the next ``message/send`` is safe.\n\nClients should code defensively against all five canonical codes so that\na future enforcement-mode change requires no client-side update.","operationId":"rpc_dispatch_api_v1_assistant_trademe_conversations__conversation_id__rpc_post","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"Anvil-Session-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Anvil-Session-Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_AnyRpcRequest"}}}},"responses":{"200":{"description":"JSON-RPC 2.0 response. HTTP status is always 200 for any request that reaches the dispatcher; success vs error is signalled by the presence of ``result`` vs ``error`` in the body. On error, ``error.code`` is one of: -32600 Invalid Request, -32602 validation, -32001 quota, -32002 safety, -32003 rate_limit, -32603 internal. See the error contract reference for envelope shape and per-category ``data.kind`` registry. Non-200 responses (401/403 auth, 422 malformed body, 404 unknown route, 429 edge rate-limit/capacity) are resolved before the JSON-RPC envelope exists and are not part of this body contract.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/_RpcResponse"},"examples":{"validation_error":{"summary":"validation (-32602) — input doesn't fit the rules","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32602,"message":"Invalid params","data":{"category":"validation","kind":"plain_text_length","retryable":false,"userMessage":"Your message is too long. Try splitting it into smaller parts."}}}},"quota_error":{"summary":"quota (-32001) — token or turn allowance exhausted","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32001,"message":"Quota exceeded","data":{"category":"quota","kind":"tokens","retryable":true,"resetAt":"2026-05-26T05:00:00Z","userMessage":"You've reached your usage limit for this period. Please try again later."}}}},"safety_error":{"summary":"safety (-32002) — code-of-conduct or content policy violation","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32002,"message":"Safety","data":{"category":"safety","kind":"content_policy","retryable":false,"userMessage":"That request isn't something I can help with."}}}},"rate_limit_error":{"summary":"rate_limit (-32003) — concurrency or per-turn cap","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32003,"message":"Rate limit","data":{"category":"rate_limit","kind":"concurrent_conversations","retryable":true,"userMessage":"You have too many conversations open. Close one to start a new conversation."}}}},"rate_limit_concurrent_sessions":{"summary":"rate_limit (-32003) — per-member active-session cap (initialize)","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32003,"message":"Rate limit","data":{"category":"rate_limit","kind":"concurrent_sessions","retryable":true,"userMessage":"You have too many active sessions. Close one to start another."}}}},"internal_error":{"summary":"internal (-32603) — unanticipated server failure","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32603,"message":"Internal error","data":{"category":"internal","kind":"unknown","retryable":true,"userMessage":"Sorry, something went wrong."}}}},"invalid_request":{"summary":"Invalid Request (-32600) — valid envelope, unusable request (e.g. unknown method); no data block","value":{"jsonrpc":"2.0","error":{"code":-32600,"message":"Invalid Request"}}}}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/assistant/trademe/config":{"get":{"tags":["v1"],"summary":"Get Config","description":"Return client-side configuration for the assistant frontend.\n\nReturns Google Maps API credentials so the Angular app can load the\nMaps JS API at runtime without build-time injection.  Both values are\nempty strings when not configured (Leaflet fallback activated by client).","operationId":"get_config_api_v1_assistant_trademe_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Get Config Api V1 Assistant Trademe Config Get"}}}}}}},"/api/v1/assistant/trademe/prompt-starters":{"post":{"tags":["v1"],"summary":"Get Prompt Starters","description":"Return conversation starter prompts tailored to the referrer context.\n\nThe referrer determines which domain's starters are returned (V1: property only).\nAuth context is accepted for future personalisation; V1 ignores it.","operationId":"get_prompt_starters_api_v1_assistant_trademe_prompt_starters_post","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"max_prompts","in":"query","required":false,"schema":{"type":"integer","maximum":20,"minimum":3,"default":3,"title":"Max Prompts"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptStartersBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptStartersResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1beta1/assistant/trademe/conversations":{"get":{"tags":["v1beta1"],"summary":"List Conversations","operationId":"list_conversations_api_v1beta1_assistant_trademe_conversations_get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Per Page"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["v1beta1"],"summary":"Create Conversation","operationId":"create_conversation_api_v1beta1_assistant_trademe_conversations_post","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"is-in-staff-group","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Is-In-Staff-Group"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateConversationBody"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1beta1/assistant/trademe/conversations/{conversation_id}":{"get":{"tags":["v1beta1"],"summary":"Get Conversation","operationId":"get_conversation_api_v1beta1_assistant_trademe_conversations__conversation_id__get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["v1beta1"],"summary":"Update Conversation","operationId":"update_conversation_api_v1beta1_assistant_trademe_conversations__conversation_id__patch","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateConversationBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["v1beta1"],"summary":"Delete Conversation","operationId":"delete_conversation_api_v1beta1_assistant_trademe_conversations__conversation_id__delete","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1beta1/assistant/trademe/conversations/{conversation_id}/participants/{target_member_id}":{"patch":{"tags":["v1beta1"],"summary":"Upsert Participant","description":"Invite a member or update their role.\n\nSelf-action (target == caller): only an invited member may change their own role,\nand only to 'member' (accepting the invitation).\nOther-action (target != caller): caller must be an owner. Any role is allowed.","operationId":"upsert_participant_api_v1beta1_assistant_trademe_conversations__conversation_id__participants__target_member_id__patch","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"target_member_id","in":"path","required":true,"schema":{"type":"string","title":"Target Member Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertParticipantBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["v1beta1"],"summary":"Remove Participant","description":"Remove a participant (leave, decline, or kick).\n\nSelf-action (target == caller): any participant may leave/decline. Owners may not leave\nif they are the only owner.\nOther-action (target != caller): caller must be an owner.","operationId":"remove_participant_api_v1beta1_assistant_trademe_conversations__conversation_id__participants__target_member_id__delete","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"target_member_id","in":"path","required":true,"schema":{"type":"string","title":"Target Member Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1beta1/assistant/trademe/conversations/{conversation_id}/messages":{"get":{"tags":["v1beta1"],"summary":"List Messages","description":"List messages for a conversation with block downgrade for client capabilities.\n\nWhy Accept-Nodes is a required header (not optional, not a query param):\n- Required: forces clients to declare capabilities; no silent degraded experiences.\n- Header: content negotiation is a header concern (HTTP Accept-* convention); no\n  URL encoding issues; FastAPI parses comma-separated values natively.\n- Same semantics as POST /rpc initialize acceptNodes — different encoding per call site.\n\nBlocks are returned already hydrated — the streaming markdown parser populates\nall snapshot fields at parse time.  The REST endpoint applies only the block\ndowngrade step (no artifact fetching).","operationId":"list_messages_api_v1beta1_assistant_trademe_conversations__conversation_id__messages_get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"after_seq","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"title":"After Seq"}},{"name":"before_seq","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"title":"Before Seq"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":100,"title":"Limit"}},{"name":"order","in":"query","required":false,"schema":{"enum":["asc","desc"],"type":"string","default":"desc","title":"Order"}},{"name":"accept-nodes","in":"header","required":true,"schema":{"type":"array","items":{"type":"string"},"description":"Required. Comma-separated list of supported node types and versions in type:version format. e.g. 'plain_text:1, map:1'. Mirrors the capabilities declaration sent on POST /rpc initialize. Server downgrades nodes to the highest mutually supported version; nodes with no fallback are omitted. Using a required header follows HTTP content negotiation conventions (Accept, Accept-Language) — this is what Accept-* headers exist for.","title":"Accept-Nodes"},"description":"Required. Comma-separated list of supported node types and versions in type:version format. e.g. 'plain_text:1, map:1'. Mirrors the capabilities declaration sent on POST /rpc initialize. Server downgrades nodes to the highest mutually supported version; nodes with no fallback are omitted. Using a required header follows HTTP content negotiation conventions (Accept, Accept-Language) — this is what Accept-* headers exist for."},{"name":"X-Anvil-Node-Capabilities","in":"header","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Optional. Protocol capabilities in type:version form — the same header the SSE subscribe request accepts. Today only 'provisional_blocks:2' changes this response: a client that declares it receives each message's blocks in stored order; every other client (including one declaring 'provisional_blocks:1') receives a message's provisional blocks first, the order it was shown them live. Omitted = provisional-first (and, for a message with no provisional block, exactly the stored order).","title":"X-Anvil-Node-Capabilities"},"description":"Optional. Protocol capabilities in type:version form — the same header the SSE subscribe request accepts. Today only 'provisional_blocks:2' changes this response: a client that declares it receives each message's blocks in stored order; every other client (including one declaring 'provisional_blocks:1') receives a message's provisional blocks first, the order it was shown them live. Omitted = provisional-first (and, for a message with no provisional block, exactly the stored order)."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1beta1/assistant/trademe/conversations/{conversation_id}/rpc":{"get":{"tags":["v1beta1"],"summary":"Rpc Subscribe","description":"Subscribe to live events for a conversation via SSE with JSON-RPC notification framing.\n\nUnified endpoint (Streamable HTTP style): both GET (subscribe) and POST (send)\nshare the same URL, enabling session affinity through proxies like Fastly Fanout.\n\nRequires ``Anvil-Session-Id`` header obtained from ``POST /rpc initialize``.\n\nSSE frame format:\n    id:    per-stream unique ID (Tier 1 replay; non-digit format bypasses Tier 2)\n    event: message (fixed -- dispatch moves to JSON-RPC method in data body)\n    data:  {\"jsonrpc\":\"2.0\",\"method\":\"<slash/namespaced>\",\"params\":{...}}","operationId":"rpc_subscribe_api_v1beta1_assistant_trademe_conversations__conversation_id__rpc_get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"pending","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"title":"Pending"}},{"name":"Anvil-Session-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Anvil-Session-Id"}},{"name":"Last-Event-ID","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last-Event-Id"}},{"name":"X-Anvil-Node-Capabilities","in":"header","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Optional. Same type:version vocabulary as the session's initialize accept_nodes, parsed by the same function — union'd with the session's declared capabilities for this connection. Lets a client opt into 'artifact_updates:1' on a reconnect without re-running initialize. Omitted = the session's initialize-time capabilities only (existing behaviour, unchanged).","title":"X-Anvil-Node-Capabilities"},"description":"Optional. Same type:version vocabulary as the session's initialize accept_nodes, parsed by the same function — union'd with the session's declared capabilities for this connection. Lets a client opt into 'artifact_updates:1' on a reconnect without re-running initialize. Omitted = the session's initialize-time capabilities only (existing behaviour, unchanged)."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Server-sent events stream","content":{"application/json":{"schema":{}},"text/event-stream":{"schema":{"oneOf":[{"$ref":"#/components/schemas/MessageCreatedNotification"},{"$ref":"#/components/schemas/MessageOpenedNotification"},{"$ref":"#/components/schemas/MessageBlockNotification"},{"$ref":"#/components/schemas/MessageProvisionalBlockNotification"},{"$ref":"#/components/schemas/MessageBlockAdoptedNotification"},{"$ref":"#/components/schemas/MessageCompleteNotification"},{"$ref":"#/components/schemas/MessageRemovedNotification"},{"$ref":"#/components/schemas/MessageRejectedNotification"},{"$ref":"#/components/schemas/AgentTypingNotification"},{"$ref":"#/components/schemas/AgentThinkingNotification"},{"$ref":"#/components/schemas/TaskSetNotification"},{"$ref":"#/components/schemas/TaskProgressNotification"},{"$ref":"#/components/schemas/TaskClearNotification"},{"$ref":"#/components/schemas/ArtifactUpdatedNotification"}],"discriminator":{"propertyName":"method","mapping":{"message/created":"#/components/schemas/MessageCreatedNotification","message/streamOpened":"#/components/schemas/MessageOpenedNotification","message/block":"#/components/schemas/MessageBlockNotification","message/provisionalBlock":"#/components/schemas/MessageProvisionalBlockNotification","message/blockAdopted":"#/components/schemas/MessageBlockAdoptedNotification","message/streamComplete":"#/components/schemas/MessageCompleteNotification","message/removed":"#/components/schemas/MessageRemovedNotification","message/rejected":"#/components/schemas/MessageRejectedNotification","agent/typing":"#/components/schemas/AgentTypingNotification","agent/thinking":"#/components/schemas/AgentThinkingNotification","task/set":"#/components/schemas/TaskSetNotification","task/progress":"#/components/schemas/TaskProgressNotification","task/clear":"#/components/schemas/TaskClearNotification","artifact/updated":"#/components/schemas/ArtifactUpdatedNotification"}}}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["v1beta1"],"summary":"Rpc Delete Session","description":"Terminate a session explicitly (client-initiated).\n\nDeletes the session record and event buffer.\nAfter this call, GET /rpc with the same ``Anvil-Session-Id`` will return 404.","operationId":"rpc_delete_session_api_v1beta1_assistant_trademe_conversations__conversation_id__rpc_delete","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"Anvil-Session-Id","in":"header","required":true,"schema":{"type":"string","title":"Anvil-Session-Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["v1beta1"],"summary":"Rpc Dispatch","description":"Dispatch JSON-RPC 2.0 requests for the unified /rpc POST endpoint.\n\nSupported methods:\n    initialize              — MCP-aligned session initialisation (no session header needed)\n    notifications/initialized — client acknowledgement after initialize (requires session header)\n    message/send            — send a user message (session header optional for backward compat)\n\nError envelope (camelCase via AssistantGatewayMixin):\n\n    {\"jsonrpc\": \"2.0\", \"id\": <id>, \"error\": {\n        \"code\": <int>, \"message\": <str>,\n        \"data\": {\"category\", \"kind\", \"retryable\",\n                 \"resetAt\", \"retryAfter\", \"userMessage\"}}}\n\nSee ``docs/source/domains/assistant-gateway/concepts/error-contract.md`` for\nfull field semantics, per-category ``data.kind`` registry, and rendering\nguidance. The rationale lives in Decision 006\n(``docs/source/domains/assistant-gateway/decisions/decisions-006-error-taxonomy.md``).\n\nError codes — five canonical categories plus the JSON-RPC envelope error:\n\n    -32600  Invalid Request — a structurally valid JSON-RPC envelope that is\n            not a usable request (unknown method, request missing its id, or\n            params/blocks that fail the per-method model). Bare shape, no\n            ``data`` block; not one of the five canonical categories. An\n            unparseable body is rejected as HTTP 422 *before* this handler\n            (we do not emit -32700); unknown method is folded here rather\n            than -32601 (see Decision 006 §\"What carries a JSON-RPC envelope\").\n    -32602  validation   — input shape, length, file size, structural limit\n            breach; see Decision 003 and Decision 006.\n    -32001  quota        — token or turn quota exhausted; carries\n            ``data.resetAt``; see Decision 005 and Decision 006.\n    -32002  safety       — code-of-conduct or content-policy violation;\n            see Decision 006.\n    -32003  rate_limit   — application-level concurrency or per-turn caps\n            (``concurrent_sessions`` per-member session cap at initialize,\n            ``concurrent_conversations``, ``tool_calls_per_turn``,\n            ``task_agents_per_input``). Distinct from Cloud Armor\n            transport-level rate limits.\n    -32603  internal     — unanticipated server failure (incl. the\n            process/fleet-wide session backstop); assistant state\n            transactionally reverts so the next ``message/send`` is safe.\n\nClients should code defensively against all five canonical codes so that\na future enforcement-mode change requires no client-side update.","operationId":"rpc_dispatch_api_v1beta1_assistant_trademe_conversations__conversation_id__rpc_post","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"Anvil-Session-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Anvil-Session-Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_AnyRpcRequest"}}}},"responses":{"200":{"description":"JSON-RPC 2.0 response. HTTP status is always 200 for any request that reaches the dispatcher; success vs error is signalled by the presence of ``result`` vs ``error`` in the body. On error, ``error.code`` is one of: -32600 Invalid Request, -32602 validation, -32001 quota, -32002 safety, -32003 rate_limit, -32603 internal. See the error contract reference for envelope shape and per-category ``data.kind`` registry. Non-200 responses (401/403 auth, 422 malformed body, 404 unknown route, 429 edge rate-limit/capacity) are resolved before the JSON-RPC envelope exists and are not part of this body contract.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/_RpcResponse"},"examples":{"validation_error":{"summary":"validation (-32602) — input doesn't fit the rules","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32602,"message":"Invalid params","data":{"category":"validation","kind":"plain_text_length","retryable":false,"userMessage":"Your message is too long. Try splitting it into smaller parts."}}}},"quota_error":{"summary":"quota (-32001) — token or turn allowance exhausted","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32001,"message":"Quota exceeded","data":{"category":"quota","kind":"tokens","retryable":true,"resetAt":"2026-05-26T05:00:00Z","userMessage":"You've reached your usage limit for this period. Please try again later."}}}},"safety_error":{"summary":"safety (-32002) — code-of-conduct or content policy violation","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32002,"message":"Safety","data":{"category":"safety","kind":"content_policy","retryable":false,"userMessage":"That request isn't something I can help with."}}}},"rate_limit_error":{"summary":"rate_limit (-32003) — concurrency or per-turn cap","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32003,"message":"Rate limit","data":{"category":"rate_limit","kind":"concurrent_conversations","retryable":true,"userMessage":"You have too many conversations open. Close one to start a new conversation."}}}},"rate_limit_concurrent_sessions":{"summary":"rate_limit (-32003) — per-member active-session cap (initialize)","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32003,"message":"Rate limit","data":{"category":"rate_limit","kind":"concurrent_sessions","retryable":true,"userMessage":"You have too many active sessions. Close one to start another."}}}},"internal_error":{"summary":"internal (-32603) — unanticipated server failure","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32603,"message":"Internal error","data":{"category":"internal","kind":"unknown","retryable":true,"userMessage":"Sorry, something went wrong."}}}},"invalid_request":{"summary":"Invalid Request (-32600) — valid envelope, unusable request (e.g. unknown method); no data block","value":{"jsonrpc":"2.0","error":{"code":-32600,"message":"Invalid Request"}}}}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1beta1/assistant/trademe/config":{"get":{"tags":["v1beta1"],"summary":"Get Config","description":"Return client-side configuration for the assistant frontend.\n\nReturns Google Maps API credentials so the Angular app can load the\nMaps JS API at runtime without build-time injection.  Both values are\nempty strings when not configured (Leaflet fallback activated by client).","operationId":"get_config_api_v1beta1_assistant_trademe_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Get Config Api V1Beta1 Assistant Trademe Config Get"}}}}}}},"/api/v1beta1/assistant/trademe/prompt-starters":{"post":{"tags":["v1beta1"],"summary":"Get Prompt Starters","description":"Return conversation starter prompts tailored to the referrer context.\n\nThe referrer determines which domain's starters are returned (V1: property only).\nAuth context is accepted for future personalisation; V1 ignores it.","operationId":"get_prompt_starters_api_v1beta1_assistant_trademe_prompt_starters_post","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"max_prompts","in":"query","required":false,"schema":{"type":"integer","maximum":20,"minimum":3,"default":3,"title":"Max Prompts"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptStartersBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptStartersResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/trademe/conversations":{"get":{"tags":["v1alpha1"],"summary":"List Conversations","operationId":"list_conversations_api_v1alpha1_assistant_trademe_conversations_get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Per Page"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["v1alpha1"],"summary":"Create Conversation","operationId":"create_conversation_api_v1alpha1_assistant_trademe_conversations_post","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"is-in-staff-group","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Is-In-Staff-Group"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateConversationBody"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/trademe/conversations/{conversation_id}":{"get":{"tags":["v1alpha1"],"summary":"Get Conversation","operationId":"get_conversation_api_v1alpha1_assistant_trademe_conversations__conversation_id__get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["v1alpha1"],"summary":"Update Conversation","operationId":"update_conversation_api_v1alpha1_assistant_trademe_conversations__conversation_id__patch","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateConversationBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["v1alpha1"],"summary":"Delete Conversation","operationId":"delete_conversation_api_v1alpha1_assistant_trademe_conversations__conversation_id__delete","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/trademe/conversations/{conversation_id}/participants/{target_member_id}":{"patch":{"tags":["v1alpha1"],"summary":"Upsert Participant","description":"Invite a member or update their role.\n\nSelf-action (target == caller): only an invited member may change their own role,\nand only to 'member' (accepting the invitation).\nOther-action (target != caller): caller must be an owner. Any role is allowed.","operationId":"upsert_participant_api_v1alpha1_assistant_trademe_conversations__conversation_id__participants__target_member_id__patch","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"target_member_id","in":"path","required":true,"schema":{"type":"string","title":"Target Member Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertParticipantBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["v1alpha1"],"summary":"Remove Participant","description":"Remove a participant (leave, decline, or kick).\n\nSelf-action (target == caller): any participant may leave/decline. Owners may not leave\nif they are the only owner.\nOther-action (target != caller): caller must be an owner.","operationId":"remove_participant_api_v1alpha1_assistant_trademe_conversations__conversation_id__participants__target_member_id__delete","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"target_member_id","in":"path","required":true,"schema":{"type":"string","title":"Target Member Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/trademe/conversations/{conversation_id}/messages":{"get":{"tags":["v1alpha1"],"summary":"List Messages","description":"List messages for a conversation with block downgrade for client capabilities.\n\nWhy Accept-Nodes is a required header (not optional, not a query param):\n- Required: forces clients to declare capabilities; no silent degraded experiences.\n- Header: content negotiation is a header concern (HTTP Accept-* convention); no\n  URL encoding issues; FastAPI parses comma-separated values natively.\n- Same semantics as POST /rpc initialize acceptNodes — different encoding per call site.\n\nBlocks are returned already hydrated — the streaming markdown parser populates\nall snapshot fields at parse time.  The REST endpoint applies only the block\ndowngrade step (no artifact fetching).","operationId":"list_messages_api_v1alpha1_assistant_trademe_conversations__conversation_id__messages_get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"after_seq","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"title":"After Seq"}},{"name":"before_seq","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"title":"Before Seq"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":100,"title":"Limit"}},{"name":"order","in":"query","required":false,"schema":{"enum":["asc","desc"],"type":"string","default":"desc","title":"Order"}},{"name":"accept-nodes","in":"header","required":true,"schema":{"type":"array","items":{"type":"string"},"description":"Required. Comma-separated list of supported node types and versions in type:version format. e.g. 'plain_text:1, map:1'. Mirrors the capabilities declaration sent on POST /rpc initialize. Server downgrades nodes to the highest mutually supported version; nodes with no fallback are omitted. Using a required header follows HTTP content negotiation conventions (Accept, Accept-Language) — this is what Accept-* headers exist for.","title":"Accept-Nodes"},"description":"Required. Comma-separated list of supported node types and versions in type:version format. e.g. 'plain_text:1, map:1'. Mirrors the capabilities declaration sent on POST /rpc initialize. Server downgrades nodes to the highest mutually supported version; nodes with no fallback are omitted. Using a required header follows HTTP content negotiation conventions (Accept, Accept-Language) — this is what Accept-* headers exist for."},{"name":"X-Anvil-Node-Capabilities","in":"header","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Optional. Protocol capabilities in type:version form — the same header the SSE subscribe request accepts. Today only 'provisional_blocks:2' changes this response: a client that declares it receives each message's blocks in stored order; every other client (including one declaring 'provisional_blocks:1') receives a message's provisional blocks first, the order it was shown them live. Omitted = provisional-first (and, for a message with no provisional block, exactly the stored order).","title":"X-Anvil-Node-Capabilities"},"description":"Optional. Protocol capabilities in type:version form — the same header the SSE subscribe request accepts. Today only 'provisional_blocks:2' changes this response: a client that declares it receives each message's blocks in stored order; every other client (including one declaring 'provisional_blocks:1') receives a message's provisional blocks first, the order it was shown them live. Omitted = provisional-first (and, for a message with no provisional block, exactly the stored order)."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/trademe/conversations/{conversation_id}/rpc":{"get":{"tags":["v1alpha1"],"summary":"Rpc Subscribe","description":"Subscribe to live events for a conversation via SSE with JSON-RPC notification framing.\n\nUnified endpoint (Streamable HTTP style): both GET (subscribe) and POST (send)\nshare the same URL, enabling session affinity through proxies like Fastly Fanout.\n\nRequires ``Anvil-Session-Id`` header obtained from ``POST /rpc initialize``.\n\nSSE frame format:\n    id:    per-stream unique ID (Tier 1 replay; non-digit format bypasses Tier 2)\n    event: message (fixed -- dispatch moves to JSON-RPC method in data body)\n    data:  {\"jsonrpc\":\"2.0\",\"method\":\"<slash/namespaced>\",\"params\":{...}}","operationId":"rpc_subscribe_api_v1alpha1_assistant_trademe_conversations__conversation_id__rpc_get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"pending","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"title":"Pending"}},{"name":"Anvil-Session-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Anvil-Session-Id"}},{"name":"Last-Event-ID","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last-Event-Id"}},{"name":"X-Anvil-Node-Capabilities","in":"header","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Optional. Same type:version vocabulary as the session's initialize accept_nodes, parsed by the same function — union'd with the session's declared capabilities for this connection. Lets a client opt into 'artifact_updates:1' on a reconnect without re-running initialize. Omitted = the session's initialize-time capabilities only (existing behaviour, unchanged).","title":"X-Anvil-Node-Capabilities"},"description":"Optional. Same type:version vocabulary as the session's initialize accept_nodes, parsed by the same function — union'd with the session's declared capabilities for this connection. Lets a client opt into 'artifact_updates:1' on a reconnect without re-running initialize. Omitted = the session's initialize-time capabilities only (existing behaviour, unchanged)."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Server-sent events stream","content":{"application/json":{"schema":{}},"text/event-stream":{"schema":{"oneOf":[{"$ref":"#/components/schemas/MessageCreatedNotification"},{"$ref":"#/components/schemas/MessageOpenedNotification"},{"$ref":"#/components/schemas/MessageBlockNotification"},{"$ref":"#/components/schemas/MessageProvisionalBlockNotification"},{"$ref":"#/components/schemas/MessageBlockAdoptedNotification"},{"$ref":"#/components/schemas/MessageCompleteNotification"},{"$ref":"#/components/schemas/MessageRemovedNotification"},{"$ref":"#/components/schemas/MessageRejectedNotification"},{"$ref":"#/components/schemas/AgentTypingNotification"},{"$ref":"#/components/schemas/AgentThinkingNotification"},{"$ref":"#/components/schemas/TaskSetNotification"},{"$ref":"#/components/schemas/TaskProgressNotification"},{"$ref":"#/components/schemas/TaskClearNotification"},{"$ref":"#/components/schemas/ArtifactUpdatedNotification"}],"discriminator":{"propertyName":"method","mapping":{"message/created":"#/components/schemas/MessageCreatedNotification","message/streamOpened":"#/components/schemas/MessageOpenedNotification","message/block":"#/components/schemas/MessageBlockNotification","message/provisionalBlock":"#/components/schemas/MessageProvisionalBlockNotification","message/blockAdopted":"#/components/schemas/MessageBlockAdoptedNotification","message/streamComplete":"#/components/schemas/MessageCompleteNotification","message/removed":"#/components/schemas/MessageRemovedNotification","message/rejected":"#/components/schemas/MessageRejectedNotification","agent/typing":"#/components/schemas/AgentTypingNotification","agent/thinking":"#/components/schemas/AgentThinkingNotification","task/set":"#/components/schemas/TaskSetNotification","task/progress":"#/components/schemas/TaskProgressNotification","task/clear":"#/components/schemas/TaskClearNotification","artifact/updated":"#/components/schemas/ArtifactUpdatedNotification"}}}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["v1alpha1"],"summary":"Rpc Delete Session","description":"Terminate a session explicitly (client-initiated).\n\nDeletes the session record and event buffer.\nAfter this call, GET /rpc with the same ``Anvil-Session-Id`` will return 404.","operationId":"rpc_delete_session_api_v1alpha1_assistant_trademe_conversations__conversation_id__rpc_delete","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"Anvil-Session-Id","in":"header","required":true,"schema":{"type":"string","title":"Anvil-Session-Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["v1alpha1"],"summary":"Rpc Dispatch","description":"Dispatch JSON-RPC 2.0 requests for the unified /rpc POST endpoint.\n\nSupported methods:\n    initialize              — MCP-aligned session initialisation (no session header needed)\n    notifications/initialized — client acknowledgement after initialize (requires session header)\n    message/send            — send a user message (session header optional for backward compat)\n\nError envelope (camelCase via AssistantGatewayMixin):\n\n    {\"jsonrpc\": \"2.0\", \"id\": <id>, \"error\": {\n        \"code\": <int>, \"message\": <str>,\n        \"data\": {\"category\", \"kind\", \"retryable\",\n                 \"resetAt\", \"retryAfter\", \"userMessage\"}}}\n\nSee ``docs/source/domains/assistant-gateway/concepts/error-contract.md`` for\nfull field semantics, per-category ``data.kind`` registry, and rendering\nguidance. The rationale lives in Decision 006\n(``docs/source/domains/assistant-gateway/decisions/decisions-006-error-taxonomy.md``).\n\nError codes — five canonical categories plus the JSON-RPC envelope error:\n\n    -32600  Invalid Request — a structurally valid JSON-RPC envelope that is\n            not a usable request (unknown method, request missing its id, or\n            params/blocks that fail the per-method model). Bare shape, no\n            ``data`` block; not one of the five canonical categories. An\n            unparseable body is rejected as HTTP 422 *before* this handler\n            (we do not emit -32700); unknown method is folded here rather\n            than -32601 (see Decision 006 §\"What carries a JSON-RPC envelope\").\n    -32602  validation   — input shape, length, file size, structural limit\n            breach; see Decision 003 and Decision 006.\n    -32001  quota        — token or turn quota exhausted; carries\n            ``data.resetAt``; see Decision 005 and Decision 006.\n    -32002  safety       — code-of-conduct or content-policy violation;\n            see Decision 006.\n    -32003  rate_limit   — application-level concurrency or per-turn caps\n            (``concurrent_sessions`` per-member session cap at initialize,\n            ``concurrent_conversations``, ``tool_calls_per_turn``,\n            ``task_agents_per_input``). Distinct from Cloud Armor\n            transport-level rate limits.\n    -32603  internal     — unanticipated server failure (incl. the\n            process/fleet-wide session backstop); assistant state\n            transactionally reverts so the next ``message/send`` is safe.\n\nClients should code defensively against all five canonical codes so that\na future enforcement-mode change requires no client-side update.","operationId":"rpc_dispatch_api_v1alpha1_assistant_trademe_conversations__conversation_id__rpc_post","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"Anvil-Session-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Anvil-Session-Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_AnyRpcRequest"}}}},"responses":{"200":{"description":"JSON-RPC 2.0 response. HTTP status is always 200 for any request that reaches the dispatcher; success vs error is signalled by the presence of ``result`` vs ``error`` in the body. On error, ``error.code`` is one of: -32600 Invalid Request, -32602 validation, -32001 quota, -32002 safety, -32003 rate_limit, -32603 internal. See the error contract reference for envelope shape and per-category ``data.kind`` registry. Non-200 responses (401/403 auth, 422 malformed body, 404 unknown route, 429 edge rate-limit/capacity) are resolved before the JSON-RPC envelope exists and are not part of this body contract.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/_RpcResponse"},"examples":{"validation_error":{"summary":"validation (-32602) — input doesn't fit the rules","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32602,"message":"Invalid params","data":{"category":"validation","kind":"plain_text_length","retryable":false,"userMessage":"Your message is too long. Try splitting it into smaller parts."}}}},"quota_error":{"summary":"quota (-32001) — token or turn allowance exhausted","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32001,"message":"Quota exceeded","data":{"category":"quota","kind":"tokens","retryable":true,"resetAt":"2026-05-26T05:00:00Z","userMessage":"You've reached your usage limit for this period. Please try again later."}}}},"safety_error":{"summary":"safety (-32002) — code-of-conduct or content policy violation","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32002,"message":"Safety","data":{"category":"safety","kind":"content_policy","retryable":false,"userMessage":"That request isn't something I can help with."}}}},"rate_limit_error":{"summary":"rate_limit (-32003) — concurrency or per-turn cap","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32003,"message":"Rate limit","data":{"category":"rate_limit","kind":"concurrent_conversations","retryable":true,"userMessage":"You have too many conversations open. Close one to start a new conversation."}}}},"rate_limit_concurrent_sessions":{"summary":"rate_limit (-32003) — per-member active-session cap (initialize)","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32003,"message":"Rate limit","data":{"category":"rate_limit","kind":"concurrent_sessions","retryable":true,"userMessage":"You have too many active sessions. Close one to start another."}}}},"internal_error":{"summary":"internal (-32603) — unanticipated server failure","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32603,"message":"Internal error","data":{"category":"internal","kind":"unknown","retryable":true,"userMessage":"Sorry, something went wrong."}}}},"invalid_request":{"summary":"Invalid Request (-32600) — valid envelope, unusable request (e.g. unknown method); no data block","value":{"jsonrpc":"2.0","error":{"code":-32600,"message":"Invalid Request"}}}}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/trademe/config":{"get":{"tags":["v1alpha1"],"summary":"Get Config","description":"Return client-side configuration for the assistant frontend.\n\nReturns Google Maps API credentials so the Angular app can load the\nMaps JS API at runtime without build-time injection.  Both values are\nempty strings when not configured (Leaflet fallback activated by client).","operationId":"get_config_api_v1alpha1_assistant_trademe_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Get Config Api V1Alpha1 Assistant Trademe Config Get"}}}}}}},"/api/v1alpha1/assistant/trademe/prompt-starters":{"post":{"tags":["v1alpha1"],"summary":"Get Prompt Starters","description":"Return conversation starter prompts tailored to the referrer context.\n\nThe referrer determines which domain's starters are returned (V1: property only).\nAuth context is accepted for future personalisation; V1 ignores it.","operationId":"get_prompt_starters_api_v1alpha1_assistant_trademe_prompt_starters_post","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"max_prompts","in":"query","required":false,"schema":{"type":"integer","maximum":20,"minimum":3,"default":3,"title":"Max Prompts"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptStartersBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptStartersResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/trademe/files":{"post":{"tags":["v1alpha1"],"summary":"Create File","description":"Mint a signed PUT URL for a new upload. Write-only, like POST /conversations —\nthis mints a new id and surfaces no pre-existing state.","operationId":"create_file_api_v1alpha1_assistant_trademe_files_post","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFileBody"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["v1alpha1"],"summary":"List Files","description":"The calling member's files for this kind, newest first — the \"all files\" view.\n\nBoth pending and active files are returned with their state, so a client\nrendering \"all files\" can show an in-flight upload rather than a gap. This\ndoes not mint signed GET URLs — clients resolve individual files via\n``GET /files/{file_id}/content`` when rendering, same as any other ref.","operationId":"list_files_api_v1alpha1_assistant_trademe_files_get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"created_before","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Exclusive cursor: only files created before this timestamp. Pass the last item's createdAt to fetch the next (older) page.","title":"Created Before"},"description":"Exclusive cursor: only files created before this timestamp. Pass the last item's createdAt to fetch the next (older) page."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":100,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/trademe/files/{file_id}/complete":{"post":{"tags":["v1alpha1"],"summary":"Complete File","operationId":"complete_file_api_v1alpha1_assistant_trademe_files__file_id__complete_post","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","title":"File Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteFileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/trademe/files/{file_id}":{"get":{"tags":["v1alpha1"],"summary":"Get File","description":"Metadata for one file — OpenAI's \"Retrieve file\" / Anthropic's \"Get\nFile Metadata\". Ownership-scoped like every other read here (wrong\nmember, wrong kind, and unknown file_id are indistinguishable 404s), but\nreading metadata is not a \"use\": unlike ``GET /files/{file_id}/content``,\nthis never mints a signed URL and never bumps custom_time/last_used_at.\n\nReports ``pending`` files too (state visible), same as ``GET /files``'s\nlist — a client's \"all files\" view needs a single-file fetch that\nbehaves like the list it paginates, not a narrower one.","operationId":"get_file_api_v1alpha1_assistant_trademe_files__file_id__get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","title":"File Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileResource"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["v1alpha1"],"summary":"Delete File","description":"Member deletes their own file — bucket object(s) + store record.\n\nWorks on both pending (cancels the upload) and active files. Ownership\nscoping is identical to ``get_file``/``download_file``: wrong member,\nwrong kind, and unknown ``file_id`` are indistinguishable 404s (no\nexistence oracle).\nRefs to a deleted file already in a conversation's history resolve via the\nsame expired/missing-ref placeholder path as any other reclaimed object —\nnothing else to update there.","operationId":"delete_file_api_v1alpha1_assistant_trademe_files__file_id__delete","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","title":"File Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/trademe/files/{file_id}/content":{"get":{"tags":["v1alpha1"],"summary":"Download File","description":"Signed GET URL for one active file's bytes — OpenAI's \"Retrieve file\ncontent\" / Anthropic's \"Download a File\". This is the endpoint clients\nuse to render the file: the returned ``get_url`` goes straight into an\n``<img src>`` (or equivalent); bytes flow from GCS to the client\ndirectly, never through Anvil.\n\nMember+kind-gated: wrong member, wrong kind, unknown file_id, a file\nstill ``pending`` (no active object exists yet to sign), and an\nexpired/reclaimed bucket object are all indistinguishable 404s — no\nexistence oracle. Every successful call is a \"use\" — re-bumps\ncustom_time/last_used_at, unlike ``GET /files/{file_id}``'s metadata read.","operationId":"download_file_api_v1alpha1_assistant_trademe_files__file_id__content_get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","title":"File Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DownloadFileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/trademe/conversations/{conversation_id}/artifacts":{"get":{"tags":["v1alpha1"],"summary":"List Artifacts","description":"Current HEAD of every artifact this conversation owns, newest change first.\n\n**Presented-only by default.** Without ``include_unpresented=true`` the\nresult contains only artifacts whose ``presentedAt`` is set — the ones the\nmember has actually been shown. The filter is applied by the store\n(``ix_artifacts_conversation_presented`` covers it), not in Python, so\nunsurfaced rows are never read.\n\n**Authored documents by default.** Without ``include_snapshots=true`` the\nresult contains only the artifacts the agent AUTHORED from the member's\nsteering. The snapshot types — search results, property cards, listing\nsummaries, crisis resources — are copies of upstream-mastered data that a\nconversation accumulates as its tools run, and a member's document list is\nnot the place for them. Both flags are independent, so a caller can ask for\nthe machinery, the unpresented drafts, either, or both.\n\nOne query, no N+1 (``ArtifactStore.list_latest_versions``). Ordering is\n``lastChangedAt`` descending — see ``_last_changed_key`` for why that is\nderived from the head version rather than stored on the artifact.\n\nScoped exactly like the messages endpoints (``MemberConversationDep``): a\nconversation of another kind, one that does not exist, and one the caller\ndoes not participate in are all the same 403.","operationId":"list_artifacts_api_v1alpha1_assistant_trademe_conversations__conversation_id__artifacts_get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"include_unpresented","in":"query","required":false,"schema":{"type":"boolean","description":"Include artifacts that have never been surfaced to the member. Defaults to false: this endpoint returns ONLY artifacts with a presentedAt timestamp, so an in-progress draft the assistant has not shown yet cannot appear in a member's list.","default":false,"title":"Include Unpresented"},"description":"Include artifacts that have never been surfaced to the member. Defaults to false: this endpoint returns ONLY artifacts with a presentedAt timestamp, so an in-progress draft the assistant has not shown yet cannot appear in a member's list."},{"name":"include_snapshots","in":"query","required":false,"schema":{"type":"boolean","description":"Also include SNAPSHOT-provenance artifacts — the search results, property cards, listing summaries and crisis resources a conversation accumulates as its tools run. Defaults to false: the member-facing view of this endpoint is the AUTHORED documents, and the snapshots are machinery. Set it to watch that machinery work, which is what the assistant app's artifacts sidebar does behind its 'show machinery' toggle.","default":false,"title":"Include Snapshots"},"description":"Also include SNAPSHOT-provenance artifacts — the search results, property cards, listing summaries and crisis resources a conversation accumulates as its tools run. Defaults to false: the member-facing view of this endpoint is the AUTHORED documents, and the snapshots are machinery. Set it to watch that machinery work, which is what the assistant app's artifacts sidebar does behind its 'show machinery' toggle."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationArtifactsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/trademe/conversations/{conversation_id}/artifacts/{artifact_id}":{"get":{"tags":["v1alpha1"],"summary":"Get Artifact","description":"One artifact's current HEAD.\n\nCross-conversation IDOR posture matches the artifact store's own protocol\n(see ``ArtifactStore.get_latest_version``): an ``artifact_id`` that does not\nexist, one that exists but is owned by a different conversation, and one\nthat is not a UUID at all ALL resolve to the SAME 404, never distinguishable\nfrom each other.\n\nUnlike the list endpoint, this does not filter on ``presentedAt``: the\ncaller already holds the id (it came from a ref block in a message that was\ndelivered to them), so there is nothing here to enumerate.","operationId":"get_artifact_api_v1alpha1_assistant_trademe_conversations__conversation_id__artifacts__artifact_id__get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"artifact_id","in":"path","required":true,"schema":{"type":"string","title":"Artifact Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArtifactHead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/trademe/conversations/{conversation_id}/artifacts/{artifact_id}/versions":{"get":{"tags":["v1alpha1"],"summary":"List Artifact Versions","description":"One artifact's history, newest first. MILESTONES BY DEFAULT.\n\nA milestone is not a stored flag. It is derived, per request, from the two\nfacts each version's writer recorded — its ``origin`` and its ``causedBy`` —\nby the rule in ``anvil.assistant.artifacts.version_origin``: the last\nversion of each turn, plus every restore, plus every member edit, plus the\nhead, minus the autosaves. So a UI's back button steps through states a\nmember would recognise, and ``include_all=true`` still offers the raw chain.\n\nPAGINATED FROM THE START, and keyset rather than offset: ``before_ordinal``\nis a boundary VALUE. Nothing counts versions or assumes their ordinals are\ncontiguous — a chain that a retention sweep has left with holes pages\nexactly like one that has none.\n\nAn artifact this conversation cannot see returns an EMPTY page, not a 404 —\nidentical to an artifact with no versions. That is the same posture as the\nrest of this router by a different mechanism: there is nothing to refuse\nhere, because there is nothing to distinguish.","operationId":"list_artifact_versions_api_v1alpha1_assistant_trademe_conversations__conversation_id__artifacts__artifact_id__versions_get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"artifact_id","in":"path","required":true,"schema":{"type":"string","title":"Artifact Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"include_all","in":"query","required":false,"schema":{"type":"boolean","description":"Return every version instead of the milestone view. Defaults to false: the default view hides autosaves and the intermediate states of a turn, so a back button steps through meaningful states. Set this when you want fine-grained undo.","default":false,"title":"Include All"},"description":"Return every version instead of the milestone view. Defaults to false: the default view hides autosaves and the intermediate states of a turn, so a back button steps through meaningful states. Set this when you want fine-grained undo."},{"name":"limit","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":200,"minimum":1},{"type":"null"}],"description":"Page size. Clamped server-side; omit for the default.","title":"Limit"},"description":"Page size. Clamped server-side; omit for the default."},{"name":"before_ordinal","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Cursor: return only versions below this ordinal. Pass back the nextBeforeOrdinal from the previous page, and treat the value as opaque — it is a boundary, not a count.","title":"Before Ordinal"},"description":"Cursor: return only versions below this ordinal. Pass back the nextBeforeOrdinal from the previous page, and treat the value as opaque — it is a boundary, not a count."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArtifactVersionsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/trademe/conversations/{conversation_id}/artifacts/{artifact_id}/versions/{version_id}":{"get":{"tags":["v1alpha1"],"summary":"Get Artifact Version","description":"One specific version of an artifact, with its payload.\n\nThis is where a history UI gets the content to show once someone picks an\nentry from the list — which is why the list itself carries no payloads.\n\nEvery unusable combination is the same 404: an unknown version, a version of\na different artifact, an artifact of a different conversation, and an id\nthat is not a UUID at all.","operationId":"get_artifact_version_api_v1alpha1_assistant_trademe_conversations__conversation_id__artifacts__artifact_id__versions__version_id__get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"artifact_id","in":"path","required":true,"schema":{"type":"string","title":"Artifact Id"}},{"name":"version_id","in":"path","required":true,"schema":{"type":"string","title":"Version Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArtifactVersionDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/trademe/conversations/{conversation_id}/artifacts/{artifact_id}/versions/{version_id}/restore":{"post":{"tags":["v1alpha1"],"summary":"Restore Artifact Version","description":"Append a copy of an older version as the artifact's NEW head.\n\n**201, because a restore CREATES a version.** Nothing is rewound and no\nversion is mutated: the chain keeps everything it had, the restored content\narrives as a new head recording ``origin=\"restore\"`` and the version it came\nfrom, and ``artifact/updated`` fires by itself because this is, mechanically,\nan ordinary append. Redo is therefore free — restore the version you undid\nfrom.\n\nDoing this SERVER-side rather than having the client fetch version 3 and\npost it back is not merely an optimisation. The round trip would carry a\npotentially large payload through a client that only wants to point at it,\nand — the part that cannot be recovered later — the resulting version would\nbe indistinguishable from an ordinary edit that happens to contain old text.\n\n**409 on a lost race.** The append is compare-and-swap, so a restore racing\nanother edit fails rather than clobbering it. Send\n``expectedHeadVersionId`` to assert the head the UI actually rendered; the\n409 body carries the head that won, so the client can re-read and decide.\n\n**404 covers everything a caller may not see**, and is byte-identical to the\nreads' 404: this endpoint must not become an existence oracle for another\nconversation's artifacts just because it happens to be a write.","operationId":"restore_artifact_version_api_v1alpha1_assistant_trademe_conversations__conversation_id__artifacts__artifact_id__versions__version_id__restore_post","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"artifact_id","in":"path","required":true,"schema":{"type":"string","title":"Artifact Id"}},{"name":"version_id","in":"path","required":true,"schema":{"type":"string","title":"Version Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RestoreArtifactVersionRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArtifactHead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/{assistant_kind}/conversations":{"get":{"tags":["v1alpha1"],"summary":"List Conversations","operationId":"list_conversations_api_v1alpha1_assistant__assistant_kind__conversations_get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Per Page"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["v1alpha1"],"summary":"Create Conversation","operationId":"create_conversation_api_v1alpha1_assistant__assistant_kind__conversations_post","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"is-in-staff-group","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Is-In-Staff-Group"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateConversationBody"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/{assistant_kind}/conversations/{conversation_id}":{"get":{"tags":["v1alpha1"],"summary":"Get Conversation","operationId":"get_conversation_api_v1alpha1_assistant__assistant_kind__conversations__conversation_id__get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["v1alpha1"],"summary":"Update Conversation","operationId":"update_conversation_api_v1alpha1_assistant__assistant_kind__conversations__conversation_id__patch","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateConversationBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["v1alpha1"],"summary":"Delete Conversation","operationId":"delete_conversation_api_v1alpha1_assistant__assistant_kind__conversations__conversation_id__delete","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/{assistant_kind}/conversations/{conversation_id}/participants/{target_member_id}":{"patch":{"tags":["v1alpha1"],"summary":"Upsert Participant","description":"Invite a member or update their role.\n\nSelf-action (target == caller): only an invited member may change their own role,\nand only to 'member' (accepting the invitation).\nOther-action (target != caller): caller must be an owner. Any role is allowed.","operationId":"upsert_participant_api_v1alpha1_assistant__assistant_kind__conversations__conversation_id__participants__target_member_id__patch","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"target_member_id","in":"path","required":true,"schema":{"type":"string","title":"Target Member Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertParticipantBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["v1alpha1"],"summary":"Remove Participant","description":"Remove a participant (leave, decline, or kick).\n\nSelf-action (target == caller): any participant may leave/decline. Owners may not leave\nif they are the only owner.\nOther-action (target != caller): caller must be an owner.","operationId":"remove_participant_api_v1alpha1_assistant__assistant_kind__conversations__conversation_id__participants__target_member_id__delete","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"target_member_id","in":"path","required":true,"schema":{"type":"string","title":"Target Member Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/{assistant_kind}/conversations/{conversation_id}/messages":{"get":{"tags":["v1alpha1"],"summary":"List Messages","description":"List messages for a conversation with block downgrade for client capabilities.\n\nWhy Accept-Nodes is a required header (not optional, not a query param):\n- Required: forces clients to declare capabilities; no silent degraded experiences.\n- Header: content negotiation is a header concern (HTTP Accept-* convention); no\n  URL encoding issues; FastAPI parses comma-separated values natively.\n- Same semantics as POST /rpc initialize acceptNodes — different encoding per call site.\n\nBlocks are returned already hydrated — the streaming markdown parser populates\nall snapshot fields at parse time.  The REST endpoint applies only the block\ndowngrade step (no artifact fetching).","operationId":"list_messages_api_v1alpha1_assistant__assistant_kind__conversations__conversation_id__messages_get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"after_seq","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"title":"After Seq"}},{"name":"before_seq","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"title":"Before Seq"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":100,"title":"Limit"}},{"name":"order","in":"query","required":false,"schema":{"enum":["asc","desc"],"type":"string","default":"desc","title":"Order"}},{"name":"accept-nodes","in":"header","required":true,"schema":{"type":"array","items":{"type":"string"},"description":"Required. Comma-separated list of supported node types and versions in type:version format. e.g. 'plain_text:1, map:1'. Mirrors the capabilities declaration sent on POST /rpc initialize. Server downgrades nodes to the highest mutually supported version; nodes with no fallback are omitted. Using a required header follows HTTP content negotiation conventions (Accept, Accept-Language) — this is what Accept-* headers exist for.","title":"Accept-Nodes"},"description":"Required. Comma-separated list of supported node types and versions in type:version format. e.g. 'plain_text:1, map:1'. Mirrors the capabilities declaration sent on POST /rpc initialize. Server downgrades nodes to the highest mutually supported version; nodes with no fallback are omitted. Using a required header follows HTTP content negotiation conventions (Accept, Accept-Language) — this is what Accept-* headers exist for."},{"name":"X-Anvil-Node-Capabilities","in":"header","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Optional. Protocol capabilities in type:version form — the same header the SSE subscribe request accepts. Today only 'provisional_blocks:2' changes this response: a client that declares it receives each message's blocks in stored order; every other client (including one declaring 'provisional_blocks:1') receives a message's provisional blocks first, the order it was shown them live. Omitted = provisional-first (and, for a message with no provisional block, exactly the stored order).","title":"X-Anvil-Node-Capabilities"},"description":"Optional. Protocol capabilities in type:version form — the same header the SSE subscribe request accepts. Today only 'provisional_blocks:2' changes this response: a client that declares it receives each message's blocks in stored order; every other client (including one declaring 'provisional_blocks:1') receives a message's provisional blocks first, the order it was shown them live. Omitted = provisional-first (and, for a message with no provisional block, exactly the stored order)."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/{assistant_kind}/conversations/{conversation_id}/rpc":{"get":{"tags":["v1alpha1"],"summary":"Rpc Subscribe","description":"Subscribe to live events for a conversation via SSE with JSON-RPC notification framing.\n\nUnified endpoint (Streamable HTTP style): both GET (subscribe) and POST (send)\nshare the same URL, enabling session affinity through proxies like Fastly Fanout.\n\nRequires ``Anvil-Session-Id`` header obtained from ``POST /rpc initialize``.\n\nSSE frame format:\n    id:    per-stream unique ID (Tier 1 replay; non-digit format bypasses Tier 2)\n    event: message (fixed -- dispatch moves to JSON-RPC method in data body)\n    data:  {\"jsonrpc\":\"2.0\",\"method\":\"<slash/namespaced>\",\"params\":{...}}","operationId":"rpc_subscribe_api_v1alpha1_assistant__assistant_kind__conversations__conversation_id__rpc_get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"pending","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"title":"Pending"}},{"name":"Anvil-Session-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Anvil-Session-Id"}},{"name":"Last-Event-ID","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last-Event-Id"}},{"name":"X-Anvil-Node-Capabilities","in":"header","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Optional. Same type:version vocabulary as the session's initialize accept_nodes, parsed by the same function — union'd with the session's declared capabilities for this connection. Lets a client opt into 'artifact_updates:1' on a reconnect without re-running initialize. Omitted = the session's initialize-time capabilities only (existing behaviour, unchanged).","title":"X-Anvil-Node-Capabilities"},"description":"Optional. Same type:version vocabulary as the session's initialize accept_nodes, parsed by the same function — union'd with the session's declared capabilities for this connection. Lets a client opt into 'artifact_updates:1' on a reconnect without re-running initialize. Omitted = the session's initialize-time capabilities only (existing behaviour, unchanged)."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Server-sent events stream","content":{"application/json":{"schema":{}},"text/event-stream":{"schema":{"oneOf":[{"$ref":"#/components/schemas/MessageCreatedNotification"},{"$ref":"#/components/schemas/MessageOpenedNotification"},{"$ref":"#/components/schemas/MessageBlockNotification"},{"$ref":"#/components/schemas/MessageProvisionalBlockNotification"},{"$ref":"#/components/schemas/MessageBlockAdoptedNotification"},{"$ref":"#/components/schemas/MessageCompleteNotification"},{"$ref":"#/components/schemas/MessageRemovedNotification"},{"$ref":"#/components/schemas/MessageRejectedNotification"},{"$ref":"#/components/schemas/AgentTypingNotification"},{"$ref":"#/components/schemas/AgentThinkingNotification"},{"$ref":"#/components/schemas/TaskSetNotification"},{"$ref":"#/components/schemas/TaskProgressNotification"},{"$ref":"#/components/schemas/TaskClearNotification"},{"$ref":"#/components/schemas/ArtifactUpdatedNotification"}],"discriminator":{"propertyName":"method","mapping":{"message/created":"#/components/schemas/MessageCreatedNotification","message/streamOpened":"#/components/schemas/MessageOpenedNotification","message/block":"#/components/schemas/MessageBlockNotification","message/provisionalBlock":"#/components/schemas/MessageProvisionalBlockNotification","message/blockAdopted":"#/components/schemas/MessageBlockAdoptedNotification","message/streamComplete":"#/components/schemas/MessageCompleteNotification","message/removed":"#/components/schemas/MessageRemovedNotification","message/rejected":"#/components/schemas/MessageRejectedNotification","agent/typing":"#/components/schemas/AgentTypingNotification","agent/thinking":"#/components/schemas/AgentThinkingNotification","task/set":"#/components/schemas/TaskSetNotification","task/progress":"#/components/schemas/TaskProgressNotification","task/clear":"#/components/schemas/TaskClearNotification","artifact/updated":"#/components/schemas/ArtifactUpdatedNotification"}}}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["v1alpha1"],"summary":"Rpc Delete Session","description":"Terminate a session explicitly (client-initiated).\n\nDeletes the session record and event buffer.\nAfter this call, GET /rpc with the same ``Anvil-Session-Id`` will return 404.","operationId":"rpc_delete_session_api_v1alpha1_assistant__assistant_kind__conversations__conversation_id__rpc_delete","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"Anvil-Session-Id","in":"header","required":true,"schema":{"type":"string","title":"Anvil-Session-Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["v1alpha1"],"summary":"Rpc Dispatch","description":"Dispatch JSON-RPC 2.0 requests for the unified /rpc POST endpoint.\n\nSupported methods:\n    initialize              — MCP-aligned session initialisation (no session header needed)\n    notifications/initialized — client acknowledgement after initialize (requires session header)\n    message/send            — send a user message (session header optional for backward compat)\n\nError envelope (camelCase via AssistantGatewayMixin):\n\n    {\"jsonrpc\": \"2.0\", \"id\": <id>, \"error\": {\n        \"code\": <int>, \"message\": <str>,\n        \"data\": {\"category\", \"kind\", \"retryable\",\n                 \"resetAt\", \"retryAfter\", \"userMessage\"}}}\n\nSee ``docs/source/domains/assistant-gateway/concepts/error-contract.md`` for\nfull field semantics, per-category ``data.kind`` registry, and rendering\nguidance. The rationale lives in Decision 006\n(``docs/source/domains/assistant-gateway/decisions/decisions-006-error-taxonomy.md``).\n\nError codes — five canonical categories plus the JSON-RPC envelope error:\n\n    -32600  Invalid Request — a structurally valid JSON-RPC envelope that is\n            not a usable request (unknown method, request missing its id, or\n            params/blocks that fail the per-method model). Bare shape, no\n            ``data`` block; not one of the five canonical categories. An\n            unparseable body is rejected as HTTP 422 *before* this handler\n            (we do not emit -32700); unknown method is folded here rather\n            than -32601 (see Decision 006 §\"What carries a JSON-RPC envelope\").\n    -32602  validation   — input shape, length, file size, structural limit\n            breach; see Decision 003 and Decision 006.\n    -32001  quota        — token or turn quota exhausted; carries\n            ``data.resetAt``; see Decision 005 and Decision 006.\n    -32002  safety       — code-of-conduct or content-policy violation;\n            see Decision 006.\n    -32003  rate_limit   — application-level concurrency or per-turn caps\n            (``concurrent_sessions`` per-member session cap at initialize,\n            ``concurrent_conversations``, ``tool_calls_per_turn``,\n            ``task_agents_per_input``). Distinct from Cloud Armor\n            transport-level rate limits.\n    -32603  internal     — unanticipated server failure (incl. the\n            process/fleet-wide session backstop); assistant state\n            transactionally reverts so the next ``message/send`` is safe.\n\nClients should code defensively against all five canonical codes so that\na future enforcement-mode change requires no client-side update.","operationId":"rpc_dispatch_api_v1alpha1_assistant__assistant_kind__conversations__conversation_id__rpc_post","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"Anvil-Session-Id","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Anvil-Session-Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_AnyRpcRequest"}}}},"responses":{"200":{"description":"JSON-RPC 2.0 response. HTTP status is always 200 for any request that reaches the dispatcher; success vs error is signalled by the presence of ``result`` vs ``error`` in the body. On error, ``error.code`` is one of: -32600 Invalid Request, -32602 validation, -32001 quota, -32002 safety, -32003 rate_limit, -32603 internal. See the error contract reference for envelope shape and per-category ``data.kind`` registry. Non-200 responses (401/403 auth, 422 malformed body, 404 unknown route, 429 edge rate-limit/capacity) are resolved before the JSON-RPC envelope exists and are not part of this body contract.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/_RpcResponse"},"examples":{"validation_error":{"summary":"validation (-32602) — input doesn't fit the rules","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32602,"message":"Invalid params","data":{"category":"validation","kind":"plain_text_length","retryable":false,"userMessage":"Your message is too long. Try splitting it into smaller parts."}}}},"quota_error":{"summary":"quota (-32001) — token or turn allowance exhausted","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32001,"message":"Quota exceeded","data":{"category":"quota","kind":"tokens","retryable":true,"resetAt":"2026-05-26T05:00:00Z","userMessage":"You've reached your usage limit for this period. Please try again later."}}}},"safety_error":{"summary":"safety (-32002) — code-of-conduct or content policy violation","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32002,"message":"Safety","data":{"category":"safety","kind":"content_policy","retryable":false,"userMessage":"That request isn't something I can help with."}}}},"rate_limit_error":{"summary":"rate_limit (-32003) — concurrency or per-turn cap","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32003,"message":"Rate limit","data":{"category":"rate_limit","kind":"concurrent_conversations","retryable":true,"userMessage":"You have too many conversations open. Close one to start a new conversation."}}}},"rate_limit_concurrent_sessions":{"summary":"rate_limit (-32003) — per-member active-session cap (initialize)","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32003,"message":"Rate limit","data":{"category":"rate_limit","kind":"concurrent_sessions","retryable":true,"userMessage":"You have too many active sessions. Close one to start another."}}}},"internal_error":{"summary":"internal (-32603) — unanticipated server failure","value":{"jsonrpc":"2.0","id":"1","error":{"code":-32603,"message":"Internal error","data":{"category":"internal","kind":"unknown","retryable":true,"userMessage":"Sorry, something went wrong."}}}},"invalid_request":{"summary":"Invalid Request (-32600) — valid envelope, unusable request (e.g. unknown method); no data block","value":{"jsonrpc":"2.0","error":{"code":-32600,"message":"Invalid Request"}}}}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/{assistant_kind}/config":{"get":{"tags":["v1alpha1"],"summary":"Get Config","description":"Return client-side configuration for the assistant frontend.\n\nReturns Google Maps API credentials so the Angular app can load the\nMaps JS API at runtime without build-time injection.  Both values are\nempty strings when not configured (Leaflet fallback activated by client).","operationId":"get_config_api_v1alpha1_assistant__assistant_kind__config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Get Config Api V1Alpha1 Assistant  Assistant Kind  Config Get"}}}}}}},"/api/v1alpha1/assistant/{assistant_kind}/prompt-starters":{"post":{"tags":["v1alpha1"],"summary":"Get Prompt Starters","description":"Return conversation starter prompts tailored to the referrer context.\n\nThe referrer determines which domain's starters are returned (V1: property only).\nAuth context is accepted for future personalisation; V1 ignores it.","operationId":"get_prompt_starters_api_v1alpha1_assistant__assistant_kind__prompt_starters_post","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"max_prompts","in":"query","required":false,"schema":{"type":"integer","maximum":20,"minimum":3,"default":3,"title":"Max Prompts"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptStartersBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptStartersResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/{assistant_kind}/files":{"post":{"tags":["v1alpha1"],"summary":"Create File","description":"Mint a signed PUT URL for a new upload. Write-only, like POST /conversations —\nthis mints a new id and surfaces no pre-existing state.","operationId":"create_file_api_v1alpha1_assistant__assistant_kind__files_post","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFileBody"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["v1alpha1"],"summary":"List Files","description":"The calling member's files for this kind, newest first — the \"all files\" view.\n\nBoth pending and active files are returned with their state, so a client\nrendering \"all files\" can show an in-flight upload rather than a gap. This\ndoes not mint signed GET URLs — clients resolve individual files via\n``GET /files/{file_id}/content`` when rendering, same as any other ref.","operationId":"list_files_api_v1alpha1_assistant__assistant_kind__files_get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"created_before","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Exclusive cursor: only files created before this timestamp. Pass the last item's createdAt to fetch the next (older) page.","title":"Created Before"},"description":"Exclusive cursor: only files created before this timestamp. Pass the last item's createdAt to fetch the next (older) page."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":100,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/{assistant_kind}/files/{file_id}/complete":{"post":{"tags":["v1alpha1"],"summary":"Complete File","operationId":"complete_file_api_v1alpha1_assistant__assistant_kind__files__file_id__complete_post","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","title":"File Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteFileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/{assistant_kind}/files/{file_id}":{"get":{"tags":["v1alpha1"],"summary":"Get File","description":"Metadata for one file — OpenAI's \"Retrieve file\" / Anthropic's \"Get\nFile Metadata\". Ownership-scoped like every other read here (wrong\nmember, wrong kind, and unknown file_id are indistinguishable 404s), but\nreading metadata is not a \"use\": unlike ``GET /files/{file_id}/content``,\nthis never mints a signed URL and never bumps custom_time/last_used_at.\n\nReports ``pending`` files too (state visible), same as ``GET /files``'s\nlist — a client's \"all files\" view needs a single-file fetch that\nbehaves like the list it paginates, not a narrower one.","operationId":"get_file_api_v1alpha1_assistant__assistant_kind__files__file_id__get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","title":"File Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileResource"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["v1alpha1"],"summary":"Delete File","description":"Member deletes their own file — bucket object(s) + store record.\n\nWorks on both pending (cancels the upload) and active files. Ownership\nscoping is identical to ``get_file``/``download_file``: wrong member,\nwrong kind, and unknown ``file_id`` are indistinguishable 404s (no\nexistence oracle).\nRefs to a deleted file already in a conversation's history resolve via the\nsame expired/missing-ref placeholder path as any other reclaimed object —\nnothing else to update there.","operationId":"delete_file_api_v1alpha1_assistant__assistant_kind__files__file_id__delete","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","title":"File Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/{assistant_kind}/files/{file_id}/content":{"get":{"tags":["v1alpha1"],"summary":"Download File","description":"Signed GET URL for one active file's bytes — OpenAI's \"Retrieve file\ncontent\" / Anthropic's \"Download a File\". This is the endpoint clients\nuse to render the file: the returned ``get_url`` goes straight into an\n``<img src>`` (or equivalent); bytes flow from GCS to the client\ndirectly, never through Anvil.\n\nMember+kind-gated: wrong member, wrong kind, unknown file_id, a file\nstill ``pending`` (no active object exists yet to sign), and an\nexpired/reclaimed bucket object are all indistinguishable 404s — no\nexistence oracle. Every successful call is a \"use\" — re-bumps\ncustom_time/last_used_at, unlike ``GET /files/{file_id}``'s metadata read.","operationId":"download_file_api_v1alpha1_assistant__assistant_kind__files__file_id__content_get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","title":"File Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DownloadFileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/{assistant_kind}/conversations/{conversation_id}/artifacts":{"get":{"tags":["v1alpha1"],"summary":"List Artifacts","description":"Current HEAD of every artifact this conversation owns, newest change first.\n\n**Presented-only by default.** Without ``include_unpresented=true`` the\nresult contains only artifacts whose ``presentedAt`` is set — the ones the\nmember has actually been shown. The filter is applied by the store\n(``ix_artifacts_conversation_presented`` covers it), not in Python, so\nunsurfaced rows are never read.\n\n**Authored documents by default.** Without ``include_snapshots=true`` the\nresult contains only the artifacts the agent AUTHORED from the member's\nsteering. The snapshot types — search results, property cards, listing\nsummaries, crisis resources — are copies of upstream-mastered data that a\nconversation accumulates as its tools run, and a member's document list is\nnot the place for them. Both flags are independent, so a caller can ask for\nthe machinery, the unpresented drafts, either, or both.\n\nOne query, no N+1 (``ArtifactStore.list_latest_versions``). Ordering is\n``lastChangedAt`` descending — see ``_last_changed_key`` for why that is\nderived from the head version rather than stored on the artifact.\n\nScoped exactly like the messages endpoints (``MemberConversationDep``): a\nconversation of another kind, one that does not exist, and one the caller\ndoes not participate in are all the same 403.","operationId":"list_artifacts_api_v1alpha1_assistant__assistant_kind__conversations__conversation_id__artifacts_get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"include_unpresented","in":"query","required":false,"schema":{"type":"boolean","description":"Include artifacts that have never been surfaced to the member. Defaults to false: this endpoint returns ONLY artifacts with a presentedAt timestamp, so an in-progress draft the assistant has not shown yet cannot appear in a member's list.","default":false,"title":"Include Unpresented"},"description":"Include artifacts that have never been surfaced to the member. Defaults to false: this endpoint returns ONLY artifacts with a presentedAt timestamp, so an in-progress draft the assistant has not shown yet cannot appear in a member's list."},{"name":"include_snapshots","in":"query","required":false,"schema":{"type":"boolean","description":"Also include SNAPSHOT-provenance artifacts — the search results, property cards, listing summaries and crisis resources a conversation accumulates as its tools run. Defaults to false: the member-facing view of this endpoint is the AUTHORED documents, and the snapshots are machinery. Set it to watch that machinery work, which is what the assistant app's artifacts sidebar does behind its 'show machinery' toggle.","default":false,"title":"Include Snapshots"},"description":"Also include SNAPSHOT-provenance artifacts — the search results, property cards, listing summaries and crisis resources a conversation accumulates as its tools run. Defaults to false: the member-facing view of this endpoint is the AUTHORED documents, and the snapshots are machinery. Set it to watch that machinery work, which is what the assistant app's artifacts sidebar does behind its 'show machinery' toggle."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationArtifactsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/{assistant_kind}/conversations/{conversation_id}/artifacts/{artifact_id}":{"get":{"tags":["v1alpha1"],"summary":"Get Artifact","description":"One artifact's current HEAD.\n\nCross-conversation IDOR posture matches the artifact store's own protocol\n(see ``ArtifactStore.get_latest_version``): an ``artifact_id`` that does not\nexist, one that exists but is owned by a different conversation, and one\nthat is not a UUID at all ALL resolve to the SAME 404, never distinguishable\nfrom each other.\n\nUnlike the list endpoint, this does not filter on ``presentedAt``: the\ncaller already holds the id (it came from a ref block in a message that was\ndelivered to them), so there is nothing here to enumerate.","operationId":"get_artifact_api_v1alpha1_assistant__assistant_kind__conversations__conversation_id__artifacts__artifact_id__get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"artifact_id","in":"path","required":true,"schema":{"type":"string","title":"Artifact Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArtifactHead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/{assistant_kind}/conversations/{conversation_id}/artifacts/{artifact_id}/versions":{"get":{"tags":["v1alpha1"],"summary":"List Artifact Versions","description":"One artifact's history, newest first. MILESTONES BY DEFAULT.\n\nA milestone is not a stored flag. It is derived, per request, from the two\nfacts each version's writer recorded — its ``origin`` and its ``causedBy`` —\nby the rule in ``anvil.assistant.artifacts.version_origin``: the last\nversion of each turn, plus every restore, plus every member edit, plus the\nhead, minus the autosaves. So a UI's back button steps through states a\nmember would recognise, and ``include_all=true`` still offers the raw chain.\n\nPAGINATED FROM THE START, and keyset rather than offset: ``before_ordinal``\nis a boundary VALUE. Nothing counts versions or assumes their ordinals are\ncontiguous — a chain that a retention sweep has left with holes pages\nexactly like one that has none.\n\nAn artifact this conversation cannot see returns an EMPTY page, not a 404 —\nidentical to an artifact with no versions. That is the same posture as the\nrest of this router by a different mechanism: there is nothing to refuse\nhere, because there is nothing to distinguish.","operationId":"list_artifact_versions_api_v1alpha1_assistant__assistant_kind__conversations__conversation_id__artifacts__artifact_id__versions_get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"artifact_id","in":"path","required":true,"schema":{"type":"string","title":"Artifact Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"include_all","in":"query","required":false,"schema":{"type":"boolean","description":"Return every version instead of the milestone view. Defaults to false: the default view hides autosaves and the intermediate states of a turn, so a back button steps through meaningful states. Set this when you want fine-grained undo.","default":false,"title":"Include All"},"description":"Return every version instead of the milestone view. Defaults to false: the default view hides autosaves and the intermediate states of a turn, so a back button steps through meaningful states. Set this when you want fine-grained undo."},{"name":"limit","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":200,"minimum":1},{"type":"null"}],"description":"Page size. Clamped server-side; omit for the default.","title":"Limit"},"description":"Page size. Clamped server-side; omit for the default."},{"name":"before_ordinal","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Cursor: return only versions below this ordinal. Pass back the nextBeforeOrdinal from the previous page, and treat the value as opaque — it is a boundary, not a count.","title":"Before Ordinal"},"description":"Cursor: return only versions below this ordinal. Pass back the nextBeforeOrdinal from the previous page, and treat the value as opaque — it is a boundary, not a count."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArtifactVersionsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/{assistant_kind}/conversations/{conversation_id}/artifacts/{artifact_id}/versions/{version_id}":{"get":{"tags":["v1alpha1"],"summary":"Get Artifact Version","description":"One specific version of an artifact, with its payload.\n\nThis is where a history UI gets the content to show once someone picks an\nentry from the list — which is why the list itself carries no payloads.\n\nEvery unusable combination is the same 404: an unknown version, a version of\na different artifact, an artifact of a different conversation, and an id\nthat is not a UUID at all.","operationId":"get_artifact_version_api_v1alpha1_assistant__assistant_kind__conversations__conversation_id__artifacts__artifact_id__versions__version_id__get","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"artifact_id","in":"path","required":true,"schema":{"type":"string","title":"Artifact Id"}},{"name":"version_id","in":"path","required":true,"schema":{"type":"string","title":"Version Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArtifactVersionDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1alpha1/assistant/{assistant_kind}/conversations/{conversation_id}/artifacts/{artifact_id}/versions/{version_id}/restore":{"post":{"tags":["v1alpha1"],"summary":"Restore Artifact Version","description":"Append a copy of an older version as the artifact's NEW head.\n\n**201, because a restore CREATES a version.** Nothing is rewound and no\nversion is mutated: the chain keeps everything it had, the restored content\narrives as a new head recording ``origin=\"restore\"`` and the version it came\nfrom, and ``artifact/updated`` fires by itself because this is, mechanically,\nan ordinary append. Redo is therefore free — restore the version you undid\nfrom.\n\nDoing this SERVER-side rather than having the client fetch version 3 and\npost it back is not merely an optimisation. The round trip would carry a\npotentially large payload through a client that only wants to point at it,\nand — the part that cannot be recovered later — the resulting version would\nbe indistinguishable from an ordinary edit that happens to contain old text.\n\n**409 on a lost race.** The append is compare-and-swap, so a restore racing\nanother edit fails rather than clobbering it. Send\n``expectedHeadVersionId`` to assert the head the UI actually rendered; the\n409 body carries the head that won, so the client can re-read and decide.\n\n**404 covers everything a caller may not see**, and is byte-identical to the\nreads' 404: this endpoint must not become an existence oracle for another\nconversation's artifacts just because it happens to be a write.","operationId":"restore_artifact_version_api_v1alpha1_assistant__assistant_kind__conversations__conversation_id__artifacts__artifact_id__versions__version_id__restore_post","security":[{"AnvilBearerToken":[]}],"parameters":[{"name":"artifact_id","in":"path","required":true,"schema":{"type":"string","title":"Artifact Id"}},{"name":"version_id","in":"path","required":true,"schema":{"type":"string","title":"Version Id"}},{"name":"conversation_id","in":"path","required":true,"schema":{"type":"string","title":"Conversation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RestoreArtifactVersionRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ArtifactHead"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"AgencyInfo":{"properties":{"name":{"type":"string","title":"Name"},"logoUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logourl"},"logo2Url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo2Url"},"largeBannerUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Largebannerurl"},"brandColor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brandcolor"},"foregroundColor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Foregroundcolor"},"agents":{"items":{"$ref":"#/components/schemas/PropertyListingAgent"},"type":"array","title":"Agents"}},"type":"object","required":["name"],"title":"AgencyInfo","description":"Agency information for a property listing.\n\nExtends VendorInfo with brand colours and agent list.\nFields: name, logo_url (from VendorInfo), logo2_url, large_banner_url, brand_color, foreground_color, agents."},"ArtifactHead":{"properties":{"id":{"type":"string","title":"Id"},"uri":{"type":"string","title":"Uri"},"artifactType":{"type":"string","title":"Artifacttype"},"provenance":{"type":"string","title":"Provenance"},"payload":{"additionalProperties":true,"type":"object","title":"Payload"},"versionId":{"type":"string","title":"Versionid"},"versionOrdinal":{"type":"integer","title":"Versionordinal"},"parentVersionId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parentversionid"},"author":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author"},"nameHint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Namehint"},"presentedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Presentedat"},"lastChangedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Lastchangedat"}},"type":"object","required":["id","uri","artifactType","provenance","payload","versionId","versionOrdinal"],"title":"ArtifactHead","description":"The current state of one artifact — identity, chain position, payload.\n\nBuilt from an ``anvil.assistant.artifacts.models.Artifact`` via\n:meth:`from_artifact`, never constructed from wire input: every field here\nis server-derived.\n\nNaming follows the AIS-1171 split. It is Artifact + ArtifactVersion: ``id``\nis the ARTIFACT's identity (what the old ``document_id`` meant — the word\n\"document\" is retired), and the ``version*`` fields describe the one version\nbeing reported, which for these endpoints is always the head.\n\n``artifactType`` is a registry slug (``\"search-results\"``), NOT a media\ntype — Anvil deliberately leaves the name ``contentType`` free for a real\nmedia type later. ``provenance`` (``\"snapshot\"`` | ``\"authored\"``) is\nDERIVED from it, never stored, and tells a client whether the artifact is a\ncopy of upstream-mastered data or something the agent wrote from the\nmember's steering.\n\n``presentedAt`` is the ONLY publication fact, and it is artifact-level.\nThere is no ``published`` flag and no per-version ``presentedAt``: once an\nartifact is presented, every version of it is live and a read serves the\nhead."},"ArtifactVersionDetail":{"properties":{"artifactId":{"type":"string","title":"Artifactid"},"uri":{"type":"string","title":"Uri"},"artifactType":{"type":"string","title":"Artifacttype"},"provenance":{"type":"string","enum":["snapshot","authored"],"title":"Provenance"},"payload":{"additionalProperties":true,"type":"object","title":"Payload"},"versionId":{"type":"string","title":"Versionid"},"versionOrdinal":{"type":"integer","title":"Versionordinal"},"parentVersionId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parentversionid"},"origin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Origin"},"causedBy":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Causedby"},"turnFinal":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Turnfinal"},"author":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author"},"nameHint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Namehint"},"restoredFromVersionId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Restoredfromversionid"},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Createdat"},"presentedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Presentedat"}},"type":"object","required":["artifactId","uri","artifactType","provenance","payload","versionId","versionOrdinal"],"title":"ArtifactVersionDetail","description":"One specific version, WITH its payload.\n\nDeliberately not :class:`ArtifactHead`. That model's name and its\n``lastChangedAt`` field both assert \"this is the artifact's current state\",\nwhich is precisely what a historical version is not — reusing it would have\nevery consumer reading an old payload through a model that claims it is\ncurrent. The artifact-level facts (``uri``, ``artifactType``,\n``provenance``, ``presentedAt``) are repeated here because they describe the\nartifact this version belongs to and do not vary between its versions."},"ArtifactVersionSummary":{"properties":{"artifactId":{"type":"string","title":"Artifactid"},"versionId":{"type":"string","title":"Versionid"},"versionOrdinal":{"type":"integer","title":"Versionordinal"},"parentVersionId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parentversionid"},"origin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Origin"},"causedBy":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Causedby"},"turnFinal":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Turnfinal"},"turnOutcome":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Turnoutcome"},"author":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author"},"nameHint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Namehint"},"restoredFromVersionId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Restoredfromversionid"},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Createdat"},"isHead":{"type":"boolean","title":"Ishead","default":false},"isMilestone":{"type":"boolean","title":"Ismilestone","default":false}},"type":"object","required":["artifactId","versionId","versionOrdinal"],"title":"ArtifactVersionSummary","description":"One entry in an artifact's history. NO PAYLOAD — see the domain model.\n\nA history list exists so someone can CHOOSE a version; only the chosen one's\ncontent is needed, and it comes from ``GET .../versions/{versionId}``.\nShipping every payload with the list would make the response grow with the\nlength of the session for nobody's benefit.\n\n``origin`` says WHY the version exists — ``agent_edit``, ``member_edit``,\n``restore``, ``autosave``, ``legacy``, or ``null`` for a row written before\nthe cause could be recorded. ``author`` says WHO wrote it. Together they are\nwhat lets a UI label \"your edit\" against \"the assistant's edit\", which is\nthe entire point of an undo that spans both.\n\n``isMilestone`` is reported even when the caller asked for the full history,\nso a client can render the noise differently instead of re-deriving the\nrule. There is no stored flag behind it — it is computed per request.\n\n``turnFinal`` is the writer's own DECLARATION that this was its last write\nof the turn (``true`` terminal, ``false`` explicitly partial, ``null`` not\ndeclared). ``turnOutcome`` is what that means for the turn this version\nrepresents, and it is the field a UI should read before calling anything a\nfinished state:\n\n- ``\"settled\"`` — a version in this turn declared itself terminal. It ended\n  on purpose and this is its finished content.\n- ``\"abandoned\"`` — the writer participates in the protocol and never said\n  it was done, so the turn STOPPED rather than ended: an error, a timeout,\n  a usage-limit abort. What you are looking at is the last thing that\n  happened, not a finished state, and it is worth saying so in the UI.\n- ``\"unknown\"`` — nothing in this turn declared anything, so there is\n  nothing to go on. Legacy versions and writers that do not participate land\n  here, and they are NOT ``abandoned`` — \"we cannot tell\" is a different\n  claim from \"it stopped\".\n- ``null`` — this version represents no turn (a direct edit, a restore, or a\n  version some other version settles for).\n\nEVERY ID HERE IS ``.hex`` — 32 characters, no dashes — matching every other\nidentifier Anvil puts on the wire, and matching the tail of the ``art://``\nURI so a client can compare the two. Either form is still ACCEPTED on input.\n\n``versionOrdinal`` ORDERS; it does not COUNT. Ordinals may have holes (a\nretention sweep may remove interior versions), so a client must not reach a\nneighbouring version by adding or subtracting one, nor read the ordinal as a\nposition in the list."},"ArtifactVersionsResponse":{"properties":{"artifactId":{"type":"string","title":"Artifactid"},"view":{"type":"string","title":"View"},"versions":{"items":{"$ref":"#/components/schemas/ArtifactVersionSummary"},"type":"array","title":"Versions"},"nextBeforeOrdinal":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Nextbeforeordinal"}},"type":"object","required":["artifactId","view","versions"],"title":"ArtifactVersionsResponse","description":"Response body for ``GET /conversations/{id}/artifacts/{id}/versions``.\n\n``nextBeforeOrdinal`` is the cursor: pass it back as the ``before_ordinal``\nquery parameter for\nthe next page, and stop when it is ``null``. (Response FIELDS are camelCase;\nquery PARAMETERS are snake_case, as everywhere in this service.) Treat the\nvalue as OPAQUE. It happens\nto be an ordinal today, and a client that does arithmetic on it — or that\ncounts pages, or assumes ``n`` pages of ``limit`` covers the history — is\nwrong the day a retention sweep leaves a hole.\n\n``view`` echoes which history was served (``\"milestones\"`` or ``\"all\"``), so\nthe response is self-describing rather than only meaningful alongside the\nrequest that produced it."},"AssistantAuthor":{"properties":{"authorType":{"type":"string","const":"assistant","title":"Authortype","default":"assistant"},"assistantId":{"type":"string","title":"Assistantid"},"assistantVersion":{"type":"string","title":"Assistantversion"},"parametersVersion":{"type":"string","title":"Parametersversion"},"contextId":{"type":"string","title":"Contextid"}},"type":"object","required":["assistantId","assistantVersion","parametersVersion","contextId"],"title":"AssistantAuthor","description":"Domain AssistantAuthor with camelCase wire aliases."},"AssistantParticipant":{"properties":{"participantType":{"type":"string","const":"assistant","title":"Participanttype","default":"assistant"},"assistantId":{"type":"string","title":"Assistantid"},"displayName":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Displayname"}},"type":"object","required":["assistantId"],"title":"AssistantParticipant","description":"Domain AssistantParticipant with camelCase wire aliases."},"AssistantStatus":{"type":"string","enum":["idle","running","error"],"title":"AssistantStatus"},"AvailableDateField":{"properties":{"name":{"type":"string","title":"Name"},"type":{"type":"string","const":"available_date","title":"Type","default":"available_date"},"isoDate":{"type":"string","title":"Isodate"},"display":{"type":"string","title":"Display"}},"type":"object","required":["name","isoDate","display"],"title":"AvailableDateField","description":"Rental availability date with ISO date and a display string."},"BathroomsField":{"properties":{"name":{"type":"string","title":"Name"},"type":{"type":"string","const":"bathrooms","title":"Type","default":"bathrooms"},"value":{"type":"integer","title":"Value"}},"type":"object","required":["name","value"],"title":"BathroomsField","description":"Number of bathrooms."},"BedroomsField":{"properties":{"name":{"type":"string","title":"Name"},"type":{"type":"string","const":"bedrooms","title":"Type","default":"bedrooms"},"value":{"type":"integer","title":"Value"}},"type":"object","required":["name","value"],"title":"BedroomsField","description":"Number of bedrooms."},"BlockquoteBlock":{"properties":{"children":{"items":{"oneOf":[{"$ref":"#/components/schemas/PlainTextBlock"},{"$ref":"#/components/schemas/DividerBlock"},{"$ref":"#/components/schemas/HeadingBlock"},{"$ref":"#/components/schemas/ParagraphBlock"},{"$ref":"#/components/schemas/CodeBlock"},{"$ref":"#/components/schemas/BlockquoteBlock"},{"$ref":"#/components/schemas/ListBlock"}],"discriminator":{"propertyName":"type","mapping":{"blockquote":"#/components/schemas/BlockquoteBlock","code_block":"#/components/schemas/CodeBlock","divider":"#/components/schemas/DividerBlock","heading":"#/components/schemas/HeadingBlock","list":"#/components/schemas/ListBlock","paragraph":"#/components/schemas/ParagraphBlock","plain_text":"#/components/schemas/PlainTextBlock"}}},"type":"array","title":"Children"},"type":{"type":"string","const":"blockquote","title":"Type","default":"blockquote"},"version":{"type":"string","const":"1","title":"Version","default":"1"}},"type":"object","title":"BlockquoteBlock","description":"Block-level quotation (``> …``).\n\nChildren are simple blocks only — a blockquote may contain paragraphs,\ncode blocks, nested blockquotes, or lists.  Complex/custom block types\n(choices, images, listing summaries, search results) are not valid children;\nthey must appear at the top level only.  The markdown-syntax fallback (each\nline prefixed by ``\"> \"``) is produced at downgrade time by\n:func:`render_block`.\n\n:ivar children: Simple blocks inside the quotation (typed as ``ChildBlock``\n    to enforce the restriction at schema validation time)."},"BoolField":{"properties":{"name":{"type":"string","title":"Name"},"type":{"type":"string","const":"bool","title":"Type","default":"bool"},"value":{"type":"boolean","title":"Value"}},"type":"object","required":["name","value"],"title":"BoolField","description":"Generic boolean field."},"ChoiceItem":{"properties":{"id":{"type":"string","title":"Id"},"label":{"type":"string","title":"Label"}},"type":"object","required":["id","label"],"title":"ChoiceItem","description":"A single selectable option within a :class:`ChoicesBlock`.\n\n:ivar id:    Stable identifier (e.g. ``\"choice_0\"``), derived from the item's\n    index in the original ``items`` list.\n:ivar label: Display text shown on the chip button."},"ChoicesBlock":{"properties":{"type":{"type":"string","const":"choices","title":"Type","default":"choices"},"version":{"type":"string","const":"1","title":"Version","default":"1"},"label":{"type":"string","title":"Label"},"selectionType":{"type":"string","enum":["single","multi"],"title":"Selectiontype","default":"single"},"choices":{"items":{"$ref":"#/components/schemas/ChoiceItem"},"type":"array","title":"Choices"}},"type":"object","required":["label"],"title":"ChoicesBlock","description":"A labelled group of selectable chip buttons.\n\nParsed from the inline tag::\n\n    <choices label=\"Favourite colour\" type=\"single\" items='[\"Red\",\"Blue\",\"Green\"]' />\n\nThe model writes choices inline (no artifact fetch required).  Clients that\ndon't support ``choices:1`` receive a markdown fallback rendered by\n:func:`render_block`.\n\n:ivar label:          Human-readable question / prompt label shown above the chips.\n:ivar selection_type: ``\"single\"`` — auto-fires after a 3 s countdown;\n                      ``\"multi\"`` — accumulates selections, user fires manually.\n:ivar choices:        Ordered list of :class:`ChoiceItem` instances."},"CodeBlock":{"properties":{"type":{"type":"string","const":"code_block","title":"Type","default":"code_block"},"version":{"type":"string","const":"1","title":"Version","default":"1"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"},"code":{"type":"string","title":"Code"}},"type":"object","required":["code"],"title":"CodeBlock","description":"Fenced or indented code block.\n\nNo inline formatting is parsed inside a code block — the content is raw\nsource text.\n\n:ivar language: Language hint from the opening fence (e.g. ``\"python\"``), or\n    ``None`` when no hint was given (bare `` ``` ``).\n:ivar code: Raw source text of the block, including internal newlines."},"CodeSpanNode":{"properties":{"type":{"type":"string","const":"code_span","title":"Type","default":"code_span"},"raw":{"type":"string","title":"Raw"}},"type":"object","required":["raw"],"title":"CodeSpanNode","description":"Inline code span — `` `code` ``.\n\n``raw`` is the raw code content, stripped of the surrounding backticks.\nNo further parsing is applied inside a code span."},"CompleteFileResponse":{"properties":{"fileId":{"type":"string","title":"Fileid"},"ref":{"type":"string","title":"Ref"},"state":{"type":"string","title":"State"},"sizeBytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Sizebytes"},"caption":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Caption"}},"type":"object","required":["fileId","ref","state"],"title":"CompleteFileResponse"},"ConversationArtifactsResponse":{"properties":{"artifacts":{"items":{"$ref":"#/components/schemas/ArtifactHead"},"type":"array","title":"Artifacts"}},"type":"object","required":["artifacts"],"title":"ConversationArtifactsResponse","description":"Response body for ``GET /conversations/{id}/artifacts`` — the current heads.\n\nAn object rather than a bare list: a top-level array leaves nowhere to add\npagination or a total later without a breaking change."},"ConversationListResponse":{"properties":{"conversations":{"items":{"$ref":"#/components/schemas/ConversationSummary"},"type":"array","title":"Conversations"},"total":{"type":"integer","title":"Total","default":0},"page":{"type":"integer","title":"Page","default":1},"perPage":{"type":"integer","title":"Perpage","default":50}},"type":"object","required":["conversations"],"title":"ConversationListResponse","description":"Domain ConversationListResponse with camelCase wire aliases."},"ConversationSummary":{"properties":{"id":{"type":"string","title":"Id"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"createdAt":{"type":"string","format":"date-time","title":"Createdat"},"updatedAt":{"type":"string","format":"date-time","title":"Updatedat"},"lastMessageAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Lastmessageat"},"isShared":{"type":"boolean","title":"Isshared"},"participants":{"items":{"oneOf":[{"$ref":"#/components/schemas/MemberParticipant"},{"$ref":"#/components/schemas/AssistantParticipant"}],"discriminator":{"propertyName":"participantType","mapping":{"assistant":"#/components/schemas/AssistantParticipant","member":"#/components/schemas/MemberParticipant"}}},"type":"array","title":"Participants"},"assistantStatus":{"anyOf":[{"$ref":"#/components/schemas/AssistantStatus"},{"type":"null"}]},"messageCount":{"type":"integer","title":"Messagecount","default":0},"referrer":{"anyOf":[{"oneOf":[{"$ref":"#/components/schemas/DirectReferrer"},{"$ref":"#/components/schemas/HomepageReferrer"},{"$ref":"#/components/schemas/PropertyHomepageReferrer"},{"$ref":"#/components/schemas/PropertyDeepLinkReferrer"},{"$ref":"#/components/schemas/SearchFormReferrer"},{"$ref":"#/components/schemas/SearchResultsReferrer"},{"$ref":"#/components/schemas/ListingReferrer"}],"discriminator":{"propertyName":"sourceType","mapping":{"direct":"#/components/schemas/DirectReferrer","homepage":"#/components/schemas/HomepageReferrer","listing":"#/components/schemas/ListingReferrer","property_deep_link":"#/components/schemas/PropertyDeepLinkReferrer","property_homepage":"#/components/schemas/PropertyHomepageReferrer","search_form":"#/components/schemas/SearchFormReferrer","search_results":"#/components/schemas/SearchResultsReferrer"}}},{"type":"null"}],"title":"Referrer"},"capabilities":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Capabilities"},"experimentMetadata":{"anyOf":[{"$ref":"#/components/schemas/ExperimentMetadata"},{"type":"null"}]},"kind":{"type":"string","title":"Kind","default":"trademe"}},"type":"object","required":["id","createdAt","updatedAt","isShared","participants"],"title":"ConversationSummary","description":"Domain ConversationSummary with camelCase wire aliases."},"CreateConversationBody":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"referrer":{"anyOf":[{"oneOf":[{"$ref":"#/components/schemas/DirectReferrer"},{"$ref":"#/components/schemas/HomepageReferrer"},{"$ref":"#/components/schemas/PropertyHomepageReferrer"},{"$ref":"#/components/schemas/PropertyDeepLinkReferrer"},{"$ref":"#/components/schemas/SearchFormReferrer"},{"$ref":"#/components/schemas/SearchResultsReferrer"},{"$ref":"#/components/schemas/ListingReferrer"}],"discriminator":{"propertyName":"sourceType","mapping":{"direct":"#/components/schemas/DirectReferrer","homepage":"#/components/schemas/HomepageReferrer","listing":"#/components/schemas/ListingReferrer","property_deep_link":"#/components/schemas/PropertyDeepLinkReferrer","property_homepage":"#/components/schemas/PropertyHomepageReferrer","search_form":"#/components/schemas/SearchFormReferrer","search_results":"#/components/schemas/SearchResultsReferrer"}}},{"type":"null"}],"title":"Referrer"},"invitees":{"items":{"type":"string"},"type":"array","title":"Invitees","default":[]}},"type":"object","title":"CreateConversationBody","description":"Request body for POST /conversations. Accepts camelCase or snake_case keys."},"CreateFileBody":{"properties":{"contentType":{"type":"string","title":"Contenttype"}},"type":"object","required":["contentType"],"title":"CreateFileBody"},"CreateFileResponse":{"properties":{"fileId":{"type":"string","title":"Fileid"},"ref":{"type":"string","title":"Ref"},"putUrl":{"type":"string","title":"Puturl"},"expiresAt":{"type":"string","format":"date-time","title":"Expiresat"}},"type":"object","required":["fileId","ref","putUrl","expiresAt"],"title":"CreateFileResponse"},"CrisisResourceBlock":{"properties":{"type":{"type":"string","const":"crisis_resource","title":"Type","default":"crisis_resource"},"version":{"type":"string","const":"1","title":"Version","default":"1"},"name":{"type":"string","title":"Name"},"text":{"type":"string","title":"Text"},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"sms":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sms"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"artifactUri":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Artifacturi"}},"type":"object","required":["name","text"],"title":"CrisisResourceBlock"},"DirectReferrer":{"properties":{"sourceType":{"type":"string","const":"direct","title":"Sourcetype","default":"direct"}},"type":"object","title":"DirectReferrer","description":"Domain DirectReferrer with camelCase wire aliases."},"DividerBlock":{"properties":{"type":{"type":"string","const":"divider","title":"Type","default":"divider"},"version":{"type":"string","const":"1","title":"Version","default":"1"}},"type":"object","title":"DividerBlock","description":"Horizontal rule / thematic break."},"DownloadFileResponse":{"properties":{"getUrl":{"type":"string","title":"Geturl"},"expiresAt":{"type":"string","format":"date-time","title":"Expiresat"}},"type":"object","required":["getUrl","expiresAt"],"title":"DownloadFileResponse"},"EmphasisNode":{"properties":{"type":{"type":"string","const":"emphasis","title":"Type","default":"emphasis"},"children":{"items":{"oneOf":[{"$ref":"#/components/schemas/TextNode"},{"$ref":"#/components/schemas/CodeSpanNode"},{"$ref":"#/components/schemas/SoftBreakNode"},{"$ref":"#/components/schemas/HardBreakNode"},{"$ref":"#/components/schemas/StrongNode"},{"$ref":"#/components/schemas/EmphasisNode"},{"$ref":"#/components/schemas/StrikethroughNode"},{"$ref":"#/components/schemas/LinkNode"}],"discriminator":{"propertyName":"type","mapping":{"code_span":"#/components/schemas/CodeSpanNode","emphasis":"#/components/schemas/EmphasisNode","hard_break":"#/components/schemas/HardBreakNode","link":"#/components/schemas/LinkNode","soft_break":"#/components/schemas/SoftBreakNode","strikethrough":"#/components/schemas/StrikethroughNode","strong":"#/components/schemas/StrongNode","text":"#/components/schemas/TextNode"}}},"type":"array","title":"Children"}},"type":"object","required":["children"],"title":"EmphasisNode","description":"Italic text — ``_italic_`` or ``*italic*``.\n\nChildren may include any inline nodes, enabling constructs like\n``_italic **bold italic** italic_``."},"ExperimentMetadata":{"properties":{"isInStaffGroup":{"type":"boolean","title":"Isinstaffgroup","default":false},"servedModel":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Servedmodel"}},"additionalProperties":false,"type":"object","title":"ExperimentMetadata","description":"Domain ExperimentMetadata with camelCase wire aliases (AIS-1065).\n\nWithout this, the nested keys (is_in_staff_group / served_model) would appear\nsnake_case in the OpenAPI schema — every other nested model on ConversationSummary\n(referrer, participants) has the same gateway override."},"FileListResponse":{"properties":{"files":{"items":{"$ref":"#/components/schemas/FileResource"},"type":"array","title":"Files"},"hasMore":{"type":"boolean","title":"Hasmore"}},"type":"object","required":["files","hasMore"],"title":"FileListResponse"},"FileResource":{"properties":{"fileId":{"type":"string","title":"Fileid"},"ref":{"type":"string","title":"Ref"},"state":{"type":"string","title":"State"},"contentType":{"type":"string","title":"Contenttype"},"sizeBytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Sizebytes"},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Createdat"},"caption":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Caption"}},"type":"object","required":["fileId","ref","state","contentType"],"title":"FileResource","description":"Metadata for one file — the shape both ``GET /files`` (list) and\n``GET /files/{file_id}`` (single) return. Never carries a signed\nURL; resolving bytes is ``GET /files/{file_id}/content``'s job alone."},"FloorAreaField":{"properties":{"name":{"type":"string","title":"Name"},"type":{"type":"string","const":"floor_area","title":"Type","default":"floor_area"},"valueM2":{"type":"integer","title":"Valuem2"},"display":{"type":"string","title":"Display"}},"type":"object","required":["name","valueM2","display"],"title":"FloorAreaField","description":"Floor area with raw m² value and a display string."},"GeneralListingSummaryWithCoordinatesBlock":{"properties":{"latitude":{"type":"number","title":"Latitude"},"longitude":{"type":"number","title":"Longitude"},"listingId":{"type":"string","title":"Listingid"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"tva":{"$ref":"#/components/schemas/TemplatedViewAddress"},"isOnWatchlist":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Isonwatchlist"},"type":{"type":"string","const":"listing_summary_with_coordinates","title":"Type","default":"listing_summary_with_coordinates"},"version":{"type":"string","const":"1","title":"Version","default":"1"},"images":{"items":{"type":"string"},"type":"array","title":"Images"}},"type":"object","required":["latitude","longitude","listingId","tva"],"title":"GeneralListingSummaryWithCoordinatesBlock","description":"A generic listing summary block that includes geographic coordinates.\n\nUsed as a fallback for vertical-specific blocks that carry coordinates\nwhen the client does not support the vertical-specific type but does\nsupport ``listing_summary_with_coordinates:1``."},"GeographicLocation":{"properties":{"latitude":{"type":"number","title":"Latitude"},"longitude":{"type":"number","title":"Longitude"},"accuracy":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Accuracy"}},"type":"object","required":["latitude","longitude"],"title":"GeographicLocation","description":"Optional map-pin location for a listing card.\n\nA card has a full location or none — both coordinates are required within\nthe object, since a lone coordinate can't place a pin. Unlike\n:class:`CoordinatesFields`, the location itself is nullable on the block.\n\n:ivar accuracy: Trade Me's ``MapAccuracy`` geocoding indicator, as the integer\n    the API sends: 0 = None (unknown), 1 = Address, 2 = Suburb, 3 = Street,\n    4 = AdminPinpoint. Optional; ``None`` when the source doesn't supply one."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HardBreakNode":{"properties":{"type":{"type":"string","const":"hard_break","title":"Type","default":"hard_break"}},"type":"object","title":"HardBreakNode","description":"Hard line break: two trailing spaces or a backslash at end of line.\n\nAlways rendered as ``<br>`` (a visible line break within the paragraph).\nmistune calls this ``\"linebreak\"`` in its AST; we normalise to ``hard_break``\nhere."},"HeadingBlock":{"properties":{"type":{"type":"string","const":"heading","title":"Type","default":"heading"},"version":{"type":"string","const":"1","title":"Version","default":"1"},"level":{"type":"integer","title":"Level"},"inline":{"items":{"oneOf":[{"$ref":"#/components/schemas/TextNode"},{"$ref":"#/components/schemas/CodeSpanNode"},{"$ref":"#/components/schemas/SoftBreakNode"},{"$ref":"#/components/schemas/HardBreakNode"},{"$ref":"#/components/schemas/StrongNode"},{"$ref":"#/components/schemas/EmphasisNode"},{"$ref":"#/components/schemas/StrikethroughNode"},{"$ref":"#/components/schemas/LinkNode"}],"discriminator":{"propertyName":"type","mapping":{"code_span":"#/components/schemas/CodeSpanNode","emphasis":"#/components/schemas/EmphasisNode","hard_break":"#/components/schemas/HardBreakNode","link":"#/components/schemas/LinkNode","soft_break":"#/components/schemas/SoftBreakNode","strikethrough":"#/components/schemas/StrikethroughNode","strong":"#/components/schemas/StrongNode","text":"#/components/schemas/TextNode"}}},"type":"array","title":"Inline"}},"type":"object","required":["level"],"title":"HeadingBlock","description":"Markdown heading — ``# H1`` through ``###### H6``.\n\nCarries a rich inline tree for clients that render formatted headings.\nThe markdown-syntax fallback (ATX markers + inline syntax) is produced\nat downgrade time by :func:`render_block`.\n\n:ivar level:  Heading depth, 1 (largest) to 6 (smallest).\n:ivar inline: Full inline node tree, preserving formatting such as inline\n    code inside a heading."},"HomepageReferrer":{"properties":{"sourceType":{"type":"string","const":"homepage","title":"Sourcetype","default":"homepage"},"tva":{"$ref":"#/components/schemas/TemplatedViewAddress"}},"type":"object","required":["tva"],"title":"HomepageReferrer","description":"Domain HomepageReferrer with camelCase wire aliases."},"IconAttributeField":{"properties":{"key":{"type":"string","title":"Key"},"icon":{"type":"string","title":"Icon"},"displayValue":{"type":"string","title":"Displayvalue"},"rawValue":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Rawvalue"},"sortValue":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Sortvalue"}},"type":"object","required":["key","icon","displayValue"],"title":"IconAttributeField"},"ImageBlock":{"properties":{"type":{"type":"string","const":"image_block","title":"Type","default":"image_block"},"version":{"type":"string","const":"1","title":"Version","default":"1"},"url":{"type":"string","title":"Url"},"alt":{"type":"string","title":"Alt"},"width":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Width"},"height":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Height"},"aspectRatio":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Aspectratio"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"source":{"type":"string","enum":["trademe","user_provided","external","ai_generated"],"title":"Source","default":"external"}},"type":"object","required":["url","alt"],"title":"ImageBlock","description":"A standalone image block for any non-listing-photo image.\n\nUsed for inline-promoted images (markdown ``![alt](url)``), AI-generated\nimages, user-provided images, and external images.\n\nThe ``source`` field is server-set provenance — clients use it as a rendering\nhint (trust indicators, \"AI image\" labels, caching strategy).\n\n:ivar url:         Image URL (already sanitised — no query string or fragment).\n:ivar alt:         Alternative text description.\n:ivar width:       Optional pixel width.\n:ivar height:      Optional pixel height.\n:ivar aspect_ratio: Optional pre-computed aspect ratio.\n:ivar description: Extended description (AI prompt or richer alt text).\n:ivar source:      Server-set provenance:\n\n                   - ``\"trademe\"`` — URL on Trade Me infrastructure (CDN, assets).\n                   - ``\"user_provided\"`` — uploaded by the conversation participant.\n                   - ``\"external\"`` — not on Trade Me infrastructure.\n                   - ``\"ai_generated\"`` — AI-generated image."},"LandAreaField":{"properties":{"name":{"type":"string","title":"Name"},"type":{"type":"string","const":"land_area","title":"Type","default":"land_area"},"valueM2":{"type":"integer","title":"Valuem2"},"display":{"type":"string","title":"Display"}},"type":"object","required":["name","valueM2","display"],"title":"LandAreaField","description":"Land area with raw m² value and a display string."},"LinkAnnotation":{"properties":{"title":{"type":"string","title":"Title"},"summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary"},"imageUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Imageurl"}},"type":"object","required":["title"],"title":"LinkAnnotation","description":"Optional human-readable annotation attached to a TVA link."},"LinkNode":{"properties":{"type":{"type":"string","const":"link","title":"Type","default":"link"},"href":{"type":"string","title":"Href"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"children":{"items":{"oneOf":[{"$ref":"#/components/schemas/TextNode"},{"$ref":"#/components/schemas/CodeSpanNode"},{"$ref":"#/components/schemas/SoftBreakNode"},{"$ref":"#/components/schemas/HardBreakNode"},{"$ref":"#/components/schemas/StrongNode"},{"$ref":"#/components/schemas/EmphasisNode"},{"$ref":"#/components/schemas/StrikethroughNode"},{"$ref":"#/components/schemas/LinkNode"}],"discriminator":{"propertyName":"type","mapping":{"code_span":"#/components/schemas/CodeSpanNode","emphasis":"#/components/schemas/EmphasisNode","hard_break":"#/components/schemas/HardBreakNode","link":"#/components/schemas/LinkNode","soft_break":"#/components/schemas/SoftBreakNode","strikethrough":"#/components/schemas/StrikethroughNode","strong":"#/components/schemas/StrongNode","text":"#/components/schemas/TextNode"}}},"type":"array","title":"Children"}},"type":"object","required":["href","children"],"title":"LinkNode","description":"Hyperlink — ``[label](url \"title\")``.\n\n``children`` carry the link label, which may itself contain formatted\ninline nodes (e.g. ``[**bold link**](url)`` → a ``StrongNode`` child).\n\n:ivar href:     The destination URL.\n:ivar title:    Optional tooltip text (the quoted string after the URL).\n:ivar children: Inline nodes forming the visible link label."},"ListBlock":{"properties":{"children":{"items":{"$ref":"#/components/schemas/ListItemBlock"},"type":"array","title":"Children"},"type":{"type":"string","const":"list","title":"Type","default":"list"},"version":{"type":"string","const":"1","title":"Version","default":"1"},"ordered":{"type":"boolean","title":"Ordered"}},"type":"object","required":["ordered"],"title":"ListBlock","description":"An ordered (``1. …``) or unordered (``- …``) list.\n\nChildren are exclusively ``ListItemBlock`` instances.  Nesting is\nrepresented by a ``ListBlock`` child inside a ``ListItemBlock``.  The\nmarkdown-syntax fallback (bullet-prefixed items) is produced at downgrade\ntime by :func:`render_block`.\n\n:ivar ordered:  ``True`` for ``<ol>`` (numbered), ``False`` for ``<ul>``\n    (bulleted).\n:ivar children: ``ListItemBlock`` instances in document order."},"ListItemBlock":{"properties":{"children":{"items":{"oneOf":[{"$ref":"#/components/schemas/PlainTextBlock"},{"$ref":"#/components/schemas/DividerBlock"},{"$ref":"#/components/schemas/HeadingBlock"},{"$ref":"#/components/schemas/ParagraphBlock"},{"$ref":"#/components/schemas/CodeBlock"},{"$ref":"#/components/schemas/BlockquoteBlock"},{"$ref":"#/components/schemas/ListBlock"}],"discriminator":{"propertyName":"type","mapping":{"blockquote":"#/components/schemas/BlockquoteBlock","code_block":"#/components/schemas/CodeBlock","divider":"#/components/schemas/DividerBlock","heading":"#/components/schemas/HeadingBlock","list":"#/components/schemas/ListBlock","paragraph":"#/components/schemas/ParagraphBlock","plain_text":"#/components/schemas/PlainTextBlock"}}},"type":"array","title":"Children"},"type":{"type":"string","const":"list_item","title":"Type","default":"list_item"},"version":{"type":"string","const":"1","title":"Version","default":"1"}},"type":"object","title":"ListItemBlock","description":"A single item in an ordered or unordered list.\n\nChildren are simple blocks only — for tight lists (no blank lines between\nitems) this is typically a ``ParagraphBlock``; for loose lists it may\ninclude additional blocks, nested lists, or blockquotes.  Complex/custom\nblock types (choices, images, listing summaries, search results) are not\nvalid children.  The markdown-syntax fallback (without the leading bullet\nmarker) is produced at downgrade time by :func:`render_block`.\n\n:ivar children: Simple blocks inside the item (typed as ``ChildBlock``\n    to enforce the restriction at schema validation time)."},"ListingAddressField":{"properties":{"displayValue":{"type":"string","title":"Displayvalue"},"rawValue":{"$ref":"#/components/schemas/ListingAddressValue"}},"type":"object","required":["displayValue","rawValue"],"title":"ListingAddressField"},"ListingAddressValue":{"properties":{"street":{"type":"string","title":"Street"},"suburb":{"type":"string","title":"Suburb"},"district":{"type":"string","title":"District"}},"type":"object","required":["street","suburb","district"],"title":"ListingAddressValue"},"ListingPhotoBlock":{"properties":{"type":{"type":"string","const":"listing_photo","title":"Type","default":"listing_photo"},"version":{"type":"string","const":"1","title":"Version","default":"1"},"photoId":{"type":"string","minLength":1,"title":"Photoid"},"listingId":{"type":"string","minLength":1,"title":"Listingid"},"photoIndex":{"type":"integer","title":"Photoindex"},"urls":{"additionalProperties":{"type":"string"},"type":"object","title":"Urls"},"originalWidth":{"type":"integer","exclusiveMinimum":0.0,"title":"Originalwidth"},"originalHeight":{"type":"integer","exclusiveMinimum":0.0,"title":"Originalheight"},"alt":{"type":"string","title":"Alt","default":""}},"type":"object","required":["photoId","listingId","photoIndex","urls","originalWidth","originalHeight"],"title":"ListingPhotoBlock","description":"A Trade Me listing photo with structured metadata.\n\nOnly emitted from listing data (e.g. ``PropertyListingDetails.photos``),\nnever from inline markdown promotion.\n\n:ivar photo_id:        Trade Me photo identifier (the ``Key`` field).\n:ivar listing_id:      Listing this photo belongs to.\n:ivar photo_index:     Zero-based position in the photo gallery.\n:ivar urls:            Named size map (``Thumbnail``, ``List``, ``Medium``,\n                       ``Gallery``, ``Large``, ``FullSize``, ``PlusSize``).\n:ivar original_width:  Pixel width of the original image (must be > 0).\n:ivar original_height: Pixel height of the original image (must be > 0).\n:ivar alt:             Alternative text description."},"ListingRefBlock":{"properties":{"type":{"type":"string","const":"listing_ref","title":"Type","default":"listing_ref"},"version":{"type":"string","const":"1","title":"Version","default":"1"},"ref":{"type":"string","minLength":20,"title":"Ref"}},"type":"object","required":["ref"],"title":"ListingRefBlock","description":"A reference to a Trade Me listing the member is asking about.\n\n:ivar ref: ``trademe://listings/<listing_id>`` — resolved at send time and\n    fetched by an agent tool, never expanded into listing fields here."},"ListingReferrer":{"properties":{"sourceType":{"type":"string","const":"listing","title":"Sourcetype","default":"listing"},"listingId":{"type":"string","title":"Listingid"},"listingSummary":{"anyOf":[{"$ref":"#/components/schemas/ListingSummary"},{"type":"null"}]}},"type":"object","required":["listingId"],"title":"ListingReferrer","description":"Domain ListingReferrer with camelCase wire aliases."},"ListingSummary":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"priceDisplay":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pricedisplay"},"address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Address"},"suburb":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Suburb"},"propertyType":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Propertytype"},"bedrooms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Bedrooms"},"bathrooms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Bathrooms"},"parking":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Parking"},"floorArea":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Floorarea"},"landArea":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Landarea"},"tva":{"anyOf":[{"$ref":"#/components/schemas/TemplatedViewAddress"},{"type":"null"}]}},"type":"object","title":"ListingSummary","description":"Domain ListingSummary with camelCase wire aliases."},"ListingSummaryBlock":{"properties":{"listingId":{"type":"string","title":"Listingid"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"tva":{"$ref":"#/components/schemas/TemplatedViewAddress"},"isOnWatchlist":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Isonwatchlist"},"type":{"type":"string","const":"listing_summary","title":"Type","default":"listing_summary"},"version":{"type":"string","const":"1","title":"Version","default":"1"},"images":{"items":{"type":"string"},"type":"array","title":"Images"},"imagesEnabled":{"type":"boolean","title":"Imagesenabled","default":true},"fields":{"items":{"oneOf":[{"$ref":"#/components/schemas/PriceField"},{"$ref":"#/components/schemas/WeeklyRentField"},{"$ref":"#/components/schemas/BedroomsField"},{"$ref":"#/components/schemas/BathroomsField"},{"$ref":"#/components/schemas/LandAreaField"},{"$ref":"#/components/schemas/FloorAreaField"},{"$ref":"#/components/schemas/PropertyTypeField"},{"$ref":"#/components/schemas/AvailableDateField"},{"$ref":"#/components/schemas/TextField"},{"$ref":"#/components/schemas/NumberField"},{"$ref":"#/components/schemas/MoneyField"},{"$ref":"#/components/schemas/BoolField"}],"discriminator":{"propertyName":"type","mapping":{"available_date":"#/components/schemas/AvailableDateField","bathrooms":"#/components/schemas/BathroomsField","bedrooms":"#/components/schemas/BedroomsField","bool":"#/components/schemas/BoolField","floor_area":"#/components/schemas/FloorAreaField","land_area":"#/components/schemas/LandAreaField","money":"#/components/schemas/MoneyField","number":"#/components/schemas/NumberField","price":"#/components/schemas/PriceField","property_type":"#/components/schemas/PropertyTypeField","text":"#/components/schemas/TextField","weekly_rent":"#/components/schemas/WeeklyRentField"}}},"type":"array","title":"Fields"}},"type":"object","required":["listingId","tva"],"title":"ListingSummaryBlock","description":"Configuration-driven listing summary. The server controls which fields\nappear and in what order via the ``fields`` list."},"MarketplaceListingSummaryBlock":{"properties":{"listingId":{"type":"string","title":"Listingid"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"tva":{"$ref":"#/components/schemas/TemplatedViewAddress"},"isOnWatchlist":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Isonwatchlist"},"type":{"type":"string","const":"marketplace_listing_summary","title":"Type","default":"marketplace_listing_summary"},"version":{"type":"string","const":"1","title":"Version","default":"1"},"images":{"items":{"type":"string"},"type":"array","title":"Images"},"region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Region"},"closesAt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Closesat"},"reservePrice":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reserveprice"},"buyNowPrice":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Buynowprice"},"isBuyNow":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Isbuynow"},"condition":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Condition"},"shipping":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Shipping"}},"type":"object","required":["listingId","tva"],"title":"MarketplaceListingSummaryBlock","description":"Vertical-specific listing summary for marketplace listings (books, clothing, general).\n\nVersion \"1\". Clients not supporting (marketplace_listing_summary, 1) receive\na downgraded listing_summary:1 block via the adapter chain."},"MemberAuthor":{"properties":{"authorType":{"type":"string","const":"member","title":"Authortype","default":"member"},"memberId":{"type":"string","title":"Memberid"},"displayName":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Displayname"}},"type":"object","required":["memberId"],"title":"MemberAuthor","description":"Domain MemberAuthor with camelCase wire aliases."},"MemberParticipant":{"properties":{"participantType":{"type":"string","const":"member","title":"Participanttype","default":"member"},"memberId":{"type":"string","title":"Memberid"},"displayName":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Displayname"},"role":{"$ref":"#/components/schemas/ParticipantRole","default":"member"}},"type":"object","required":["memberId"],"title":"MemberParticipant","description":"Domain MemberParticipant with camelCase wire aliases."},"Message":{"properties":{"id":{"type":"string","title":"Id"},"conversationId":{"type":"string","title":"Conversationid"},"kind":{"$ref":"#/components/schemas/MessageKind"},"state":{"$ref":"#/components/schemas/MessageState","default":"complete"},"author":{"oneOf":[{"$ref":"#/components/schemas/MemberAuthor"},{"$ref":"#/components/schemas/AssistantAuthor"}],"title":"Author","discriminator":{"propertyName":"authorType","mapping":{"assistant":"#/components/schemas/AssistantAuthor","member":"#/components/schemas/MemberAuthor"}}},"blocks":{"items":{"oneOf":[{"$ref":"#/components/schemas/PlainTextBlock"},{"$ref":"#/components/schemas/RichTextBlock"},{"$ref":"#/components/schemas/ListingPhotoBlock"},{"$ref":"#/components/schemas/ImageBlock"},{"$ref":"#/components/schemas/ChoicesBlock"},{"$ref":"#/components/schemas/CrisisResourceBlock"},{"$ref":"#/components/schemas/ListingSummaryBlock"},{"$ref":"#/components/schemas/GeneralListingSummaryWithCoordinatesBlock"},{"$ref":"#/components/schemas/PropertyResiForSaleListingSummaryBlock"},{"$ref":"#/components/schemas/PropertyResiForRentListingSummaryBlock"},{"$ref":"#/components/schemas/MotorsVehicleListingSummaryBlock"},{"$ref":"#/components/schemas/MarketplaceListingSummaryBlock"},{"$ref":"#/components/schemas/SearchResultsBlock"},{"$ref":"#/components/schemas/SearchResultsWithCoordinatesBlock"},{"$ref":"#/components/schemas/UserFileRefBlock"},{"$ref":"#/components/schemas/ListingRefBlock"}],"discriminator":{"propertyName":"type","mapping":{"choices":"#/components/schemas/ChoicesBlock","crisis_resource":"#/components/schemas/CrisisResourceBlock","image_block":"#/components/schemas/ImageBlock","listing_photo":"#/components/schemas/ListingPhotoBlock","listing_ref":"#/components/schemas/ListingRefBlock","listing_summary":"#/components/schemas/ListingSummaryBlock","listing_summary_with_coordinates":"#/components/schemas/GeneralListingSummaryWithCoordinatesBlock","marketplace_listing_summary":"#/components/schemas/MarketplaceListingSummaryBlock","motors_vehicle_listing_summary":"#/components/schemas/MotorsVehicleListingSummaryBlock","plain_text":"#/components/schemas/PlainTextBlock","property_resi_for_rent_listing_summary":"#/components/schemas/PropertyResiForRentListingSummaryBlock","property_resi_for_sale_listing_summary":"#/components/schemas/PropertyResiForSaleListingSummaryBlock","rich_text":"#/components/schemas/RichTextBlock","search_results":"#/components/schemas/SearchResultsBlock","search_results_with_coordinates":"#/components/schemas/SearchResultsWithCoordinatesBlock","user_file_ref":"#/components/schemas/UserFileRefBlock"}}},"type":"array","title":"Blocks"},"createdAt":{"type":"string","format":"date-time","title":"Createdat"},"finalisedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Finalisedat"},"seq":{"type":"integer","title":"Seq","default":0},"inputId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Inputid"}},"type":"object","required":["conversationId","kind","author"],"title":"Message","description":"Domain Message with camelCase wire aliases.\n\nauthor is overridden to use gateway-aliased union instead of domain union."},"MessageKind":{"type":"string","enum":["user","response","update"],"title":"MessageKind"},"MessageListResponse":{"properties":{"messages":{"items":{"$ref":"#/components/schemas/Message"},"type":"array","title":"Messages"},"hasMore":{"type":"boolean","title":"Hasmore"}},"type":"object","required":["messages","hasMore"],"title":"MessageListResponse","description":"Domain MessageListResponse with camelCase wire aliases."},"MessageState":{"type":"string","enum":["pending","complete","removed"],"title":"MessageState"},"MoneyField":{"properties":{"name":{"type":"string","title":"Name"},"type":{"type":"string","const":"money","title":"Type","default":"money"},"value":{"type":"number","title":"Value"},"display":{"type":"string","title":"Display"}},"type":"object","required":["name","value","display"],"title":"MoneyField","description":"Generic money field with raw numeric value and a display string."},"MotorsDealerInfo":{"properties":{"name":{"type":"string","title":"Name"},"logoUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logourl"},"brandColor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brandcolor"}},"type":"object","required":["name"],"title":"MotorsDealerInfo","description":"Dealer information for a motors listing. name + logo_url inherited from VendorInfo."},"MotorsVehicleListingSummaryBlock":{"properties":{"iconAttributes":{"items":{"$ref":"#/components/schemas/IconAttributeField"},"type":"array","title":"Iconattributes"},"listingId":{"type":"string","title":"Listingid"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"tva":{"$ref":"#/components/schemas/TemplatedViewAddress"},"isOnWatchlist":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Isonwatchlist"},"type":{"type":"string","const":"motors_vehicle_listing_summary","title":"Type","default":"motors_vehicle_listing_summary"},"version":{"type":"string","const":"1","title":"Version","default":"1"},"images":{"items":{"type":"string"},"type":"array","title":"Images"},"askingPrice":{"$ref":"#/components/schemas/MotorsVehiclePriceField"},"dealer":{"anyOf":[{"$ref":"#/components/schemas/MotorsDealerInfo"},{"type":"null"}]},"isDealer":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Isdealer"}},"type":"object","required":["listingId","tva","askingPrice"],"title":"MotorsVehicleListingSummaryBlock","description":"Vertical-specific listing summary for motors vehicle listings (cars + motorbikes).\n\nVersion \"1\". Clients not supporting (motors_vehicle_listing_summary, 1) receive\na downgraded listing_summary:1 block via the adapter chain."},"MotorsVehiclePriceField":{"properties":{"displayValue":{"type":"string","title":"Displayvalue"},"priceNote":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pricenote"},"asOf":{"type":"string","title":"Asof"}},"type":"object","required":["displayValue","asOf"],"title":"MotorsVehiclePriceField"},"NumberField":{"properties":{"name":{"type":"string","title":"Name"},"type":{"type":"string","const":"number","title":"Type","default":"number"},"value":{"type":"number","title":"Value"}},"type":"object","required":["name","value"],"title":"NumberField","description":"Generic numeric field."},"OpenHomeTime":{"properties":{"start":{"type":"string","title":"Start"},"end":{"type":"string","title":"End"}},"type":"object","required":["start","end"],"title":"OpenHomeTime"},"OpenHomesField":{"properties":{"items":{"items":{"$ref":"#/components/schemas/OpenHomeTime"},"type":"array","title":"Items"},"asOf":{"type":"string","title":"Asof"}},"type":"object","required":["items","asOf"],"title":"OpenHomesField"},"ParagraphBlock":{"properties":{"type":{"type":"string","const":"paragraph","title":"Type","default":"paragraph"},"version":{"type":"string","const":"1","title":"Version","default":"1"},"inline":{"items":{"oneOf":[{"$ref":"#/components/schemas/TextNode"},{"$ref":"#/components/schemas/CodeSpanNode"},{"$ref":"#/components/schemas/SoftBreakNode"},{"$ref":"#/components/schemas/HardBreakNode"},{"$ref":"#/components/schemas/StrongNode"},{"$ref":"#/components/schemas/EmphasisNode"},{"$ref":"#/components/schemas/StrikethroughNode"},{"$ref":"#/components/schemas/LinkNode"}],"discriminator":{"propertyName":"type","mapping":{"code_span":"#/components/schemas/CodeSpanNode","emphasis":"#/components/schemas/EmphasisNode","hard_break":"#/components/schemas/HardBreakNode","link":"#/components/schemas/LinkNode","soft_break":"#/components/schemas/SoftBreakNode","strikethrough":"#/components/schemas/StrikethroughNode","strong":"#/components/schemas/StrongNode","text":"#/components/schemas/TextNode"}}},"type":"array","title":"Inline"}},"type":"object","title":"ParagraphBlock","description":"A paragraph of inline-formatted text.\n\nThe most common block type produced by the streaming markdown parser.\nCarries a rich inline tree for clients that render formatted paragraphs.\nThe markdown-syntax fallback (preserving inline syntax) is produced at\ndowngrade time by :func:`render_block`.\n\n:ivar inline: Full inline node tree preserving bold, italic, links, etc."},"ParticipantRole":{"type":"string","enum":["owner","member","invited"],"title":"ParticipantRole"},"PlainTextBlock":{"properties":{"type":{"type":"string","const":"plain_text","title":"Type","default":"plain_text"},"version":{"type":"string","const":"1","title":"Version","default":"1"},"text":{"type":"string","title":"Text"}},"type":"object","required":["text"],"title":"PlainTextBlock","description":"Plain unformatted text. The baseline block type all clients must support."},"PriceField":{"properties":{"name":{"type":"string","title":"Name"},"type":{"type":"string","const":"price","title":"Type","default":"price"},"value":{"type":"string","title":"Value"}},"type":"object","required":["name","value"],"title":"PriceField","description":"Server-formatted price string, e.g. ``\"Asking $1,582,840\"``."},"PromptStarter":{"properties":{"symbol":{"type":"string","title":"Symbol"},"text":{"type":"string","title":"Text"}},"type":"object","required":["symbol","text"],"title":"PromptStarter","description":"A single conversation starter prompt with an emoji symbol and display text."},"PromptStartersBody":{"properties":{"referrer":{"anyOf":[{"oneOf":[{"$ref":"#/components/schemas/DirectReferrer"},{"$ref":"#/components/schemas/HomepageReferrer"},{"$ref":"#/components/schemas/PropertyHomepageReferrer"},{"$ref":"#/components/schemas/PropertyDeepLinkReferrer"},{"$ref":"#/components/schemas/SearchFormReferrer"},{"$ref":"#/components/schemas/SearchResultsReferrer"},{"$ref":"#/components/schemas/ListingReferrer"}],"discriminator":{"propertyName":"sourceType","mapping":{"direct":"#/components/schemas/DirectReferrer","homepage":"#/components/schemas/HomepageReferrer","listing":"#/components/schemas/ListingReferrer","property_deep_link":"#/components/schemas/PropertyDeepLinkReferrer","property_homepage":"#/components/schemas/PropertyHomepageReferrer","search_form":"#/components/schemas/SearchFormReferrer","search_results":"#/components/schemas/SearchResultsReferrer"}}},{"type":"null"}],"title":"Referrer"}},"type":"object","title":"PromptStartersBody","description":"Request body for POST /prompt-starters."},"PromptStartersResponse":{"properties":{"prompts":{"items":{"$ref":"#/components/schemas/PromptStarter"},"type":"array","title":"Prompts"}},"type":"object","required":["prompts"],"title":"PromptStartersResponse","description":"Response envelope for the prompt starters endpoint."},"PropertyDeepLinkReferrer":{"properties":{"sourceType":{"type":"string","const":"property_deep_link","title":"Sourcetype","default":"property_deep_link"}},"type":"object","title":"PropertyDeepLinkReferrer","description":"Domain PropertyDeepLinkReferrer with camelCase wire aliases."},"PropertyHomepageReferrer":{"properties":{"sourceType":{"type":"string","const":"property_homepage","title":"Sourcetype","default":"property_homepage"}},"type":"object","title":"PropertyHomepageReferrer","description":"Domain PropertyHomepageReferrer with camelCase wire aliases."},"PropertyListingAgent":{"properties":{"agentFullName":{"type":"string","title":"Agentfullname"},"agentPhotoUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agentphotourl"}},"type":"object","required":["agentFullName"],"title":"PropertyListingAgent"},"PropertyListingPriceField":{"properties":{"displayValue":{"type":"string","title":"Displayvalue"},"asOf":{"type":"string","title":"Asof"}},"type":"object","required":["displayValue","asOf"],"title":"PropertyListingPriceField"},"PropertyResiForRentListingSummaryBlock":{"properties":{"iconAttributes":{"items":{"$ref":"#/components/schemas/IconAttributeField"},"type":"array","title":"Iconattributes"},"listingId":{"type":"string","title":"Listingid"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"tva":{"$ref":"#/components/schemas/TemplatedViewAddress"},"isOnWatchlist":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Isonwatchlist"},"type":{"type":"string","const":"property_resi_for_rent_listing_summary","title":"Type","default":"property_resi_for_rent_listing_summary"},"version":{"type":"string","const":"1","title":"Version","default":"1"},"images":{"items":{"type":"string"},"type":"array","title":"Images"},"weeklyRent":{"$ref":"#/components/schemas/PropertyListingPriceField"},"availableFrom":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Availablefrom"},"address":{"$ref":"#/components/schemas/ListingAddressField"},"propertyType":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Propertytype"},"isBold":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Isbold"},"isFeatured":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Isfeatured"},"isHighlighted":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Ishighlighted"},"has3DTour":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has3Dtour"},"hasEmbeddedVideo":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Hasembeddedvideo"},"agency":{"anyOf":[{"$ref":"#/components/schemas/AgencyInfo"},{"type":"null"}]}},"type":"object","required":["listingId","tva","weeklyRent","address"],"title":"PropertyResiForRentListingSummaryBlock","description":"Vertical-specific listing summary for property residential for-rent listings.\n\nVersion \"1\". Clients not supporting (property_resi_for_rent_listing_summary, 1)\nreceive a downgraded listing_summary:1 block via the adapter chain.\n\nReuses PropertyListingPriceField (display_value + as_of), ListingAddressField,\nAgencyInfo, and PropertyIconKey from this module."},"PropertyResiForSaleListingSummaryBlock":{"properties":{"iconAttributes":{"items":{"$ref":"#/components/schemas/IconAttributeField"},"type":"array","title":"Iconattributes"},"listingId":{"type":"string","title":"Listingid"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"tva":{"$ref":"#/components/schemas/TemplatedViewAddress"},"isOnWatchlist":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Isonwatchlist"},"type":{"type":"string","const":"property_resi_for_sale_listing_summary","title":"Type","default":"property_resi_for_sale_listing_summary"},"version":{"type":"string","const":"1","title":"Version","default":"1"},"price":{"$ref":"#/components/schemas/PropertyListingPriceField"},"address":{"$ref":"#/components/schemas/ListingAddressField"},"geographicLocation":{"anyOf":[{"$ref":"#/components/schemas/GeographicLocation"},{"type":"null"}]},"propertyType":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Propertytype"},"openHomes":{"anyOf":[{"$ref":"#/components/schemas/OpenHomesField"},{"type":"null"}]},"listingExtras":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Listingextras"},"premiumPackageCode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Premiumpackagecode"},"isBold":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Isbold"},"isFeatured":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Isfeatured"},"isHighlighted":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Ishighlighted"},"has3DTour":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Has3Dtour"},"hasEmbeddedVideo":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Hasembeddedvideo"},"startDate":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Startdate"},"mainPhoto":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mainphoto"},"photoUrls":{"items":{"type":"string"},"type":"array","title":"Photourls"},"canonicalPath":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Canonicalpath"},"category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"},"agency":{"anyOf":[{"$ref":"#/components/schemas/AgencyInfo"},{"type":"null"}]}},"type":"object","required":["listingId","tva","price","address"],"title":"PropertyResiForSaleListingSummaryBlock","description":"Vertical-specific listing summary for property residential for-sale listings.\n\nVersion ``\"1\"`` of this type — it is a purpose-built new block type, not a\nrevision of an existing one.  Clients that do not advertise support for\n``(property_resi_for_sale_listing_summary, 1)`` receive a downgraded\n``listing_summary:1`` block via the adapter chain."},"PropertyTypeField":{"properties":{"name":{"type":"string","title":"Name"},"type":{"type":"string","const":"property_type","title":"Type","default":"property_type"},"value":{"type":"string","title":"Value"}},"type":"object","required":["name","value"],"title":"PropertyTypeField","description":"Property type, e.g. ``\"House\"``, ``\"Apartment\"``, ``\"Townhouse\"``."},"RestoreArtifactVersionRequest":{"properties":{"expectedHeadVersionId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expectedheadversionid"},"causedBy":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Causedby"}},"type":"object","title":"RestoreArtifactVersionRequest","description":"Body for ``POST .../versions/{versionId}/restore``. Every field optional.\n\n``expectedHeadVersionId`` is a compare-and-swap assertion: pass the head the\nUI actually rendered and the restore fails with 409 if anything landed since\nit was drawn. Omit it to restore onto whatever is current at the moment of\nthe call. Either way the append itself is CAS-protected, so a restore racing\na concurrent edit can never silently clobber it — this field only decides\nWHICH head the caller is asserting against.\n\n``causedBy`` lets a client tie the restore to the rpc that asked for it, so\nit groups with that turn in the history view. It is bounded (Coriax CR-2):\nthe intended values are a message uuid or a JSON-RPC id, both short, and it\nis stored verbatim and echoed back in every history response — so an\nunbounded string would bloat the column and every listing that reads it. A\nlength nobody legitimately reaches is the cheapest place to say so."},"RichTextBlock":{"properties":{"children":{"items":{"oneOf":[{"$ref":"#/components/schemas/PlainTextBlock"},{"$ref":"#/components/schemas/DividerBlock"},{"$ref":"#/components/schemas/HeadingBlock"},{"$ref":"#/components/schemas/ParagraphBlock"},{"$ref":"#/components/schemas/CodeBlock"},{"$ref":"#/components/schemas/BlockquoteBlock"},{"$ref":"#/components/schemas/ListBlock"}],"discriminator":{"propertyName":"type","mapping":{"blockquote":"#/components/schemas/BlockquoteBlock","code_block":"#/components/schemas/CodeBlock","divider":"#/components/schemas/DividerBlock","heading":"#/components/schemas/HeadingBlock","list":"#/components/schemas/ListBlock","paragraph":"#/components/schemas/ParagraphBlock","plain_text":"#/components/schemas/PlainTextBlock"}}},"type":"array","title":"Children"},"type":{"type":"string","const":"rich_text","title":"Type","default":"rich_text"},"version":{"type":"string","const":"1","title":"Version","default":"1"}},"type":"object","title":"RichTextBlock","description":"Top-level wrapper that groups markdown/richtext blocks as a logical unit.\n\nSeparates standard markdown content (``heading``, ``paragraph``, ``code``,\n``blockquote``, ``list``, ``divider``) from custom interactive components\n(``choices``, listing summaries, search results) at the top level of a\nmessage.  This enables independent versioning of the richtext contract\nwithout versioning every individual block type.\n\nChildren are restricted to the same set of block types permitted in\n``ChildBlock`` — custom/complex components are not valid children and must\nappear at the top level alongside ``RichTextBlock``, not inside it.\n\n:ivar children: Richtext child blocks (typed as ``ChildBlock`` to enforce\n    the top-level-only constraint on custom components at schema validation\n    time)."},"SearchFormReferrer":{"properties":{"sourceType":{"type":"string","const":"search_form","title":"Sourcetype","default":"search_form"},"tva":{"anyOf":[{"$ref":"#/components/schemas/TemplatedViewAddress"},{"type":"null"}]}},"type":"object","title":"SearchFormReferrer","description":"Domain SearchFormReferrer with camelCase wire aliases."},"SearchMetadata":{"properties":{"searchQuery":{"anyOf":[{"$ref":"#/components/schemas/SearchQuery"},{"type":"null"}]},"totalCount":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Totalcount"}},"type":"object","title":"SearchMetadata","description":"Metadata about a search result set.\n\nGroups search-related context that accompanies a SearchResultsBlock:\nthe labelled query TVA for the \"view all\" CTA, and the total result count."},"SearchQuery":{"properties":{"tva":{"$ref":"#/components/schemas/TemplatedViewAddress"},"label":{"type":"string","title":"Label"}},"type":"object","required":["tva","label"],"title":"SearchQuery","description":"A labelled search query expressed as a TVA."},"SearchResultsBlock":{"properties":{"type":{"type":"string","const":"search_results","title":"Type","default":"search_results"},"version":{"type":"string","const":"1","title":"Version","default":"1"},"artifactUri":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Artifacturi"},"toolCallId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Toolcallid"},"preferType":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prefertype"},"items":{"items":{"oneOf":[{"$ref":"#/components/schemas/ListingSummaryBlock"},{"$ref":"#/components/schemas/GeneralListingSummaryWithCoordinatesBlock"},{"$ref":"#/components/schemas/PropertyResiForSaleListingSummaryBlock"},{"$ref":"#/components/schemas/PropertyResiForRentListingSummaryBlock"},{"$ref":"#/components/schemas/MotorsVehicleListingSummaryBlock"},{"$ref":"#/components/schemas/MarketplaceListingSummaryBlock"}],"discriminator":{"propertyName":"type","mapping":{"listing_summary":"#/components/schemas/ListingSummaryBlock","listing_summary_with_coordinates":"#/components/schemas/GeneralListingSummaryWithCoordinatesBlock","marketplace_listing_summary":"#/components/schemas/MarketplaceListingSummaryBlock","motors_vehicle_listing_summary":"#/components/schemas/MotorsVehicleListingSummaryBlock","property_resi_for_rent_listing_summary":"#/components/schemas/PropertyResiForRentListingSummaryBlock","property_resi_for_sale_listing_summary":"#/components/schemas/PropertyResiForSaleListingSummaryBlock"}}},"type":"array","title":"Items"},"searchMetadata":{"anyOf":[{"$ref":"#/components/schemas/SearchMetadata"},{"type":"null"}]}},"type":"object","title":"SearchResultsBlock","description":"A set of search results where items may or may not have coordinates.\n\nHydrated by the streaming markdown parser from the artifact store when a\n``<search_results data=\"art://...\" />`` tag is encountered.\n\nThe LLM emits a container type, not a layout type — the client decides\nhow to render (list, carousel, map if coordinates are present).\n``prefer_type`` is a model hint the client may ignore.\n\n:ivar artifact_uri:    Artifact URI for analytics attribution (set by parser).\n:ivar tool_call_id:    FK to AssistantToolCall (from ``artifact.metadata``).\n:ivar prefer_type:     Model hint: ``\"map\"``, ``\"list\"``, or ``\"carousel\"``.\n:ivar items:           Typed listing summary blocks (discriminated by ``type``).\n:ivar search_metadata: Grouped search context (query TVA + total count)."},"SearchResultsReferrer":{"properties":{"sourceType":{"type":"string","const":"search_results","title":"Sourcetype","default":"search_results"},"tva":{"$ref":"#/components/schemas/TemplatedViewAddress"}},"type":"object","required":["tva"],"title":"SearchResultsReferrer","description":"Domain SearchResultsReferrer with camelCase wire aliases."},"SearchResultsWithCoordinatesBlock":{"properties":{"type":{"type":"string","const":"search_results_with_coordinates","title":"Type","default":"search_results_with_coordinates"},"version":{"type":"string","const":"1","title":"Version","default":"1"},"artifactUri":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Artifacturi"},"toolCallId":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Toolcallid"},"preferType":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prefertype"},"items":{"items":{"$ref":"#/components/schemas/GeneralListingSummaryWithCoordinatesBlock"},"type":"array","title":"Items"},"searchMetadata":{"anyOf":[{"$ref":"#/components/schemas/SearchMetadata"},{"type":"null"}]}},"type":"object","title":"SearchResultsWithCoordinatesBlock","description":"A set of search results where ALL items are guaranteed to have coordinates.\n\nHydrated by the streaming markdown parser from the artifact store when a\n``<search_results_with_coordinates data=\"art://...\" />`` tag is encountered.\n\nType-level contract: every item in ``items`` has ``latitude`` and ``longitude``.\nThe client does not need to perform nil-checks at runtime.\n\n:ivar artifact_uri:    Artifact URI for analytics attribution (set by parser).\n:ivar tool_call_id:    FK to AssistantToolCall (from ``artifact.metadata``).\n:ivar prefer_type:     Model hint: ``\"map\"``, ``\"list\"``, or ``\"carousel\"``.\n:ivar items:           Typed listing summary blocks with coordinates (discriminated by ``type``).\n:ivar search_metadata: Grouped search context (query TVA + total count)."},"SoftBreakNode":{"properties":{"type":{"type":"string","const":"soft_break","title":"Type","default":"soft_break"}},"type":"object","title":"SoftBreakNode","description":"Soft line break: a single newline within a paragraph source.\n\nRendered as a space or ``<br>`` depending on client preference.  Agents\nrarely produce these explicitly; they appear when source lines wrap without\na blank line between them."},"StrikethroughNode":{"properties":{"type":{"type":"string","const":"strikethrough","title":"Type","default":"strikethrough"},"children":{"items":{"oneOf":[{"$ref":"#/components/schemas/TextNode"},{"$ref":"#/components/schemas/CodeSpanNode"},{"$ref":"#/components/schemas/SoftBreakNode"},{"$ref":"#/components/schemas/HardBreakNode"},{"$ref":"#/components/schemas/StrongNode"},{"$ref":"#/components/schemas/EmphasisNode"},{"$ref":"#/components/schemas/StrikethroughNode"},{"$ref":"#/components/schemas/LinkNode"}],"discriminator":{"propertyName":"type","mapping":{"code_span":"#/components/schemas/CodeSpanNode","emphasis":"#/components/schemas/EmphasisNode","hard_break":"#/components/schemas/HardBreakNode","link":"#/components/schemas/LinkNode","soft_break":"#/components/schemas/SoftBreakNode","strikethrough":"#/components/schemas/StrikethroughNode","strong":"#/components/schemas/StrongNode","text":"#/components/schemas/TextNode"}}},"type":"array","title":"Children"}},"type":"object","required":["children"],"title":"StrikethroughNode","description":"Strikethrough text — ``~~strike~~`` (GitHub Flavored Markdown extension).\n\nChildren may include any inline nodes."},"StrongNode":{"properties":{"type":{"type":"string","const":"strong","title":"Type","default":"strong"},"children":{"items":{"oneOf":[{"$ref":"#/components/schemas/TextNode"},{"$ref":"#/components/schemas/CodeSpanNode"},{"$ref":"#/components/schemas/SoftBreakNode"},{"$ref":"#/components/schemas/HardBreakNode"},{"$ref":"#/components/schemas/StrongNode"},{"$ref":"#/components/schemas/EmphasisNode"},{"$ref":"#/components/schemas/StrikethroughNode"},{"$ref":"#/components/schemas/LinkNode"}],"discriminator":{"propertyName":"type","mapping":{"code_span":"#/components/schemas/CodeSpanNode","emphasis":"#/components/schemas/EmphasisNode","hard_break":"#/components/schemas/HardBreakNode","link":"#/components/schemas/LinkNode","soft_break":"#/components/schemas/SoftBreakNode","strikethrough":"#/components/schemas/StrikethroughNode","strong":"#/components/schemas/StrongNode","text":"#/components/schemas/TextNode"}}},"type":"array","title":"Children"}},"type":"object","required":["children"],"title":"StrongNode","description":"Bold text — ``**bold**`` or ``__bold__``.\n\nChildren may include any inline nodes, enabling constructs like\n``**bold with `code` inside**``."},"TVAParameter":{"properties":{"key":{"type":"string","title":"Key"},"value":{"$ref":"#/components/schemas/TypedValue"}},"type":"object","required":["key","value"],"title":"TVAParameter","description":"A single key/value parameter on a TemplatedViewAddress."},"TemplatedViewAddress":{"properties":{"viewName":{"type":"string","title":"Viewname"},"uri":{"type":"string","title":"Uri"},"parameters":{"items":{"$ref":"#/components/schemas/TVAParameter"},"type":"array","title":"Parameters"},"annotation":{"anyOf":[{"$ref":"#/components/schemas/LinkAnnotation"},{"type":"null"}]}},"type":"object","required":["viewName","uri"],"title":"TemplatedViewAddress","description":"Structured navigation address compatible with Koru's responseAddress shape.\n\n:ivar view_name: Koru view name, e.g. ``\"ListingDetailsPage\"``.\n:ivar uri:       Canonical URL string (same role as the old ``canonical_url``).\n:ivar parameters: Typed key/value parameters for the view.\n:ivar annotation: Optional human-readable link metadata.\n:ivar relative_path: Path portion of ``uri`` with the Trade Me web ``/a/`` prefix\n    stripped, intended for client-side routing. ``None`` when uri is empty or\n    unparseable. Not suitable as input to ``build_listing_url()`` — that function\n    expects the canonical_path form which includes the ``/a/`` segment."},"TextField":{"properties":{"name":{"type":"string","title":"Name"},"type":{"type":"string","const":"text","title":"Type","default":"text"},"value":{"type":"string","title":"Value"}},"type":"object","required":["name","value"],"title":"TextField","description":"Generic text field."},"TextNode":{"properties":{"type":{"type":"string","const":"text","title":"Type","default":"text"},"raw":{"type":"string","title":"Raw"}},"type":"object","required":["raw"],"title":"TextNode","description":"Plain text fragment.\n\nThe most common inline node.  ``raw`` is the raw text as it appears in\nthe source, with no HTML-entity decoding (mistune does this before the AST\nis built)."},"TypedValue":{"properties":{"strValue":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Strvalue"},"numValue":{"anyOf":[{"type":"number"},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Numvalue"},"boolValue":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Boolvalue"},"arrValue":{"anyOf":[{"items":{"$ref":"#/components/schemas/TypedValue"},"type":"array"},{"type":"null"}],"title":"Arrvalue"}},"type":"object","title":"TypedValue","description":"A discriminated-union value carrier for TVA parameters.\n\nExactly one of the four value fields must be non-``None``.  An all-``None``\ninstance is semantically invalid — it represents a parameter with no value."},"UpdateConversationBody":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"}},"type":"object","title":"UpdateConversationBody","description":"Request body for PATCH /conversations/{id}. Accepts camelCase or snake_case keys."},"UpsertParticipantBody":{"properties":{"role":{"$ref":"#/components/schemas/ParticipantRole"}},"type":"object","required":["role"],"title":"UpsertParticipantBody","description":"Request body for PATCH /conversations/{id}/participants/{member_id}. Accepts camelCase or snake_case keys."},"UserFileRefBlock":{"properties":{"type":{"type":"string","const":"user_file_ref","title":"Type","default":"user_file_ref"},"version":{"type":"string","const":"1","title":"Version","default":"1"},"ref":{"type":"string","minLength":14,"title":"Ref"},"alt":{"type":"string","maxLength":512,"title":"Alt","default":""},"width":{"anyOf":[{"type":"integer","exclusiveMinimum":0.0},{"type":"null"}],"title":"Width"},"height":{"anyOf":[{"type":"integer","exclusiveMinimum":0.0},{"type":"null"}],"title":"Height"}},"type":"object","required":["ref"],"title":"UserFileRefBlock","description":"A reference to a member-uploaded file — byte-free, ref only.\n\nFiles are general; image-oriented presentation fields are optional\nsince images are the only kind AIS-1157 currently accepts.\n\n:ivar ref: ``user://files/<file_id>`` — resolved to a signed URL via\n    ``GET /files/{file_id}/content``, never embedded here.\n:ivar alt: Alternative text description (client-supplied, optional).\n:ivar width: Optional pixel width, if known at send time (images only).\n:ivar height: Optional pixel height, if known at send time (images only)."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"WeeklyRentField":{"properties":{"name":{"type":"string","title":"Name"},"type":{"type":"string","const":"weekly_rent","title":"Type","default":"weekly_rent"},"value":{"type":"string","title":"Value"}},"type":"object","required":["name","value"],"title":"WeeklyRentField","description":"Server-formatted weekly rent string, e.g. ``\"$450 per week\"``."},"_AnyRpcRequest":{"properties":{"jsonrpc":{"type":"string","const":"2.0","title":"Jsonrpc"},"id":{"anyOf":[{"type":"string","maxLength":1024},{"type":"integer"},{"type":"null"}],"title":"Id"},"method":{"type":"string","title":"Method"},"params":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Params"}},"type":"object","required":["jsonrpc","method"],"title":"_AnyRpcRequest","description":"Flexible JSON-RPC 2.0 envelope — dispatches to per-method sub-handlers.\n\n``method`` accepts any string so that the dispatch handler can return an\nappropriate -32600 (Invalid Request) for unrecognised methods rather than\nletting Pydantic raise a 422 before the handler runs.\n\n``id`` is optional because JSON-RPC 2.0 notifications (e.g.\n``notifications/initialized``) do not include an id field.\n\n``params`` is validated per-method inside the dispatch handler."},"_RpcErrorDetail":{"properties":{"code":{"type":"integer","title":"Code"},"message":{"type":"string","title":"Message"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"}},"type":"object","required":["code","message"],"title":"_RpcErrorDetail","description":"Error object per JSON-RPC 2.0 spec."},"_RpcResponse":{"properties":{"jsonrpc":{"type":"string","const":"2.0","title":"Jsonrpc","default":"2.0"},"id":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"null"}],"title":"Id"},"result":{"$ref":"#/components/schemas/_RpcResultPayload"},"error":{"$ref":"#/components/schemas/_RpcErrorDetail"}},"type":"object","required":["id"],"title":"_RpcResponse","description":"Single JSON-RPC 2.0 response envelope — always HTTP 200.\n\nExactly one of result/error is non-None on a valid response.\nBoth fields are kept optional so one model covers success and error shapes.\nresponse_model_exclude_none=True on the route omits the absent field from\nthe wire format per JSON-RPC 2.0 §5."},"_RpcResultPayload":{"properties":{"messageId":{"type":"string","title":"Messageid"}},"type":"object","required":["messageId"],"title":"_RpcResultPayload","description":"Successful result payload returned inside the JSON-RPC envelope."},"AgentThinkingEvent":{"description":"Pre-commit indicator — fires on message receipt before agent decides to respond.","properties":{"conversationId":{"title":"Conversationid","type":"string"},"status":{"enum":["start","stop"],"title":"Status","type":"string"}},"required":["conversationId","status"],"title":"AgentThinkingEvent","type":"object"},"AgentTypingEvent":{"description":"Post-commit indicator — fires only when agent opens its first message.","properties":{"conversationId":{"title":"Conversationid","type":"string"},"status":{"enum":["start","stop"],"title":"Status","type":"string"}},"required":["conversationId","status"],"title":"AgentTypingEvent","type":"object"},"ArtifactUpdatedEvent":{"description":"Signals that an artifact's version chain advanced (a new version was created).\n\nFAT + version-stamped: ``payload`` is the full head payload, so a\nsubscribed client needs no follow-up fetch, and\n``version_ordinal``/``parent_version_id`` are enough for it to\napply-iff-newer.\n\nDelivered ONLY to clients that declared the ``artifact_updates``\ncapability — see the module docstring. Like every other event on the\nstream it is buffered for Tier 1 replay, so a reconnect with Last-Event-ID\nre-delivers it in its original position among the message frames. Replay is\nsafe precisely because of the apply-iff-newer rule above: a frame the client\nhas already applied is a no-op. REST remains the way to take a cold-start\nsnapshot of artifact state, on the client's own initiative.","properties":{"conversationId":{"title":"Conversationid","type":"string"},"artifactId":{"title":"Artifactid","type":"string"},"versionId":{"title":"Versionid","type":"string"},"versionOrdinal":{"title":"Versionordinal","type":"integer"},"parentVersionId":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Parentversionid"},"author":{"title":"Author","type":"string"},"nameHint":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Namehint"},"artifactType":{"title":"Artifacttype","type":"string"},"provenance":{"title":"Provenance","type":"string"},"payload":{"additionalProperties":true,"title":"Payload","type":"object"}},"required":["conversationId","artifactId","versionId","versionOrdinal","author","artifactType","provenance","payload"],"title":"ArtifactUpdatedEvent","type":"object"},"MessageBlockAdoptedEvent":{"description":"Places a provisional block: it becomes an ordinary block at ``block_seq``.","properties":{"conversationId":{"title":"Conversationid","type":"string"},"messageId":{"title":"Messageid","type":"string"},"provisionalKey":{"title":"Provisionalkey","type":"string"},"blockSeq":{"title":"Blockseq","type":"integer"}},"required":["conversationId","messageId","provisionalKey","blockSeq"],"title":"MessageBlockAdoptedEvent","type":"object"},"MessageBlockEvent":{"description":"Delivers one finalised block in a streaming message.","properties":{"conversationId":{"title":"Conversationid","type":"string"},"messageId":{"title":"Messageid","type":"string"},"blockSeq":{"title":"Blockseq","type":"integer"},"block":{"discriminator":{"mapping":{"choices":"#/components/schemas/ChoicesBlock","crisis_resource":"#/components/schemas/CrisisResourceBlock","image_block":"#/components/schemas/ImageBlock","listing_photo":"#/components/schemas/ListingPhotoBlock","listing_ref":"#/components/schemas/ListingRefBlock","listing_summary":"#/components/schemas/ListingSummaryBlock","listing_summary_with_coordinates":"#/components/schemas/GeneralListingSummaryWithCoordinatesBlock","marketplace_listing_summary":"#/components/schemas/MarketplaceListingSummaryBlock","motors_vehicle_listing_summary":"#/components/schemas/MotorsVehicleListingSummaryBlock","plain_text":"#/components/schemas/PlainTextBlock","property_resi_for_rent_listing_summary":"#/components/schemas/PropertyResiForRentListingSummaryBlock","property_resi_for_sale_listing_summary":"#/components/schemas/PropertyResiForSaleListingSummaryBlock","rich_text":"#/components/schemas/RichTextBlock","search_results":"#/components/schemas/SearchResultsBlock","search_results_with_coordinates":"#/components/schemas/SearchResultsWithCoordinatesBlock","user_file_ref":"#/components/schemas/UserFileRefBlock"},"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/PlainTextBlock"},{"$ref":"#/components/schemas/RichTextBlock"},{"$ref":"#/components/schemas/ListingPhotoBlock"},{"$ref":"#/components/schemas/ImageBlock"},{"$ref":"#/components/schemas/ChoicesBlock"},{"$ref":"#/components/schemas/CrisisResourceBlock"},{"$ref":"#/components/schemas/ListingSummaryBlock"},{"$ref":"#/components/schemas/GeneralListingSummaryWithCoordinatesBlock"},{"$ref":"#/components/schemas/PropertyResiForSaleListingSummaryBlock"},{"$ref":"#/components/schemas/PropertyResiForRentListingSummaryBlock"},{"$ref":"#/components/schemas/MotorsVehicleListingSummaryBlock"},{"$ref":"#/components/schemas/MarketplaceListingSummaryBlock"},{"$ref":"#/components/schemas/SearchResultsBlock"},{"$ref":"#/components/schemas/SearchResultsWithCoordinatesBlock"},{"$ref":"#/components/schemas/UserFileRefBlock"},{"$ref":"#/components/schemas/ListingRefBlock"}],"title":"Block"}},"required":["conversationId","messageId","blockSeq","block"],"title":"MessageBlockEvent","type":"object"},"MessageCompleteEvent":{"description":"Signals the end of an assistant message stream.","properties":{"conversationId":{"title":"Conversationid","type":"string"},"messageId":{"title":"Messageid","type":"string"},"finalisedAt":{"format":"date-time","title":"Finalisedat","type":"string"}},"required":["conversationId","messageId","finalisedAt"],"title":"MessageCompleteEvent","type":"object"},"MessageCreatedEvent":{"description":"Echo of a persisted message broadcast to the conversation room.\n\n``redacted`` is ``True`` when the message was rewritten by an input validator\nbefore persistence. The ``message`` field already contains the redacted blocks.","properties":{"conversationId":{"title":"Conversationid","type":"string"},"messageId":{"title":"Messageid","type":"string"},"message":{"$ref":"#/components/schemas/Message"},"redacted":{"default":false,"title":"Redacted","type":"boolean"}},"required":["conversationId","messageId","message"],"title":"MessageCreatedEvent","type":"object"},"MessageOpenedEvent":{"description":"Signals the start of an assistant message stream.","properties":{"conversationId":{"title":"Conversationid","type":"string"},"messageId":{"title":"Messageid","type":"string"},"seq":{"minimum":1,"title":"Seq","type":"integer"},"messageType":{"enum":["response","update"],"title":"Messagetype","type":"string"},"author":{"discriminator":{"mapping":{"assistant":"#/components/schemas/AssistantAuthor","member":"#/components/schemas/MemberAuthor"},"propertyName":"authorType"},"oneOf":[{"$ref":"#/components/schemas/MemberAuthor"},{"$ref":"#/components/schemas/AssistantAuthor"}],"title":"Author"},"createdAt":{"format":"date-time","title":"Createdat","type":"string"},"inputId":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Inputid"}},"required":["conversationId","messageId","seq","messageType","author","createdAt"],"title":"MessageOpenedEvent","type":"object"},"MessageProvisionalBlockEvent":{"description":"A block shown now whose place in the message is fixed later.\n\nIt renders after the message's ordinary blocks until a\n``message/blockAdopted`` frame with the same ``provisionalKey`` gives it\nits ``blockSeq``.","properties":{"conversationId":{"title":"Conversationid","type":"string"},"messageId":{"title":"Messageid","type":"string"},"provisionalKey":{"title":"Provisionalkey","type":"string"},"block":{"discriminator":{"mapping":{"choices":"#/components/schemas/ChoicesBlock","crisis_resource":"#/components/schemas/CrisisResourceBlock","image_block":"#/components/schemas/ImageBlock","listing_photo":"#/components/schemas/ListingPhotoBlock","listing_ref":"#/components/schemas/ListingRefBlock","listing_summary":"#/components/schemas/ListingSummaryBlock","listing_summary_with_coordinates":"#/components/schemas/GeneralListingSummaryWithCoordinatesBlock","marketplace_listing_summary":"#/components/schemas/MarketplaceListingSummaryBlock","motors_vehicle_listing_summary":"#/components/schemas/MotorsVehicleListingSummaryBlock","plain_text":"#/components/schemas/PlainTextBlock","property_resi_for_rent_listing_summary":"#/components/schemas/PropertyResiForRentListingSummaryBlock","property_resi_for_sale_listing_summary":"#/components/schemas/PropertyResiForSaleListingSummaryBlock","rich_text":"#/components/schemas/RichTextBlock","search_results":"#/components/schemas/SearchResultsBlock","search_results_with_coordinates":"#/components/schemas/SearchResultsWithCoordinatesBlock","user_file_ref":"#/components/schemas/UserFileRefBlock"},"propertyName":"type"},"oneOf":[{"$ref":"#/components/schemas/PlainTextBlock"},{"$ref":"#/components/schemas/RichTextBlock"},{"$ref":"#/components/schemas/ListingPhotoBlock"},{"$ref":"#/components/schemas/ImageBlock"},{"$ref":"#/components/schemas/ChoicesBlock"},{"$ref":"#/components/schemas/CrisisResourceBlock"},{"$ref":"#/components/schemas/ListingSummaryBlock"},{"$ref":"#/components/schemas/GeneralListingSummaryWithCoordinatesBlock"},{"$ref":"#/components/schemas/PropertyResiForSaleListingSummaryBlock"},{"$ref":"#/components/schemas/PropertyResiForRentListingSummaryBlock"},{"$ref":"#/components/schemas/MotorsVehicleListingSummaryBlock"},{"$ref":"#/components/schemas/MarketplaceListingSummaryBlock"},{"$ref":"#/components/schemas/SearchResultsBlock"},{"$ref":"#/components/schemas/SearchResultsWithCoordinatesBlock"},{"$ref":"#/components/schemas/UserFileRefBlock"},{"$ref":"#/components/schemas/ListingRefBlock"}],"title":"Block"}},"required":["conversationId","messageId","provisionalKey","block"],"title":"MessageProvisionalBlockEvent","type":"object"},"MessageRejectedEvent":{"description":"Signals that a user message was rejected by a safety check.\n\nMirrors the ``-32002 safety`` ``RpcErrorData`` envelope\n(``category``/``kind``/``retryable``/``user_message``) so clients render\nboth safety surfaces with shared code. See Decision 006.\n\nThe user message is NOT persisted; audit goes to Logfire. The pre-written\nresponse (when present) is delivered as a normal message stream after this\nevent (``MessageStreamStarted`` → ``message:block`` → ``MessageStreamCompleted``).","properties":{"conversationId":{"title":"Conversationid","type":"string"},"inputId":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Inputid"},"category":{"const":"safety","default":"safety","title":"Category","type":"string"},"kind":{"enum":["content_policy","pii_redacted"],"title":"Kind","type":"string"},"retryable":{"const":false,"default":false,"title":"Retryable","type":"boolean"},"userMessage":{"title":"Usermessage","type":"string"}},"required":["conversationId","kind","userMessage"],"title":"MessageRejectedEvent","type":"object"},"MessageRemovedEvent":{"description":"Signals that a message has been removed (failed/zombie stream).","properties":{"conversationId":{"title":"Conversationid","type":"string"},"messageId":{"title":"Messageid","type":"string"}},"required":["conversationId","messageId"],"title":"MessageRemovedEvent","type":"object"},"TaskClearEvent":{"description":"Signals that a background task has completed.","properties":{"conversationId":{"title":"Conversationid","type":"string"},"taskId":{"title":"Taskid","type":"string"}},"required":["conversationId","taskId"],"title":"TaskClearEvent","type":"object"},"TaskProgressEvent":{"description":"A running task called a tool — its title and the step, side by side.\n\nDelivered ONLY to clients that declared the ``task_progress`` capability\n(session ``initialize``, or the ``X-Anvil-Node-Capabilities`` header on the\nsubscribe request); for everyone else the same moment is the legacy\nre-emitted :class:`TaskSetEvent` with ``name`` = ``status``. ``title`` is\nthe task's own title, the value the opening ``task:set`` carried, so a\nclient can keep it as the pill label and show ``status`` beneath it.\n\n``at`` is the ISO-8601 UTC time the line was emitted. It is on the frame\nnow so the frame can be persisted verbatim later: the planned shape is a\ndistinct event-message kind in conversation history recording a task's\nstart, each progress line and its end, rendered collapsed to the latest\nline and expandable to one line per progress event. Nothing persists today.","properties":{"conversationId":{"title":"Conversationid","type":"string"},"taskId":{"title":"Taskid","type":"string"},"title":{"title":"Title","type":"string"},"status":{"title":"Status","type":"string"},"at":{"format":"date-time","title":"At","type":"string"}},"required":["conversationId","taskId","title","status","at"],"title":"TaskProgressEvent","type":"object"},"TaskSetEvent":{"description":"Names what a background task is doing right now.\n\nEmitted when the task begins, carrying its title. For a client that has\nNOT declared the ``task_progress`` capability it is emitted again for the\nsame ``task_id`` as the task works — each tool call replaces ``name`` with a\nshort member-facing progress line (\"Searching the web\"), which stays until\nthe next tool's line replaces it or ``task:clear`` ends the task. A client\nrenders whatever the latest frame names; the re-emission is a display\nupdate and needs no client change. A client that declared ``task_progress``\nreceives :class:`TaskProgressEvent` for those moments instead and sees this\nevent only once per task, with the title.\n\n``name`` is the DISPLAY name. The task's stored title is untouched by these\nupdates, so conversation history still shows the title the task was created\nwith.","properties":{"conversationId":{"title":"Conversationid","type":"string"},"taskId":{"title":"Taskid","type":"string"},"name":{"title":"Name","type":"string"}},"required":["conversationId","taskId","name"],"title":"TaskSetEvent","type":"object"},"AgentThinkingNotification":{"description":"JSON-RPC frame for the agent:thinking SSE event.","properties":{"jsonrpc":{"const":"2.0","default":"2.0","title":"Jsonrpc","type":"string"},"method":{"const":"agent/thinking","default":"agent/thinking","title":"Method","type":"string"},"params":{"$ref":"#/components/schemas/AgentThinkingEvent"}},"required":["params"],"title":"AgentThinkingNotification","type":"object"},"AgentTypingNotification":{"description":"JSON-RPC frame for the agent:typing SSE event.","properties":{"jsonrpc":{"const":"2.0","default":"2.0","title":"Jsonrpc","type":"string"},"method":{"const":"agent/typing","default":"agent/typing","title":"Method","type":"string"},"params":{"$ref":"#/components/schemas/AgentTypingEvent"}},"required":["params"],"title":"AgentTypingNotification","type":"object"},"ArtifactUpdatedNotification":{"description":"JSON-RPC frame for the artifact:updated SSE event.","properties":{"jsonrpc":{"const":"2.0","default":"2.0","title":"Jsonrpc","type":"string"},"method":{"const":"artifact/updated","default":"artifact/updated","title":"Method","type":"string"},"params":{"$ref":"#/components/schemas/ArtifactUpdatedEvent"}},"required":["params"],"title":"ArtifactUpdatedNotification","type":"object"},"MessageBlockAdoptedNotification":{"description":"JSON-RPC frame for the message:block_adopted SSE event.","properties":{"jsonrpc":{"const":"2.0","default":"2.0","title":"Jsonrpc","type":"string"},"method":{"const":"message/blockAdopted","default":"message/blockAdopted","title":"Method","type":"string"},"params":{"$ref":"#/components/schemas/MessageBlockAdoptedEvent"}},"required":["params"],"title":"MessageBlockAdoptedNotification","type":"object"},"MessageBlockNotification":{"description":"JSON-RPC frame for the message:block SSE event.","properties":{"jsonrpc":{"const":"2.0","default":"2.0","title":"Jsonrpc","type":"string"},"method":{"const":"message/block","default":"message/block","title":"Method","type":"string"},"params":{"$ref":"#/components/schemas/MessageBlockEvent"}},"required":["params"],"title":"MessageBlockNotification","type":"object"},"MessageCompleteNotification":{"description":"JSON-RPC frame for the message:stream_complete SSE event.","properties":{"jsonrpc":{"const":"2.0","default":"2.0","title":"Jsonrpc","type":"string"},"method":{"const":"message/streamComplete","default":"message/streamComplete","title":"Method","type":"string"},"params":{"$ref":"#/components/schemas/MessageCompleteEvent"}},"required":["params"],"title":"MessageCompleteNotification","type":"object"},"MessageCreatedNotification":{"description":"JSON-RPC frame for the message:created SSE event.","properties":{"jsonrpc":{"const":"2.0","default":"2.0","title":"Jsonrpc","type":"string"},"method":{"const":"message/created","default":"message/created","title":"Method","type":"string"},"params":{"$ref":"#/components/schemas/MessageCreatedEvent"}},"required":["params"],"title":"MessageCreatedNotification","type":"object"},"MessageOpenedNotification":{"description":"JSON-RPC frame for the message:stream_opened SSE event.","properties":{"jsonrpc":{"const":"2.0","default":"2.0","title":"Jsonrpc","type":"string"},"method":{"const":"message/streamOpened","default":"message/streamOpened","title":"Method","type":"string"},"params":{"$ref":"#/components/schemas/MessageOpenedEvent"}},"required":["params"],"title":"MessageOpenedNotification","type":"object"},"MessageProvisionalBlockNotification":{"description":"JSON-RPC frame for the message:provisional_block SSE event.","properties":{"jsonrpc":{"const":"2.0","default":"2.0","title":"Jsonrpc","type":"string"},"method":{"const":"message/provisionalBlock","default":"message/provisionalBlock","title":"Method","type":"string"},"params":{"$ref":"#/components/schemas/MessageProvisionalBlockEvent"}},"required":["params"],"title":"MessageProvisionalBlockNotification","type":"object"},"MessageRejectedNotification":{"description":"JSON-RPC frame for the message:rejected SSE event.","properties":{"jsonrpc":{"const":"2.0","default":"2.0","title":"Jsonrpc","type":"string"},"method":{"const":"message/rejected","default":"message/rejected","title":"Method","type":"string"},"params":{"$ref":"#/components/schemas/MessageRejectedEvent"}},"required":["params"],"title":"MessageRejectedNotification","type":"object"},"MessageRemovedNotification":{"description":"JSON-RPC frame for the message:removed SSE event.","properties":{"jsonrpc":{"const":"2.0","default":"2.0","title":"Jsonrpc","type":"string"},"method":{"const":"message/removed","default":"message/removed","title":"Method","type":"string"},"params":{"$ref":"#/components/schemas/MessageRemovedEvent"}},"required":["params"],"title":"MessageRemovedNotification","type":"object"},"TaskClearNotification":{"description":"JSON-RPC frame for the task:clear SSE event.","properties":{"jsonrpc":{"const":"2.0","default":"2.0","title":"Jsonrpc","type":"string"},"method":{"const":"task/clear","default":"task/clear","title":"Method","type":"string"},"params":{"$ref":"#/components/schemas/TaskClearEvent"}},"required":["params"],"title":"TaskClearNotification","type":"object"},"TaskProgressNotification":{"description":"JSON-RPC frame for the task:progress SSE event.","properties":{"jsonrpc":{"const":"2.0","default":"2.0","title":"Jsonrpc","type":"string"},"method":{"const":"task/progress","default":"task/progress","title":"Method","type":"string"},"params":{"$ref":"#/components/schemas/TaskProgressEvent"}},"required":["params"],"title":"TaskProgressNotification","type":"object"},"TaskSetNotification":{"description":"JSON-RPC frame for the task:set SSE event.","properties":{"jsonrpc":{"const":"2.0","default":"2.0","title":"Jsonrpc","type":"string"},"method":{"const":"task/set","default":"task/set","title":"Method","type":"string"},"params":{"$ref":"#/components/schemas/TaskSetEvent"}},"required":["params"],"title":"TaskSetNotification","type":"object"}},"securitySchemes":{"AnvilBearerToken":{"type":"http","description":"Access token issued by the Trade Me Auth Service (Duende IdentityServer), sent as `Authorization: Bearer <token>`. Must carry the audience Anvil is configured to accept, plus the scopes the endpoint requires.","scheme":"bearer","bearerFormat":"JWT"}}}}