Amazon Alexa PHP Hello World Example

created December 13, 2016, last updated August 9, 2017.

.

This Christmas (2016) a lot of people in Europe will be unpacking a shiny new Amazon Echo device and enjoying meaningful discussions with Alexa about life, the universe and everything.

If you are a geek like me then you will probably want Alexa to do a bit more than tell you about the weather or play Last Christmas by Wham. I bought my Amazon Echo Dot primarily to use for extremely important Home Automation tasks such as ‘Alexa, switch on the Christmas Tree lights‘.

The developer documentation from Amazon is good but very biased towards node.js. I know absolutely nothing about node.js and some of the online examples seemed overly complex to me. Eventually I found some useable PHP code examples and set about creating my own flexible PHP server side app for handling Alexa requests.

The app I created uses Alexa to send home automation commands via an Internet server to a home based Raspberry Pi server that lets me control Lights and Audio equipment. This is a step by step Hello World example using the same script that shows how relatively simple it is to create an Amazon Alexa App using PHP on your server to handle requests.

Before we start, here is a shopping list of things you will need :

  1. Amazon developer account
  2. Internet accessible web server (project tested with Apache)
  3. SSL Certificate
  4. PHP
  5. Amazon Echo

 

1. Install and Test the PHP Application

  • Download my example PHP application and extract it to your web server
  • create a folder that is writeable by your www server user, i.e. www-admin
    • configure this folder path as amazonCacheFolder in config.php
      • const amazonCacheFolder=’/home/www/amazon/cache/’;
  • make the file PAJ/www/AmazonDev/alexa.php web accessible
    • Test that the application is working by browsing to
      • https://www.your.server/youralias/alexa.php?debug
    • Make sure you see
      • PAJ\Application\AmazonDev\AlexaRequest
      • logfolder is writeable
    • note the debug curl information as this will be helpful in testing access to your application.

2. Register a Developer Account

Register and login to Amazon Developer Services at https://developer.amazon.com. You should register with the Amazon account you used to setup your Amazon Echo device. Once you are registered and logged in, click on the Alexa tab, and then the Get Started button for the Alexa Skills Kit and click the Add a New Skill button.

Getting started with Alexa
Getting started with Alexa
Add a new skill
Add a new skill

 

3. Create and Test the Alexa Skill

Skill Information

Amazon calls the process used to interact with the Alexa Voice Service a Skill. We will create a new private skill called Hello World.

Alexa Skill Information
Alexa Skill Information

 

  • Skill Type
    • choose Custom Interaction Model
  • Language
    • Select from drop down
  • Name
    • The name of the skill
      • Hello World
  • Invocation Name
    • This is the name we will use to activate the skill when talking to Alexa
      • hello world
        • Alexa, ask hello world to say hello world
  • Click Next

 

Interaction Model – Skill Builder Beta

The interaction model describes how the user will interact with the voice service. In the context of Alexa, an intent represents an action that fulfills a user’s spoken request. Intents can optionally have arguments called slots. The intent schema is configured as an array in JavaScript Object Notation (JSON) format.

Use the Skill Builder Beta and paste the following skills builder intent json schema into the code section :

 

Show interaction model json
{
  "intents": [
    {
      "name": "AMAZON.CancelIntent",
      "samples": []
    },
    {
      "name": "AMAZON.HelpIntent",
      "samples": []
    },
    {
      "name": "AMAZON.StopIntent",
      "samples": []
    },
    {
      "name": "HelloWorld",
      "samples": [
        "say {command}",
        "for {command}",
        "{prompt}"
      ],
      "slots": [
        {
          "name": "command",
          "type": "COMMANDOBJECTS",
          "samples": []
        },
        {
          "name": "prompt",
          "type": "PROMPTOBJECTS",
          "samples": []
        }
      ]
    }
  ],
  "types": [
    {
      "name": "COMMANDOBJECTS",
      "values": [
        {
          "name": {
            "value": "hello world"
          }
        },
        {
          "name": {
            "value": "clever quotes"
          }
        }
      ]
    },
    {
      "name": "PROMPTOBJECTS",
      "values": [
        {
          "name": {
            "value": "yes"
          }
        },
        {
          "name": {
            "value": "no"
          }
        },
        {
          "name": {
            "value": "ok"
          }
        },
        {
          "name": {
            "value": "sure"
          }
        }
      ]
    }
  ]
}
Amazon Skill Builder

Here we have defined our intent name HelloWorld and associated slots.

Some default Intents are added by Amazon

AMAZON.CancelIntent
AMAZON.HelplIntent
AMAZON.StoplIntent

You can think of slots as variables to the intent, we have a slot (variable) named command with possible values contained in the COMMANDOBJECTS intent slot.

Next we configure the sample utterances for the skill. These are what people say to Alexa to invoke the intents configured in the skill.

Sample Utterances

The sample utterance starts with the intent name HelloWorld, followed by any combination of words a user might use to interact with the skill, and then the slot name that corresponds with this interaction.

When a user interacts with our Alexa Skill

‘Alexa, ask hello world to say hello world’

The voice service try to determine the user intention by matching the spoken words to the configured utterances, invoking the configured intent and matching the slot values to the phrases provided to the slot. In this case it will match ‘hello world‘ to the value configured in the command slot of the HelloWorld intent. This data will be sent to our server for us to process.

Why use slots? By using the amazon voice service to process and match the users spoken words to a configured set of slot variables (object types) we do not have to process the spoken words ourselves, we simply match the slot type to the command or processes we want to execute making it easier to determine user intent.

When you are finished configuring the Skill Builder you must save your configuration by clicking the Save Model button, and then build the model by clicking the Build Model button.

Configuration

Configuration
Configuration

Now we will configure how the Skill communicates with our PHP application.

  • Select HTTPS
  • Select the geographical location closest to your server
  • Enter the URL to the Alexa php script on your server
    • https://www.your.server/youralias/alexa.php

 

Click Next.

SSL Certificate

SSL Certificate
SSL Certificate

Assuming your web server has a valid SSL certificate select ‘My development endpoint has a certificate from a trusted certificate authority’.

Click Next.

TEST

We can now test the Skill and server PHP application by using the service simulator.

Testing Alexa Skill
Testing Alexa Skill

 

In the Enter Utterance text box we can type the phrase we would say to invoke the application using our Echo device

Alexa, ask hello world to say hello world

The application will fail with an exception error if you have not yet added your amazon credentials to the config file in PAJ/Application/Amazon/Alexa/config.php. You can see the application id and userid in the service request. Copy these and add them to config.php.

	// AMAZON ALEXA
	const amazonSkillId = 'amzn1.ask.skill.XXX';
	const amazonUserId = 'amzn1.ask.account.XXX';
	const amazonEchoServiceDomain = 'echo-api.amazon.com';
	const amazonCacheFolder='/home/www/amazon/cache';

Run the test again. If the application is working you will see the service response JSON data returned from our PHP application, and the text that would be spoken by the Echo device.

  "response": {
    "outputSpeech": {
      "type": "PlainText",
      "text": "OK, I will now say hello world"
    },

Publishing

The publishing information is used to define how the app will appear to users if it is made public. This includes the category, countries the app will be available in, description, sample phrases and images.

This information will also be displayed in the Alexa mobile app.

You cannot publish your app until you have completed this section. When all sections are complete the Submit for Certification button will be available. Clicking this will begin the certification process where Amazon checks your app for compliance and functionality before publishing it in the Alexa Skills store and making it publicly accessible.

Privacy & Compliance

Complete the privacy and Compliance section and click Save to save the Skill.

 

4. Test with Amazon Echo device

To use your newly created Hello World skill with your Echo device make sure the skill is visible and enabled in the Alexa app, under Home -> Skills select Your Skills

 

Select your skill in Alexa App
Select your skill in Alexa App

Now say to Alexa

Alexa, ask hello world to say hello world

Your Echo device will respond with

OK, I will now say hello world

You can also say, ‘Alexa, open hello world‘, this initiates a Launch Request.

Ok, so this is a bit lame, change the output text response by modifying the response text in the HelloWorld.php intent return array.

	return array(
		'intent' => array(
			'response' => 'OK, I will now say something much more meaningful',
			'target' => $_target,
			'status' => false
		)
	);

How the PHP application works

The PHP server side process is relatively straightforward:

  • Amazon sends the Alexa request data as an HTTP POST request to alexa.php.
    • alexa.php initiates the wwwApp bootload which injects the following php objects
      • Config
        • config data for the app
      • Security
        • validates the Alexa Request as per the guidelines here.
        •  This includes :
          • Validating the amazon application id
          • Verifying that the Request was Sent by Alexa
            • determine whether the URL meets each of the following criteria:
              • The protocol is equal to https (case insensitive).
              • The hostname is equal to s3.amazonaws.com (case insensitive).
              • The path starts with /echo.api/ (case sensitive).
              • If a port is defined in the URL, the port is equal to 443.
          • Validate the Signature of the Request
          • Validate the Timestamp of the Request
      • Log
        • logging
      • Request
        • parses the alexa post data
      • Data
        • renders the response
        • renders default intent responses
          • STOP, CANCEL, HELP
        • render LaunchRequest response
          • open hello world
        • initiating PAJ\Application\AmazonDev\Alexa\Intent\IntentName()
        • processing the Amazon intent data
          • formulating a text response based on the process data, in this case simply returning the response text for hello world
            • OK, I will now say hello world
          • managing session attribute data
        • wrapping the response text and card data into a JSON request and outputting it

If you look at HelloWorld.php in Alexa\Intent you will see how we are parsing the intent data from Amazon, looping through the slot data and performing an action on each slot. In this case looking for the ‘command’ slot, validating the spoken words and then acting on them to formulate a response.

 

Further customisation

Here you can also see how you can now further customise the application. For example suppose you want to ask alexa to turn the lounge lights on

‘Alexa, ask Home to switch the lounge lights on’

Here my Alexa Skill is called Home, my intent name is Command, and I have configures a slot called switch which contains object types (variables) such as ‘lounge lights on’ and ‘lounge lights off’.

In Alexa/Intent/Command.php I parse the intent data to find the switch slot, verify that the slot value contains either ‘lounge lights on’, or ‘lounge lights off’ and the send the appropriate commands to my home automation system, i.e. FHEM. When the command has been successfully executed I can return the appropriate response.

‘Ok, I have switched the lounge lights on’

Debugging

If the Application is not working as expected check out the logfiles in your cache folder, for each Amazon Alexa request you will see the request, intent and response data logged in a file called helloworld_yyyy-mm-dd.log.

Use the skills tester in the Amazon skill builder to test and debug your skill. To debug further use the example curl command from the debug page. Execute this from a bash terminal to run curl and simulate an alexa request to your application.

Amazon Default Intents

You will notice that Amazon includes some default intents in the interaction model : AMAZON.StopIntent, AMAZON.CancelIntent, AMAZON.HelpIntent. If you want to publish your skill you must also include responses for these default intents. The default response for the Stop and Cancel intent is included in the renderAlexaResponse method. An examples for the Stop intent can be found in the Help folder.

Prompt and Response Dialog

Having completed a basic Alex command skill I will extend this PHP example to allow for prompts and responses in part 2 of this blog post.

Submit for Certification

Alexa Skills built with this project will pass the Amazon certification process. After submitting your skill you will receive a confirmation email from Amazon, and within 24 hours a response to notify you whether your skill has passed certification of not. Once your skill has been certified you cannot make any changes to the skill configuration without revoking it’s published status.

If you publish a skill with this application please let us know in the comments!

Comments

  1. Richard says:

    Dear Sir:
    This article is really helpful to me.
    I haved completed the steps above and can let Alexa say “hello world” to me.
    But, I have a question,
    I have a WIFI IoT plug too, I can turn it “ON” or “OFF” through API now.
    And, I would like to let Alexa to turn it “ON” or “OFF” (maybe use the API).
    For example, ‘Alexa, ask HomePlug to turn the plug on’.
    Would you please show me the steps or examples for reference?
    BTW, do you know any article/reference that can let Alexa to “find” my device (plug).
    I can modify the firmware if needed.

    Any hints appreciated.
    Thanks in advance.

    Best Regards
    Richard

  2. Peter Freimann says:

    Hey,

    thanks a lot for the great work! I set up the example project and extented it already.
    Currently I try to figure out, how one could ask questions back from Alexa.

    For instance, when I have an intend with two slotItem, and the first one is given, so Alexa could ask back: “Okay, I understand, you would like to do . Could you please tell me what is your ?”

  3. Hastaelnabo2000 says:

    Hi,
    nice explained. Trying to test it I always get following error:
    “Exception error, HTTP GET when POST was expected”
    Line 152 of AlexaRequest.php file

      • PAJ says:

        I have included some new debug information including a curl command that will simulate a post from the command line. You can see the debug info by browsing to alexa.php?debug

  4. Jens says:

    You probably should state somewhere in the begining of the article, that your demo project requires an apache webserver not an »internet accessible webserver« since it runs into a fatal error while using nginx+php-fpm.

  5. JK says:

    Thanks for sharing! That’s a nice starting point.

    There are two issues while testing on nginx + php 7.1:

    1)
    PAJ\Application\Amazon\Alexa\Intent\Data:

    Add “static” keyword to function cleverQuotes to prevent a php Warning:
    static function cleverQuotes()

    2)
    PAJ\Application\Amazon\AlexaRequest:

    I changed
    $_headers = apache_request_headers();
    to
    $_headers = $_REQUEST; // Or $_POST

    to make it working with nginx

    • PAJ says:

      Thanks for pointing that out, I will update the code with a non apache specific header request function.

  6. Sam says:

    This is really great stuff! I don’t know what I’m doing wrong but I have a skill with multiple intents. (A oneshot, a dialog and a help)

    I’m trying to use “`If ($EchoJArray->request->intent->name == “myIntentName”)“`
    to determine my intent and it doesn’t seem to work to return the current event. I’m probably missing something stupid.

  7. Thomas says:

    When I try to set the Intent Schema to what you have given above, “Save” gives:
    Error: There was a problem with your request: Invalid intent name ‘null’.
    It works better if, within the “intents”, “name” is changed to “intent”.
    Afterwords, it complained about undefined “COMMANDOBJECT”. It obiously does not
    understand your “types” section. I had to define both COMMANDOBJECT and PROMPTOBJECT als list.

    • PAJ says:

      I will check the intent json in the blog post for errors. The code should work, I currently have three published skills using this script.

  8. Sean says:

    Hi,
    I’m very new to Alexa but no more PHP than Node.JS, so am trying to get hello world up and running.
    As a blind person I’m having difficulty getting through the wizard to add the skill.
    I pasted the entire JSON Intent Schema, do I have to add an object type custom with value hello world ? I am a little confused by this part, and obviously can’t see the screenshots.
    I press save and get, Error: There was a problem with your request: Invalid intent name ‘null’.
    Which implies to me the JSON in the Schema is wrong, but without having gotten a skill to work and fully understood the flow I am at a loss.

    Also, assuming I eventually get this to work I would like to be able to use the new audio player features. Is there a reason I couldn’t add this in from PHP? Or am I better off going back to Nodejs and learning something new?

    Thanks for your patience!
    S.

    • PAJ says:

      Amazon seem to be changing the timestamp format to a 13 digit millisecond timestamp which was breaking my code, I will update it asap.

  9. avani says:

    I have amazon developer account .Please tell me from where i can get following details:

    const amazonSkillId = ‘amzn1.ask.skill.XXX’;
    const amazonUserId = ‘amzn1.ask.account.XXX’;
    const amazonEchoServiceDomain = ‘echo-api.amazon.com’;
    const amazonCacheFolder=’/home/www/amazon/cache’;

  10. Flo says:

    Hi, thank you for your work! Unfortunately I can’t get it working.
    I downloaded your repo to my webspace but how do I make the file alexa.php web accessible?
    I mean first of all the folder is named /AmazonDev/ not Amazon. Is that correct? Do I have to change that?
    Testing the application by browsing to https://my.server/mySkill/CalendarReader/PAJ/www/AmazonDev/alexa.php?debug seems to work, since it returns “DEBUG!”, but without the parameter I get a 500 Server Error.
    Where am I supposed to see PAJ\Application\Amazon\AlexaRequest? Again, in /PAJ/Application there is only a AmazonDev folder, but it does not contain the AlexaRequest (-folder?).
    Further, “logfolder is writeable”, do you mean the amazonCacheFolder?
    And also “note the debug curl information”, where do I find that?
    I am puzzled.

    I’d be grateful for every bit of enlightenment!
    Cheers
    Flo

    • PAJ says:

      I recently updated my code and the path is now AmazonDev, I have updated the blog to reflect that. If you are seeing the debug page, and your cache folder is writeable you can now go on to build/test your skill using the Alexa skill development page. Build the skill and test using the test function. Without the debug parameter you will see an error returned as the app is expecting POST data from your Amazon skill. Check your error logs for further info or use the curl command to debug a POST data request to the app.

  11. Matthias says:

    Hi there,
    I followed your guideline but the debug says:
    HTTP GET when POST was expected

    curl says error 500:
    Note: Unnecessary use of -X or –request, POST is already inferred.
    * Trying 85.214.250.119…
    * Connected to alexa.omtiot.com (85.214.250.119) port 443 (#0)
    * found 148 certificates in /etc/ssl/certs/ca-certificates.crt
    * found 596 certificates in /etc/ssl/certs
    * ALPN, offering http/1.1
    * SSL connection using TLS1.2 / ECDHE_RSA_AES_128_GCM_SHA256
    * server certificate verification OK
    * server certificate status verification SKIPPED
    * common name: alexa.omtiot.com (matched)
    * server certificate expiration date OK
    * server certificate activation date OK
    * certificate public key: RSA
    * certificate version: #3
    * subject: CN=alexa.omtiot.com
    * start date: Mon, 09 Oct 2017 17:18:26 GMT
    * expire date: Sun, 07 Jan 2018 17:18:26 GMT
    * issuer: C=US,O=Let’s Encrypt,CN=Let’s Encrypt Authority X3
    * compression: NULL
    * ALPN, server accepted to use http/1.1
    > POST /www/AmazonDev/alexa.php HTTP/1.1
    > Host: edited
    > User-Agent: curl/7.47.0
    > Accept: */*
    > Content-Type: application/json
    > SignatureCertChainUrl: https://s3.amazonaws.com/echo.api/echo-api-cert-2.pem
    > Signature: OMEN68E8S0H9vTHRBVQMmWxeXLV8hpQoodoU6NdLAUB12BjGVvOAgCq7LffPDKCW7zXI6wRc3dx0pklYWqZHXbNsMfx8xSN3lqJTYw6zLZGwt2MgcjajHa1AnMbTnZOjrq9WPZuFG0pyJj9ucKB0w/k4r123vOLzVI0pEISo3WTIDsfKMycIpGiNcDHdJIc2LQGG5Bum9TFJuUllpt5c5LQC9g1rKIS2nj55QCQ8a3EeeqDe3N85Sw6OT7k7oPkKVLPee5fAvWfkQQqW1fmA7sGIWKDpVTi1Jq46I2MiJM+48m+rxOVEPXky3j8u8+lPWg6vOnKogoXTb52foAurmAA==
    > Content-Length: 347
    >
    * upload completely sent off: 347 out of 347 bytes
    < HTTP/1.1 500 Internal Server Error
    < Date: Wed, 11 Oct 2017 09:50:51 GMT
    < Server: Apache/2.4.18 (Ubuntu)
    < Connection: close
    < Content-Type: text/html; charset=iso-8859-1
    <

    500 Internal Server Error

    Internal Server Error
    The server encountered an internal error or
    misconfiguration and was unable to complete
    your request.
    Please contact the server administrator at
    X to inform them of the time this error occurred,
    and the actions you performed just before this error.
    More information about this error may be available
    in the server error log.
    Additionally, a 400 Bad Request
    error was encountered while trying to use an ErrorDocument to handle the request.

    Apache/2.4.18 (Ubuntu) Server at edited Port 443

    * Closing connection 0

  12. kcjames1138 says:

    Hello,

    Great work!

    I have some questions on identifying specific users. Is the userID that is hard coded always required and specific to the skill developer account or is it something other users of the skill will be sending?

    I am wanting to implement account linking and am curious how to identify specific users in each intent request so I know what intents/functions each user has access to.