Sync API - In Parts

Prev Next

The Sync API In Parts feature transforms the traditional request-response pattern by breaking large responses into manageable, progressive chunks delivered across multiple synchronous HTTP requests. Instead of waiting for a complete response, clients receive partial results immediately and fetch successive segments until the conversation completes. This approach combines the responsiveness and interactivity of streaming protocols with the simplicity and reliability of standard HTTP.

Key Benefits

  • Enhanced User Experience: Enables chat and voice channels to feel more natural by delivering responses progressively. Clients receive partial results as they are generated, so users don’t have to wait for the entire response. This allows for faster perceived performance and more interactive experiences.

  • Event-Driven Control: Supports multiple response types (message, typing, NextPartRequest, ResponseCompleted, endOfConversation), giving developers fine-grained control over the interaction flow. For example, typing indicators can be shown in chat interfaces, or realistic typing sounds and background noise can be triggered in voice applications to enhance conversational authenticity.

  • Simple Integration: Provides streaming-like responsiveness through standard HTTP requests without requiring complex WebSocket protocols or asynchronous infrastructure. Works seamlessly with existing REST API clients.


Quick Start Guide

Enable Parts Mode: Add SendResponseInParts: true header to all requests
Start: Send initial message with type: "message"
Continue: Send type: "NextPartRequest" requests until completion
Stop: When you receive type: "ResponseCompleted" or type: "endOfConversation"




Protocol Overview

Step 1: Initial Request to Start Conversation

The client initiates the interaction by sending an Activity object with type="message". The text field should contain the user's input (can be empty in some scenarios, depending on context).


Here's an example of a request:

curl --location '{{BaseUrl}}/magpie/ext-api/messages/synchronized' \
--header 'Content-Type: application/json' \
--header 'Project: {{ProjectName}}' \
--header 'X-Knovvu-Conversation-Id: {{ConversationId}}' \
--header 'SendResponseInParts: true' \
--header 'Authorization: Bearer {{AccessToken}}' \
--data '{
    "text": "{{UserInput}}",
    "conversation": {
        "id": "{{ConversationId}}"
    },
    "channelId": "ivr-external",
    "type": "message",
    "attachments": [],
    "channelData": {
        "ResponseType": "Text"
    }
}'


Expected Response:

{
    "type": "message",
    "id": "d9efa756-4f8b-4db8-b9f1-dc85e6fc712c",
    "timestamp": null,
    "localTimestamp": null,
    "localTimezone": null,
    "serviceUrl": "http://localhost",
    "channelId": "ivr-external",
    "from": null,
    "conversation": {
        "isGroup": null,
        "conversationType": null,
        "id": "{{ConversationId}}",
        "name": null,
        "aadObjectId": null,
        "role": null,
        "tenantId": null
    },
    "recipient": null,
    "textFormat": null,
    "attachmentLayout": null,
    "membersAdded": null,
    "membersRemoved": null,
    "reactionsAdded": null,
    "reactionsRemoved": null,
    "topicName": null,
    "historyDisclosed": null,
    "locale": null,
    "text": "{{BotResponse}}",
    "speak": null,
    "inputHint": null,
    "summary": null,
    "suggestedActions": null,
    "attachments": [],
    "entities": null,
    "channelData": {
        "IsSynchronizer": false,
        "HasExternalAudioContent": false,
        "SrConfidence": null,
        "BargeInType": 0,
        "CustomProperties": {},
        "CustomAction": null,
        "CustomActionData": null,
        "ProjectName": "{{ProjectName}}",
        "ResponseType": 0,
        "EndUser": {
            "Name": null,
            "Phone": null,
            "Email": null,
            "Twitter": null,
            "Id": null
        }
    },
    "action": null,
    "replyToId": null,
    "label": null,
    "valueType": null,
    "value": null,
    "name": null,
    "relatesTo": null,
    "code": null,
    "expiration": null,
    "importance": null,
    "deliveryMode": null,
    "listenFor": null,
    "textHighlights": null,
    "semanticAction": null,
    "callerId": null
}




Step 2: Fetching Next Parts of the Response

After sending the initial message, the client must repeatedly send requests to retrieve incremental results with type="NextPartRequest" and text="" (empty string).

Supported Response Types:
When the client sends NextPartRequest, the server replies with one of the following response types:

Response Type Description Client Action
typing Server is still processing Continue with NextPartRequest
message Partial response content Continue with NextPartRequest
ResponseCompleted Full response delivered Stop or send new message
endOfConversation Conversation closed Start new conversation
Error Protocol misuse or internal issue Handle error appropriately

The server responds with partial outputs until either type="ResponseCompleted" or type="endOfConversation" response is emitted.


Here's an example of a request:

curl --location '{{BaseUrl}}/magpie/ext-api/messages/synchronized' \
--header 'Content-Type: application/json' \
--header 'Project: {{ProjectName}}' \
--header 'X-Knovvu-Conversation-Id: {{ConversationId}}' \
--header 'SendResponseInParts: true' \
--header 'Authorization: Bearer {{AccessToken}}' \
--data '{
    "text": "",
    "conversation": {
        "id": "{{ConversationId}}"
    },
    "channelId": "ivr-external",
    "type": "NextPartRequest",
    "attachments": [],
    "channelData": {
        "ResponseType": "Text"
    }
}'


Expected Response if More Parts Available

{
    "type": "message",
    "id": "be303d36-5242-403e-b00a-458d79062181",
    "timestamp": null,
    "localTimestamp": null,
    "localTimezone": null,
    "serviceUrl": "http://localhost",
    "channelId": "ivr-external",
    "from": null,
    "conversation": {
        "isGroup": null,
        "conversationType": null,
        "id": "{{ConversationId}}",
        "name": null,
        "aadObjectId": null,
        "role": null,
        "tenantId": null
    },
    "recipient": null,
    "textFormat": null,
    "attachmentLayout": null,
    "membersAdded": null,
    "membersRemoved": null,
    "reactionsAdded": null,
    "reactionsRemoved": null,
    "topicName": null,
    "historyDisclosed": null,
    "locale": null,
    "text": "{{PartialBotResponse}}",
    "speak": null,
    "inputHint": null,
    "summary": null,
    "suggestedActions": null,
    "attachments": [],
    "entities": null,
    "channelData": {
        "IsSynchronizer": false,
        "HasExternalAudioContent": false,
        "SrConfidence": null,
        "BargeInType": 0,
        "CustomProperties": {},
        "CustomAction": null,
        "CustomActionData": null,
        "ProjectName": "{{ProjectName}}",
        "ResponseType": 0,
        "EndUser": {
            "Name": null,
            "Phone": null,
            "Email": null,
            "Twitter": null,
            "Id": null
        }
    },
    "action": null,
    "replyToId": null,
    "label": null,
    "valueType": null,
    "value": null,
    "name": null,
    "relatesTo": null,
    "code": null,
    "expiration": null,
    "importance": null,
    "deliveryMode": null,
    "listenFor": null,
    "textHighlights": null,
    "semanticAction": null,
    "callerId": null
}


Expected Response If Response Completed

{
    "type": "ResponseCompleted",
    "id": null,
    "timestamp": null,
    "localTimestamp": null,
    "localTimezone": null,
    "serviceUrl": "http://localhost",
    "channelId": "ivr-external",
    "from": null,
    "conversation": {
        "isGroup": null,
        "conversationType": null,
        "id": "{{ConversationId}}",
        "name": null,
        "aadObjectId": null,
        "role": null,
        "tenantId": null
    },
    "recipient": null,
    "textFormat": null,
    "attachmentLayout": null,
    "membersAdded": null,
    "membersRemoved": null,
    "reactionsAdded": null,
    "reactionsRemoved": null,
    "topicName": null,
    "historyDisclosed": null,
    "locale": null,
    "text": null,
    "speak": null,
    "inputHint": null,
    "summary": null,
    "suggestedActions": null,
    "attachments": null,
    "entities": null,
    "channelData": {
        "IsSynchronizer": false,
        "HasExternalAudioContent": false,
        "SrConfidence": null,
        "BargeInType": 0,
        "CustomProperties": {},
        "CustomAction": null,
        "CustomActionData": null,
        "ProjectName": "{{ProjectName}}",
        "ResponseType": 0,
        "EndUser": {
            "Name": null,
            "Phone": null,
            "Email": null,
            "Twitter": null,
            "Id": null
        }
    },
    "action": null,
    "replyToId": null,
    "label": null,
    "valueType": null,
    "value": null,
    "name": null,
    "relatesTo": null,
    "code": null,
    "expiration": null,
    "importance": null,
    "deliveryMode": null,
    "listenFor": null,
    "textHighlights": null,
    "semanticAction": null,
    "callerId": null
}


Expected Response If Conversation Ends

{
    "type": "endOfConversation",
    "id": null,
    "timestamp": null,
    "localTimestamp": null,
    "localTimezone": null,
    "serviceUrl": "http://localhost",
    "channelId": "ivr-external",
    "from": null,
    "conversation": {
        "isGroup": null,
        "conversationType": null,
        "id": "{{ConversationId}}",
        "name": null,
        "aadObjectId": null,
        "role": null,
        "tenantId": null
    },
    "recipient": null,
    "textFormat": null,
    "attachmentLayout": null,
    "membersAdded": null,
    "membersRemoved": null,
    "reactionsAdded": null,
    "reactionsRemoved": null,
    "topicName": null,
    "historyDisclosed": null,
    "locale": null,
    "text": null,
    "speak": null,
    "inputHint": null,
    "summary": null,
    "suggestedActions": null,
    "attachments": null,
    "entities": null,
    "channelData": {
        "IsSynchronizer": false,
        "HasExternalAudioContent": false,
        "SrConfidence": null,
        "BargeInType": 0,
        "CustomProperties": {},
        "CustomAction": null,
        "CustomActionData": null,
        "ProjectName": "{{ProjectName}}",
        "ResponseType": 0,
        "EndUser": {
            "Name": null,
            "Phone": null,
            "Email": null,
            "Twitter": null,
            "Id": null
        }
    },
    "action": null,
    "replyToId": null,
    "label": null,
    "valueType": null,
    "value": null,
    "name": null,
    "relatesTo": null,
    "code": null,
    "expiration": null,
    "importance": null,
    "deliveryMode": null,
    "listenFor": null,
    "textHighlights": null,
    "semanticAction": null,
    "callerId": null
}




Barge-in support (Interrupting a Response)

Clients may interrupt an ongoing response by sending a new message request with a BargeInType header. When barge-in type is sent, the server stops current generation and begins generating a response for the new input.

Supported BargeInType Values:

  • Voice - Voice interruption detected
  • Digit - DTMF/keypad input detected
  • None - No interruption


Here's an example of a request:

curl --location '{{BaseUrl}}/magpie/ext-api/messages/synchronized' \
--header 'Content-Type: application/json' \
--header 'Project: {{ProjectName}}' \
--header 'X-Knovvu-Conversation-Id: {{ConversationId}}' \
--header 'SendResponseInParts: true' \
--header 'BargeInType: Voice' \
--header 'Authorization: Bearer {{AccessToken}}' \
--data '{
    "text": "",
    "conversation": {
        "id": "{{ConversationId}}"
    },
    "channelId": "ivr-external",
    "type": "NextPartRequest",
    "attachments": [],
    "channelData": {
        "ResponseType": "Text"
    }
}'




Common Protocol Errors

1. Conversation must start with a message

If the first Activity is not a message, the server rejects the request. Ensure the first request uses type: "message".


Error Response:

{
    "error": {
        "code": null,
        "message": "Protocol Error. A conversation can be started only with a message type of Activity",
        "details": null
    }
}


2. Don't forget to send NextPartRequest requests

After the initial message, client must continue with NextPartRequest requests.


Error Response:

{
    "error": {
        "code": null,
        "message": "Protocol error, send NextPartRequest until ResponseCompleted or endOfConversation. Or set BargeInType explicitly",
        "details": null
    }
}


3. Do not send NextPartRequest after completion

Once ResponseCompleted or endOfConversation has been received, the client must stop sending NextPartRequest. If violated, the server will return an error.


Error Response:

{
    "error": {
        "code": null,
        "message": "Protocol error, there is no part to send. Send an input first",
        "details": null
    }
}




Summary

✅ Start with type: message and user input
✅ Continue with type: NextPartRequest and empty text
✅ Stop after ResponseCompleted or endOfConversation
✅ Handle typing events for enhanced UX
✅ Use BargeInType headers for interruptions
✅ Implement proper error handling and retries

This protocol provides a simple yet powerful way to create responsive, interactive conversations using standard HTTP requests, delivering the benefits of streaming while maintaining the simplicity and reliability of synchronous communication.