REST API
The whole platform is plain HTTPS and JSON. This is the curated reference for the endpoints most integrations need; every example runs as written once MIRIEL_API and MIRIEL_API_KEY are exported (see Get started).
| Base URL | https://api.miriel.ai — every path below is under /api/v2 |
| Bodies | JSON, except uploads, which are multipart/form-data |
| Health | GET /api/v2/health — no auth |
| Trailing slashes | Namespace roots accept both: /api/v2/query and /api/v2/query/ |
| Explorer | Swagger UI at /api/v2/docs, spec at /api/v2/swagger.json (a few routes, learn among them, are documented here but missing from the generated spec) |
Send your credential on every request in the `x-access-token` header. There is no Authorization: Bearer scheme on Miriel's own endpoints.
curl -s -X POST "$MIRIEL_API/api/v2/get_projects" \
-H "x-access-token: $MIRIEL_API_KEY"| Credential | Shape | Use |
|---|---|---|
| API key | UUID | Servers, scripts, integrations. From Settings → Developers. |
| Session JWT | JWT | Interactive clients. POST /api/dashboard/v1/login with HTTP Basic (email / password) returns a two-week token. |
| Status | Body | Meaning |
|---|---|---|
| 401 | {"message": "Api key / token is missing!"} | No x-access-token header |
| 401 | {"message": "Token is invalid!"} | Wrong key, or an expired JWT |
| 403 | {"error": "…"} | The credential cannot use this endpoint |
| 4xx | an explanatory message | An account limit was hit (resources, daily/monthly queries, monthly tokens) |
Current usage is on the workspace's Usage & billing page; GET /api/v2/limits/ reports the configured limits.
The primary ingest endpoint. input is a list of items; each value is inline text, a URL, or the name of a file uploaded in the same request.
curl -s -X POST "$MIRIEL_API/api/v2/learn" \
-H "x-access-token: $MIRIEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"project": ["default"],
"input": [
{"value": "Our pricing decision: usage-based, with a free tier up to 1,000 queries a month."},
{"value": "https://miriel.ai/docs"}
]
}'Request fields
| Field | Type | Required | Description |
|---|---|---|---|
| input | array | Yes | Items to learn — see item fields below. |
| project | string[] | No | Projects (workspace tags) to file the items under. Default ["default"]. |
| metadata | object | No | Key/values stored on every resource; filter on them with metadata_query. |
| priority | int | "pin" | "norank" | No | Ranking weight, default 100. "pin" (-2) always ranks above unpinned results; "norank" (-1) keeps a resource out of ranked results. |
| grant_ids | string[] | No | Access grants for the new resources (["*"] = everyone in the account). |
| discoverable | boolean | No | Whether the resources appear in cross-project discovery. |
| recursion_depth | integer | No | For URLs: how many links deep to crawl. 0 = just the page. |
| chunk_size | integer | No | Override chunking for embedding. |
| streams / streaming_options | object | No | For RTSP and video-stream ingestion. |
Item fields
| Field | Type | Required | Description |
|---|---|---|---|
| value | string | Yes | Text, a URL, or an uploaded filename. |
| upsert_id | string | No | A stable id you choose. Learning again with the same id replaces the resource instead of adding a duplicate. |
| command | "add" | "upsert" | "append" | No | upsert and append require upsert_id. |
| expiration_seconds | integer | No | Time to live; expired items are swept automatically. |
The response carries job_ids. POST /api/v2/upload_files is a multipart-oriented alias of the same handler; GET /api/v2/get_supported_types lists accepted file types.
curl -s -X POST "$MIRIEL_API/api/v2/query/" \
-H "x-access-token: $MIRIEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "What did we decide about pricing?",
"project": ["default"],
"num_results": 5
}'| Field | Type | Required | Description |
|---|---|---|---|
| query | string | Yes | The question. |
| project | string | string[] | No | Scope. Omit (or "*") to search every project. |
| num_results | integer | No | Passages to retrieve, default 10. Pinned resources count toward it. |
| metadata_query | string | No | Filter on metadata: key=value, >, <, >=, <=, combined with AND / OR (spaces required, case-sensitive). |
| want_llm / want_vector / want_graph | boolean | No | Toggle answer synthesis and each retrieval leg. All default true — set want_llm false for retrieval only. |
| model | string | No | Override the answering model for this query. |
| response_format | object | No | Structured output schema, e.g. {"founders": ["string"], "count": "integer"}. llm_result comes back as a JSON string. |
| input_images | string | string[] | No | Image URLs to ask about alongside your data. |
| conversation_mode / conversation_history | boolean / array | No | Multi-turn — see Conversations. |
| streaming | boolean | No | Stream the answer as server-sent events — see Streaming. |
| query_type | "search" | "auto" | "exhaustive" | No | search (default) answers from the best passages. exhaustive reads every matching document — for "how many contracts mention sales tax?" — and returns a query_id to poll. |
| publish_options | array | No | Deliver the result somewhere when done, e.g. [{"type": "email", "to_addresses": ["you@example.com"]}]. |
{
"results": {
"query_id": "…",
"status": "complete", // an exhaustive query returns early — poll until complete
"llm_result": "…", // the answer (a JSON string when response_format is set)
"vector_db_results": [ … ], // ranked passages, each with its source and metadata
"timings": { … },
"conversation_history": [ … ] // when conversation_mode is on
}
}Every query is stored. GET /api/v2/query/<query_id> returns the stored result with status at the top level (not wrapped in results).
/api/v2/query/query history — page_size, page_offset, statuses, created_before/after/api/v2/query/<query_id>one stored result/api/v2/query/<query_id>/downloadpresigned URL for the full result/api/v2/query/timedthe same query with a stage-by-stage timing breakdown/api/v2/format_query_resultre-format a stored resultQueries are independent by default. With conversation_mode: true the response includes results.conversation_history; send it back as conversation_history on the next query. The server keeps no conversation state — persist the history yourself to resume later.
{
"query": "And what did we charge before that?",
"conversation_mode": true,
"conversation_history": [ /* results.conversation_history from the previous response */ ]
}With "streaming": true the answer arrives as server-sent events: metadata, start, then chunk events carrying content, and finally done (or error). With voice_mode an audio event carries base64 audio near the end.
curl -N -X POST "$MIRIEL_API/api/v2/query/" \
-H "x-access-token: $MIRIEL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "Summarize the Q3 report", "streaming": true}'
# data: {"type": "chunk", "content": "Revenue grew …"}/api/v2/get_all_documentslist resources (filter by project, metadata_query)/api/v2/get_resourcefetch one resource (stream_resource to stream it)/api/v2/get_resource_chunksevery chunk of one resource/api/v2/remove_resourceforget a resource/api/v2/resource_versionsversion history (restore_resource_version, delete_resource_version)/api/v2/edit_contentedit in place (also edit_csv, edit_docx_content, edit_file_metadata …)/api/v2/set_resource_accesschange who can read a resource/api/v2/get_projectslist projects (create_project, delete_project)GET /api/v2/project/<project>/resources?modified_since=, GET /api/v2/project/<project>/changes?since=, and PUT / DELETE /api/v2/project/<project>/resource. Content still goes in through learn.Entities and relationships are extracted during ingest and feed every answer (want_graph). Explore or curate the graph directly:
/api/v2/graph/relationshipsrelationships for an entity/api/v2/graph/traversewalk the knowledge graph/api/v2/graph/statsgraph size and coverage/api/v2/graph/mergemerge duplicate entities (/graph/delete removes one)Rather than calling learn for every change, connect a provider once and let a source backfill its history and keep syncing. The workspace's Connect page is a UI over these endpoints; GET /api/v2/providers/ is the manifest of what can be connected.
OAuth connections
/api/v2/oauth/<provider>/authorizestart the flow — returns the consent URL/api/v2/oauth/<provider>/statusis this provider connected?/api/v2/oauth/<provider>/disconnectremove the connection/api/v2/oauth/connectionsall connectionsProviders include google, microsoft, slack, discord, dropbox, github, notion, salesforce and zoom. Once connected, browse before choosing what to sync — e.g. /oauth/google/drives, /oauth/slack/channels, /oauth/github/repos.
Sources
/api/v2/sources/create — provider, scope (folder, channel, repo…), target project/api/v2/sources/list (GET/PATCH/DELETE /sources/<id> for one)/api/v2/sources/<id>/backfillpull historical items/api/v2/sources/<id>/pausepause live sync (/resume restarts it)/api/v2/sources/<id>/statusbackfill and sync progressPush from your own systems
/api/v2/webhooks/ingest/<watcher_id>generic inbound webhook — the id in the URL authorizes it/api/v2/git_repos/sync_git_repoingest a git repo and keep it syncedDescribe an application in plain language and Miriel compiles it into a typed, reviewable manifest — its workflows, the data it needs and the grants it may use — then runs it on the platform. See Workflow optimization for what that looks like in practice.
/api/v2/workflow-apps/compile{"description": "…"} → a draft manifest/api/v2/workflow-apps/create the app from a manifest/api/v2/workflow-apps/<app_id>/activateprovision data, register workflows, start/api/v2/workflow-apps/<app_id>/amendchange it in plain language (/rollback undoes a version)/api/v2/workflow-apps/<app_id>/runsruns (/records, /interactions for its data and questions for you)