1

NOTE: Code snippets below are functional. The "dequeue" error mentioned in this post was based on an existing Assignment Callback external to these scripts. Once the URL was removed and the reservation.dequeue moved to this code, the error was resolved.

We are in the process of developing a chat application using Conversations between two people. I currently have it "wired" up with the following steps when a user initiates the chat:

  1. Conversation is created.
  2. User is created.
  3. User is added to the conversation.
  4. Task is created with conversation meta-data in attributes.

(followed by steps on the other user's session to accept the reservation, etc.)

These steps work as expected, but a "40140 - Failed to issue Dequeue instruction due to missing 'call_sid' property" is generated since the task isn't an incoming phone call. I tried putting the task into the "SMS" Task Channel, but that didn't stop the error.

I couldn't find any specific documentation on creating non-phone call-based tasks so I might be setting up the task routing incorrectly.

Here are code snippets showing how I create (in .NET) the conversation, user, and task, and how I accept (in TaskRouter.js) the reservation.

/***********************************************************************************************************
This code is server-side in .NET
***********************************************************************************************************/
public ConversationCredentials CreateConversation( string program, string name )
{
  var memberId = DateTime.Now.ToString( "yyyyMMdd" );  // Temporary
  TwilioClient.Init( _twilioAccountSid,_twilioAuthToken );

  // If we decide to keep conversations on Twilio, we should replace the memberid with phiid, since member id might change

  var conversation = ConversationResource.Create(
    friendlyName: memberId + "_" + DateTime.Now.ToString( "HHmmss" )
  );

  var conversationCredentials = JoinConversation( conversation.Sid, name );
  var taskSid = CreateTask( program, conversation.Sid, memberId );

  conversationCredentials.taskSid = taskSid;
  
  return conversationCredentials;
}

public ConversationCredentials JoinConversation( string conversationSid, string name )
{
  var identity = name + "_" + DateTime.Now.ToString( "HHmmss" ); // Makes sure the user is unique, in case it's an employee joining more than one chat session)
  TwilioClient.Init( _twilioAccountSid,_twilioAuthToken );

  var participant = ParticipantResource.Create(
    pathConversationSid: conversationSid,
    identity: identity
  );

  var user = UserResource.Update(
    pathSid: identity,
    friendlyName: name
  );

  var token = GetJWT( _twilioConversationServiceSid, name );  // Conversation Service Sid

  var conversationCredentials = new ConversationCredentials();
  
  conversationCredentials.token = token;
  conversationCredentials.conversationSid = conversationSid;
  conversationCredentials.participantSid = participant.Sid;
  conversationCredentials.participantName = name;
  conversationCredentials.participantIdentity = participant.Identity;

  return conversationCredentials;
}

public string CreateTask( string program, string conversationSid, string memberId )
{

  TwilioClient.Init( _twilioAccountSid, _twilioAuthToken );

  var attributes = JsonConvert.SerializeObject( new Dictionary<string,Object>()
  {
    {"conversationSid", conversationSid },
    {"memberId",        memberId        },
    {"program",         program         },
    {"call_sid",        "CHAT"          }
  }, Formatting.Indented);

  var task = TaskResource.Create(
    attributes: attributes,
    workflowSid: _twilioWorkflowSid,
    pathWorkspaceSid: _twilioWorkspaceSid_Nurses,
    taskChannel: "Default"
  );

  return task.Sid;

}
/***********************************************************************************************************
This code is browser-side using TaskRouter.js
NOTE: This handles both voice (works fine) and conversations (the part in question)
***********************************************************************************************************/
registerTaskRouterCallbacks( _this ) : void {
this.worker.on('ready', function(worker) {
  _this.updateButton( worker.activityName, "" );
});

this.worker.on("reservation.created", function(reservation) {
  if ( reservation.task.attributes.type != "CHAT" )
  {
    _this.updateButton( "Call", reservation.task.attributes.from.replace( "+1", "" ) );
    reservation.dequeue();
  } else {
    _this.updateButton( "Chat", reservation.task.attributes.memberId );
    confirm("You have an incoming chat!");
    reservation.accept();
    // This is where the chat window would pop-up
  }
});

this.worker.on("reservation.accepted", function(reservation) {
  _this.worker.update({"ActivitySid": _this.activitySids["Busy"][0].sid});
  _this.updateButton( "Busy", "" );
});
tcbeaton
  • 85
  • 7
  • Are you trying to use the `dequeue` instruction in the assignment callback? – philnash Aug 12 '22 at 23:11
  • According to the docs for dequeue, "Note: This will perform telephony to dequeue a task that was enqueued using the Enqueue TwiML verb." Since this isn't a voice call, there was no enqueue issued, I just issued reservation.create. Incidentally, when using dequeue I receive a different error, "Missing or Invalid 'from' parameter - Dequeue Instruction." I'm executing reservation.accept to accept the reservation, the docs say, "Note: This will NOT perform any telephony." So it seems like accept is correct in this situation, but I'm receiving the failed to dequeue error. – tcbeaton Aug 15 '22 at 14:42
  • Ok, that sounds fine then, I was just ruling out whether you were using `dequeue`. Can you share the code you are using, by editing your question, so I can see what is happening and maybe reproduce it? – philnash Aug 15 '22 at 23:45
  • Thanks, Phil. Code attached to the question. – tcbeaton Aug 16 '22 at 04:10
  • BTW - I tried changing the call_sid to a +1 Twilio number, and I tried adding a phone number to the dequeue instruction as I saw in one of the docs; one or both of those (sorry, don't remember) still threw errors but also rang the number (obviously with no one on the other end). – tcbeaton Aug 16 '22 at 04:38
  • Where does the error get thrown? Calling on `reservation.accept()` should work fine. – philnash Aug 16 '22 at 06:04
  • Yes, the error is thrown at reservation.accept(), although the reservation is still accepted. – tcbeaton Aug 16 '22 at 12:47
  • I'm going to raise this with the TaskRouter team and see if they have any suggestions. I'm a bit lost as to why this would be an issue. – philnash Aug 16 '22 at 12:50
  • I figured out what is causing the error. I have an Assignment URL callback in the workflow that is necessary to dequeue the phone calls when they come in. That is where the dequeue is being issued. I just need to add some logic only to dequeue the phone tasks. – tcbeaton Aug 16 '22 at 13:47
  • Ah, glad you discovered what was going on! That explains why you got an error but the reservation was also accepted. – philnash Aug 16 '22 at 23:55

1 Answers1

0

The "dequeue" error mentioned in this post was based on an existing Assignment Callback external to these scripts. Once the URL was removed and the reservation.dequeue moved to this code, the error was resolved.

tcbeaton
  • 85
  • 7