Skip to content

Send a message and stream the agent's reply.

POST
/conversations/{conversation_id}/messages
curl --request POST \
--url 'https://shiftagent.example.com/conversations/example/messages?stream=true' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "content": "Summarize today'\''s open jobs." }'

Appends a user message and runs the agent. Default response is a streaming application/x-ndjson body (200): the platform’s native agent event stream, one JSON object per line. Pass ?stream=false for a blocking 201 with the completed assistant Message JSON instead.

Stream protocol (X-Shiftagent-Stream-Format: claude-sdk/1)

The stream is raw Claude Agent SDK events passed through verbatim — the adapter translates them into whatever shape its host system needs. Known top-level type values:

  • system — run initialization (carries the session id).
  • stream_event — incremental deltas; text arrives as text_delta chunks inside content_block_delta events.
  • assistant / user — complete turn payloads, including tool calls and tool results.
  • result — the terminal event: run outcome (success or error subtype) plus usage. Exactly one per complete stream.

Interleaved platform lines: {"event":"ping"} keepalives, queued platform notices under capacity hold, and {"object":"platform.event","type":"error"} carrying an RFC 9457 problem when the platform (not the agent) aborts the run — e.g. missing-secret. New event types are additive: ignore unknown lines. A stream that closes without a terminal line is truncated — the run continues server-side; reconcile via listMessages (the assistant message lands in history regardless).

{"object":"platform.event","type":"queued","position":2,"retry_hint_seconds":15}
{"type":"system","subtype":"init","session_id":"…","tools":[…]}
{"type":"stream_event","event":{"type":"content_block_delta","delta":{"type":"text_delta","text":"You have three open jobs"}}}
{"event":"ping"}
{"type":"assistant","message":{"content":[…]}}
{"type":"result","subtype":"success","usage":{…}}

Per-message knobs

  • skill_ids — narrow to specific skills so the agent context stays lean (must be within the conversation’s effective skills; the repository is always the tenant’s).
  • env — plaintext, non-secret run parameters. Never place secret material here — the runtime sees these values verbatim.
  • secrets — write-only alias → value map, vaulted on arrival and scoped to this message’s run (ephemeral; resolved before conversation-, user-, and tenant-scoped secrets). Values never appear in any response; the agent sees only {{secret:ALIAS}} placeholders, resolved by the egress proxy at the network boundary.
  • filler {enabled} — override the filler cascade for this message (tenant → conversation → message; filler output is part of the reply, indistinguishable by design).
  • on_capacityreject (default): 429 capacity-exhausted + Retry-After when no sandbox is available; hold: the stream first emits queued platform notices, bounded by the deployment’s max hold time.

Missing secrets fail fast

A run that references a secret alias vaulted at no scope terminates with a missing-secret platform error line naming the alias (?stream=false422 problem). Re-send the message with the secret attached — a one-step retry; there is no mid-run pause.

conversation_id
required
string
/^con_[A-Za-z0-9]+$/

Internal conversation ID.

Idempotency-Key
string
<= 255 characters

Optional idempotency key (any unique string, e.g. a UUID; max 255 chars). Responses are cached 24h per (key principal, operation, key); replays return the original status and body with Idempotency-Replayed: true. Reusing a key with a different payload responds 409 idempotency-key-conflict.

stream
boolean
default: true

true (default) streams the native agent event stream; false blocks until the run completes and returns the assistant message as JSON.

Media type application/json

Body for createMessage (and initial_message on createConversation).

object
content
required

The user’s message text.

string
>= 1 characters
parts

Optional typed blocks (extensibility).

Array<object>

Typed content block — the extensibility seam for richer runs. Known types: text, tool_call, tool_result; unknown types must be ignored by clients.

object
type
required

Block type (open enum).

string
text

Text content (for text blocks).

string
key
additional properties
any
skill_ids

Per-message skill narrowing — keeps the agent context lean. Must be within the conversation’s effective skills.

Array<string>
env

Plaintext, non-secret run parameters, visible to the agent verbatim. Never place secret material here — use secrets.

object
key
additional properties
string
secrets

Write-only alias → value map, scoped to this message’s run (ephemeral — resolved before conversation-, user-, and tenant-scoped secrets). Never echoed anywhere; the agent sees only {{secret:ALIAS}} placeholders resolved by the egress proxy.

object
>= 1 properties
key
additional properties
string
filler

Per-message filler override (most specific wins).

object
enabled
required

Whether the low-latency filler agent runs for this scope.

boolean
on_capacity

reject429 capacity-exhausted + Retry-After when no sandbox is available; hold → the stream first emits queued platform notices until one frees (bounded by the deployment’s max hold time).

string
default: reject
Allowed values: reject hold
metadata

Free-form string key–value map for host/adapter bookkeeping (e.g. a host-side reference ID). Max 50 keys; values max 500 chars. Replaced wholesale when provided in updates.

object
<= 50 properties
key
additional properties
string
<= 500 characters
Examples

Plain message

{
"content": "Summarize today's open jobs."
}

The native agent event stream (default): raw Claude Agent SDK events plus platform notice lines, terminated by the agent result event (or a platform error line). See the operation description for the full protocol and an end-to-end transcript.

Media type application/x-ndjson

One NDJSON stream line. The stream is the platform’s native agent event stream — raw Claude Agent SDK events passed through verbatim (vocabulary versioned by the X-Shiftagent-Stream-Format response header, currently claude-sdk/1) — interleaved with platform lines:

  • Agent events carry a top-level typesystem, stream_event, assistant, user, and the terminal result.
  • {"event":"ping"} — keepalive.
  • {"object":"platform.event","type":"queued","position":n, "retry_hint_seconds":n} — capacity hold notice.
  • {"object":"platform.event","type":"conversation", "conversation":{…}} — leading line on create/upsert-with- initial_message streams, carrying the conversation.
  • {"object":"platform.event","type":"error","problem":{…}} — terminal platform abort carrying an RFC 9457 problem (e.g. missing-secret).

Clients MUST ignore unknown line shapes (new event types are additive). Exactly one terminal line (result or platform error) ends every complete stream; a stream that closes without one is truncated — reconcile via listMessages.

object
key
additional properties
any
Examples

Platform notice: held for capacity (on_capacity=hold)

{
"object": "platform.event",
"type": "queued",
"position": 2,
"retry_hint_seconds": 15
}
X-Shiftagent-Stream-Format
string

Stream vocabulary version (e.g. claude-sdk/1).

Completed assistant message (only with ?stream=false). The user message and this reply both land in history.

Media type application/json

A persisted conversation message. Write-only request fields (secrets) are never present; env is echoed as sent (non-secret by contract).

object
object
required
string
Allowed value: message
id
required
string
/^msg_[A-Za-z0-9]+$/
conversation_id
required
string
/^con_[A-Za-z0-9]+$/
role
required

Author role.

string
Allowed values: user assistant system
content
required

Full text content. Assistant content references secrets only by alias ({{secret:ALIAS}}) — never by value.

string
parts

Typed content blocks.

Array<object>

Typed content block — the extensibility seam for richer runs. Known types: text, tool_call, tool_result; unknown types must be ignored by clients.

object
type
required

Block type (open enum).

string
text

Text content (for text blocks).

string
key
additional properties
any
skill_ids

Per-message skill narrowing used for this run.

Array<string> | null
env

Plaintext run parameters as sent (never secret material by contract).

object | null
status
required

failed — the run errored or was aborted by the platform (e.g. missing-secret).

string
Allowed values: completed in_progress failed
usage
One of:

Token accounting for an assistant message.

object
input_tokens
required

Tokens consumed composing the run input.

integer
output_tokens
required

Tokens generated.

integer
metadata

Free-form string key–value map for host/adapter bookkeeping (e.g. a host-side reference ID). Max 50 keys; values max 500 chars. Replaced wholesale when provided in updates.

object
<= 50 properties
key
additional properties
string
<= 500 characters
created_at
required

RFC 3339 / ISO 8601 timestamp, UTC.

string format: date-time
Example
{
"object": "message",
"role": "user",
"status": "completed"
}

Missing or invalid credentials — no bearer token, an unknown/revoked sk_int_ key, or an expired platform JWT.

Media type application/problem+json

RFC 9457 problem+json error envelope. type is a URI under https://shiftagent.example.com/problems/{slug} (deployment host substituted); see the API-level problem registry for every slug.

object
type
required

Problem type URI (registry slug).

string format: uri-reference
title
required

Short, human-readable summary of the problem type.

string
status
required

HTTP status code.

integer format: int32
detail

Human-readable explanation specific to this occurrence.

string
instance

URI reference identifying this occurrence.

string format: uri-reference
request_id

Correlation ID for support and log lookup.

string
conflicting_resource_id

On name-conflict, external-id-conflict, and resource-in-use: the ID of the existing/depended-on resource — fetch it and continue (replay recovery).

string
errors

On validation-error, field-level details.

Array<object>
object
pointer
required

JSON pointer to the offending field.

string
message
required

What failed.

string
Examples
Example unauthorized

Missing or invalid bearer token

{
"type": "https://shiftagent.example.com/problems/insufficient-scope",
"title": "Unauthorized",
"status": 401,
"detail": "Provide a valid sk_int_ service key or platform JWT.",
"request_id": "req_01hzx8auth001"
}

Forbidden — tenant-suspended (writes to a suspended tenant) or insufficient-scope (key/token lacks the scope or a platform JWT reaches beyond its user).

Media type application/problem+json

RFC 9457 problem+json error envelope. type is a URI under https://shiftagent.example.com/problems/{slug} (deployment host substituted); see the API-level problem registry for every slug.

object
type
required

Problem type URI (registry slug).

string format: uri-reference
title
required

Short, human-readable summary of the problem type.

string
status
required

HTTP status code.

integer format: int32
detail

Human-readable explanation specific to this occurrence.

string
instance

URI reference identifying this occurrence.

string format: uri-reference
request_id

Correlation ID for support and log lookup.

string
conflicting_resource_id

On name-conflict, external-id-conflict, and resource-in-use: the ID of the existing/depended-on resource — fetch it and continue (replay recovery).

string
errors

On validation-error, field-level details.

Array<object>
object
pointer
required

JSON pointer to the offending field.

string
message
required

What failed.

string
Examples
Example tenant_suspended

Suspended tenant rejects conversation writes

{
"type": "https://shiftagent.example.com/problems/tenant-suspended",
"title": "Tenant suspended",
"status": 403,
"detail": "Tenant tnt_01hzx8acme001 is suspended; conversation writes are rejected.",
"request_id": "req_01hzx8sus001"
}

Not found — the resource does not exist, was deprovisioned, or lies outside the integration key’s subtree (indistinguishable by design).

Media type application/problem+json

RFC 9457 problem+json error envelope. type is a URI under https://shiftagent.example.com/problems/{slug} (deployment host substituted); see the API-level problem registry for every slug.

object
type
required

Problem type URI (registry slug).

string format: uri-reference
title
required

Short, human-readable summary of the problem type.

string
status
required

HTTP status code.

integer format: int32
detail

Human-readable explanation specific to this occurrence.

string
instance

URI reference identifying this occurrence.

string format: uri-reference
request_id

Correlation ID for support and log lookup.

string
conflicting_resource_id

On name-conflict, external-id-conflict, and resource-in-use: the ID of the existing/depended-on resource — fetch it and continue (replay recovery).

string
errors

On validation-error, field-level details.

Array<object>
object
pointer
required

JSON pointer to the offending field.

string
message
required

What failed.

string
Examples
Example not_found

Unknown resource

{
"type": "https://shiftagent.example.com/problems/not-found",
"title": "Not found",
"status": 404,
"detail": "No tenant with external_id acme:tenant:999999.",
"request_id": "req_01hzx8nf001"
}

Conflict — name-conflict / external-id-conflict (unique name or external ID taken; conflicting_resource_id names the holder — fetch it and continue), resource-in-use (guarded delete refused), cross-tenant (referenced resource belongs to another tenant), conversation-archived (write to an archived conversation), or idempotency-key-conflict (same key, different payload).

Media type application/problem+json

RFC 9457 problem+json error envelope. type is a URI under https://shiftagent.example.com/problems/{slug} (deployment host substituted); see the API-level problem registry for every slug.

object
type
required

Problem type URI (registry slug).

string format: uri-reference
title
required

Short, human-readable summary of the problem type.

string
status
required

HTTP status code.

integer format: int32
detail

Human-readable explanation specific to this occurrence.

string
instance

URI reference identifying this occurrence.

string format: uri-reference
request_id

Correlation ID for support and log lookup.

string
conflicting_resource_id

On name-conflict, external-id-conflict, and resource-in-use: the ID of the existing/depended-on resource — fetch it and continue (replay recovery).

string
errors

On validation-error, field-level details.

Array<object>
object
pointer
required

JSON pointer to the offending field.

string
message
required

What failed.

string
Examples

Named sub-resource already exists — recoverable

{
"type": "https://shiftagent.example.com/problems/name-conflict",
"title": "Name conflict",
"status": 409,
"detail": "A role named \"csr\" already exists in this tenant.",
"conflicting_resource_id": "rol_01hzx8csr001",
"request_id": "req_01hzx8conf01"
}

Unprocessable — validation-error (schema/semantic validation failed; errors[] lists JSON-pointer details), role-required (the user has no role assigned, so no conversation context can resolve), or missing-secret (a referenced alias is vaulted at no scope).

Media type application/problem+json

RFC 9457 problem+json error envelope. type is a URI under https://shiftagent.example.com/problems/{slug} (deployment host substituted); see the API-level problem registry for every slug.

object
type
required

Problem type URI (registry slug).

string format: uri-reference
title
required

Short, human-readable summary of the problem type.

string
status
required

HTTP status code.

integer format: int32
detail

Human-readable explanation specific to this occurrence.

string
instance

URI reference identifying this occurrence.

string format: uri-reference
request_id

Correlation ID for support and log lookup.

string
conflicting_resource_id

On name-conflict, external-id-conflict, and resource-in-use: the ID of the existing/depended-on resource — fetch it and continue (replay recovery).

string
errors

On validation-error, field-level details.

Array<object>
object
pointer
required

JSON pointer to the offending field.

string
message
required

What failed.

string
Examples

Field-level validation failure

{
"type": "https://shiftagent.example.com/problems/validation-error",
"title": "Validation error",
"status": 422,
"detail": "One or more fields failed validation.",
"errors": [
{
"pointer": "/skill_access/skill_ids/0",
"message": "skl_01hzx8unknown does not belong to the tenant's repository."
}
],
"request_id": "req_01hzx8val001"
}

Too many requests — capacity-exhausted (no sandbox available, or the maximum hold time elapsed under on_capacity=hold) or rate-limited. Honor Retry-After.

Media type application/problem+json

RFC 9457 problem+json error envelope. type is a URI under https://shiftagent.example.com/problems/{slug} (deployment host substituted); see the API-level problem registry for every slug.

object
type
required

Problem type URI (registry slug).

string format: uri-reference
title
required

Short, human-readable summary of the problem type.

string
status
required

HTTP status code.

integer format: int32
detail

Human-readable explanation specific to this occurrence.

string
instance

URI reference identifying this occurrence.

string format: uri-reference
request_id

Correlation ID for support and log lookup.

string
conflicting_resource_id

On name-conflict, external-id-conflict, and resource-in-use: the ID of the existing/depended-on resource — fetch it and continue (replay recovery).

string
errors

On validation-error, field-level details.

Array<object>
object
pointer
required

JSON pointer to the offending field.

string
message
required

What failed.

string
Examples
Example capacity_exhausted

Sandbox pool exhausted (on_capacity=reject)

{
"type": "https://shiftagent.example.com/problems/capacity-exhausted",
"title": "Capacity exhausted",
"status": 429,
"detail": "No sandbox available; retry after the indicated delay or use on_capacity=hold.",
"request_id": "req_01hzx8cap001"
}
Retry-After
integer

Seconds to wait before retrying.