Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Amazon Alexa Bundle Laravel Package

maxbeckers/amazon-alexa-bundle

Symfony bundle for building Amazon Alexa skills with ease. It handles Alexa requests and responses, intent and event dispatching, session and slot data, and integrates the amazon-alexa-php library into your Symfony app through services and configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and register

composer require maxbeckers/amazon-alexa-bundle

Symfony Flex likely has no recipe for this bundle, so register it manually in config/bundles.php:

return [
    // ...
    MaxBeckers\AmazonAlexaBundle\AmazonAlexaBundle::class => ['all' => true],
];

Configure the skill

The bundle’s extension exposes the Alexa application ID (and possibly signature-validation settings). Confirm the exact root key and options with:

bin/console config:dump-reference amazon_alexa
# or, if that fails:
bin/console config:dump-reference maxbeckers_amazon_alexa

Typical config:

# config/packages/amazon_alexa.yaml
amazon_alexa:
    application_id: '%env(ALEXA_SKILL_ID)%'
ALEXA_SKILL_ID=amzn1.ask.skill.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Where to look first

  1. vendor/maxbeckers/amazon-alexa-bundle/src/Resources/config/ — the service definitions and any compiler passes. This tells you which services are public, which tag is used to collect request handlers, and whether a controller is provided.
  2. vendor/maxbeckers/amazon-alexa-php/README.md and src/ — the bundle is a thin Symfony wrapper around this library; all the Request, Response, OutputSpeech, and RequestHandlerInterface classes come from there.
  3. bin/console debug:container --tag=<handler-tag> and debug:router — verify handler discovery and whether you need to wire your own endpoint.

First use case: a Hello World intent handler

Create one handler per intent, implementing the library’s request-handler interface and tagged for auto-collection by the bundle:

namespace App\Alexa;

use MaxBeckers\AmazonAlexa\Request\Request;
use MaxBeckers\AmazonAlexa\RequestHandler\RequestHandlerInterface;
use MaxBeckers\AmazonAlexa\Response\OutputSpeech;
use MaxBeckers\AmazonAlexa\Response\Response;

class HelloWorldHandler implements RequestHandlerInterface
{
    public function supportsRequest(Request $request): bool
    {
        return 'IntentRequest' === $request->request->type
            && 'HelloWorldIntent' === $request->request->intent->name;
    }

    public function handleRequest(Request $request): Response
    {
        $response = new Response();
        $response->addOutputSpeech(new OutputSpeech('Hello from Symfony'));

        return $response;
    }
}

Register it with the bundle’s handler tag (check the bundle’s compiler pass for the exact name; commonly amazon_alexa.request_handler):

# config/services.yaml
services:
    App\Alexa\HelloWorldHandler:
        tags: ['amazon_alexa.request_handler']

If the bundle does not ship a controller/route, expose a thin endpoint that converts the raw HTTP request into an Alexa request and delegates to the registry:

namespace App\Controller;

use MaxBeckers\AmazonAlexa\Request\Request as AlexaRequest;
use MaxBeckers\AmazonAlexa\RequestHandler\RequestHandlerRegistry;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request as HttpRequest;
use Symfony\Component\Routing\Attribute\Route;

class AlexaController
{
    #[Route('/alexa', methods: ['POST'])]
    public function __invoke(HttpRequest $http, RequestHandlerRegistry $registry): JsonResponse
    {
        $alexaRequest = AlexaRequest::fromAmazonRequest(
            $http->getContent(),
            $http->headers->get('SignatureCertChainUrl', ''),
            $http->headers->get('Signature', '')
        );

        $response = $registry->getRequestHandler($alexaRequest)->handleRequest($alexaRequest);

        return new JsonResponse($response);
    }
}

Point your skill’s endpoint at this route, expose it over HTTPS, and test through the Alexa Developer Console simulator or ask CLI.


Implementation Patterns

One handler per intent, with dedicated fallback handlers

The cleanest structure is a handler per intent plus separate handlers for the built-in request types every skill needs:

  • LaunchRequest — welcome message, optionally keep the session open with a reprompt.
  • IntentRequest — one class per intent, supportsRequest() checks the intent name.
  • SessionEndedRequest — cleanup/logging; no meaningful response.
  • AMAZON.HelpIntent, AMAZON.StopIntent, AMAZON.CancelIntent, AMAZON.FallbackIntent — handle these explicitly instead of relying on a generic catch-all.

Order matters if your registry is a first-match list. Register the specific handlers before a catch-all fallback.

Keep handlers thin; inject domain services

class OrderStatusHandler implements RequestHandlerInterface
{
    public function __construct(private OrderRepository $orders) {}

    public function supportsRequest(Request $request): bool
    {
        return 'IntentRequest' === $request->request->type
            && 'OrderStatusIntent' === $request->request->intent->name;
    }

    public function handleRequest(Request $request): Response
    {
        $slot = $request->request->intent->slots['orderNumber'] ?? null;
        $order = $slot ? $this->orders->findByNumber($slot->value) : null;

        $speech = $order
            ? sprintf('Order %s is %s.', $order->getNumber(), $order->getStatus())
            : 'I could not find that order.';

        $response = new Response();
        $response->addOutputSpeech(new OutputSpeech($speech));

        return $response;
    }
}

Slot extraction and validation

Slots live at $request->request->intent->slots, keyed by slot name. Treat value as nullable and validate before querying. Use confirmationStatus if your interaction model uses confirmations. For dates/numbers, normalise explicitly — Alexa sends strings.

Session attributes

Read from $request->session->attributes and write back via the response’s session attributes:

$attributes = $request->session->attributes ?? [];
$attributes['lastOrder'] = $order->getNumber();

$response = new Response();
$response->sessionAttributes = $attributes;

This is the simplest way to keep multi-turn context without a database round trip, but don’t store sensitive data — Alexa session attributes are client-side.

Response construction

  • OutputSpeech supports plain text and SSML (<speak>...</speak>); use SSML for pauses, emphasis, and audio.
  • addReprompt() when shouldEndSession is false — Alexa requires a reprompt if the session stays open.
  • Card for visual responses in the Alexa app.
  • Directives (audio player, display) are added to the response body; check the library for the directive classes you need.

Testing

  • Unit-test handlers directly: construct a Request object (or a fixture-backed one) and assert on the returned Response speech and session attributes.
  • Functional-test the endpoint with a recorded Alexa JSON payload, but note signature validation will reject arbitrary bodies unless you disable it or call the lower-level parser.
  • Keep canned request JSON fixtures in tests/Fixtures/Alexa/ for each intent and built-in request type.

Operational integration

  • Alexa expects a response within roughly 8 seconds. Move slow work (API calls, emails) to Symfony Messenger and return an immediate acknowledgement.
  • Use a dedicated Monolog channel for Alexa traffic; log requestId, intent name, and user ID for debugging.
  • Cache skill/user lookups where possible — the same user can trigger the skill frequently.

Gotchas and Tips

Use the raw body, not parsed request data

Alexa signature validation is computed over the exact raw JSON body. Read $http->getContent(). Do not use $http->request->all() or a deserialized object, and beware proxies or middleware that rewrite the body.

Headers can be stripped in transit

Signature validation needs Signature and SignatureCertChainUrl. Reverse proxies, load balancers, and some PHP-FPM configurations drop or rename these. Verify they arrive at the controller before blaming the library. If validation fails behind a proxy, fix the proxy rather than disabling validation.

HTTPS is mandatory

Alexa only calls HTTPS endpoints with a valid, publicly trusted certificate. Local development requires a tunnel (ngrok, Expose, etc.) or testing through unit tests. Self-signed certificates will not work with the real service.

Application ID mismatch

If the configured application ID doesn’t match session.application.applicationId, requests should be rejected. This is a common cause of “the skill works in the simulator but not on a device” — double-check the env var per environment, and be aware that development and production skills have different IDs.

Symfony Request name collision

The library and Symfony both have a Request class. Alias one:

use MaxBeckers\AmazonAlexa\Request\Request as AlexaRequest;
use Symfony\Component\HttpFoundation\Request as HttpRequest;

This bites quickly in controllers.

Handler tag and registry wiring

The bundle relies on a compiler pass to collect tagged handlers. If your handler is never invoked, run debug:container --tag=<tag> to confirm it was collected, and check that the service is not private in a way that prevents injection. If there is no registry service, the bundle may expect you to build one yourself from the library.

Always return a valid Alexa response

If your code throws, returning a 500 produces “the skill is not responding.” Catch domain exceptions inside handlers and return a spoken error response. Let infrastructure errors be logged, but keep the Alexa contract intact.

SSML escaping

Ampersands and angle brackets must be escaped inside SSML, or Alexa rejects the response. Use htmlspecialchars() for user-supplied values interpolated into <speak> content.

Locale and i18n

$request->request->locale tells you the user’s locale. If you support multiple languages, route speech strings through Symfony’s translator and set the locale per request rather than assuming en-US.

Small ecosystem — read the source

This bundle has low adoption and no dependents. There is no large body of blog posts or Stack Overflow answers, and no Flex recipe. Budget time to read services.xml, the compiler pass, and the underlying maxbeckers/amazon-alexa-php source. Pin the bundle and library versions in composer.json, and check compatibility with your Symfony major version when upgrading.

Extension points

  • Decorate the request-handler registry to add logging or metrics around dispatch.
  • Add a request listener/subscriber for cross-cutting concerns (user resolution, feature flags) that runs before the handler.
  • Implement custom directive classes in the underlying library if the built-in set doesn’t cover your response type.
  • If the bundle’s controller is too opinionated, bypass it and keep your own thin controller — the library is usable standalone.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource