Pushing proactive web messages to a guest with an existing session ID

Hey folks,

Trying to send a proactive message to a customer who’s already got an open web messaging session. The docs are a bit light on this, so I’m using the /api/v2/conversations/messaging endpoint with a POST. I’m passing the externalContactId from the previous session, but I keep getting a 409 Conflict saying the contact is already engaged.

Here’s the payload:

{
 "to": [{"id": "ext_contact_id_here"}],
 "text": "Hey, need help?"
}

Am I missing a flag to force the send, or is there a different endpoint for this?

You’re hitting the 409 because you’re trying to create a new conversation resource for a contact that’s already active in an existing one. The API sees the externalContactId and says, “Hey, this person is already in a chat, don’t spawn another one.”

The docs for POST /api/v2/conversations/messaging are clear: “Creates a new messaging conversation.” That’s not what you want. You want to inject a message into the existing session.

Since you have the sessionId (which is basically the conversationId in Genesys terms), you should be using the specific endpoint for adding parts to a conversation. In C#, it looks like this:

var client = PlatformClientFactory.BuildFromConfiguration();
var body = new WebchatMessagePart
{
 From = new ExternalId { Id = "agent-system-id" },
 To = new ExternalId { Id = guestExternalContactId },
 Type = "customer",
 Text = new List<PlainText> { new PlainText { Text = "Hello from the system!" } }
};

try
{
 await client.ConversationsMessagingApi.PostConversationsMessagingPartsAsync(
 conversationId: existingSessionId, 
 body: body
 );
}
catch (ApiException ex)
{
 // Handle errors
}

Notice I’m using PostConversationsMessagingPartsAsync. This appends the message to the ongoing thread. The From field needs to match a valid participant or system ID depending on your setup, but usually, if it’s a proactive system message, you might need to ensure the routing queue or agent is actually part of that conversation context if you want it to appear as an agent message. If it’s just a bot/system ping, make sure the Type aligns with your widget config.

Also, check if the session is actually still active. If the guest closed the widget but the server session is lingering, you might still get a conflict. Query the conversation status first. GET /api/v2/conversations/messaging/{conversationId} will tell you if it’s active or closed. If it’s closed, you can’t push to it. You’d have to start fresh, but that won’t link to the old history unless you use the previousConversationId param in the new POST, which is a different beast entirely.