Conversations
Create a conversation
/api/v1/workspaces/{workspaceId}/conversationsCreate a new chat conversation in the workspace. The conversation id is generated server-side and returned in the response — use it as {'{'}conversationId{'}'} on subsequent endpoints. The optional body configures the conversation's initial steering (personality, brand voice, target audience, LLM).
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
workspaceId | string | Yes | The workspace the request operates on. Must match the workspace claim in the gateway token. |
Request Body
personalitystringNoFree-text personality description woven into the system prompt for this conversation. If both personality and a brand-voice resolution from brandVoiceId are present, the explicit personality string wins.personalityIdintegerNoIdentifier of a saved personality preset associated with this conversation. Stored for back-reference only — the actual personality text used at generation time still comes from personality.brandVoiceIdstringNoIdentifier of the brand voice that should steer assistant replies. The service resolves the brand voice server-side from this id.targetAudienceIdstringNoIdentifier of the target audience that should steer assistant replies. The service resolves the audience description server-side from this id.modelNamestringNoAn enumeration.Response
idstringStable identifier of the conversation. Generated server-side on POST /conversations; required for all subsequent operations on the conversation.createdAtstringWhen the conversation was first created.modelNamestringLLM used for assistant replies. null falls back to the service-wide default. Returned as a free-form string rather than an enum so historical conversations that were created with models no longer in the accepted-for-create set (see :class:ConversationCreateCmd.modelName) still round-trip through reads.legacyCustomerIdintegerAuthor of the conversation, captured from the gateway token at create time. null for conversations created before this field existed — ownership for those falls back to the api_master.ai_writer_projects.author_id join.Example
- cURL
- Python
- Node.js
- Go
curl -X POST "https://app.neuroflash.com/api/v1/workspaces/{workspace_id}/conversations" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"personality": "string",
"personalityId": 0,
"brandVoiceId": "string",
"targetAudienceId": "string",
"modelName": "string"
}'
import requests
response = requests.post(
f"https://app.neuroflash.com/api/v1/workspaces/{workspace_id}/conversations",
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
json={
"personality": "string",
"personalityId": 0,
"brandVoiceId": "string",
"targetAudienceId": "string",
"modelName": "string"
},
).json()
const response = await fetch(
`https://app.neuroflash.com/api/v1/workspaces/${workspaceId}/conversations`,
{
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"personality": "string",
"personalityId": 0,
"brandVoiceId": "string",
"targetAudienceId": "string",
"modelName": "string"
}),
}
).then((r) => r.json());
body, _ := json.Marshal(map[string]any{
"personality": "string",
"personalityId": 0,
"brandVoiceId": "string",
"targetAudienceId": "string",
"modelName": "string",
})
req, _ := http.NewRequest("POST", "https://app.neuroflash.com/api/v1/workspaces/"+workspaceID+"/conversations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"createdAt": "2026-05-01T09:00:00",
"modelName": "gpt-4",
"legacyCustomerId": 42
}
Send a message and receive a synchronous reply
/api/v1/workspaces/{workspaceId}/conversations/{conversationId}/messagesAppend a user message to the conversation and return the assistant reply once generation finishes. Replaying the same message id is idempotent: the previously generated reply is returned without a new LLM call.
For streaming replies, use POST .../message-streams instead.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
conversationId | string | Yes | Identifier of the conversation the message is added to. |
workspaceId | string | Yes | The workspace the request operates on. Must match the workspace claim in the gateway token. |
Request Body
idstringYesClient-supplied id for the user message. Idempotent: replaying the same id returns the previously generated assistant reply without re-running the LLM.textstringYesThe user message body.createdAtstringYesClient-side timestamp of when the user sent the message.additionalContextarray<object>NoExtra context snippets injected into the prompt for this turn (e.g. product facts, reference excerpts). Each entry has content and optional source.
idintegerYescontentstringYescategory_namestringNocontextSizeintegerNoMaximum number of input tokens of conversation history to feed into the LLM. Older messages are trimmed to fit. Defaults to 16000.Response
idstringStable identifier of the message.textstringThe message body.sourcestringAn enumeration.createdAtstringWhen the message was stored.Example
- cURL
- Python
- Node.js
- Go
curl -X POST "https://app.neuroflash.com/api/v1/workspaces/{workspace_id}/conversations/{conversation_id}/messages" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"id": "string",
"text": "string",
"createdAt": "string",
"additionalContext": [],
"contextSize": 16000
}'
import requests
response = requests.post(
f"https://app.neuroflash.com/api/v1/workspaces/{workspace_id}/conversations/{conversation_id}/messages",
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
json={
"id": "string",
"text": "string",
"createdAt": "string",
"additionalContext": [],
"contextSize": 16000
},
).json()
const response = await fetch(
`https://app.neuroflash.com/api/v1/workspaces/${workspaceId}/conversations/${conversationId}/messages`,
{
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"id": "string",
"text": "string",
"createdAt": "string",
"additionalContext": [],
"contextSize": 16000
}),
}
).then((r) => r.json());
body, _ := json.Marshal(map[string]any{
"id": "string",
"text": "string",
"createdAt": "string",
"additionalContext": []any{},
"contextSize": 16000,
})
req, _ := http.NewRequest("POST", "https://app.neuroflash.com/api/v1/workspaces/"+workspaceID+"/conversations/"+conversationID+"/messages", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
Response:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"text": "What's a catchy headline for a sustainable shoe brand?",
"source": "user",
"createdAt": "2026-05-01T09:01:30"
}
Open a streaming reply for a conversation turn
/api/v1/workspaces/{workspaceId}/conversations/{conversationId}/message-streamsAppend a user message and stream the assistant reply as Server-Sent Events (Content-Type: text/event-stream). Each event carries either a partial delta, the final message payload, or an error.
Only one stream per conversation may be active at a time — a second call returns 409 conversationLocked until the active stream finishes or is cancelled via DELETE .../message-streams. Streams are not persisted: there is no GET to retrieve them.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
conversationId | string | Yes | Identifier of the conversation to stream a reply in. |
workspaceId | string | Yes | The workspace the request operates on. Must match the workspace claim in the gateway token. |
Request Body
idstringYesClient-supplied id for the user message that kicks off the stream. Idempotent: replaying the same id replays the existing assistant reply without re-running the LLM (so be careful with retries — they will NOT trigger fresh generation).textstringYesThe user message body.createdAtstringYesClient-side timestamp of when the user sent the message.additionalContextarray<object>NoExtra context snippets injected into the prompt for this turn (e.g. product facts, reference excerpts). Each entry has content and optional source.
idintegerYescontentstringYescategory_namestringNocontextSizeintegerNoMaximum number of input tokens of conversation history to feed into the LLM. Older messages are trimmed to fit. Defaults to 16000.Example
- cURL
- Python
- Node.js
- Go
curl -X POST "https://app.neuroflash.com/api/v1/workspaces/{workspace_id}/conversations/{conversation_id}/message-streams" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"id": "string",
"text": "string",
"createdAt": "string",
"additionalContext": [],
"contextSize": 16000
}'
import requests
response = requests.post(
f"https://app.neuroflash.com/api/v1/workspaces/{workspace_id}/conversations/{conversation_id}/message-streams",
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
json={
"id": "string",
"text": "string",
"createdAt": "string",
"additionalContext": [],
"contextSize": 16000
},
).json()
const response = await fetch(
`https://app.neuroflash.com/api/v1/workspaces/${workspaceId}/conversations/${conversationId}/message-streams`,
{
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"id": "string",
"text": "string",
"createdAt": "string",
"additionalContext": [],
"contextSize": 16000
}),
}
).then((r) => r.json());
body, _ := json.Marshal(map[string]any{
"id": "string",
"text": "string",
"createdAt": "string",
"additionalContext": []any{},
"contextSize": 16000,
})
req, _ := http.NewRequest("POST", "https://app.neuroflash.com/api/v1/workspaces/"+workspaceID+"/conversations/"+conversationID+"/message-streams", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
Response:
{}
List conversations in the workspace
/api/v1/workspaces/{workspaceId}/conversationsReturn the conversations in the workspace that the current caller authored, newest first, in the standard pagination envelope.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
workspaceId | string | Yes | The workspace the request operates on. Must match the workspace claim in the gateway token. |
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number to retrieve. The first page is 1 (not 0). |
size | integer | 20 | Number of elements per page. Maximum 100. |
Response
pageobject
sizeintegerRequested page size. Echoed back even when data contains fewer elements (e.g. on the last page).totalElementsintegerTotal number of elements across all pages.totalPagesintegerTotal number of pages given the requested size.currentPageintegerPage number returned. The first page is 1 (not 0).dataarray<object>Elements on the current page. May contain fewer than page.size on the last page.
idstringStable identifier of the conversation. Generated server-side on POST /conversations; required for all subsequent operations on the conversation.createdAtstringWhen the conversation was first created.modelNamestringLLM used for assistant replies. null falls back to the service-wide default. Returned as a free-form string rather than an enum so historical conversations that were created with models no longer in the accepted-for-create set (see :class:ConversationCreateCmd.modelName) still round-trip through reads.legacyCustomerIdintegerAuthor of the conversation, captured from the gateway token at create time. null for conversations created before this field existed — ownership for those falls back to the api_master.ai_writer_projects.author_id join.Example
- cURL
- Python
- Node.js
- Go
curl "https://app.neuroflash.com/api/v1/workspaces/{workspace_id}/conversations" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
import requests
response = requests.get(
f"https://app.neuroflash.com/api/v1/workspaces/{workspace_id}/conversations",
headers={"Authorization": f"Bearer {token}"},
).json()
const response = await fetch(
`https://app.neuroflash.com/api/v1/workspaces/${workspaceId}/conversations`,
{ headers: { Authorization: `Bearer ${token}` } }
).then((r) => r.json());
req, _ := http.NewRequest("GET", "https://app.neuroflash.com/api/v1/workspaces/"+workspaceID+"/conversations", nil)
req.Header.Set("Authorization", "Bearer "+token)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
Response:
{
"page": {
"size": 20,
"totalElements": 137,
"totalPages": 7,
"currentPage": 1
},
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"createdAt": "2026-05-01T09:00:00",
"modelName": "gpt-4",
"legacyCustomerId": 42
}
]
}
List messages in a conversation
/api/v1/workspaces/{workspaceId}/conversations/{conversationId}/messagesReturn the messages of the conversation in chronological order (oldest first), in the standard pagination envelope. Offset / limit are derived from the page and size query parameters.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
conversationId | string | Yes | Identifier of the conversation. |
workspaceId | string | Yes | The workspace the request operates on. Must match the workspace claim in the gateway token. |
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number to retrieve. The first page is 1 (not 0). |
size | integer | 20 | Number of elements per page. Maximum 100. |
Response
pageobject
sizeintegerRequested page size. Echoed back even when data contains fewer elements (e.g. on the last page).totalElementsintegerTotal number of elements across all pages.totalPagesintegerTotal number of pages given the requested size.currentPageintegerPage number returned. The first page is 1 (not 0).dataarray<object>Elements on the current page. May contain fewer than page.size on the last page.
idstringStable identifier of the message.textstringThe message body.sourcestringAn enumeration.createdAtstringWhen the message was stored.Example
- cURL
- Python
- Node.js
- Go
curl "https://app.neuroflash.com/api/v1/workspaces/{workspace_id}/conversations/{conversation_id}/messages" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
import requests
response = requests.get(
f"https://app.neuroflash.com/api/v1/workspaces/{workspace_id}/conversations/{conversation_id}/messages",
headers={"Authorization": f"Bearer {token}"},
).json()
const response = await fetch(
`https://app.neuroflash.com/api/v1/workspaces/${workspaceId}/conversations/${conversationId}/messages`,
{ headers: { Authorization: `Bearer ${token}` } }
).then((r) => r.json());
req, _ := http.NewRequest("GET", "https://app.neuroflash.com/api/v1/workspaces/"+workspaceID+"/conversations/"+conversationID+"/messages", nil)
req.Header.Set("Authorization", "Bearer "+token)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
Response:
{
"page": {
"size": 20,
"totalElements": 137,
"totalPages": 7,
"currentPage": 1
},
"data": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"text": "What's a catchy headline for a sustainable shoe brand?",
"source": "user",
"createdAt": "2026-05-01T09:01:30"
}
]
}
Update a conversation
/api/v1/workspaces/{workspaceId}/conversations/{conversationId}Patch a conversation's steering configuration. Omitted fields are left unchanged; sending an explicit null clears a field. The body must contain at least one of personality, brandVoiceId or targetAudienceId.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
conversationId | string | Yes | Identifier of the conversation to update. |
workspaceId | string | Yes | The workspace the request operates on. Must match the workspace claim in the gateway token. |
Request Body
personalityobjectNoReplace the conversation's personality with this string. Send null to clear it. Omit the field to leave it unchanged.personalityIdintegerNoIdentifier of a saved personality preset associated with the conversation. Stored for back-reference only — the actual personality text used at generation time comes from personality.brandVoiceIdobjectNoReplace the conversation's brand-voice steering with the brand voice having this id. The service resolves the brand-voice payload server-side. Send null to clear it; omit the field to leave it unchanged.targetAudienceIdobjectNoReplace the conversation's target-audience steering with the audience having this id. The service resolves the audience description server-side. Send null to clear it; omit the field to leave it unchanged.Example
- cURL
- Python
- Node.js
- Go
curl -X PATCH "https://app.neuroflash.com/api/v1/workspaces/{workspace_id}/conversations/{conversation_id}" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"personality": {},
"personalityId": 0,
"brandVoiceId": {},
"targetAudienceId": {}
}'
import requests
response = requests.patch(
f"https://app.neuroflash.com/api/v1/workspaces/{workspace_id}/conversations/{conversation_id}",
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
json={
"personality": {},
"personalityId": 0,
"brandVoiceId": {},
"targetAudienceId": {}
},
).json()
const response = await fetch(
`https://app.neuroflash.com/api/v1/workspaces/${workspaceId}/conversations/${conversationId}`,
{
method: "PATCH",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"personality": {},
"personalityId": 0,
"brandVoiceId": {},
"targetAudienceId": {}
}),
}
).then((r) => r.json());
body, _ := json.Marshal(map[string]any{
"personality": map[string]any{},
"personalityId": 0,
"brandVoiceId": map[string]any{},
"targetAudienceId": map[string]any{},
})
req, _ := http.NewRequest("PATCH", "https://app.neuroflash.com/api/v1/workspaces/"+workspaceID+"/conversations/"+conversationID+"", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
Response:
{}
Cancel the active streaming reply
/api/v1/workspaces/{workspaceId}/conversations/{conversationId}/message-streamsSignal the service to stop the active SSE stream for the given conversation. The stream's SSE channel will close shortly after (any partial output already produced is kept). Calling this when no stream is active is a no-op and still returns 200.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
conversationId | string | Yes | Identifier of the conversation whose stream to cancel. |
workspaceId | string | Yes | The workspace the request operates on. Must match the workspace claim in the gateway token. |
Example
- cURL
- Python
- Node.js
- Go
curl -X DELETE "https://app.neuroflash.com/api/v1/workspaces/{workspace_id}/conversations/{conversation_id}/message-streams" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
import requests
requests.delete(
f"https://app.neuroflash.com/api/v1/workspaces/{workspace_id}/conversations/{conversation_id}/message-streams",
headers={"Authorization": f"Bearer {token}"},
)
await fetch(
`https://app.neuroflash.com/api/v1/workspaces/${workspaceId}/conversations/${conversationId}/message-streams`,
{
method: "DELETE",
headers: { Authorization: `Bearer ${token}` },
}
);
req, _ := http.NewRequest("DELETE", "https://app.neuroflash.com/api/v1/workspaces/"+workspaceID+"/conversations/"+conversationID+"/message-streams", nil)
req.Header.Set("Authorization", "Bearer "+token)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
Response:
{}