PHPackages                             ligoo/zammad-laravel - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. ligoo/zammad-laravel

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

ligoo/zammad-laravel
====================

Laravel package for Zammad ticketing system integration with contact form

v1.0.0(3mo ago)17MITPHPPHP ^8.1

Since Jan 29Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/ligoo/zammad-laravel)[ Packagist](https://packagist.org/packages/ligoo/zammad-laravel)[ RSS](/packages/ligoo-zammad-laravel/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (6)Versions (2)Used By (0)

Zammad Laravel
==============

[](#zammad-laravel)

A Laravel package for integrating contact forms with the [Zammad](https://zammad.org/) ticketing system.

Features
--------

[](#features)

- **Easy Integration**: Drop-in contact form that creates tickets in Zammad
- **Flexible CAPTCHA**: Support for Google reCAPTCHA v3, Cloudflare Turnstile, or disabled
- **8-Layer Spam Protection**: Rate limiting, honeypot, form timer, keyword detection, and more
- **Multiple Frameworks**: Blade, React, and Vue.js components included
- **Customizable Views**: Framework-agnostic styling with CSS variables
- **Full Configuration**: All options via config file with env variable support

Requirements
------------

[](#requirements)

- PHP 8.1+
- Laravel 10.x, 11.x, or 12.x
- Zammad instance with API access

Installation
------------

[](#installation)

```
composer require ligoo/zammad-laravel
```

Configuration
-------------

[](#configuration)

Publish the configuration file:

```
php artisan vendor:publish --tag=zammad-config
```

Add the following to your `.env` file:

```
# Required
ZAMMAD_URL=https://your-zammad-instance.com
ZAMMAD_TOKEN=your-api-token-here
ZAMMAD_DEFAULT_GROUP=1

# Optional - CAPTCHA (choose one)
ZAMMAD_CAPTCHA_DRIVER=none  # or: recaptcha_v3, turnstile

# For reCAPTCHA v3
RECAPTCHA_SITE_KEY=your-site-key
RECAPTCHA_SECRET_KEY=your-secret-key

# For Cloudflare Turnstile
TURNSTILE_SITE_KEY=your-site-key
TURNSTILE_SECRET_KEY=your-secret-key
```

### Getting a Zammad API Token

[](#getting-a-zammad-api-token)

1. Log into Zammad as admin
2. Go to **Admin → API → Personal Access Tokens**
3. Create a new token with permissions:
    - `ticket.agent` (to create/update tickets)
    - `admin` (recommended for full API access)

### Setting Up the API User

[](#setting-up-the-api-user)

The user who owns the API token must have **group access** to create tickets:

1. Go to **Admin → Manage → Users**
2. Find the user who created the API token
3. Edit the user and go to **Group permissions**
4. Add your target group with **full** access
5. Save

> **Important:** Token permissions alone are not enough. The user must also have explicit group access in their user profile.

### Finding Your Group ID

[](#finding-your-group-id)

1. Go to **Admin → Manage → Groups**
2. Click on the group you want to use
3. The ID is in the URL: `/manage/groups/38` → ID is `38`

### Testing Your Configuration

[](#testing-your-configuration)

After setup, verify everything works:

```
php artisan zammad:test
```

This command tests:

- API connectivity
- Token validity
- User permissions
- Group access
- Ticket creation

If any test fails, the output will explain what's wrong.

Usage
-----

[](#usage)

### Blade (Default)

[](#blade-default)

The package automatically registers routes at `/contact`. Visit `/contact` to see the form.

Include in your own layout:

```
@extends('layouts.app')

@section('content')

        Get in Touch
        @include('zammad::contact')

@endsection
```

Add the stylesheet to your layout's ``:

```

```

Publish assets:

```
php artisan vendor:publish --tag=zammad-assets
```

### React

[](#react)

Publish the React component:

```
php artisan vendor:publish --tag=zammad-react
```

This copies `ZammadContactForm.tsx` to `resources/js/vendor/zammad/react/`.

**Step 1:** Disable the package's GET route (so you can use your own Inertia page):

```
ZAMMAD_ROUTES_GET=false
```

**Step 2:** Create your Inertia route and controller:

```
// routes/web.php
Route::get('/contact', [ContactController::class, 'contact'])->name('contact.form');

// In your controller
use Ligoo\ZammadLaravel\Http\Controllers\ContactController as ZammadController;

public function contact()
{
    return Inertia::render('Contact', [
        'contactConfig' => ZammadController::getFormConfig(),
    ]);
}
```

**Step 3:** Ensure your layout has the CSRF meta tag (required for form submission):

```

```

> **Note:** Inertia.js apps typically include this by default. Check your `app.blade.php`.

Use in your React component:

```
import ZammadContactForm from './vendor/zammad/react/ZammadContactForm';

function ContactPage({ contactConfig }) {
    return (
         console.log('Success:', response.message)}
        />
    );
}
```

#### React Props

[](#react-props)

PropTypeRequiredDescription`config``ContactFormConfig`YesForm configuration from `ContactController::getFormConfig()``submitUrl``string`NoSubmit URL (default: `/contact`)`url``string`NoOptional URL to attach to the ticket`onSuccess``function`NoCallback on successful submission`onError``function`NoCallback on validation errors`className``string`NoAdditional CSS classes### Vue.js

[](#vuejs)

Publish the Vue component:

```
php artisan vendor:publish --tag=zammad-vue
```

This copies `ZammadContactForm.vue` to `resources/js/vendor/zammad/vue/`.

Follow the same setup as React (disable GET route, create your Inertia route).

Use in your Vue component:

```

import ZammadContactForm from './vendor/zammad/vue/ZammadContactForm.vue';

// Config from your backend (Inertia props, etc.)
const props = defineProps(['contactConfig']);

```

#### Vue Props

[](#vue-props)

PropTypeRequiredDescription`config``ContactFormConfig`YesForm configuration from `ContactController::getFormConfig()``submitUrl``string`NoSubmit URL (default: `/contact`)`url``string`NoOptional URL to attach to the ticket`className``string`NoAdditional CSS classes#### Vue Events

[](#vue-events)

EventPayloadDescription`success``{ message }`Emitted on successful submission`error``{ [field]: string[] }`Emitted on validation errorsRoute Configuration
-------------------

[](#route-configuration)

Routes use generic names to keep your backend technology hidden:

- **URL**: `/contact`
- **Route names**: `support.contact.form`, `support.contact.submit`

Customize in your `.env`:

```
# Change route name prefix (default: support.)
ZAMMAD_ROUTE_NAME_PREFIX=myapp.

# Change URL prefix (default: contact)
ZAMMAD_ROUTES_PREFIX=help
```

### Inertia / SPA Setup

[](#inertia--spa-setup)

For Inertia, React, or Vue.js apps, disable only the GET route so you can define your own page while keeping the package's POST handler:

```
ZAMMAD_ROUTES_GET=false
```

Then in your routes file:

```
use Ligoo\ZammadLaravel\Http\Controllers\ContactController;

// Your Inertia page
Route::get('/contact', function () {
    return Inertia::render('Contact', [
        'contactConfig' => ContactController::getFormConfig(),
    ]);
})->name('contact.form');

// The package's POST route is still registered automatically at /contact
```

### Manual Route Registration

[](#manual-route-registration)

To fully control all routes, disable auto-routes:

```
ZAMMAD_ROUTES_ENABLED=false
```

Register manually:

```
use Ligoo\ZammadLaravel\Http\Controllers\ContactController;

Route::get('/contact', [ContactController::class, 'create'])->name('contact.form');
Route::post('/contact', [ContactController::class, 'store'])
    ->middleware('zammad.throttle')
    ->name('contact.submit');
```

Configuration Options
---------------------

[](#configuration-options)

### CAPTCHA Drivers

[](#captcha-drivers)

DriverDescription`none`Disabled (default)`recaptcha_v3`Google reCAPTCHA v3 (invisible, score-based)`turnstile`Cloudflare Turnstile (privacy-focused)### Spam Protection

[](#spam-protection)

The package includes 8 layers of spam protection:

1. **Rate Limiting (Middleware)** - 5 attempts per 60 seconds
2. **Rate Limiting (Cache)** - 2 minute cooldown after submission
3. **CAPTCHA** - Configurable provider
4. **Honeypot Field** - Hidden field that must remain empty
5. **Form Timer** - Minimum 3 seconds before submission
6. **Spam Keywords** - Blocks messages with blacklisted words
7. **URL Count** - Blocks messages with too many URLs
8. **Confirmation Checkbox** - User must confirm

### Customizing Subject Options

[](#customizing-subject-options)

Edit `config/zammad.php`:

```
'form' => [
    'subjects' => [
        'sales' => 'Sales Inquiry',
        'support' => 'Technical Support',
        'billing' => 'Billing Question',
    ],
],
```

CSS Customization
-----------------

[](#css-customization)

The CSS uses CSS variables for easy theming. Override in your own stylesheet:

```
:root {
    --zammad-primary: #your-brand-color;
    --zammad-primary-hover: #your-hover-color;
    --zammad-radius: 0.5rem;
}
```

Available variables:

VariableDefaultDescription`--zammad-primary``#2563eb`Primary button/accent color`--zammad-primary-hover``#1d4ed8`Hover state`--zammad-error``#dc2626`Error messages`--zammad-success``#16a34a`Success messages`--zammad-text``#1f2937`Text color`--zammad-border``#e5e7eb`Border color`--zammad-radius``0.375rem`Border radiusLocalization
------------

[](#localization)

The package includes translations for 13 languages:

CodeLanguage`en`English`fr`French`de`German`it`Italian`es`Spanish`pt`Portuguese`nl`Dutch`no`Norwegian`sv`Swedish`da`Danish`fi`Finnish`hu`Hungarian`pl`PolishThe package automatically uses Laravel's current locale (`app()->getLocale()`).

### Customizing Translations

[](#customizing-translations)

Publish the translation files to customize:

```
php artisan vendor:publish --tag=zammad-lang
```

This copies files to `lang/vendor/zammad/`. Edit the files in your preferred language.

### Adding New Languages

[](#adding-new-languages)

Create a new file at `lang/vendor/zammad/{locale}/zammad.php` following the structure of the English file.

Publishing Assets
-----------------

[](#publishing-assets)

```
# Config only
php artisan vendor:publish --tag=zammad-config

# Blade views
php artisan vendor:publish --tag=zammad-views

# CSS assets
php artisan vendor:publish --tag=zammad-assets

# Translations
php artisan vendor:publish --tag=zammad-lang

# React component
php artisan vendor:publish --tag=zammad-react

# Vue component
php artisan vendor:publish --tag=zammad-vue

# All JS components (React + Vue)
php artisan vendor:publish --tag=zammad-components
```

Facades
-------

[](#facades)

### Zammad Facade

[](#zammad-facade)

```
use Ligoo\ZammadLaravel\Facades\Zammad;

// Check if enabled
Zammad::isEnabled();

// Create a ticket programmatically
Zammad::createTicket([
    'email' => 'customer@example.com',
    'name' => 'John Doe',
    'subject' => 'Help needed',
    'message' => 'I need assistance...',
]);

// Add a note to a ticket
Zammad::addTicketNote($ticketId, 'Internal note', internal: true);
```

### Captcha Facade

[](#captcha-facade)

```
use Ligoo\ZammadLaravel\Facades\Captcha;

// Get current driver
$driver = Captcha::driver();

// Verify a token
$result = Captcha::driver()->verify($token, $ip);

if ($result->passed()) {
    // Valid
}
```

Troubleshooting
---------------

[](#troubleshooting)

### 403 Forbidden when creating tickets

[](#403-forbidden-when-creating-tickets)

**Symptom:** `php artisan zammad:test` passes connectivity tests but fails on ticket creation with 403.

**Cause:** The API user doesn't have access to the configured group.

**Fix:**

1. Run `php artisan zammad:test` to see which groups the user has access to
2. Go to **Admin → Manage → Users** in Zammad
3. Edit the API user and add the target group with **full** access

### 422 Unprocessable Entity

[](#422-unprocessable-entity)

**Symptom:** Ticket creation fails with 422 error.

**Cause:** Invalid data being sent (wrong group ID, invalid customer, etc.)

**Fix:**

1. Verify `ZAMMAD_DEFAULT_GROUP` is a valid group ID
2. Run `php artisan zammad:test` to see available groups

### 419 Page Expired (CSRF token mismatch)

[](#419-page-expired-csrf-token-mismatch)

**Symptom:** React/Vue form submission fails with 419 error.

**Cause:** Missing CSRF meta tag in your layout.

**Fix:** Add to your layout's ``:

```

```

### Translations showing as keys

[](#translations-showing-as-keys)

**Symptom:** Form shows `zammad::zammad.contact.title` instead of actual text.

**Cause:** Translation files not loaded or published incorrectly.

**Fix:**

```
php artisan vendor:publish --tag=zammad-lang --force
php artisan cache:clear
```

Testing
-------

[](#testing)

```
composer test
```

Disclaimer
----------

[](#disclaimer)

This package is an **independent, community-developed project**. It is NOT affiliated with, endorsed by, or officially connected to Zammad GmbH, Laravel LLC, Google LLC, Cloudflare Inc., or any of their products.

**No Warranty:** The authors are not liable for any loss of messages, data, communications, security vulnerabilities, or any other issues that may occur through the use of this software. Users are responsible for:

- Verifying ticket creation and message delivery
- Conducting their own security audits
- Implementing appropriate backup and security measures

See [LICENSE](LICENSE) for full terms.

License
-------

[](#license)

MIT License. See [LICENSE](LICENSE) for details.

###  Health Score

36

—

LowBetter than 82% of packages

Maintenance81

Actively maintained with recent releases

Popularity6

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity43

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

103d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/45f9795b81a4dc4fe62c19df03a8a8e2979135e0f6b84764d01a1e97aba95f47?d=identicon)[ligoo](/maintainers/ligoo)

---

Top Contributors

[![ligoo](https://avatars.githubusercontent.com/u/2145088?v=4)](https://github.com/ligoo "ligoo (1 commits)")

---

Tags

laravelsupportzammadhelpdeskticketingcontact form

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/ligoo-zammad-laravel/health.svg)

```
[![Health](https://phpackages.com/badges/ligoo-zammad-laravel/health.svg)](https://phpackages.com/packages/ligoo-zammad-laravel)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3274.9M308](/packages/psalm-plugin-laravel)[watson/active

Laravel helper for recognising the current route, controller and action

3253.6M14](/packages/watson-active)[glhd/conveyor-belt

14797.0k](/packages/glhd-conveyor-belt)[dragon-code/pretty-routes

Pretty Routes for Laravel

10058.7k4](/packages/dragon-code-pretty-routes)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

255.2k](/packages/aedart-athenaeum)[ankurk91/laravel-ses-webhooks

Handle AWS SES webhooks in Laravel php framework

2534.2k](/packages/ankurk91-laravel-ses-webhooks)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
