botman/driver-amazon-alexa
Amazon Alexa driver for BotMan, enabling PHP developers to connect conversational bots to Alexa voice skills. Provides request handling and response integration for building voice-driven Laravel and PHP applications.
composer require botman/botman botman/driver-amazon-alexa. Pin versions and commit composer.lock — the Alexa driver's last release was 2018, so Composer resolution against a current BotMan 2.x is the first thing to validate in a throwaway branch.vendor/botman/driver-amazon-alexa/src/. There is effectively no documentation; the driver class, its service provider, and the code that builds the response envelope are the spec. Read matchesRequest(), getMessages(), and reply()/sendRequest() to learn exactly what Alexa JSON becomes a BotMan message and what your reply() becomes.BotMan\Drivers\AmazonAlexa\AmazonAlexaServiceProvider::class to config/app.php (verify the exact class name in src/) and publish BotMan's own config if you haven't.routes/api.php is the cleanest option (remember the /api prefix when you paste the URL into the Alexa console), or a plain route added to VerifyCsrfToken::$except. The handler is thin:Route::post('/alexa', function () {
app('botman')->listen();
});
HelloIntent end to end. The driver converts an IntentRequest into a BotMan message keyed by the intent name (confirm with a payload dump before writing many handlers), so:$bot->hears('HelloIntent', function (BotMan $bot) {
$bot->reply('Hello from Alexa.');
});
AMAZON.HelpIntent, AMAZON.StopIntent, AMAZON.CancelIntent, and a $bot->fallback(), and send a hand-written sample request with curl so you can see the exact JSON envelope the driver returns.$payload = $bot->getMessage()->getPayload();
$city = $payload['request']['intent']['slots']['city']['value'] ?? null;
Wrap extraction in one helper — every handler otherwise ends up littered with null checks.
ask() and Conversations do work, but conversation state lives in BotMan's cache, not in Alexa's session.attributes. Configure a shared cache (Redis or database) and confirm the conversation key is stable across separate Alexa invocations. This is the single most important thing for slot-filling-style flows.LaunchRequest and SessionEndedRequest by checking request.type in the payload rather than assuming they become hears()-able messages. Always register the built-in intents — unhandled ones drop into fallback, which is rarely what you want.$bot->middleware(...)) is the right place for logging the raw request, validating the app ID, and normalizing slots. Intent handlers should contain business logic and reply()/ask() only.$bot->getDriver()->getName() or on payload keys rather than splitting the whole conversation.outputSpeech. SSML, cards, reprompts, and directives require overriding the response-building code or post-processing the returned JSON, including setting shouldEndSession and reprompt yourself. Put that shaping behind a small helper so handlers stay readable.SignatureCertChainUrl, Signature, the timestamp, and the skill's app ID from session.application.applicationId. The driver does not do this; add middleware. It is mandatory for a published skill./api/alexa versus /alexa) before pasting it into the console.hears() is regex-based. Intent names containing . (such as AMAZON.HelpIntent) and other regex metacharacters should be escaped (AMAZON\\.HelpIntent) or matched deliberately, otherwise you get loose or surprising matches. Always register a fallback.ask() conversations restart and users hear the same prompt forever. Reproduce by sending two IntentRequests with the same sessionId/userId.shouldEndSession and reprompts: an "ask" reply that returns shouldEndSession: true closes the microphone. Inspect the returned JSON for ask-style replies and add a reprompt whenever you keep the session open.intent.slots exists.$bot->getMessage()->getPayload() in middleware. The Alexa console's JSON Editor plus ask simulate/ask dialog show exactly what was sent and what your endpoint returned.session.attributes with BotMan session state. If you need data across turns, store it in BotMan's cache (or your own store keyed by the Alexa userId) and copy values in and out — the driver will not round-trip attributes for you.request.locale and request.timestamp; parse them yourself if your responses depend on them.context.System.user.accessToken in the payload. Extract it in middleware and hand it to your auth layer rather than digging through payload arrays in handlers.How can I help you explore Laravel packages today?