Maia-analytics chat API
The chat API from Maia-analytics — 8 operation(s) for chat.
The chat API from Maia-analytics — 8 operation(s) for chat.
openapi: 3.1.0
info:
title: MAIA Ah chat API
description: API for MAIA application (migrated from Firebase)
version: 0.1.0
tags:
- name: chat
paths:
/api/v1/chat/feedback:
post:
tags:
- chat
summary: Submit Chat Feedback
description: "Receives and processes feedback for a specific chat message via the ChatService.\n\nArgs:\n request: The feedback request data.\n chat_service: The ChatService instance (dependency).\n\nReturns:\n A confirmation response.\n\nRaises:\n HTTPException: 400 if saving feedback fails (e.g., invalid data).\n HTTPException: 500 for unexpected errors."
operationId: submit_chat_feedback_api_v1_chat_feedback_post
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ChatFeedbackRequest'
required: true
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/ChatFeedbackResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
security:
- FirebaseAuthMiddleware: []
/api/v1/chat/models:
get:
tags:
- chat
summary: List Picker Models
description: 'Curated model-picker options for the chat composer dropdown.
Served from the backend registry so the client list cannot drift from the
keys ``ChatRequest.selected_model`` validates against. 404s when the
workspace''s model-picker toggle is off — off means the surface is absent.'
operationId: list_picker_models_api_v1_chat_models_get
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/ChatModelsResponse'
security:
- FirebaseAuthMiddleware: []
/api/v1/chat/stream:
post:
tags:
- chat
summary: Chat Main Agent
description: "Receives and processes a streaming chat request via the AgentService.\n\nThis endpoint streams the agent's response in real-time using Server-Sent Events.\n\nArgs:\n request: The chat request data containing message and project_id (pre-validated).\n agent_service: The AgentService instance (dependency).\n\nReturns:\n StreamingResponse with Server-Sent Events containing streaming chat data.\n\nRaises:\n HTTPException: 400 for validation errors.\n HTTPException: 404 if project not found or user doesn't own it.\n HTTPException: 500 for unexpected errors."
operationId: chat_main_agent_api_v1_chat_stream_post
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ChatRequest'
required: true
responses:
'200':
description: Successful Response
content:
application/json:
schema: {}
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
security:
- FirebaseAuthMiddleware: []
/api/v1/chat/stream/resume:
post:
tags:
- chat
summary: Resume Chat Main Agent
description: 'Re-attach to a project''s in-flight chat run after a mid-stream refresh.
Read-only second reader: tails the existing workflow''s durable stream
(``read_stream`` replays from offset 0 → the full ordered sequence: the
user-message echo, every delta so far, then the terminal) WITHOUT starting
a run or claiming a slot. If no run is live the generator yields nothing; the
FE then sees no terminal and falls back to reloading persisted history (the
empty replay is the signal that the run finished and released its slot before
this reattach).'
operationId: resume_chat_main_agent_api_v1_chat_stream_resume_post
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ChatResumeRequest'
required: true
responses:
'200':
description: Successful Response
content:
application/json:
schema: {}
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
security:
- FirebaseAuthMiddleware: []
/api/v1/chat/chat/interrupt:
post:
tags:
- chat
summary: Interrupt Chat
description: "Stop an in-flight chat run via the cooperative interrupt flag.\n\nStopping resolves the chat's ``active_workflow_id`` (liveness from the DBOS\nstatus row) and raises the single cooperative stop signal. The workflow\nobserves it at its next node boundary and finalizes normally — persisting\nthe sanitized partial turn, stamping last-edit, capturing the trace, and\nemitting the ``interrupted`` terminal — while in-flight tools observe the\nsame flag at their checkpoints and bail BEFORE committing DB side-effects.\nNo hard DBOS cancel is issued here: a hard cancel skips finalization and is\nonly seen at the next step boundary (tools would commit after Stop); it\nsurvives solely as the run monitor's escalation backstop for wedged runs.\n\nArgs:\n request: The interrupt request containing project_id and reason (pre-validated).\n\nReturns:\n ChatInterruptResponse with ``was_active`` True iff a live run was signalled.\n\nRaises:\n HTTPException: 400 for an invalid project id; 500 for unexpected errors."
operationId: interrupt_chat_api_v1_chat_chat_interrupt_post
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ChatInterruptRequest'
required: true
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/ChatInterruptResponse'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
security:
- FirebaseAuthMiddleware: []
/api/v1/chat/schemas:
get:
tags:
- chat
summary: Expose Schemas
description: 'Schema exposure endpoint for OpenAPI/TypeScript generation.
This endpoint is never called by the frontend but ensures all chat-related
schemas are included in the OpenAPI specification for type generation.'
operationId: expose_schemas_api_v1_chat_schemas_get
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/SchemaExposure'
/api/v1/chat/archives/{project_id}:
get:
tags:
- chat
summary: Get Chat Archives
description: Get archive metadata for a project's chat, without message bodies.
operationId: get_chat_archives_api_v1_chat_archives__project_id__get
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/ChatArchiveMetadataResponseSchema'
security:
- FirebaseAuthMiddleware: []
/api/v1/chat/archives/{project_id}/{archive_id}:
get:
tags:
- chat
summary: Get Chat Archive Content
description: 'Get one archive''s message bodies on demand, scoped to the project''s chat.
The archive UUID doubles as a strong ETag (content never changes once
written), so a matching ``If-None-Match`` short-circuits to 304 before the
expensive blob read. The validator is client-constructible from the URL,
so the short-circuit is gated on a cheap existence+scope check — a forged
validator for an out-of-scope or missing archive still 404s. Blob
validation is intentionally skipped on the 304 path: blobs are immutable
after write, so a validator for a corrupt archive cannot come from a real
200, and a fresh (unconditional) read still 422s.'
operationId: get_chat_archive_content_api_v1_chat_archives__project_id___archive_id__get
security:
- FirebaseAuthMiddleware: []
parameters:
- name: archive_id
in: path
required: true
schema:
type: string
format: uuid
title: Archive Id
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/ArchiveContentResponseSchema'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
components:
schemas:
ThinkingPart:
properties:
content:
type: string
title: Content
id:
anyOf:
- type: string
- type: 'null'
title: Id
signature:
anyOf:
- type: string
- type: 'null'
title: Signature
provider_name:
anyOf:
- type: string
- type: 'null'
title: Provider Name
provider_details:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
title: Provider Details
part_kind:
type: string
const: thinking
title: Part Kind
default: thinking
type: object
required:
- content
title: ThinkingPart
description: A thinking response from a model.
AudioUrl:
properties:
url:
type: string
title: Url
force_download:
anyOf:
- type: boolean
- type: string
const: allow-local
title: Force Download
default: false
vendor_metadata:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
title: Vendor Metadata
kind:
type: string
const: audio-url
title: Kind
default: audio-url
media_type:
type: string
title: Media Type
description: Return the media type of the file, based on the URL or the provided `media_type`.
readOnly: true
identifier:
type: string
title: Identifier
description: 'The identifier of the file, such as a unique ID.
This identifier can be provided to the model in a message to allow it to refer to this file in a tool call argument,
and the tool can look up the file in question by iterating over the message history and finding the matching `FileUrl`.
This identifier is only automatically passed to the model when the `FileUrl` is returned by a tool.
If you''re passing the `FileUrl` as a user message, it''s up to you to include a separate text part with the identifier,
e.g. "This is file <identifier>:" preceding the `FileUrl`.
It''s also included in inline-text delimiters for providers that require inlining text documents, so the model can
distinguish multiple files.'
readOnly: true
type: object
required:
- url
- media_type
- identifier
title: AudioUrl
description: A URL to an audio file.
AgeRange:
properties:
min:
anyOf:
- type: integer
- type: 'null'
title: Min
max:
anyOf:
- type: integer
- type: 'null'
title: Max
is_approximate:
type: boolean
title: Is Approximate
default: true
reasoning:
anyOf:
- type: string
- type: 'null'
title: Reasoning
description: The reasoning for the age range.
sources:
items:
type: string
type: array
title: Sources
description: The sources for the age range.
type: object
title: AgeRange
description: Age range from PDL. Can represent exact age (min == max) or range.
LayerStateChangedEvent:
properties:
type:
type: string
const: layer_state_changed
title: Type
default: layer_state_changed
projectId:
type: string
format: uuid
title: Projectid
layers:
items:
$ref: '#/components/schemas/LayerModel'
type: array
title: Layers
default: []
deletedLayerIds:
items:
type: string
type: array
title: Deletedlayerids
default: []
type: object
required:
- projectId
title: LayerStateChangedEvent
description: 'Fired after a layer metadata mutation commits (create / delete / rename /
restyle / recolor), over the same per-user channel — the layer sibling of
``ViewStateChangedEvent``.
Payload-rich + version-stamped: the FE applies the carried layer(s) straight
to its React Query cache without a refetch round-trip, and uses each layer''s
``metadata_version`` to discard stale or out-of-order events. Carries only the
*affected* layer(s) plus ``deletedLayerIds`` so the FE merges rather than
overwrites siblings.
Unlike views, the nested layers serialize in their **snake_case** wire shape —
``LayerModel`` has no field aliases, and the FE reads the same snake_case keys
off ``GET /projects/{id}/layers`` through ``layerMappers.fromApiModel``. So no
``field_serializer`` is needed here; the default dump is already the wire shape.'
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
ChatStopReason:
type: string
enum:
- user_requested
- timeout
- lock_lost
title: ChatStopReason
description: 'Why a chat run was stopped — the cooperative flag''s reason taxonomy.
Carried in the Redis stop flag and, on the monitor''s escalation path, in the
durable stop event. ``USER_REQUESTED`` (and any unknown reason) synthesizes
the generic ``interrupted`` terminal; the two monitor-initiated reasons get
their specific error copy.'
StreamChatUserMessage:
properties:
type:
type: string
const: user_message
title: Type
default: user_message
display:
type: string
const: internal
title: Display
default: internal
content:
type: string
title: Content
description: The user's prompt text for this turn.
timestamp:
type: string
format: date-time
title: Timestamp
description: Turn start, stamped once when the workflow first writes this event; durable-stream replay preserves the original value, so a reconnecting client anchors its elapsed-time display to the true start instead of the reconnect instant.
type: object
required:
- content
title: StreamChatUserMessage
description: 'The acting user''s prompt, emitted as the FIRST durable stream event.
The live sender already rendered its own message optimistically, so it
ignores this echo. A client that *reconnects* to an in-flight run (page
refresh mid-stream) has no optimistic bubble — it replays the durable stream
from offset 0 and renders the question from this event, then the deltas that
follow. This makes the stream the single source of truth for an in-progress
turn (persisted history only carries completed turns).'
SkillPlanPhase:
properties:
label:
type: string
maxLength: 200
title: Label
description: Short user-facing phase name, e.g. 'Filter parcels by zoning'.
status:
$ref: '#/components/schemas/SkillPlanPhaseStatus'
description: pending | in_progress | completed | skipped
type: object
required:
- label
- status
title: SkillPlanPhase
NativeToolReturnPart:
properties:
tool_name:
type: string
title: Tool Name
content:
$ref: '#/components/schemas/ToolReturnContent'
tool_call_id:
type: string
title: Tool Call Id
tool_kind:
anyOf:
- type: string
enum:
- tool-search
- capability-load
- type: 'null'
title: Tool Kind
metadata:
title: Metadata
timestamp:
type: string
format: date-time
title: Timestamp
outcome:
type: string
enum:
- success
- failed
- denied
title: Outcome
default: success
provider_name:
anyOf:
- type: string
- type: 'null'
title: Provider Name
provider_details:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
title: Provider Details
part_kind:
type: string
const: builtin-tool-return
title: Part Kind
default: builtin-tool-return
type: object
required:
- tool_name
- content
title: NativeToolReturnPart
description: 'A tool return message from a native tool.
For native tools with a stable cross-provider shape (currently `tool_search`), a
`NativeToolReturnPart` may be promoted to a typed subclass like
[`NativeToolSearchReturnPart`][pydantic_ai.messages.NativeToolSearchReturnPart]
with a narrowed `content` `TypedDict`. See `NativeToolCallPart` for the pattern.'
TenureRange:
properties:
min:
anyOf:
- type: integer
- type: 'null'
title: Min
max:
anyOf:
- type: integer
- type: 'null'
title: Max
reasoning:
anyOf:
- type: string
- type: 'null'
title: Reasoning
description: The reasoning for the tenure range.
sources:
items:
type: string
type: array
title: Sources
description: The sources for the tenure range.
type: object
title: TenureRange
description: 'Job tenure range representing when someone started at their current position/company.
Used for employment duration (e.g., via PDL data).
Can represent exact year (min == max) or range of possible years.
Confidence is inferred: exact year = confirmed, range = approximate.'
PDLLocation:
properties:
name:
anyOf:
- type: string
- type: 'null'
title: Name
locality:
anyOf:
- type: string
- type: 'null'
title: Locality
region:
anyOf:
- type: string
- type: 'null'
title: Region
country:
anyOf:
- type: string
- type: 'null'
title: Country
continent:
anyOf:
- type: string
- type: 'null'
title: Continent
metro:
anyOf:
- type: string
- type: 'null'
title: Metro
geo:
anyOf:
- type: string
- type: 'null'
title: Geo
street_address:
anyOf:
- type: string
- type: 'null'
title: Street Address
address_line_2:
anyOf:
- type: string
- type: 'null'
title: Address Line 2
postal_code:
anyOf:
- type: string
- type: 'null'
title: Postal Code
type: object
title: PDLLocation
description: Location information from PDL.
StreamToolCallStart:
properties:
type:
type: string
const: tool_call_start
title: Type
default: tool_call_start
display:
type: string
const: tool_call_start
title: Display
default: tool_call_start
tool_name:
type: string
title: Tool Name
description: Name of the tool being called.
tool_call_id:
type: string
title: Tool Call Id
description: Unique identifier for this tool call.
args:
additionalProperties: true
type: object
title: Args
description: Arguments passed to the tool.
type: object
required:
- tool_name
- tool_call_id
- args
title: StreamToolCallStart
description: Schema for when a tool call begins.
ColumnDataType:
type: string
enum:
- string
- number
- integer
- boolean
- date
- geometry
- array
- object
title: ColumnDataType
description: Optional frontend type hint for a layer column.
UnprocessableQueryReason:
type: string
enum:
- out_of_scope
- too_vague
- geographic_restrictions
- execution_blocked
title: UnprocessableQueryReason
OwnerResidentialMailingAddressModel:
properties:
street:
anyOf:
- type: string
- type: 'null'
title: Street
description: Street line of the residential mailing address.
city:
anyOf:
- type: string
- type: 'null'
title: City
description: City.
state:
anyOf:
- type: string
- type: 'null'
title: State
description: Two-letter state code.
zip_code:
anyOf:
- type: string
- type: 'null'
title: Zip Code
description: ZIP / postal code.
resolved_owner_name:
anyOf:
- type: string
- type: 'null'
title: Resolved Owner Name
description: The person this address belongs to. For an entity owner this is the decision-maker the entity was pierced to, not the entity itself.
owner_type:
type: string
enum:
- individual
- entity
- unknown
title: Owner Type
description: Whether the parcel owner is an individual or an entity.
default: unknown
is_available:
type: boolean
title: Is Available
description: True only when a reliable RESIDENTIAL mailing address was determined. False when only a business / registered-agent address was found — the caller must not persist a business address as a result.
confidence:
anyOf:
- type: string
- type: 'null'
title: Confidence
description: Qualitative confidence (e.g. high / medium / low).
mobile_phone:
anyOf:
- type: string
- type: 'null'
title: Mobile Phone
description: Resolved owner's mobile phone, if known.
landline_phone:
anyOf:
- type: string
- type: 'null'
title: Landline Phone
description: Resolved owner's landline phone, if known.
email:
anyOf:
- type: string
- type: 'null'
title: Email
description: Resolved owner's email, if known.
company:
anyOf:
- type: string
- type: 'null'
title: Company
description: Company inferred for the resolved owner (from work email domain), if any.
type: object
required:
- is_available
title: OwnerResidentialMailingAddressModel
description: A resolved residential mailing address for a parcel's decision-making owner.
ModelResponse:
properties:
parts:
items:
oneOf:
- $ref: '#/components/schemas/TextPart'
- $ref: '#/components/schemas/ToolSearchCallPart'
- $ref: '#/components/schemas/LoadCapabilityCallPart'
- $ref: '#/components/schemas/ToolCallPart'
- $ref: '#/components/schemas/NativeToolSearchCallPart'
- $ref: '#/components/schemas/NativeToolCallPart'
- $ref: '#/components/schemas/NativeToolSearchReturnPart'
- $ref: '#/components/schemas/NativeToolReturnPart'
- $ref: '#/components/schemas/ThinkingPart'
- $ref: '#/components/schemas/CompactionPart'
- $ref: '#/components/schemas/FilePart'
type: array
title: Parts
usage:
$ref: '#/components/schemas/RequestUsage'
model_name:
anyOf:
- type: string
- type: 'null'
title: Model Name
timestamp:
type: string
format: date-time
title: Timestamp
kind:
type: string
const: response
title: Kind
default: response
provider_name:
anyOf:
- type: string
- type: 'null'
title: Provider Name
provider_url:
anyOf:
- type: string
- type: 'null'
title: Provider Url
provider_details:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
title: Provider Details
provider_response_id:
anyOf:
- type: string
- type: 'null'
title: Provider Response Id
finish_reason:
anyOf:
- type: string
enum:
- stop
- length
- content_filter
- tool_call
- error
- type: 'null'
title: Finish Reason
run_id:
anyOf:
- type: string
- type: 'null'
title: Run Id
conversation_id:
anyOf:
- type: string
- type: 'null'
title: Conversation Id
metadata:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
title: Metadata
state:
type: string
enum:
- complete
- incomplete
- interrupted
title: State
default: complete
type: object
required:
- parts
title: ModelResponse
description: A response from a model, e.g. a message from the model to the Pydantic AI app.
EnrichmentStatusChangedEvent:
properties:
type:
type: string
const: enrichment_status_changed
title: Type
default: enrichment_status_changed
workflow_id:
type: string
title: Workflow Id
project_id:
type: string
format: uuid
title: Project Id
enrichment_id:
type: string
format: uuid
title: Enrichment Id
feature_id:
type: string
title: Feature Id
status:
type: string
enum:
- SUCCESS
- FAILURE
title: Status
value:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
title: Value
type: object
required:
- workflow_id
- project_id
- enrichment_id
- feature_id
- status
title: EnrichmentStatusChangedEvent
description: "Fired when one enrichment workflow reaches a terminal state.\n\nUnlike ``CreditStateChangedEvent`` this is payload-rich. It carries:\n\n- ``workflow_id`` — the client registry's key; routes the event to the\n cell/column it belongs to.\n- ``project_id`` / ``enrichment_id`` / ``feature_id`` — the coordinates,\n so an event for a workflow the client didn't dispatch in this session\n (e.g. after a reload, before reconcile) can still build a registry\n entry from the event alone.\n- ``value`` — on SUCCESS, the persisted ``EnrichRowResponse``-shaped dict\n (``{value, reasoning, citations, state}``) built from the SAME\n ``db_value`` written to the sandbox, so the live cell is byte-identical\n to what a refresh re-reads. ``None`` on FAILURE (no value was written).\n\nThe terminal-status policy lives on the server (the workflow's own\nterminal state), not on the client — so the client can no longer drift a\ntransient signal into a false terminal (the bug class this replaces)."
ToolSearchReturnPart:
properties:
tool_name:
type: string
const: search_tools
title: Tool Name
default: search_tools
tool_call_id:
type: string
title: Tool Call Id
tool_kind:
type: string
const: tool-search
title: Tool Kind
default: tool-search
content:
$ref: '#/components/schemas/ToolSearchReturnContent'
metadata:
title: Metadata
timestamp:
type: string
format: date-time
title: Timestamp
outcome:
type: string
enum:
- success
- failed
- denied
title: Outcome
default: success
part_kind:
type: string
const: tool-return
title: Part Kind
default: tool-return
type: object
required:
- content
title: ToolSearchReturnPart
description: 'Typed view of a [`ToolReturnPart`][pydantic_ai.messages.ToolReturnPart] for the local `search_tools` function return.
Used on the local-fallback path (and as the synthetic-injection target on
non-native providers receiving cross-provider history). The native server-side
path uses
[`NativeToolSearchReturnPart`][pydantic_ai.messages.NativeToolSearchReturnPart]
instead.
To detect a tool-search part regardless of execution path (native server-side
vs. local fallback), check `part.tool_kind == ''tool-search''` — this works
across both call/return and both server/local variants.
Shadows `content` with a narrower
[`ToolSearchReturnContent`][pydantic_ai.messages.ToolSearchReturnContent]
`TypedDict`.'
StreamWebSearchStatus:
properties:
type:
type: string
const: web_search_status
title: Type
default: web_search_status
display:
type: string
const: internal
title: Display
default: internal
status:
type: string
title: Status
description: Status of the web search operation.
tool_call_id:
type: string
title: Tool Call Id
description: Unique identifier for this tool call.
content:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
title: Content
description: Additional status data if available.
type: object
required:
- status
- tool_call_id
title: StreamWebSearchStatus
description: Schema for web search status updates from built-in tools.
ChatModelOption:
properties:
key:
type: string
title: Key
label:
type: string
title: Label
# --- truncated at 32 KB (177 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/maia-analytics/refs/heads/main/openapi/maia-analytics-chat-api-openapi.yml