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.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/maia-analytics-chat-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: MAIA Ah Chat API
description: API for MAIA application (migrated from Firebase)
version: 0.1.0
servers:
- url: https://api.maia-analytics.com
description: Base URL declared by the provider in apis.yml (roadmap#122).
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:
TextPart:
properties:
content:
type: string
title: Content
id:
anyOf:
- type: string
- type: 'null'
title: Id
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: text
title: Part Kind
default: text
type: object
required:
- content
title: TextPart
description: A plain text response from a model.
ViewOfferPreview:
properties:
proposedName:
type: string
title: Proposedname
description: The name the View will be saved under if the user accepts.
summary:
type: string
title: Summary
description: One line stating what will be saved — the filters/slice that define the current set (e.g. 'Region = West, Status = Active'). Shown to the user in the offer card so they know what they're saving.
type: object
required:
- proposedName
- summary
title: ViewOfferPreview
DocumentUrl:
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: document-url
title: Kind
default: document-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: DocumentUrl
description: The URL of the document.
StreamChatDelta:
properties:
type:
type: string
const: delta
title: Type
default: delta
display:
type: string
const: prose
title: Display
default: prose
content:
type: string
title: Content
description: Incremental content from the agent.
type: object
required:
- content
title: StreamChatDelta
description: Schema for streaming chat content deltas.
ChatInterruptResponse:
properties:
success:
type: boolean
title: Success
description: Whether the interrupt was successful.
message:
type: string
title: Message
description: Status message.
was_active:
type: boolean
title: Was Active
description: Whether the chat stream was active when interrupt was requested.
type: object
required:
- success
- message
- was_active
title: ChatInterruptResponse
description: Schema for chat interrupt responses.
ModelRequest:
properties:
parts:
items:
oneOf:
- $ref: '#/components/schemas/SystemPromptPart'
- $ref: '#/components/schemas/UserPromptPart'
- $ref: '#/components/schemas/ToolSearchReturnPart'
- $ref: '#/components/schemas/LoadCapabilityReturnPart'
- $ref: '#/components/schemas/ToolReturnPart'
- $ref: '#/components/schemas/RetryPromptPart'
type: array
title: Parts
timestamp:
anyOf:
- type: string
format: date-time
- type: 'null'
title: Timestamp
instructions:
anyOf:
- type: string
- type: 'null'
title: Instructions
kind:
type: string
const: request
title: Kind
default: request
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
type: object
required:
- parts
title: ModelRequest
description: A request generated by Pydantic AI and sent to a model, e.g. a message from the Pydantic AI app to the model.
LayerModel:
properties:
id:
type: string
format: uuid
title: Id
project_id:
type: string
format: uuid
title: Project Id
reference_layer_id:
anyOf:
- type: string
format: uuid
- type: 'null'
title: Reference Layer Id
reference_layer_ids:
items:
type: string
format: uuid
type: array
title: Reference Layer Ids
name:
type: string
title: Name
description:
anyOf:
- type: string
- type: 'null'
title: Description
style_config:
additionalProperties: true
type: object
title: Style Config
feature_count:
type: integer
title: Feature Count
total_size_bytes:
type: integer
title: Total Size Bytes
data_version:
type: integer
title: Data Version
default: 0
schema_version:
type: integer
title: Schema Version
default: 0
metadata_version:
type: integer
title: Metadata Version
default: 0
base_attributes:
additionalProperties: true
type: object
title: Base Attributes
feature_enrichments:
items:
$ref: '#/components/schemas/EnrichmentModel'
type: array
title: Feature Enrichments
sandbox_relation_name:
anyOf:
- type: string
- type: 'null'
title: Sandbox Relation Name
render_mode:
type: string
enum:
- detail_always
- agg_at_low_zoom
title: Render Mode
default: agg_at_low_zoom
display_kind:
type: string
enum:
- data
- boundary
title: Display Kind
description: Whether the layer is a queryable dataset (``data``) or a styling-only overlay (``boundary``). Boundary layers paint on the map but suppress the per-layer table, column manager, and feature-detail panel — there is no per-row data worth surfacing.
default: data
extent_bounds:
anyOf:
- $ref: '#/components/schemas/ThumbnailBounds'
- type: 'null'
description: Stored unfiltered extent of the layer's geometry, computed at ingest and recomputed on geometry-changing writes. None until computed (pre-backfill rows) or when the layer has no geometry. The FE uses it for initial map framing instead of a live ST_Extent call; filtered zoom-to-features stays live.
created_at:
anyOf:
- type: string
format: date-time
- type: 'null'
title: Created At
updated_at:
anyOf:
- type: string
format: date-time
- type: 'null'
title: Updated At
columns:
anyOf:
- items:
$ref: '#/components/schemas/LayerColumn'
type: array
- type: 'null'
title: Columns
description: Producer-declared, resolved per-column metadata (id, key, provenance, display name, data type) — schema/lineage only. Visibility and ordering live on the view, seeded at register-layer time. None for legacy pre-cutover layers; the composer derives sensible defaults from physical introspection.
data_restricted:
type: boolean
title: Data Restricted
description: Share-response-only signal that the layer's geometry is shown while every per-row attribute is withheld (parcel layers on anonymous share links). Always False on authenticated reads; set True solely by the shared-project layer builder. The FE renders a 'data unavailable in the public version' state in place of an empty grid.
default: false
title_template:
items:
type: string
type: array
title: Title Template
description: Ordered column keys the FE concatenates to compose a feature's title (e.g. `[parcelnumb]`, `[primary_address_full]`). Sourced from the layer's `LayerKind`, empty for permissive kinds — the FE keeps its generic title fallback for the long tail.
address_template:
items:
type: string
type: array
title: Address Template
description: Ordered column keys the FE concatenates to compose a feature's address line. Sourced from the layer's `LayerKind`; may reference keys that aren't typed canonical columns (city, state_abbr, …). Empty for permissive kinds.
type: object
required:
- id
- project_id
- name
- style_config
- feature_count
- total_size_bytes
- base_attributes
- created_at
- updated_at
title: LayerModel
description: Model for a layer.
BinaryContent:
properties:
data:
type: string
contentEncoding: base64
contentMediaType: application/octet-stream
title: Data
media_type:
anyOf:
- type: string
enum:
- audio/wav
- audio/mpeg
- audio/ogg
- audio/flac
- audio/aiff
- audio/aac
- type: string
enum:
- image/jpeg
- image/png
- image/gif
- image/webp
- type: string
enum:
- application/pdf
- text/plain
- text/csv
- application/vnd.openxmlformats-officedocument.wordprocessingml.document
- application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
- text/html
- text/markdown
- application/msword
- application/vnd.ms-excel
- type: string
title: Media Type
vendor_metadata:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
title: Vendor Metadata
kind:
type: string
const: binary
title: Kind
default: binary
identifier:
type: string
title: Identifier
description: 'Identifier for the binary content, 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 `BinaryContent`.
This identifier is only automatically passed to the model when the `BinaryContent` is returned by a tool.
If you''re passing the `BinaryContent` 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 `BinaryContent`.
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:
- data
- media_type
- identifier
title: BinaryContent
description: Binary content, e.g. an audio or image file.
ChatRequest:
properties:
message:
type: string
title: Message
project_id:
type: string
format: uuid
title: Project Id
examples:
- 00000000-0000-0000-0000-000000000001
current_focused_layer_id:
anyOf:
- type: string
format: uuid
- type: 'null'
title: Current Focused Layer Id
examples:
- 00000000-0000-0000-0000-000000000000
action_type:
anyOf:
- $ref: '#/components/schemas/ActionType'
- type: 'null'
action_subtype:
anyOf:
- type: string
- type: 'null'
title: Action Subtype
description: Optional preset identifier that disambiguates actions sharing an action_type. For detail-panel presets, matches the client-side noteSource (e.g. 'environment', 'zoning', 'summary').
hidden_context:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
title: Hidden Context
current_view_id:
anyOf:
- type: string
- type: 'null'
title: Current View Id
iterate_over_rows:
type: boolean
title: Iterate Over Rows
default: false
selected_model:
anyOf:
- type: string
- type: 'null'
title: Selected Model
description: Model-picker selection for this chat (a curated picker key, see GET /chat/models). Persisted on the chat; honored only when the workspace's model-picker feature toggle is on.
feature_context:
anyOf:
- $ref: '#/components/schemas/FeatureContext'
- type: 'null'
mentioned_feature_contexts:
anyOf:
- items:
$ref: '#/components/schemas/FeatureContext'
type: array
- type: 'null'
title: Mentioned Feature Contexts
prior_answer:
anyOf:
- $ref: '#/components/schemas/InteractiveQuestionAnswer'
- type: 'null'
description: When the user answers a structured question by clicking a button (deterministic-alias path), the FE sends the chosen option here alongside the label as `message`. The endpoint writes this onto the prior question's `metadata.interactive_question_answer` so the agent's Authority rule treats it as ground truth and the reload path reads it off the typed field instead of reconstructing from the user-reply text.
tool_approval:
anyOf:
- $ref: '#/components/schemas/ToolApprovalAnswer'
- type: 'null'
description: 'Approve/decline for a pending requires_approval tool call (save_skill / update_skill). Rides the same round-trip as prior_answer: the endpoint marks the prior pending message answered in metadata (flipping the card inert on reload) and the workflow resumes the deferred run with the result. `message` may be empty on an approval turn.'
continue_turn:
type: boolean
title: Continue Turn
description: Continue a terminally-failed turn from its persisted partial without a new prompt. The FE sends an empty `message` and the endpoint runs a promptless turn on the persisted history (the same no-user-bubble shape as a tool approval, minus the approval payload) so the agent resumes where it left off.
default: false
type: object
required:
- message
- project_id
title: ChatRequest
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)."
UnprocessableQueryResult:
properties:
reason:
$ref: '#/components/schemas/UnprocessableQueryReason'
description: Why the query cannot be processed
message:
type: string
title: Message
description: 'User-friendly explanation of why the query cannot be processed. Format rules: - Use third-person, declarative statements (NOT first-person like "I can''t" or "I don''t understand")
- Do NOT ask questions (no "Did you mean...?" or "Can you specify...?")
- Be concise and actionable - explain what''s needed for a valid query'
suggestions:
items:
type: string
type: array
title: Suggestions
description: Optional list of example valid queries the user could try
requirements_met:
anyOf:
- $ref: '#/components/schemas/RequirementsCheck'
- type: 'null'
description: Which requirements from the query are satisfied, even if the overall query is invalid
type: object
required:
- reason
- message
title: UnprocessableQueryResult
description: Result returned when a query cannot be processed — invalid input, system limitation, or execution blocker.
PendingEnrichmentRef:
properties:
id:
type: string
title: Id
description: UUID of the skeleton enrichment row.
name:
type: string
title: Name
description: Internal name of the enrichment.
display_name:
type: string
title: Display Name
description: User-visible name for the enrichment.
type: object
required:
- id
- name
- display_name
title: PendingEnrichmentRef
description: Reference to a skeleton enrichment that is still being configured.
StreamChatRetry:
properties:
type:
type: string
const: retry
title: Type
default: retry
display:
type: string
const: internal
title: Display
default: internal
attempt:
type: integer
title: Attempt
description: 1-based model-call attempt now streaming (2 = first retry).
type: object
required:
- attempt
title: StreamChatRetry
description: 'A dead model stream is being transparently retried (MAIA-2254).
Emitted mid-stream when a transient transport failure (or stall) killed
the in-flight model response and a fresh attempt is starting. The client
must discard the in-flight assistant segment accumulated since the last
completed tool boundary — the retry re-generates it — and may show a
transient "retrying" state. Non-terminal: the turn continues with the
fresh attempt''s deltas, or ends with the usual error frame if the retry
also dies.'
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: Co
# --- 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