title: Cannot authenticate Watson Assistant credentials using Unity SDK
body: <p>Every time I try to connect to my Watson Assistant workspace using the Unity SDK, I get an error that looks like this: </p>
  
  <p>[RESTConnector.ProcessRequestQueue()][ERROR] URL: <a href="https://gateway.watsonplatform.net/assistant/api/v2/assistants/5332aca6-7a09-4485-ae17-be60a002ce39/sessions//message?version=2018-11-08" rel="nofollow noreferrer">https://gateway.watsonplatform.net/assistant/api/v2/assistants/5332aca6-7a09-4485-ae17-be60a002ce39/sessions//message?version=2018-11-08</a>, ErrorCode: 404, Error: 404 Not Found, Response: {"error":"Resource not found","code":404}</p>
  
  <p>I've tried deleting and regenerating the credentials. I've triple checked that I'm using all the right credentials from bluemix. I am configured to the US-south so I believe I am using the correct url (<a href="https://gateway.watsonplatform.net/assistant/api" rel="nofollow noreferrer">https://gateway.watsonplatform.net/assistant/api</a>). This is really baffling because I am not having any problems with speech-to-text or text-to-speech. My only other thought it that it is a problem with the Assistant V2 beta but I don't see an option to create an assistant with V1.</p>
  
  <p>Here is my code for reference:</p>
  
  <pre><code>using System.Collections;
  using System.Collections.Generic;
  using UnityEngine;
  using IBM.Watson.DeveloperCloud.Services.TextToSpeech.v1;
  using IBM.Watson.DeveloperCloud.Logging;
  using IBM.Watson.DeveloperCloud.Utilities;
  using IBM.Watson.DeveloperCloud.Connection;
  using CrazyMinnow.SALSA; // Import SALSA from the CrazyMinnow namespace
  //using IBM.Watson.DeveloperCloud.Services.Assistant.v1;
  using IBM.WatsonDeveloperCloud.Assistant.v2;
  using FullSerializer;
  using UnityEngine.UI;
  using IBM.Watson.DeveloperCloud.Services.SpeechToText.v1;
  using IBM.Watson.DeveloperCloud.DataTypes;
  using IBM.Watson.DeveloperCloud.Services.Assistant.v2;
  
  
  public class WatsonTTS : MonoBehaviour
  {
      public string myName;
      public RandomEyes3D randomEyes;
      public GameObject[] lookTargets;
  
      #region PLEASE SET THESE VARIABLES IN THE INSPECTOR
      [SerializeField]
      private string _STT_username;
      [SerializeField]
      private string _STT_password;
      [SerializeField]
      private string _STT_url;
      [SerializeField]
      private string _TTS_username;
      [SerializeField]
      private string _TTS_password;
      [SerializeField]
      private string _TTS_url;
      [SerializeField]
      private string _Assistant_username;
      [SerializeField]
      private string _Assistant_password;
      [SerializeField]
      private string _Assistant_url;
      [SerializeField]
      private string _Assistant_iamApikey;
      [SerializeField]
      private string _Assistant_iamUrl;
      [SerializeField]
      private string _Assistant_workspaceId;
      [SerializeField]
      private string _Assistant_versionDate;
      #endregion
      //as this field is Public it is set in the Inspector
      public Text ResultsField;
  
      private bool _createSessionTested = false;
      private bool _messageTested = false;
      private bool _deleteSessionTested = false;
      private string _sessionId;
  
      private int _recordingRoutine = 0;
      private string _microphoneID = null;
      private AudioClip _recording = null;
      private int _recordingBufferSize = 1;
      private int _recordingHZ = 22050;
  
      private SpeechToText _speechToText;
      private Assistant _service;
      //private bool _messageTested = false;
  
      private fsSerializer _serializer = new fsSerializer();
      private MessageContext _context = null;
      private bool _waitingForResponse = true;
      private float wait;
      private bool check;
  
      TextToSpeech _textToSpeech;
      private AudioClip audioClip; // Link an AudioClip for Salsa3D to play
      private Salsa3D salsa3D;
      private AudioSource audioSrc;
  
      private string TTS_content = "";
      public bool play = false;
  
      // Use this for initialization
      void Start()
      {
  
          LogSystem.InstallDefaultReactors();
  
          audioSrc = GetComponent&lt;AudioSource&gt;(); // Get the SALSA AudioSource from this GameObject
  
          //  Create credential and instantiate service
          /*Credentials CONVcredentials = new Credentials(_CONV_username, _CONV_password, _CONV_url);
          _service = new Assistant(CONVcredentials);
          _service.VersionDate = _CONV_versionDate;*/
  
          //  Create credential and instantiate service
          Credentials TTScredentials = new Credentials(_TTS_username, _TTS_password, _TTS_url);
          _textToSpeech = new TextToSpeech(TTScredentials);
  
          //  Create credential and instantiate service
          Credentials STTcredentials = new Credentials(_STT_username, _STT_password, _STT_url);
          _speechToText = new SpeechToText(STTcredentials);
  
          LogSystem.InstallDefaultReactors();
          Runnable.Run(CreateService());
  
  
  
      }
  
      private IEnumerator CreateService()
      {
          //  Create credential and instantiate service
          Credentials credentials = null;
          if (!string.IsNullOrEmpty(_Assistant_username) &amp;&amp; !string.IsNullOrEmpty(_Assistant_password))
          {
              //  Authenticate using username and password
              credentials = new Credentials(_Assistant_username, _Assistant_password, _Assistant_url);
          }
          else if (!string.IsNullOrEmpty(_Assistant_iamApikey))
          {
              //  Authenticate using iamApikey
              TokenOptions tokenOptions = new TokenOptions()
              {
                  IamApiKey = _Assistant_iamApikey,
                  IamUrl = _Assistant_iamUrl
              };
  
              credentials = new Credentials(tokenOptions, _Assistant_url);
  
              //  Wait for tokendata
              while (!credentials.HasIamTokenData())
                  yield return null;
          }
          else
          {
              throw new WatsonException("Please provide either username and password or IAM apikey to authenticate the service.");
          }
  
          _service = new Assistant(credentials);
          _service.VersionDate = _Assistant_versionDate;
  
          //  Message
          MessageInput input = new MessageInput();
          //Say HELLO - start the conversation
          input.Text = "first hello";
          MessageRequest messageRequest = new MessageRequest()
          {
              Input = input
          };
          _service.Message(OnMessage, OnFail, _Assistant_workspaceId, _sessionId);
  
      }
  
      private void OnDeleteSession(object response, Dictionary&lt;string, object&gt; customData)
      {
          Log.Debug("ExampleAssistantV2.OnDeleteSession()", "Session deleted.");
          _createSessionTested = true;
      }
  
  
      private void OnCreateSession(SessionResponse response, Dictionary&lt;string, object&gt; customData)
      {
          Log.Debug("ExampleAssistantV2.OnCreateSession()", "Session: {0}", response.SessionId);
          _sessionId = response.SessionId;
          _createSessionTested = true;
      }
  
      private void OnFail(RESTConnector.Error error, Dictionary&lt;string, object&gt; customData)
      {
          Log.Debug("ExampleAssistantV2.OnFail()", "Call failed: {0}: {1}", error.ErrorCode, error.ErrorMessage);
      }
  
      public bool Active
      {
          get { return _speechToText.IsListening; }
          set
          {
              if (value &amp;&amp; !_speechToText.IsListening)
              {
                  _speechToText.DetectSilence = true;
                  _speechToText.EnableWordConfidence = true;
                  _speechToText.EnableTimestamps = true;
                  _speechToText.SilenceThreshold = 0.01f;
                  _speechToText.MaxAlternatives = 0;
                  _speechToText.EnableInterimResults = true;
                  _speechToText.OnError = OnSTTError;
                  _speechToText.InactivityTimeout = -1;
                  _speechToText.ProfanityFilter = false;
                  _speechToText.SmartFormatting = true;
                  _speechToText.SpeakerLabels = false;
                  _speechToText.WordAlternativesThreshold = null;
                  _speechToText.StartListening(OnSTTRecognize, OnSTTRecognizeSpeaker);
              }
              else if (!value &amp;&amp; _speechToText.IsListening)
              {
                  _speechToText.StopListening();
              }
          }
      }
  
  
      private void StartRecording()
      {
          if (_recordingRoutine == 0)
          {
              UnityObjectUtil.StartDestroyQueue();
              _recordingRoutine = Runnable.Run(RecordingHandler());
          }
      }
  
      private void StopRecording()
      {
          if (_recordingRoutine != 0)
          {
              Microphone.End(_microphoneID);
              Runnable.Stop(_recordingRoutine);
              _recordingRoutine = 0;
          }
      }
  
      private void OnSTTError(string error)
      {
          Active = false;
          Log.Debug("STT.OnSTTError()", "Error! {0}", error);
      }
  
  
      private IEnumerator RecordingHandler()
      {
          //      Log.Debug("ExampleStreaming.RecordingHandler()", "devices: {0}", Microphone.devices);
          _recording = Microphone.Start(_microphoneID, true, _recordingBufferSize, _recordingHZ);
          yield return null;      // let _recordingRoutine get set..
  
          if (_recording == null)
          {
              StopRecording();
              yield break;
          }
  
          bool bFirstBlock = true;
          int midPoint = _recording.samples / 2;
          float[] samples = null;
  
          while (_recordingRoutine != 0 &amp;&amp; _recording != null)
          {
              int writePos = Microphone.GetPosition(_microphoneID);
              if (writePos &gt; _recording.samples || !Microphone.IsRecording(_microphoneID))
              {
                  Log.Error("STT.RecordingHandler()", "Microphone disconnected.");
  
                  StopRecording();
                  yield break;
              }
  
              if ((bFirstBlock &amp;&amp; writePos &gt;= midPoint)
                  || (!bFirstBlock &amp;&amp; writePos &lt; midPoint))
              {
                  // front block is recorded, make a RecordClip and pass it onto our callback.
                  samples = new float[midPoint];
                  _recording.GetData(samples, bFirstBlock ? 0 : midPoint);
  
                  AudioData record = new AudioData();
                  record.MaxLevel = Mathf.Max(Mathf.Abs(Mathf.Min(samples)), Mathf.Max(samples));
                  record.Clip = AudioClip.Create("Recording", midPoint, _recording.channels, _recordingHZ, false);
                  record.Clip.SetData(samples, 0);
  
                  _speechToText.OnListen(record);
  
                  bFirstBlock = !bFirstBlock;
              }
              else
              {
                  // calculate the number of samples remaining until we ready for a block of audio, 
                  // and wait that amount of time it will take to record.
                  int remaining = bFirstBlock ? (midPoint - writePos) : (_recording.samples - writePos);
                  float timeRemaining = (float)remaining / (float)_recordingHZ;
  
                  yield return new WaitForSeconds(timeRemaining);
              }
  
          }
  
          yield break;
      }
  
      //  private void OnSTTRecognize(SpeechRecognitionEvent result)
      //  updated for Watson SDK 2.4.0 compatability
      private void OnSTTRecognize(SpeechRecognitionEvent result, Dictionary&lt;string, object&gt; customData)
      {
          if (result != null &amp;&amp; result.results.Length &gt; 0)
          {
              foreach (var res in result.results)
              {
                  foreach (var alt in res.alternatives)
                  {
                      string text = string.Format("{0} ({1}, {2:0.00})\n", alt.transcript, res.final ? "Final" : "Interim", alt.confidence);
                      //                  Log.Debug("STT.OnSTTRecognize()", text);
                      ResultsField.text = text;
  
                      //only send to CONV once we know the user has stopped talking
                      if (res.final)
                      {
                          string _conversationString = alt.transcript;
                          //We can now call the CONV service?
                          Log.Debug("STT.OnSTTRecognize()", _conversationString);
  
                          Active = false;  //Stop Microphone from listening
  
                          //  Message
                          MessageInput input = new MessageInput();
  
                          input.Text = _conversationString;
                          MessageRequest messageRequest = new MessageRequest()
                          {
                              Input = input,
                              Context = _context
                          };
                          _service.Message(OnMessage, OnFail, _Assistant_workspaceId, _sessionId);
  
                      }
                  }
  
                  if (res.keywords_result != null &amp;&amp; res.keywords_result.keyword != null)
                  {
                      foreach (var keyword in res.keywords_result.keyword)
                      {
                          Log.Debug("STT.OnSTTRecognize()", "keyword: {0}, confidence: {1}, start time: {2}, end time: {3}", keyword.normalized_text, keyword.confidence, keyword.start_time, keyword.end_time);
                      }
                  }
  
              }
          }
      }
  
      //potentially useful to detect difference between different people speaking?
      //  private void OnSTTRecognizeSpeaker(SpeakerRecognitionEvent result)
      //  updated for Watson SDK 2.4.0 compatability
      private void OnSTTRecognizeSpeaker(SpeakerRecognitionEvent result, Dictionary&lt;string, object&gt; customData)
      {
          if (result != null)
          {
              foreach (SpeakerLabelsResult labelResult in result.speaker_labels)
              {
                  Log.Debug("ExampleStreaming.OnRecognize()", string.Format("speaker result: {0} | confidence: {3} | from: {1} | to: {2}", labelResult.speaker, labelResult.from, labelResult.to, labelResult.confidence));
              }
          }
      }
  
  
      private void OnMessage(MessageResponse response, Dictionary&lt;string, object&gt; customData)
      {
          //  Log.Debug("Assistant.OnMessage()", "Response: {0}", customData["json"].ToString());
          //  Convert resp to fsdata
          fsData fsdata = null;
          fsResult r = _serializer.TrySerialize(response.GetType(), response, out fsdata);
          if (!r.Succeeded)
              throw new WatsonException(r.FormattedMessages);
  
          //  Convert fsdata to MessageResponse
          MessageResponse messageResponse = new MessageResponse();
          object obj = messageResponse;
          r = _serializer.TryDeserialize(fsdata, obj.GetType(), ref obj);
          if (!r.Succeeded)
              throw new WatsonException(r.FormattedMessages);
  
          //  Set context for next round of messaging
          MessageContext _tempContext = null;
          //(response as Dictionary&lt;string, object&gt;).TryGetValue("context", out _tempContext)
          _tempContext = response.Context;
  
          if (_tempContext != null)
              _context = _tempContext;
          else
              Log.Debug("ExampleConversation.OnMessage()", "Failed to get context");
  
          //  Get intent
          object tempIntentsObj = null;
          //(response as Dictionary&lt;string, object&gt;).TryGetValue("intents", out tempIntentsObj);
          tempIntentsObj = response.Output.Intents;
  
          //Need to wrap this in try/catch so don't trigger exception is has no content for some reason
          object _tempText = null;
          //(messageResponse.Output as Dictionary&lt;string, object&gt;).TryGetValue("text", out _tempText);
          _tempText = response.Output.Generic;
          object _tempTextObj = (_tempText as List&lt;object&gt;)[0];
          string output = _tempTextObj.ToString();
  
          if (output != null)
          {
              //replace any &lt;waitX&gt; tags with the value expected by the TTS service
              string replaceActionTags = output.ToString();
              int pos3 = replaceActionTags.IndexOf("&lt;wait3&gt;");
              if (pos3 != -1)
              {
                  replaceActionTags = output.Replace("&lt;wait3&gt;", "&lt;break time='3s'/&gt;");
              }
              int pos4 = replaceActionTags.IndexOf("&lt;wait4&gt;");
              if (pos4 != -1)
              {
                  replaceActionTags = output.Replace("&lt;wait4&gt;", "&lt;break time='4s'/&gt;");
              }
              int pos5 = replaceActionTags.IndexOf("&lt;wait5&gt;");
              if (pos5 != -1)
              {
                  replaceActionTags = output.Replace("&lt;wait5&gt;", "&lt;break time='5s'/&gt;");
              }
              output = replaceActionTags;
          }
          else
          {
              Log.Debug("Extract outputText", "Failed to extract outputText and set for speaking");
          }
  
          TTS_content = output;
          //trigger the Update to PLAY the TTS message
          play = true;
      }
  
  
      /*private void OnCONVFail(RESTConnector.Error error, Dictionary&lt;string, object&gt; customData)
      {
          Log.Error("ExampleConversation.OnFail()", "Error received: {0}", error.ToString());
          _messageTested = false;
      }*/
  
      //called by Update() when play=true;
      private void GetTTS()
      {
          //  Synthesize
          //      Log.Debug("WatsonTTS", "Attempting synthesize.");
          _textToSpeech.Voice = VoiceType.fr_FR_Renee; // .en_US_Allison; //.en_GB_Kate;
          _textToSpeech.ToSpeech(HandleToSpeechCallback, OnTTSFail, TTS_content, true);
      }
  
      void HandleToSpeechCallback(AudioClip clip, Dictionary&lt;string, object&gt; customData = null)
      {
          if (Application.isPlaying &amp;&amp; clip != null &amp;&amp; audioSrc != null)
          {
              audioSrc.spatialBlend = 0.0f;
              audioSrc.clip = clip;
              audioSrc.Play();
  
              //set flag values that can be picked up in the Update() loop
              wait = clip.length;
              check = true;
          }
      }
  
      private void OnTTSFail(RESTConnector.Error error, Dictionary&lt;string, object&gt; customData)
      {
          Log.Error("WatsonTTS.OnFail()", "Error received: {0}", error.ToString());
      }
  
  
      /// &lt;summary&gt;
      /// A coroutine to track a GameObject with a pre-delay and a track duration
      /// &lt;/summary&gt;
      /// &lt;param name="preDelay"&gt;Pre delay.&lt;/param&gt;
      /// &lt;param name="duration"&gt;Duration.&lt;/param&gt;
      /// &lt;param name="customShapeIndex"&gt;Custom shape index.&lt;/param&gt;
      IEnumerator Look(float preDelay, float duration, GameObject lookTarget)
      {
          yield return new WaitForSeconds(preDelay);
  
          Debug.Log("Look=" + "LEFT/RIGHT");
          randomEyes.SetLookTarget(lookTarget);
  
          yield return new WaitForSeconds(duration);
  
          randomEyes.SetLookTarget(null);
      }
  
      // Update is called once per frame
      void Update()
      {
  
          if (play)
          {
              Debug.Log("play=true");
              play = false;
              Active = false;
              GetTTS();
          }
  
          if (check)
          {
              //          Debug.Log ("Update() check=true");
              wait -= Time.deltaTime; //reverse count
          }
  
          if ((wait &lt; 0f) &amp;&amp; (check))
          { //check that clip is not playing      
            //            Debug.Log ("Speech has finished");
              check = false;
              //Now let's start listening again.....
              Active = true;
              StartRecording();
          }
      }
  }
  </code></pre>