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

Laravel Mautic Laravel Package

trianity/laravel-mautic

A Laravel package that integrates Mautic marketing automation into your app, offering a config-driven wrapper for the Mautic API so you can manage contacts, segments, emails, forms, and campaigns from familiar Laravel code.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is the Laravel-side client for the Mautic API. v1.1.0 is a meaningful step up if you want the package to own the OAuth dance: browser authorization/callback routes are now available (opt-in, disabled by default), credential lookup is more flexible, and there are two commands that answer the two questions you will always have — what callback URL do I register in Mautic? and why is my connection failing?

Minimal path:

  1. composer require the package, then publish its config and confirm config/mautic.php exists. If you already have a published config, merge the new oauth.routes block by hand — do not vendor:publish --force.
  2. Define one or more named connections in config/mautic.php, with credentials coming from the environment:
'connections' => [
    'main' => [
        'base_url'      => env('MAUTIC_BASE_URL'),
        'client_id'     => env('MAUTIC_CLIENT_ID'),
        'client_secret' => env('MAUTIC_CLIENT_SECRET'),
        // 'redirect_uri' => 'https://app.example.com/mautic/oauth/callback',
    ],
],
  1. Run php artisan mautic:diagnose. It reports credential availability (sanitized — no secrets in output), which immediately tells you whether the secret is visible to the current runtime before you go debugging HTTP.
  2. Run php artisan mautic:oauth:callback-url to get the canonical URL, and register it on the Mautic side against your API client credentials. It is read-only and safe to run anywhere.
  3. First real use case: read/write a contact through your main connection, then decide whether you need the package's browser OAuth routes or will keep host-owned routes.

Where to look first: the published config/mautic.php (it is the whole contract), docs/oauth-host-adoption.md (new in this release), and the output of the two artisan commands. Skip the docs at your peril — the OAuth section is the part that bites.

Implementation Patterns

Host-owned OAuth remains the default posture. v1.1.0 preserves existing host-owned routes; package routes require both oauth.routes.enabled and a host binding. The pragmatic split:

  • Small/internal apps — turn on package routes, bind an authorizer that checks "is this user allowed to link a Mautic instance?", allowlist one connection, done.
  • Multi-tenant or multi-Mautic apps — keep your own controller/routes and use the package for token exchange, storage, and refresh. You get the credential resolution and the diagnostics without ceding UX.

Bind OAuthBrowserAuthorizer deliberately. It denies by default. That is the correct security default and the most common "why does the callback 403?" cause. Implement it against your gate/policy — typically "user is authenticated and belongs to the tenant that owns this connection" — and register it in a service provider.

Allowlist connections for the browser flow. The allowlist is your blast radius control: only connections in that list can be driven through the public authorization route. Never allowlist a connection whose tokens reach data the current user shouldn't see.

Route shaping is configuration, not code. Route names, prefix, middleware, and the post-flow result route are all configurable under oauth.routes. Practical settings:

'oauth' => [
    'routes' => [
        'enabled'    => env('MAUTIC_OAUTH_ROUTES', false),
        'prefix'     => 'integrations/mautic',
        'middleware' => ['web', 'auth'],   // add your auth stack here, not in the controller
        'connections'=> ['main'],
    ],
],

Put authorization middleware in the route config so the package routes are protected identically to the rest of your app.

Prefer app.url over the request host for callback URLs. Resolution is canonical and driven by trusted app.url; per-connection redirect_uri takes precedence when you need an override. Use the override for genuinely different origins (separate admin domain, custom callback host), not as a workaround for a misconfigured APP_URL.

Credential resolution order to design around: explicit per-connection value → Laravel environment repository → process environment → custom credential provider. The environment-repository layer means secrets stay in env and out of the cached config file; a custom provider is the right extension point for Vault/SSM/secrets managers, not a config() hack.

Deployment routine that actually works:

  1. Set MAUTIC_CLIENT_SECRET (and friends) in every runtime that touches the package: PHP-FPM pool env, CLI, scheduler, queue workers.
  2. php artisan config:cache.
  3. php artisan mautic:diagnose — verify credential availability per environment.
  4. Restart long-running workers (Horizon, queue:work, Octane) after any config/env change.
  5. Confirm callback URL matches what's registered in Mautic (mautic:oauth:callback-url).

Testing. Fake the HTTP layer rather than the package internals; keep a distinct sandbox/testing connection so a mis-set MAUTIC_ENV can never point tests at production tokens.

Gotchas and Tips

  • Routes are opt-in and the authorizer denies by default. Two independent gates. Enabling routes without binding an authorizer gives you 403s, not a working flow.
  • Never vendor:publish --force on an existing app. It will overwrite host config. Diff the published stub against yours and merge oauth.routes manually.
  • Cached config + secrets. The environment-repository support means the secret is resolved at runtime, so it must exist in the process environment of every worker type. A secret that works in php artisan tinker but fails under FPM or a queue worker is almost always this.
  • APP_URL must be truthful. Behind a load balancer or proxy, set trusted proxies and a correct APP_URL/redirect_uri; otherwise the generated callback won't match what Mautic has registered and you'll get a redirect-URI mismatch from Mautic, not from Laravel.
  • Register the callback URL exactly. Scheme, host, path, and trailing slash all matter. Use the command's output verbatim.
  • Result redirects are relative by design (v1.1.0 fix) and no longer rebuilt from the request Host — that closes a host-header injection vector. If you were relying on absolute redirects from the flow, adjust your expectations rather than patching around it.
  • Don't run concurrent authorization attempts in the same session. v1.1.0 fixes callbacks from replaced attempts so the newer flow completes, but you should still avoid opening two authorization tabs (or clearing cookies mid-flow) — the failure mode looks like a random "invalid state" that is hard to reproduce.
  • No migrations, no token-storage changes in 1.1.0. Preserve existing token records and APP_KEY. Rotating APP_KEY invalidates encrypted tokens and forces every connection to re-authorize.
  • Long-running processes cache config and env. After changing connections, secrets, or oauth.routes, restart workers. mautic:diagnose run from a stale worker will lie to you.
  • Debugging order: mautic:diagnose (credential availability, sanitized) → mautic:oauth:callback-url (URL correctness) → Mautic-side client credentials (registered redirect) → token expiry/refresh logs. Start there before reading HTTP traces.
  • Extension points worth knowing: a custom credential provider for secret managers, the OAuthBrowserAuthorizer binding for access policy, and host-owned routes for full UI control. Those three cover nearly every customization request without forking.
  • Validation caveat for this release: CI is green across PHP 8.4/8.5 × Laravel 12/13 (334 tests), but live Mautic and opt-in MariaDB acceptance tests were not rerun. If you run an older or heavily customized Mautic, smoke-test the browser flow against a staging instance before rolling to production.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource