API
Developer guides and references for Median APIs, the CLI, MCP and webhooks.
138 articles
- CLI referenceEvery median command, its arguments and flags, and the role it needs.
- CLIInstall the median command, sign in once, then set up and run your workspace from a terminal or a script.
- Build your own widgetA custom support panel on the messaging API.
- Errors and limitsThe error envelope, every error code, rate limits per plan and size limits for the REST API.
- API overviewBase URLs, which API to call, credentials, Median keys and roles.
- MCP serverConnect Claude, Cursor, VS Code, Codex or any MCP client to your workspace.
- TasksRead, create and move cards on your organization's task board from the CLI, the REST API and MCP.
- WebhooksSigned event deliveries to your server. Add an endpoint, read the payloads, verify signatures and handle retries.
Management API
- Management APIVersion 1.0.0. Everything the dashboard can do, over HTTP: the inbox, customers, the knowledge base, signals, the agent's settings, and the numbers. Server to…
- GET /conversationsList conversations The newest 200 conversations on one side of the archive, filters applied within that page. The same page the inbox reads. Part of…
- GET /conversations/{id}Get a conversation The whole thread, up to the most recent 200 messages, team-only notes and system narration included. Part of Conversations. Parameters Name…
- DELETE /conversations/{id}Delete a conversation For good. Only archived conversations can be deleted; archive it first. Messages and their files are swept behind it. Part of…
- PATCH /conversations/{id}Update a conversation Send any subset; each field applies as its own change, in order. status narrates into the thread. snoozedUntil takes a unix ms timestamp,…
- POST /conversations/{id}/messagesReply as the team A reply from the team's side, which is every decision replying is: it takes the thread off the AI, reopens it, pulls it back from the…
- POST /conversations/{id}/notesAdd a note A note to the team. The customer never sees it, and nothing else about the conversation moves. Part of Conversations. Parameters Name In Type…
- GET /conversations/{id}/toolsList runnable tools Every switched on tool a run could reach from this conversation, with the JSON Schema its input is checked against. Part of Tool approvals.…
- POST /conversations/{id}/tools/runRun a tool Runs a tool now, as the team. No sign-off wait, whatever the risk: calling this is the decision. The call lands as an approval row at executing;…
- GET /toolsList tools Every tool this organization has wired up, switched on or not, with the endpoint answering each. GET /conversations/{id}/tools is the same list…
- POST /tools/syncSync tool endpoints Re-reads every endpoint's manifest now, so a tool that just deployed shows up without waiting for the route to be opened. The reads happen…
- POST /tools/{name}/testTest a tool Calls a tool and hands back what it answered. Supply conversationId to use a real conversation and its stored visitor; it must belong to this…
- GET /tool-approvalsList approvals Everything pending in the organization, newest first. Name a conversation to get its whole recent history instead, settled rows included. Part…
- GET /tool-approvals/{id}Get an approval One request or run, watched as it settles. result carries what the endpoint answered once the call lands. Part of Tool approvals. Parameters…
- POST /tool-approvals/{id}/approveApprove a tool call Approves a pending high risk call. It executes immediately, and the thread narrates who said yes. Part of Tool approvals. Parameters Name…
- POST /tool-approvals/{id}/denyDeny a tool call Declines it. The agent gets a turn to tell the customer gracefully, when the thread is still its to speak in. Part of Tool approvals.…
- PATCH /tools/{name}Switch a tool The switch on one tool, by its manifest name. Off means the agent stops holding it on its next turn. Part of Tool approvals. Parameters Name In…
- DELETE /tool-endpoints/{id}Remove a tool endpoint Removes the endpoint, and its tools go with it. Anything still waiting on a sign-off expires, narrated in its thread. Part of Tool…
- GET /tool-suggestionsList tool suggestions Tools the agent went without, waiting on somebody to build them. The agent files one mid-conversation when a customer needs something it…
- POST /tool-suggestions/{id}/builtMark a suggestion built Says the tool exists now. Your word, not a detection: what you built may wear a different name from the one the brief guessed, and a…
- POST /tool-suggestions/{id}/dismissDismiss a suggestion No. The row stays settled rather than leaving, so the same tool is not asked for again the next time a thread wants it. Part of Tool…
- GET /customersList customers The directory, most recently heard from first. One row per person rather than per browser: visitors sharing an email fold into a single…
- GET /customers/{id}Get a customer The whole person: their history, the browser context the widget noticed, and the facts learned from resolved conversations. Part of Customers.…
- DELETE /customers/{id}Delete a customer The whole person, for good: every browser in their group, their profile, and every conversation they ever had, swept behind them. A browser…
- POST /customers/{id}/refresh-profileRefresh the profile Asks the learned profile to catch up on settled conversations. rebuild: true wipes it and reads the history afresh. Calling it again while…
- POST /customers/{id}/reach-outReach out Writes to a customer first and opens a new conversation. The message lands in their widget on their next visit, with an email copy if they are away,…
- DELETE /customers/{id}/facts/{factId}Delete a fact Strikes a fact the model got wrong. The one write the team has into the profile. Part of Customers. Parameters Name In Type Required Description…
- GET /knowledgeGet the library The whole library at once: every folder and every document, sorted as arranged. Bodies stay home; read one with GET /knowledge/documents/{id}.…
- GET /knowledge/searchSearch documents Titles matched against the words typed, most relevant first, up to 30. A blank query answers with nothing. Part of Knowledge. Parameters Name…
- POST /knowledge/documentsWrite a document A new document, written straight in as markdown. It indexes in the background and the agent can answer from it once status reads ready. Part…
- GET /knowledge/documents/{id}Get a document One document in full, body included, plus a download URL when it arrived as a file. Part of Knowledge. Parameters Name In Type Required…
- DELETE /knowledge/documents/{id}Delete a document Both copies go: the document and its searchable index. A synced one comes back on the next sync while its file is still in the repository.…
- PATCH /knowledge/documents/{id}Edit a document Both fields, every time. GitHub-synced and Notion-imported documents refuse: their words live at the source, and an edit here would only last…
- POST /knowledge/documents/{id}/moveMove a document Which folder, and where on it. folderId: null is the library's top level. order shares one scale with siblings; the midpoint of the new…
- POST /knowledge/documents/{id}/retryRetry indexing Sends a document with status: "error" back on whichever leg of the trip failed. A no-op for anything else. Part of Knowledge. Parameters Name In…
- POST /knowledge/foldersCreate a folder A new shelf, at the bottom of wherever it goes. Folders nest three levels deep and no further. Part of Knowledge. Request body…
- DELETE /knowledge/folders/{id}Delete a folder The shelf goes, never what stood on it: documents and folders inside step up one level, keeping their order. Part of Knowledge. Parameters Name…
- PATCH /knowledge/folders/{id}Update a folder The label, and the subtitle when sent. Leaving description out keeps it; null clears it. Part of Knowledge. Parameters Name In Type Required…
- POST /knowledge/folders/{id}/moveMove a folder Same rules as dragging the shelf: it cannot move into itself or anything inside itself, and the whole stack has to clear the three level limit.…
- POST /knowledge/syncSync external sources Everything external, refreshed on demand: every connected repository gets a sync, every Notion import gets pulled again. Answers how many…
- GET /knowledge/crawlsList crawls Every trip the crawler has taken lately, newest first, up to 20. Part of Knowledge. Responses Status Description 200 The crawls. 401 The bearer…
- POST /knowledge/crawlsStart a crawl Sends the crawler to a site. Every scraped page lands as an ordinary web document while the crawl runs. Scraping a site again updates the pages…
- DELETE /knowledge/crawls/{id}Remove a crawl Takes the crawl off the list. Mid-crawl this is a cancel; every document already landed stays. Part of Knowledge. Parameters Name In Type…
- GET /knowledge/suggestionsList suggestions What the agent learned from resolved conversations, waiting for a person to say yes or no. Nothing lands in the library without an approval.…
- POST /knowledge/suggestions/{id}/approveApprove a suggestion The yes that writes: a new document, or an update to the one this finding is for. Part of Knowledge. Parameters Name In Type Required…
- POST /knowledge/suggestions/{id}/dismissDismiss a suggestion No. The row stays, settled, so the same finding cannot come back. Part of Knowledge. Parameters Name In Type Required Description id path…
- GET /signalsList signals One kind's list, most recently moved first, up to 60. filter defaults to live: everything not yet done or declined. Part of Signals. Parameters…
- POST /signalsFile a signal Files one by hand. A title already on the list is refused out loud with signalexists, naming the twin. Part of Signals. Request body…
- GET /signals/{id}Get a signal The signal, everyone who reported it with their own words, and commits that look like they finish it. Part of Signals. Parameters Name In Type…
- DELETE /signals/{id}Delete a signal Off the list for good. Reports go with it and nobody is told; an issue already sent to a tracker stays there. Part of Signals. Parameters Name…
- PATCH /signals/{id}Update a signal Send title with an optional body to reword it, status to move it, priority to rank it, in any combination. Omitting body preserves the…
- POST /signals/{id}/mergeMerge a duplicate Two are the same thing. The one in the path survives; the duplicate's reporters and title move onto it as an alias. Part of Signals.…
- POST /signals/{id}/acceptAccept a signal Sends it to every tracker the team has connected, Linear and GitHub both, and moves an open one to planned. The status moves either way; a…
- POST /signals/commits/{id}/applyApply a commit Agreeing that the commit really did finish it: the signal moves to done, which messages everybody who reported it. Part of Signals. Parameters…
- POST /signals/commits/{id}/dismissDismiss a commit The commit was about something else. The row goes; the signal stays where it was. Part of Signals. Parameters Name In Type Required…
- POST /feedbackSubmit feedback One note in, a verdict out. A fast model reads what was written, calls it a bug or a suggestion, writes the title and the write-up, picks a…
- GET /tasksList tasks One column in order, or the whole board left to right. Done comes back newest first. Part of Tasks. Parameters Name In Type Required Description…
- POST /tasksCreate a task A new card, on top of its column, planned unless another column is named. An issue in whichever tracker the board is connected to is opened for…
- GET /tasks/requestsList requests What customers asked for and nobody has decided about yet: the Requests column, newest first, minus anything already a card. Part of Tasks.…
- POST /tasks/requests/{id}Put a request on the board The card carries the report's words, its priority and whichever tracker issues the report already holds. From here on the two move…
- GET /tasks/settingsGet board settings How the board is wired up, and where it lives. Part of Tasks. Responses Status Description 200 The settings. 401 The bearer token is…
- PATCH /tasks/settingsUpdate board settings Renaming the prefix renames every key on the board, so a commit message naming an old one stops matching. Repositories need the GitHub…
- GET /tasks/{task}Get a task One card, and every commit and pull request that named it, newest first. Part of Tasks. Parameters Name In Type Required Description task path…
- DELETE /tasks/{task}Delete a task Off the board for good. Issues already opened for it elsewhere are left where they are, and nobody is told. Part of Tasks. Parameters Name In…
- PATCH /tasks/{task}Update a task The words, the facts, or the column. null clears the priority or takes the card off whoever has it. A move to done closes the report the card…
- POST /tasks/{task}/declineDecline a request The card leaves the board and the report it came from is declined in Median. Nobody is messaged, the same as declining on Signal. Only a card…
- GET /integrationsEverything connected What is connected and what each one is pointed at, in one read: the email address, the Slack and Discord channels, the GitHub accounts and…
- PATCH /integrations/emailSwitch email support Turning it on for the first time issues the workspace its address, so this can fail on a slug somebody else's address was already minted…
- PATCH /integrations/slackPoint the Slack mirror Where conversations are mirrored, and whether the mirror runs. The channel goes in by name, since only the workspace can turn one into…
- PATCH /integrations/discordSet up the Discord side Where tickets are cut, who is pinged, where conversations are mirrored, and the two switches. Names in, resolved against the live…
- PATCH /integrations/linearSet the Linear team Which team issues land in, by key or by name, and whether filing a signal opens one on its own. Part of Integrations. Request body…
- POST /integrations/issue-repoSet the issue repository Where signals open GitHub issues. The repository has to belong to a connected GitHub account. Omit repo to change only the switch on a…
- DELETE /integrations/issue-repoStop opening issues Stops opening issues anywhere. Issues already opened stay where they are, and the signals still track them. Part of Integrations. Responses…
- POST /integrations/commit-reposWatch a repository's commits Reads a repository's commits for fixes, so a commit that names a signal shows up on it. Sending a repository already watched…
- DELETE /integrations/commit-repos/{owner}/{repo}Stop reading commits Stops reading one repository's commits. What it already matched stays on the signals. Part of Integrations. Parameters Name In Type…
- POST /integrations/reposMirror a repository Points the knowledge base at a repository, or one folder of it. Every markdown file becomes a document the agent answers out of, and a push…
- DELETE /integrations/repos/{owner}/{repo}Stop mirroring a repository Stops mirroring. What it already imported stays in the library. Part of Integrations. Parameters Name In Type Required Description…
- PATCH /integrations/repos/{owner}/{repo}Set the published address Where a mirrored repository's documents are published, so the agent hands a customer a link rather than a paragraph. The hunt for the…
- GET /site/appearanceGet the site's appearance How the public site looks: theme, the color scheme a first visit opens in, where the navigation sits, brand color, and custom CSS.…
- PATCH /site/appearanceChange the site's appearance Only what is sent changes. Custom CSS loads after the theme, so it wins; it styles tokens like --site-accent and parts like…
- GET /site/domainGet the custom domain The public site's custom domain and the DNS records still to add. domain is null when the site only has its median.website address. Part…
- POST /site/domainConnect a custom domain Connects a hostname like help.example.com, replacing any other. Add the returned records at your DNS provider; the domain goes live…
- DELETE /site/domainRemove the custom domain The site goes back to its median.website address. Admins and owners only. Part of Site. Responses Status Description 200 domain is…
- POST /site/domain/checkCheck the custom domain now Checks the DNS records now instead of waiting for the next automatic check. Part of Site. Responses Status Description 200 The…
- GET /site/sign-inGet how visitors sign in The sign-in method and its settings. The OIDC client secret is never returned. Part of Site. Responses Status Description 200 The…
- PATCH /site/sign-inChange how visitors sign in Switches the method and sets it up. app needs appUrl the first time; oidc needs the provider the first time, and clientSecret can…
- GET /meWho am I The person behind an OAuth token, where the session is bound, and everywhere else it could be. Token-only: a key is an organization, not a person. org…
- POST /organizationsCreate an organization Creates one, makes the token's person its owner, and binds the session to it, so the next request is already working inside it.…
- POST /me/organizationSwitch organizations Rebinds the token to another of its person's organizations, by id or slug. Membership is checked now and again on every later call. Part…
- GET /organizationGet the organization Who this credential belongs to. Part of Organization. Responses Status Description 200 The organization. 401 The bearer token is missing,…
- PATCH /organizationRename the organization The name, the slug, or both. The slug is what the workspace's email support address is minted from, so changing it changes where new…
- GET /membersList members Everybody in the organization, owners first and oldest first within a role. userId is what a role change or a removal takes. Part of Organization.…
- DELETE /members/{userId}Remove a member Takes somebody out of the organization. What they wrote stays: a thread belongs to the organization, not to whoever happened to answer it. The…
- PATCH /members/{userId}Change somebody's role Promotes or demotes somebody. Making an owner hands the organization over. The ranking rules are checked against your own role: an admin…
- GET /invitesList invites Every invitation still waiting to be accepted, newest first. Part of Organization. Responses Status Description 200 The pending invitations. 401…
- POST /invitesInvite somebody The invitation exists immediately and the email sends itself; path is the invite link's path on the dashboard, for sharing by hand. Token-only:…
- DELETE /invites/{id}Revoke an invite Takes it back. The link stops working the moment this runs. Part of Organization. Parameters Name In Type Required Description id path string…
- GET /keysList keys Every Median key, masked. Full values were shown exactly once, at minting. Part of Account. Responses Status Description 200 The keys, newest first.…
- POST /keysCreate a key Mints a Median key pair: the secret root for MEDIANKEY, and the browser-safe publishable half the widget ships with. Returned in full exactly…
- DELETE /keys/{id}Revoke a key Kills one key. Whatever holds it stops authenticating on its next request; everything already in the inbox stays put. Part of Account. Parameters…
- GET /agentGet the agent The agent's name, personality, and autonomy switches. Defaults until somebody has edited anything. Part of Agent. Responses Status Description…
- PATCH /agentUpdate the agent Whatever is sent moves; everything else is read off the row, so saving a name can never put a switch back. The picture stays in the app. Part…
- GET /webhooksList webhooks Every endpoint, and whether each is answering. Part of Webhooks. Responses Status Description 200 The endpoints. 401 The bearer token is missing,…
- POST /webhooksAdd a webhook Points events at a URL of yours. Deliveries sign with material derived from the Median key that made the request, or the newest one for an OAuth…
- DELETE /webhooks/{id}Remove a webhook Events stop being sent to it, retries included. Part of Webhooks. Parameters Name In Type Required Description id path string Yes Responses…
- PATCH /webhooks/{id}Update a webhook Change the URL, the events, or both. Deliveries already booked keep the body they were given. Part of Webhooks. Parameters Name In Type…
- GET /analytics/overviewGet the overview The dashboard's headline numbers: open conversations, this week against last, satisfaction over the last 30 days, and open signals. Part of…
- GET /analytics/conversationsConversation volume How many started each day, which door they came through, how the window's conversations ended up, and what the agent reached for. Part of…
- GET /analytics/satisfactionSatisfaction The stars customers left, day by day: sums and counts rather than averages, so you can average whole weeks yourself. Part of Analytics. Parameters…
- GET /analytics/signalsSignal trends Bugs and suggestions filed each day, where the whole backlog stands today, and how the open ones rank. Part of Analytics. Parameters Name In Type…
- GET /analytics/activityTeam activity Everything the agent and the team did that writes a log line, per day, with the busiest kinds of work named. Part of Analytics. Parameters Name…
- GET /analytics/knowledgeKnowledge coverage The library's shape today, and what the review queue decided in the window. Part of Analytics. Parameters Name In Type Required Description…
- POST /analytics/exploreExplore any dataset One query over any dataset Median collects, the way the analytics page's explorer runs it: a measure, filters, a split into series and an…
- GET /analytics/billingSpend and model calls What the window cost, in microcredits: spend per day by service, model calls per day split into automatic and team-started, tokens per…
- POST /analytics/recordsList the rows a query matched The rows behind an explore query, newest first unless sort says otherwise, each with a title, the dashboard page it opens, and…
- GET /billing/limitsRead the effective API allowance Shared across every API key and member. Null limits mean Unlimited, subject to independent fair-use protections. No remote…
- GET /billing/overviewRead the plan and credits Owners and admins only. The plan, credits left, add-ons, the monthly total, and this period's usage, from the cached billing…
- GET /billing/invoicesList invoices Owners and admins only. Newest first. Part of Billing. Parameters Name In Type Required Description limit query integer No Invoices per page.…
- GET /billing/usageRead usage history AI, email and scraping usage. One USD is 1,000,000 integer microcredits. Only customer charges and neutral labels are returned. Maximum date…
- GET /logsRead the activity log Owners and admins only. AI runs with their tokens and credits, tool calls, knowledge syncs and imports, team and settings changes, and…
- GET /logs/{id}Read one log entry Owners and admins only. The entry, what it touched by name, and when it is about a conversation, everything else logged about that…
Median API
- Median APIVersion 1.0.0. Everything the widget does, over HTTP: read a visitor's thread, send messages, attach files, and signal typing. The tool endpoint routes point…
- GET /configGet the config Your organization's name and the agent that answers. Renaming the agent in the dashboard is reflected here. Part of Conversations. Responses…
- GET /threadGet the thread A session's conversation history: up to 200 messages across its 10 most recently started conversations, oldest first. The most recently active…
- POST /messagesSend a message The first message creates the visitor and the conversation. The AI starts answering unless a person already holds the thread. New user values…
- POST /uploadsUpload a file The request body is the file itself, up to 20 MB. Send the real Content-Type, since the stored type comes from it. Returns the id to send in a…
- POST /typingSend typing Shows the visitor's typing indicator in your team's inbox, and a no-op before the session's first message. The signal expires on its own after a…
- GET /tool-endpointsRead the tool endpoints Every route the agent looks to for your tools, and what the last sync found at each one. The endpoints are the truth, but the agent…
- PUT /tool-endpointsAdd a tool endpoint Adds a route you serve and starts a sync. Sending the same URL again re-syncs that endpoint. A different URL adds another endpoint. Tool…
- POST /tool-endpoints/syncSync the tools Re-reads one endpoint's manifest. Pass its exact URL when more than one endpoint is connected. The URL may be omitted when there is exactly one.…