Documentation

Everything you need to install, configure, and integrate the Newsletter Platform — from quick start to advanced AI and API usage.

Features

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

Requirements

  • PHP 8.3+
  • Laravel 13
  • Filament 5
  • Database: SQLite, MySQL, or PostgreSQL
  • Queue driver: database, Redis, etc.

Installation

1. Clone and install dependencies

git clone https://github.com/andreapollastri/newsletter.git
cd newsletter
composer install

2. Configure the environment

cp .env.example .env
php artisan key:generate

3. Run migrations

php artisan migrate

4. Build frontend assets

npm install
npm run build

5. Create an admin user

php artisan make:filament-user

6. Generate API documentation (optional)

php artisan l5-swagger:generate

Configuration

Mail (SMTP)

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}"

Newsletter

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

IMAP (Bounce Detection)

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

API Documentation (Swagger)

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.

Quick Start

Seed sample data (optional)

php artisan newsletter:seed-data

Start the queue worker

./start-worker.sh

Or manually:

php artisan queue:work --tries=3 --timeout=90

Access the admin panel

  • URL: https://newsletter.test (Laravel Herd) or http://localhost:8000
  • Default credentials (after seeding): admin@newsletter.test / password

Sending Newsletters

Immediate Sending

  1. Go to Newsletter > Messages
  2. Create a new message or select an existing one
  3. Set status to Ready
  4. Click Send Now
  5. Emails are queued and sent automatically via the queue worker

Scheduled Sending

  1. Create or edit a message
  2. Set the Scheduled Date field
  3. The system sends automatically at the scheduled time (requires cron)

Monitoring & Analytics

  • Dashboard: Main KPIs and send statistics
  • Messages: Status and send counts per message
  • Message Details: Individual tracking (opens, clicks) per recipient

Rate Limiting

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

Testing Tags

Tags can be marked as testing (is_testing flag) in the admin panel. When a message targets recipients only through testing tags:

  • Sends are excluded from dashboard statistics (emails sent, opens, clicks, bounces, charts)
  • After the send completes, per-recipient send rows and related bounces are removed from the database
  • Testing tags and normal tags cannot be mixed on the same message

This allows safe end-to-end testing of the sending pipeline without polluting production metrics.

Multilingual Support

The admin panel supports six languages: Italian, English, German, French, Spanish, and Portuguese.

  • Logged-in users can choose their language from the Profile page
  • Before login, the panel detects the browser Accept-Language header and uses the closest supported language (defaults to English)
  • New users automatically inherit the browser language on account creation

Public Routes

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

User Roles

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.

Navigation

  • Editor — lands on the Campaigns page after login; the dashboard is not shown.
  • Manager / Administrator — lands on the Dashboard; all navigation items are visible according to the permission matrix.
  • User menu — the API Tokens and Users links appear only for Administrators.

Permission Matrix

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

Managing Users

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:

  • Name and Email (required)
  • Password (required on create, optional on edit — leave blank to keep unchanged)
  • Role — select Editor, Manager, or Administrator
  • Locale — preferred language for the admin panel UI

An administrator cannot delete their own account.

REST API

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.

Authentication

  1. Go to the admin panel user menu > API tokens (or navigate to /api-tokens)
  2. Create a token with the API ability enabled
  3. Use the token in the Authorization header:
Authorization: Bearer <your-token>

OpenAPI Documentation

Interactive Swagger UI is available at /api/documentation. Generate or update the spec with:

php artisan l5-swagger:generate

API Endpoints

All endpoints are prefixed with /api and require a valid Bearer token with the api ability.

User

Method Endpoint Description
GET /api/user Authenticated user info
GET /api/reports/newsletter Newsletter report (filterable)

Tags

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

Subscribers

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

Templates

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

Campaigns

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

Messages (nested under campaigns)

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

MCP Server (AI Integrations)

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.

Endpoint

/mcp/newsletter

Authenticated with a Sanctum Bearer token that has the mcp ability.

Prompt

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.

MCP Tools

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

Example requests

You can phrase tasks in natural English; the assistant maps them to MCP tools:

  • Campaigns: "List all newsletter campaigns." / "What campaigns do we have?"
  • Delivery stats: "Give me a newsletter delivery report from 2026-01-01 to 2026-01-31."
  • Scoped report: "Report for campaign id X between 2026-01-01 and 2026-01-31."
  • Narrative summary: "Summarize send history and highlights for the last quarter."
  • Audience: "How many subscribers do we have by status? Which tags have the most?"
  • HTML: "Generate a responsive HTML email template for a product launch."
  • New message: "Create a draft message in campaign X, using template Y, subject 'Weekly digest'."

MCP Setup

  1. Create a token in the admin panel with the MCP ability
  2. Configure your AI client to connect to the MCP endpoint with the token as Bearer auth
  3. Debug locally with: php artisan mcp:inspector mcp/newsletter

VS Code / Cursor mcp.json example

Point 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.

Scheduled Tasks

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

Artisan Commands

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

Production Deploy

Deploy to any Ubuntu VPS with cipi.sh — an open-source CLI built exclusively for Laravel.

  • Full app isolation — own Linux user, PHP-FPM pool & database per app
  • Zero-downtime deploys with instant rollback via Deployer
  • Let's Encrypt SSL, Fail2ban, UFW firewall — all automated
  • Multi-PHP (7.4 → 8.5), queue workers, S3 backups, auto-deploy webhooks
  • AI Agent ready — any AI with SSH access can deploy & manage your server
wget -O - https://raw.githubusercontent.com/andreapollastri/cipi/refs/heads/latest/install.sh | bash

Troubleshooting

Emails not sending

  1. Ensure the queue worker is running: ./start-worker.sh or php artisan queue:work
  2. Manually process pending: php artisan newsletter:process-pending
  3. Check the jobs table: php artisan tinker --execute="DB::table('jobs')->count()"

Test email delivery

php artisan tinker --execute="Mail::raw('Test', fn(\$m) => \$m->to('test@example.com'))"

Verify rate limits

php artisan newsletter:rate-limits

Frontend changes not visible

Run npm run build or npm run dev to compile assets.

API issues

  1. Verify the token is valid: check the API tokens page in the admin panel
  2. Ensure the token has the correct ability (api for REST endpoints, mcp for the MCP server)
  3. Check the OpenAPI docs at /api/documentation for request/response format