Reply 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 archive, wakes it, and marks it read. Attachments are ids from POST /uploads.
POST
/conversations/{id}/messagesAuthorization
AuthorizationBearer token · headerrequired`MEDIAN_KEY` from Settings under API, or an MCP OAuth access token. A key acts as the organization; a token acts as the person who approved it.
Path parameters
idstringrequiredA conversation id.
Request body
requiredapplication/jsonbodystringrequiredmax length 4000
attachmentsobject[]Show propertiesHide properties
Array of
objectidstringrequirednamestringrequiredResponses
200The message that landed.
messageIdstring400The request is malformed, and the message names the field.
errorobjectShow propertiesHide properties
codestringmessagestring401The bearer token is missing, revoked, or expired.
errorobjectShow propertiesHide properties
codestringmessagestring404Not one of yours, or not there at all.
errorobjectShow propertiesHide properties
codestringmessagestring429Too many requests. Wait the seconds in `Retry-After`. Limits depend on the plan. See [rate limits](/api/errors-and-limits#rate-limits).
errorobjectShow propertiesHide properties
codestringmessagestringRequest
curl -X POST "https://api.median.sh/v1/conversations/string/messages" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"body": "That plan includes unlimited seats."
}'const response = await fetch("https://api.median.sh/v1/conversations/string/messages", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_TOKEN",
"Content-Type": "application/json"
},
body: JSON.stringify({
"body": "That plan includes unlimited seats."
})
});Response
{
"messageId": "string"
}{
"error": {
"code": "string",
"message": "string"
}
}{
"error": {
"code": "string",
"message": "string"
}
}{
"error": {
"code": "string",
"message": "string"
}
}{
"error": {
"code": "string",
"message": "string"
}
}