Everything you need to install, configure, and integrate the Newsletter Platform — from quick start to advanced AI and API usage.
| Feature | Description |
|---|---|
| Subscriber Management | Import/export CSV, tagging, status management |
| Campaigns & Messages | Hierarchical organization of campaigns and messages |
| HTML Templates | Customizable templates with placeholder support |
| Scheduled Sending | Automatic delivery via cron |
| Full Tracking | Opens, clicks, and unsubscribe tracking |
| Targeting | Filter recipients by tags and status |
| Dashboard | Statistics and monitoring widgets |
| Rate Limiting | Configurable per-minute, per-hour, and per-day limits |
| Bounce Detection | IMAP integration for bounce processing |
| REST API | Full CRUD API with Sanctum authentication and OpenAPI docs |
| MCP Server | AI integration endpoint for Cursor, Claude Code, and other clients |
| Testing Tags | Mark tags as testing to exclude sends from production statistics |
| Multilingual | Admin panel in Italian, English, German, French, Spanish, Portuguese |
| UTM Tracking | Automatic UTM parameters on outbound newsletter links |
| User Roles | Three permission levels: Editor, Manager, Administrator |
git clone https://github.com/andreapollastri/newsletter.git
cd newsletter
composer install
cp .env.example .env
php artisan key:generate
php artisan migrate
npm install
npm run build
php artisan make:filament-user
php artisan l5-swagger:generate
Configure your SMTP settings in .env:
MAIL_MAILER=smtp
MAIL_HOST=your-smtp-host.com
MAIL_PORT=587
MAIL_USERNAME=your-username
MAIL_PASSWORD=your-password
MAIL_FROM_ADDRESS="newsletter@yourdomain.com"
MAIL_FROM_NAME="${APP_NAME}"
| Variable | Description | Default |
|---|---|---|
NEWSLETTER_TRACKING_ENABLED |
Enable open/click tracking | true |
NEWSLETTER_RATE_LIMIT_PER_MINUTE |
Max sends per rolling minute (0 = no cap) | 0 |
NEWSLETTER_RATE_LIMIT_PER_HOUR |
Max sends per rolling hour (0 = no cap) | 0 |
NEWSLETTER_RATE_LIMIT_PER_DAY |
Max sends per rolling day (0 = no cap) | 0 |
Optional configuration for processing bounced emails:
NEWSLETTER_IMAP_HOST=imap.yourdomain.com
NEWSLETTER_IMAP_PORT=993
NEWSLETTER_IMAP_USERNAME=your-username
NEWSLETTER_IMAP_PASSWORD=your-password
NEWSLETTER_IMAP_ENCRYPTION=ssl
NEWSLETTER_IMAP_FOLDER=INBOX
Auto-generate the OpenAPI spec on every request in local development:
L5_SWAGGER_GENERATE_ALWAYS=true
For production, run php artisan l5-swagger:generate during deploy.
php artisan newsletter:seed-data
./start-worker.sh
Or manually:
php artisan queue:work --tries=3 --timeout=90
https://newsletter.test (Laravel Herd) or
http://localhost:8000
admin@newsletter.test /
password
Configure how many emails the queue may send using rolling windows (stored in the app cache). Set a variable to 0 to turn off that cap; the other caps still apply.
| Variable | Window | Typical use |
|---|---|---|
NEWSLETTER_RATE_LIMIT_PER_MINUTE |
~1 min | Burst control |
NEWSLETTER_RATE_LIMIT_PER_HOUR |
~1 h | Provider hourly quotas |
NEWSLETTER_RATE_LIMIT_PER_DAY |
~24 h | Daily provider caps |
For each queued send, SendNewsletterEmail checks daily, then hourly, then per-minute limits. The
send proceeds only if it fits under every enabled limit; otherwise the job is released with a delay.
Example configuration (adjust to your SMTP limits):
NEWSLETTER_RATE_LIMIT_PER_MINUTE=0
NEWSLETTER_RATE_LIMIT_PER_HOUR=1000
NEWSLETTER_RATE_LIMIT_PER_DAY=10000
Check current counters:
php artisan newsletter:rate-limits
The admin panel supports six languages: Italian, English, German, French, Spanish, and Portuguese.
Accept-Language header and uses the closest
supported language (defaults to English)| Route | Method | Description |
|---|---|---|
/subscribe |
GET | Subscription form |
/subscribe |
POST | Process subscription |
/subscribe/confirm/{token} |
GET | Confirm subscription (double opt-in) |
/unsubscribe/{subscriber} |
GET | Unsubscribe form |
/unsubscribe/{subscriber}/confirm |
POST | Confirm unsubscribe |
The application supports three user roles with progressively broader permissions. Every user is assigned exactly one role.
| Role | Description |
|---|---|
| Editor | Focused on content creation. Can create and manage draft messages within existing campaigns. |
| Manager | Full operational access. Manages campaigns, subscribers, tags, templates, and all message states. |
| Administrator | Everything a Manager can do, plus user management and API token management. |
| Area | Editor | Manager | Administrator |
|---|---|---|---|
| Dashboard | — | ✓ | ✓ |
| Campaigns (view) | ✓ | ✓ | ✓ |
| Campaigns (create/edit/delete) | — | ✓ | ✓ |
| Messages (create) | ✓ | ✓ | ✓ |
| Messages (view/edit/delete draft) | ✓ | ✓ | ✓ |
| Messages (view sent/sending) | — | ✓ | ✓ |
| Subscribers | — | ✓ | ✓ |
| Tags | — | ✓ | ✓ |
| Templates | — | ✓ | ✓ |
| Users | — | — | ✓ |
| API Tokens | — | — | ✓ |
| Newsletter Report API | — | ✓ | ✓ |
Administrators can create, edit, and delete users from the Users page in the admin panel. When creating or editing a user, the following fields are available:
An administrator cannot delete their own account.
The application exposes a full REST API authenticated via Laravel Sanctum personal access tokens. Use it to integrate with CRMs, external dashboards, or any third-party system.
/api-tokens)
Authorization header:Authorization: Bearer <your-token>
Interactive Swagger UI is available at /api/documentation. Generate or update the spec with:
php artisan l5-swagger:generate
All endpoints are prefixed with /api and require a valid Bearer token with the
api ability.
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/user |
Authenticated user info |
GET |
/api/reports/newsletter |
Newsletter report (filterable) |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/tags |
List all tags |
POST |
/api/tags |
Create a tag |
GET |
/api/tags/{tag} |
Show a tag |
PUT |
/api/tags/{tag} |
Update a tag |
DELETE |
/api/tags/{tag} |
Delete a tag |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/subscribers |
List subscribers |
POST |
/api/subscribers |
Create a subscriber |
GET |
/api/subscribers/{subscriber} |
Show a subscriber |
PUT |
/api/subscribers/{subscriber} |
Update a subscriber |
DELETE |
/api/subscribers/{subscriber} |
Delete a subscriber |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/templates |
List templates |
POST |
/api/templates |
Create a template |
GET |
/api/templates/{template} |
Show a template |
PUT |
/api/templates/{template} |
Update a template |
DELETE |
/api/templates/{template} |
Delete a template |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/campaigns |
List campaigns |
POST |
/api/campaigns |
Create a campaign |
GET |
/api/campaigns/{campaign} |
Show a campaign |
PUT |
/api/campaigns/{campaign} |
Update a campaign |
DELETE |
/api/campaigns/{campaign} |
Delete a campaign |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/campaigns/{campaign}/messages |
List messages in a campaign |
POST |
/api/campaigns/{campaign}/messages |
Create a message |
GET |
/api/campaigns/{campaign}/messages/{message} |
Show a message |
PUT |
/api/campaigns/{campaign}/messages/{message} |
Update a message |
DELETE |
/api/campaigns/{campaign}/messages/{message} |
Delete a message |
The application includes a Model Context Protocol (MCP) server that allows AI clients (Cursor, Claude Code, Windsurf, etc.) to interact with your newsletter data using natural language.
/mcp/newsletter
Authenticated with a Sanctum Bearer token that has the mcp ability.
The server ships with a newsletter-assistant prompt — a reusable skill template that
guides AI assistants through campaign planning, report interpretation, content drafting, and message
creation.
| Tool | Description |
|---|---|
list-campaigns |
Lists campaigns (all for managers/administrators; editors see their own) |
newsletter-report |
Delivery report with summary, per-message stats, and daily timeseries |
send-history-analysis |
Highlights and trends from recent send history |
subscriber-insights |
Audience breakdown by tags and statuses |
generate-email-template-html |
Generates responsive HTML email template from a description |
create-newsletter-message |
Creates a draft or ready message inside a campaign |
You can phrase tasks in natural English; the assistant maps them to MCP tools:
php artisan mcp:inspector mcp/newslettermcp.json examplePoint the URL at your app's origin plus /mcp/newsletter, and send the Sanctum token in the
Authorization header:
{
"mcpServers": {
"newsletter": {
"url": "https://your-domain.example/mcp/newsletter",
"headers": {
"Authorization": "Bearer YOUR_SANCTUM_TOKEN"
}
}
}
}
Replace your-domain.example with your deployment host (for local development, something like
http://127.0.0.1:8000/mcp/newsletter). Replace YOUR_SANCTUM_TOKEN with the
plaintext token string from the admin panel.
Add the Laravel scheduler to your crontab:
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
| Task | Schedule | Description |
|---|---|---|
newsletter:send-scheduled |
Every minute | Sends messages with scheduled date in the past |
newsletter:process-bounces |
Every 15 minutes | Processes bounced emails via IMAP |
backup:run |
Daily at 03:00 | Runs application backup |
backup:clean |
Daily at 04:00 | Cleans old backups |
| Command | Description |
|---|---|
newsletter:seed-data |
Populate database with sample subscribers, campaigns, and messages |
newsletter:send-scheduled |
Manually trigger scheduled message sending |
newsletter:process-pending |
Process pending emails in the queue |
newsletter:process-bounces |
Process bounced emails from IMAP |
newsletter:rate-limits |
Display current rate limit status |
l5-swagger:generate |
Generate or update the OpenAPI specification |
mcp:inspector mcp/newsletter |
Debug the MCP server locally |
Deploy to any Ubuntu VPS with cipi.sh — an open-source CLI built exclusively for Laravel.
wget -O - https://raw.githubusercontent.com/andreapollastri/cipi/refs/heads/latest/install.sh | bash
./start-worker.sh or
php artisan queue:work
php artisan newsletter:process-pendingphp artisan tinker --execute="DB::table('jobs')->count()"php artisan tinker --execute="Mail::raw('Test', fn(\$m) => \$m->to('test@example.com'))"
php artisan newsletter:rate-limits
Run npm run build or npm run dev to compile assets.
api for REST endpoints, mcp for the
MCP server)/api/documentation for request/response format