Messages and interactions
Verified request, response and authorization contracts.
Use /input to request execution. /messages posts discussion without scheduling a Run. On task.needs_input, fetch the interaction identified by the event and answer as its recipient. Answering or declining can enqueue a continuation Run; use the Task ID to track it.
A completed Run’s reply is the newest assistant message whose run_id is that Run’s ID. Melso stores it with secret-looking values redacted. A reply that arrives double-escaped, with no real line break inside it but \n or \r escape sequences in its place, is decoded (\n, \r, \t and \\); leading and trailing line breaks do not count. Any single-line reply that contains \n or \r is decoded the same way, including a path such as C:\new\folder. Other replies keep their backslashes, so code and paths in a multi-line answer stay intact. The Run’s result.output keeps the raw harness text, so read the reply from the messages. kind: "no_response" marks a Run that finished without text or attachments, and a message with failure_reason reports a Run that stopped.
Examples use the environment variables from Overview. UUID placeholders must be replaced with your own IDs.
POST /api/tasks/{id}/input
Auth: Workspace member with access to this resource.
Request: content:string required (not blank, no NUL characters), attachment_ids?:UUID[], client_request_id?:UUID, fresh_session?:boolean, max_attempts?:1. Task path must be a UUID. The JSON body is limited to 1 MiB, and unknown fields are rejected.
Response: TaskInputResponse.
Status: 201, also for a replay; 400 blank or invalid content, an unknown field or a field of the wrong type, max_attempts other than 1, or a client_request_id that is not a UUID; 413 body over 1 MiB; 409 cancelled Task, missing execution settings, or a client_request_id already used with a different payload ("code":"client_request_conflict"). Authentication and resource-access errors follow Overview.
fresh_session and max_attempts configure the Run this input creates. With fresh_session: true the Run starts a new harness session instead of resuming the Task's previous one; it still receives the Task's recent messages. Omitted or false, the Run resumes unless the sender, harness or execution settings changed. max_attempts is execution.max_attempts of POST /api/tasks for this Run: 1 gives it a single attempt, with no automatic retry and no Cloud lifetime rollover; omitted or null keeps the default of two attempts. Both hold when the Run waits in the queue behind other work, and for its retries: a retry of a fresh_session Run continues the session that Run started, or starts fresh again when it started none.
Send a fresh UUID as client_request_id on every send and reuse it when you retry after a network error, timeout or 5xx. A retry with the same content, attachment_ids (compared as a set), fresh_session and max_attempts sends nothing again; an omitted option equals its default (false, null). It returns 201 with the original message_id, run_id, queued, attachment_ids and created_at, even after the Task completed, was cancelled or lost its execution target. Identical requests sent concurrently create one message and one Run. The ID is scoped to the Task and to the caller that sent it: another member, workspace API key or Run using the same ID sends its own input, and your access to the Task is checked again before a retry is answered. A send that failed committed nothing, so retrying it with the same ID sends it.
curl -sS -X POST "$MELSO_URL/api/tasks/$ID/input" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{"content":"Include operating costs.","client_request_id":"8b2f6c1e-4a7d-4f5e-9c3b-1d2e3f4a5b6c"}'A workflow step that starts without the previous step's session and is never retried automatically:
curl -sS -X POST "$MELSO_URL/api/tasks/$ID/input" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{"content":"Publish the approved draft.","fresh_session":true,"max_attempts":1,"client_request_id":"3c9e1f4a-7b2d-4e8f-a1c6-5d0b9e2f7a41"}'GET /api/tasks/{id}/messages
Auth: Workspace member with access to this resource.
Request: Path: Task ID. No pagination.
Response: TaskMessageResponse array holding the oldest 2000 messages, oldest first. A longer conversation’s newest messages are missing here; read them from the page route below.
Status: 200. Authentication and resource-access errors follow Overview.
curl -sS -X GET "$MELSO_URL/api/tasks/$ID/messages" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID"GET /api/tasks/{id}/messages/page
Auth: Workspace member with access to this resource.
Request: Path: Task UUID. Query: limit?:integer, before_created_at?:RFC3339Nano, before_id?:UUID (cursor pair).
Response: TaskMessagesPageResponse; default limit 50, valid range 1–100, chronological order within each page. The first page, without a cursor, holds the newest messages. For older ones, pass next_cursor.created_at and next_cursor.id as before_created_at and before_id while has_more is true. Queued follow-ups that are not yet next in line are omitted.
Status: 200; 400 invalid limit or cursor. Authentication and resource-access errors follow Overview.
curl -sS -X GET "$MELSO_URL/api/tasks/$ID/messages/page" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID"POST /api/tasks/{id}/messages
Auth: Workspace member with access to this resource.
Request: content:string, attachment_ids?:UUID[].
Response: TaskMessageResponse. A member’s message reopens a completed Task but never starts a Run.
Status: 201; 400 empty content/invalid attachments; 409 cancelled Task. Authentication and resource-access errors follow Overview.
curl -sS -X POST "$MELSO_URL/api/tasks/$ID/messages" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID" \
-H "Content-Type: application/json" -d '{"content":"For reference: the budget is approved."}'PUT /api/task-messages/{messageId}
Auth: Workspace member with access to this resource.
Request: content:string, optional attachment_ids:UUID[]; the author or a workspace owner/admin.
Response: TaskMessageResponse.
Status: 200; 400 invalid content; 403 neither author nor admin. Authentication and resource-access errors follow Overview.
curl -sS -X PUT "$MELSO_URL/api/task-messages/$MESSAGE_ID" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID" \
-H "Content-Type: application/json" -d '{"content":"Updated note."}'DELETE /api/task-messages/{messageId}
Auth: Workspace member with access to this resource.
Request: Path: message UUID; the author or a workspace owner/admin.
Response: Empty.
Status: 204; 403 neither author nor admin; 409 message already used for execution. Authentication and resource-access errors follow Overview.
curl -sS -X DELETE "$MELSO_URL/api/task-messages/$MESSAGE_ID" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID"GET /api/tasks/{id}/interactions
Auth: Workspace member with access to this resource.
Request: Path: Task ID.
Response: InteractionRequestResponse array.
Status: 200. Authentication and resource-access errors follow Overview.
curl -sS -X GET "$MELSO_URL/api/tasks/$ID/interactions" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID"POST /api/tasks/{id}/interactions
Auth: Workspace member with access to this resource.
Request: kind:"clarification"|"approval", title?:string, message:string, payload:object, recipient_user_id?:UUID, parent_message_id?:UUID, expires_at?:RFC3339.
Response: InteractionRequestResponse.
Status: 201 created; 200 existing request; 400 invalid payload; 403 invalid source Run. Authentication and resource-access errors follow Overview.
curl -sS -X POST "$MELSO_URL/api/tasks/$ID/interactions" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID" \
-H "Content-Type: application/json" -d '{"kind":"approval","title":"Publish","message":"Publish the draft?","payload":{"version":1}}'GET /api/tasks/{id}/interactions/{interactionId}
Auth: Workspace member with access to this resource.
Request: Path: interaction UUID (and Task ID for the scoped route).
Response: InteractionRequestResponse.
Status: 200. Authentication and resource-access errors follow Overview.
curl -sS -X GET "$MELSO_URL/api/tasks/$ID/interactions/$INTERACTION_ID" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID"POST /api/tasks/{id}/interactions/{interactionId}/respond
Auth: Respond/decline: the authenticated recipient, or a current workspace owner or admin. The responder is recorded and supplies authority for any continuation Run. Cancel: authorized member, or the original active source Run.
Request: response:object for respond; optional audit_text:string. Approval response uses decision:"approve_once"|"deny"|"decline"; clarification uses answers:object keyed by question IDs.
Response: InteractionRequestResponse.
Status: 200; 400 invalid answers; 403 actor/recipient mismatch; 409 already resolved or continuation conflict. Authentication and resource-access errors follow Overview.
curl -sS -X POST "$MELSO_URL/api/tasks/$ID/interactions/$INTERACTION_ID/respond" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID" \
-H "Content-Type: application/json" -d '{"response":{"decision":"approve_once"}}'POST /api/tasks/{id}/interactions/{interactionId}/decline
Auth: Respond/decline: the authenticated recipient, or a current workspace owner or admin. The responder is recorded and supplies authority for any continuation Run. Cancel: authorized member, or the original active source Run.
Request: response:object for respond; optional audit_text:string. Approval response uses decision:"approve_once"|"deny"|"decline"; clarification uses answers:object keyed by question IDs.
Response: InteractionRequestResponse.
Status: 200; 400 invalid answers; 403 actor/recipient mismatch; 409 already resolved or continuation conflict. Authentication and resource-access errors follow Overview.
curl -sS -X POST "$MELSO_URL/api/tasks/$ID/interactions/$INTERACTION_ID/decline" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID" \
-H "Content-Type: application/json" -d '{"audit_text":"No longer needed."}'POST /api/tasks/{id}/interactions/{interactionId}/cancel
Auth: Respond/decline: the authenticated recipient, or a current workspace owner or admin. The responder is recorded and supplies authority for any continuation Run. Cancel: authorized member, or the original active source Run.
Request: response:object for respond; optional audit_text:string. Approval response uses decision:"approve_once"|"deny"|"decline"; clarification uses answers:object keyed by question IDs.
Response: InteractionRequestResponse.
Status: 200; 400 invalid answers; 403 actor/recipient mismatch; 409 already resolved or continuation conflict. Authentication and resource-access errors follow Overview.
curl -sS -X POST "$MELSO_URL/api/tasks/$ID/interactions/$INTERACTION_ID/cancel" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID" \
-H "Content-Type: application/json" -d '{"audit_text":"No longer needed."}'GET /api/interactions/{interactionId}
Auth: Workspace member with access to this resource.
Request: Path: interaction UUID (and Task ID for the scoped route).
Response: InteractionRequestResponse.
Status: 200. Authentication and resource-access errors follow Overview.
curl -sS -X GET "$MELSO_URL/api/interactions/$INTERACTION_ID" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID"POST /api/interactions/{interactionId}/respond
Auth: Respond/decline: the authenticated recipient, or a current workspace owner or admin. The responder is recorded and supplies authority for any continuation Run. Cancel: authorized member, or the original active source Run.
Request: response:object for respond; optional audit_text:string. Approval response uses decision:"approve_once"|"deny"|"decline"; clarification uses answers:object keyed by question IDs.
Response: InteractionRequestResponse.
Status: 200; 400 invalid answers; 403 actor/recipient mismatch; 409 already resolved or continuation conflict. Authentication and resource-access errors follow Overview.
curl -sS -X POST "$MELSO_URL/api/interactions/$INTERACTION_ID/respond" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID" \
-H "Content-Type: application/json" -d '{"response":{"decision":"approve_once"}}'POST /api/interactions/{interactionId}/decline
Auth: Respond/decline: the authenticated recipient, or a current workspace owner or admin. The responder is recorded and supplies authority for any continuation Run. Cancel: authorized member, or the original active source Run.
Request: response:object for respond; optional audit_text:string. Approval response uses decision:"approve_once"|"deny"|"decline"; clarification uses answers:object keyed by question IDs.
Response: InteractionRequestResponse.
Status: 200; 400 invalid answers; 403 actor/recipient mismatch; 409 already resolved or continuation conflict. Authentication and resource-access errors follow Overview.
curl -sS -X POST "$MELSO_URL/api/interactions/$INTERACTION_ID/decline" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID" \
-H "Content-Type: application/json" -d '{"audit_text":"No longer needed."}'POST /api/interactions/{interactionId}/cancel
Auth: Respond/decline: the authenticated recipient, or a current workspace owner or admin. The responder is recorded and supplies authority for any continuation Run. Cancel: authorized member, or the original active source Run.
Request: response:object for respond; optional audit_text:string. Approval response uses decision:"approve_once"|"deny"|"decline"; clarification uses answers:object keyed by question IDs.
Response: InteractionRequestResponse.
Status: 200; 400 invalid answers; 403 actor/recipient mismatch; 409 already resolved or continuation conflict. Authentication and resource-access errors follow Overview.
curl -sS -X POST "$MELSO_URL/api/interactions/$INTERACTION_ID/cancel" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID" \
-H "Content-Type: application/json" -d '{"audit_text":"No longer needed."}'TaskMessageResponse
Source: server/internal/handler/task_message.go (TaskMessageResponse). Referenced nested objects retain their handler-defined fields; clients should tolerate additional fields.
| Field | JSON type |
|---|---|
id | string |
task_id | string |
author_type | string |
author_id | string |
content | string |
role | string |
kind | string |
run_id | string or null (may be omitted) |
failure_reason | string or null (may be omitted) |
elapsed_ms | integer or null (may be omitted) |
created_at | string |
updated_at | string |
reactions | ReactionResponse[] |
attachments | AttachmentResponse[] |
final | boolean or null (may be omitted) |
InteractionRequestResponse
Source: server/internal/handler/interaction_request.go (InteractionRequestResponse). Referenced nested objects retain their handler-defined fields; clients should tolerate additional fields.
| Field | JSON type |
|---|---|
id | string |
workspace_id | string |
task_id | string |
source_run_id | string or null |
parent_message_id | string or null |
recipient_user_id | string |
kind | string |
status | string |
title | string or null |
message | string |
payload | JSON |
response | JSON |
audit_text | string or null |
response_actor_type | string or null |
response_actor_id | string or null |
expires_at | string or null |
resolved_at | string or null |
created_at | string |
updated_at | string |
TaskInputResponse
Source: server/internal/handler/task_input.go (TaskInputResponse). Referenced nested objects retain their handler-defined fields; clients should tolerate additional fields.
| Field | JSON type |
|---|---|
message_id | string |
run_id | string |
queued | boolean |
attachment_ids | string[] or null: the attachments bound to the message; null when the request named none |
created_at | string |
TaskMessagesPageResponse
Source: server/internal/handler/task_input.go (TaskMessagesPageResponse). Referenced nested objects retain their handler-defined fields; clients should tolerate additional fields.
| Field | JSON type |
|---|---|
messages | TaskMessageResponse[] |
limit | integer |
has_more | boolean |
next_cursor | TaskMessagesCursorResponse or null (may be omitted) |
TaskMessagesCursorResponse
Source: server/internal/handler/task_input.go (TaskMessagesCursorResponse). Referenced nested objects retain their handler-defined fields; clients should tolerate additional fields.
| Field | JSON type |
|---|---|
created_at | string |
id | string |
GET /api/tasks/{id}/timeline
Auth: Workspace member with access to this resource.
Request: No pagination. Legacy limit, before, after, around queries are rejected.
Response: TimelineEntry array in ascending time order; comments and activities capped separately at 2000 each. X-Timeline-Truncated reports truncation.
Status: 200; 400 pagination unsupported. Authentication and resource-access errors follow Overview.
curl -sS -X GET "$MELSO_URL/api/tasks/$ID/timeline" \
-H "Authorization: Bearer $MELSO_TOKEN" -H "X-Workspace-ID: $WORKSPACE_ID"TimelineEntry
Source: server/internal/handler/activity.go (TimelineEntry). Referenced nested objects retain their handler-defined fields; clients should tolerate additional fields.
| Field | JSON type |
|---|---|
type | string |
id | string |
actor_type | string |
actor_id | string |
created_at | string |
action | string or null (may be omitted) |
details | JSON (may be omitted) |
content | string or null (may be omitted) |
parent_id | string or null (may be omitted) |
updated_at | string or null (may be omitted) |
comment_type | string or null (may be omitted) |
reactions | ReactionResponse[] (may be omitted) |
attachments | AttachmentResponse[] (may be omitted) |
resolved_at | string or null (may be omitted) |
resolved_by_type | string or null (may be omitted) |
resolved_by_id | string or null (may be omitted) |
run_id | string or null (may be omitted) |
Clarification payloads
payload.questions contains 1–20 objects with unique id:string, label:string, type:string, optional required:boolean, and options:[{value:string,label:string}] for selection types. Supported types are text, textarea, date, boolean, number, single_select, multi_select. Answers use matching JSON types; multi-select is an array of strings. Omit optional unanswered keys; an empty string is not a skip. Required answers cannot be omitted.
Example respond body: {"response":{"answers":{"approach":"durable","budget":100}}}. Use the authenticated respond curl above with the IDs/types from the stored questions.
Connection interactions use payload.connect:string[] instead of questions. They require the recipient to complete account authorization; posting arbitrary answers does not bypass the server connection check.