Fun Project: Amazon Alexa Skill for New Relic Insights

As a fun project in some spare time, I recently worked on a way to tell Amazon Alexa how to talk to New Relic Insights and retrieve some high-level information about an account. I was just curious on what it would take to get Alexa to access our Insights API with a voice command and Alexa to speak out some result of this query.

You typically start by creating an Amazon developer account and register a new Alexa Skill with the Alexa Skills Kit.

Since I am a C# .NET guy by heart, of course I wanted to ideally develop this using Visual Studio and my favorite programming language. I found this a blog post to be very useful as a starting point “Write your Amazon Alexa Skill using C# on AWS Lambda services“. It not only highlights the basic steps to get started using .NET Core, but also targets a serverless function in AWS Lambda to run this code.

The Alexa Skill specifies an invocation name that you can use in a start of an interaction with Alexa. In my case, I used the invocation name “new relic insights”:

One of the next steps is then to define how the interaction model looks like for your skill. In my sample, I created a so called intent named “NewRelicInsights” that you can then use to identify in the code.

As you can see in the above screenshot, I have added some sample utterances that you can later use to ask to Alexa. This is not an exhaustive list, but should only show some basic examples.

My sample code for this looks like this:

public async Task<SkillResponse> FunctionHandler(SkillRequest input, ILambdaContext context)
    Alexa.NET.Response.IResponse response;
    IOutputSpeech innerResponse = null;
    var log = context.Logger;
    // check what type of a request it is like an IntentRequest or a LaunchRequest
    var requestType = input.GetRequestType();
    if (requestType != null)
        if (requestType == typeof(IntentRequest))
            // do some intent-based stuff
            // intent request, process the intent
            log.LogLine($"Intent Requested {input.Request.RequestId}");
            var intentRequest = input.Request as IntentRequest;
            string nraccountid = "1234567890";
            // check the name to determine what you should do
            if (intentRequest.Intent.Name.Equals("NewRelicInsights"))
                log.LogLine($"Intent identified as NewRelicInsights");
                nraccountid = intentRequest.Intent.Slots["nraccountid"].Value;
                // get the slots
                if (nraccountid == null)
                    log.LogLine($"Intent identified as NewRelicInsights with default account");
                    nraccountid = "1234567890";
                else if (nraccountid != null)
                    log.LogLine($"Intent identified as NewRelicInsights with nraccountid {intentRequest.Intent.Slots["nraccountid"].Value.ToString()}");
                    nraccountid = intentRequest.Intent.Slots["nraccountid"].Value;

You will notice that in line 10, I first check whether it is an intent request at all. In line 18 I then actually check to see whether or not Alexa identified the request as a “NewRelicInsights” request given the invocation name above.

Another thing you’ll notice is that I also have a parameter defined in my interaction model. This is useful to specify the New Relic account you want to query against. In the code above, I specified a default account for ease of use and also try to retrieve this account number from the Alexa request inside the requests slots.

Once I identified it as a New Relic Insights request and know what account to query against (default or provided by the user), I can then execute the request using our Insights Query API:

long iNRAccountID = long.MinValue;
if (long.TryParse(nraccountid, out iNRAccountID))
    log.LogLine($"Intent checking Insights with acount id {iNRAccountID.ToString()}");
    string url = "" + nraccountid + "/query?nrql=SELECT%20count%28*%29%20FROM%20TransactionError%20SINCE%2030%20MINUTES%20AGO";
    var client = new System.Net.Http.HttpClient();
    client.DefaultRequestHeaders.Add("Accept", "application/json");
    client.DefaultRequestHeaders.Add("X-Query-Key", "<query key>");
    HttpResponseMessage result = await client.GetAsync(url);

    // convert data into objects
    string res = await result.Content.ReadAsStringAsync();
    log.LogLine($"Result from the New Relic Insights query: " + res);
    int nrInsightsErrors = 0;
    int.TryParse(res, out nrInsightsErrors);
    int iNRaccountid = 0;
    int.TryParse(nraccountid, out iNRaccountid);
    log.LogLine("intentRequest.Locale: " + intentRequest.Locale);
    AlexaResponseStrings ars = new AlexaResponseStrings_EN_US(iNRaccountid, nrInsightsErrors);
    if (intentRequest.Locale.Equals("de-DE"))
        ars = new AlexaResponseStrings_DE_DE(iNRaccountid, nrInsightsErrors);
    innerResponse = new PlainTextOutputSpeech();
    string baseResponse = ars.BaseResponseDefault;// $"Your New Relic account {nraccountid.ToString()} currently has {nrInsightsErrors.ToString()} errors. ";
    string followUpAction = ars.FollowupActionDefault;//"You may want to check your New Relic account!";
    if (res.Equals("{\"error\":\"Account not found.\"}"))
        baseResponse = ars.BaseResponseErrNoAccount;// $"I am sorry, but the account {nraccountid.ToString()} does not seem to exist. ";
    else if (res.Equals("{\"error\":\"Invalid query key.\"}"))
        baseResponse = ars.BaseResponseErrInvQueryKey;// $"I am sorry, but you do not have access to account {nraccountid.ToString()}. ";
    else if (nrInsightsErrors.Equals(0))
        followUpAction = ars.FollowupActionWiesn;//"You are good to go to Oktoberfest (or <emphasis>Wiesn</emphasis> how we call it over here)!";
    (innerResponse as PlainTextOutputSpeech).Text = baseResponse + followUpAction;
    // build the speech response 
    var speech = new Alexa.NET.Response.SsmlOutputSpeech();
    speech.Ssml = "<speak>" + baseResponse + followUpAction + "</speak>";

    // create the response using the ResponseBuilder
    var finalResponse = ResponseBuilder.Tell(speech);
    return finalResponse;

Once the HTTP client has executed the request, I then simply parse the result and let Alexa speak out a result. Optionally, I could also ask additional follow-up questions and so on which is not handled in this post.

The idea was to just give you a high-level understanding what it would take to integrate with Amazon Alexa. The full code is available in Visual Studio Team Services. Let me know if you would like to check it out … I am happy to provide access to it.

Leave a comment

Your email address will not be published. Required fields are marked *