This is a starter template, not a production-ready plugin. It is meant to be forked and adapted to your own external data source and field definitions. Do not install it as-is expecting a working integration.
A Mattermost plugin starter template that demonstrates how to synchronize user profile attributes from external systems into Mattermost's Custom Profile Attributes (CPA). This template serves as both a working reference implementation and an educational resource for plugin developers.
Mattermost's Custom Profile Attributes system (also called Properties) allows you to store structured metadata about users. A field defines the schema (name, type, options), while a value stores the actual data for a specific user. For multiselect fields, options define the allowed choices that users can select from.
This plugin demonstrates how to create fields with hardcoded definitions and synchronize values from external data sources. Fields are defined explicitly in code with their types (text, date, multiselect), and the plugin uses Mattermost's cluster job system to run periodic synchronization tasks. The implementation includes incremental synchronization that processes only changed data after the initial sync.
The template creates three example fields that demonstrate different access control modes: Job Title (text, public access), Programs (multiselect with options, shared-only access), and Start Date (date, source-only access). All fields are marked as visible in the UI and protected (only this plugin can modify structure and write values).
Plugin Activation (Once)
├─> Create/Update CPA Fields
└─> Start Background Job
Background Job (On timed interval)
├─> Fetch Changed Values From External Source
└─> Bulk Upsert Values
- Field Definitions (
server/sync/field_sync.go) - Hardcoded schema with field types and options - Value Sync (
server/sync/value_sync.go) - User attribute value synchronization - File Provider (
server/sync/file_provider.go) - Example JSON file-based data source - Job Orchestrator (
server/job.go) - Cluster-aware periodic sync scheduler
- Mattermost server 11.5.0 or later
- Go 1.24 or later
- Node v16 and npm v8 (if modifying webapp)
-
Clone this repository:
git clone https://github.com/mattermost/mattermost-plugin-user-attribute-sync-starter-template cd mattermost-plugin-user-attribute-sync-starter-template -
Build the plugin:
make
-
Important: Copy the example data file to your Mattermost data directory:
cp data/user_attributes.json /path/to/mattermost/data/user_attributes.json
The plugin reads from
data/user_attributes.jsonrelative to the Mattermost data directory (not the plugin directory). Update the JSON file with your users' email addresses and attributes. Seedata/user_attributes.jsonin this repository for the expected format. -
Upload the plugin through System Console → Plugin Management, or use:
make deploy
When the plugin activates, it creates the three Custom Profile Attribute fields (Job Title, Programs, and Start Date) in Mattermost. These fields appear in System Console → User Attributes. If the fields already exist from a previous activation, the plugin updates them to match the hardcoded definitions.
Immediately after activation, the plugin runs its first synchronization. It reads the user_attributes.json file from the Mattermost data directory, matches users by email address, and populates the Custom Profile Attribute values for each user found in the data file. The plugin logs its progress and any errors (such as users not found in Mattermost) during this process.
After the initial sync, the plugin checks for changes every 60 minutes by default. The file provider tracks the modification time of user_attributes.json and only processes the file if it has been modified since the last sync. When changes are detected, the plugin syncs all users in the file again. You can adjust the sync interval in the plugin configuration settings.
The synced attribute values are stored as Custom Profile Attributes and can be viewed in System Console → User Attributes or through the Mattermost API.
This template demonstrates Mattermost's field-level access control system through three example fields, each using a different access mode.
Public Access (Job Title example): Everyone can read all field values via API and UI. Best for non-sensitive organizational data like job titles, departments, or office locations.
Source-Only Access (Start Date example): Only this plugin can read field values via API. Other users, admins, and integrations see empty options and no values. Useful for data that must be synchronized but should remain private, like employee start dates or internal identifiers. Even users cannot see their own values through the API.
Shared-Only Access (Programs example): Users can only see field options and values they share with the target user. Only works with select and multiselect field types. Example: If Alice is in [Apples, Bananas] and Bob is in [Bananas, Oranges], Alice viewing Bob's profile only sees [Bananas] as their common program. Best for private categorical data where users should only discover shared attributes.
All three example fields are marked as "protected", which means only this plugin can modify field structure (add/remove options, change types) and write values. Users and admins cannot manually edit protected fields. Read access is controlled separately by the access mode.
The visibility attribute controls whether values appear in the Mattermost UI (user profiles, user cards), but does NOT affect data access via the API. Even if visibility is set to hidden, data can still be retrieved via API subject to access mode permissions. To control actual data access, use the access_mode attribute.
Consider your data sensitivity and use case:
| Data Type | Recommended Mode | Example Fields |
|---|---|---|
| Public organizational info | Public | Job Title, Department, Office Location, Phone Extension |
| Sensitive internal data | Source-Only | Start Date, Salary Band, Performance Rating, Employee ID |
| Private categorical membership | Shared-Only | Programs, Projects, Teams, Certifications, Skills |
Note: Source-only and shared-only modes require the field to be marked as protected. Shared-only mode can only be used with select or multiselect field types.
Edit server/sync/field_sync.go and add entries to the fieldDefinitions array:
{
Name: "Department",
ExternalName: "department",
Type: model.PropertyFieldTypeText,
AccessMode: model.PropertyAccessModePublic, // Choose: Public, SourceOnly, or SharedOnly
},Restart the plugin to create the new field. See the Access Control section above for details on access modes.
Update the OptionNames array in fieldDefinitions:
{
Name: "Programs",
ExternalName: "programs",
Type: model.PropertyFieldTypeMultiselect,
OptionNames: []string{"Apples", "Oranges", "Lemons", "Bananas"},
},Restart the plugin to add new options. This template plugin never removes existing options from Mattermost because users may have already selected those values.
The sync interval can be configured in the plugin settings. Navigate to System Console → Plugins → User Attribute Sync Starter Template and adjust the "Sync Interval (Minutes)" setting. The default is 60 minutes.
Edit server/sync/file_provider.go and modify the constant:
const defaultDataFilePath = "data/my_custom_file.json"The template uses a file-based provider, but you can swap this for any data source:
-
Implement the
AttributeProviderinterface in a new file (e.g.,server/sync/api_provider.go):GetUserAttributes()- Fetch user data from your external systemClose()- Clean up resources
-
Update
server/job.goto use your provider:provider := sync.NewAPIProvider(apiURL, apiKey)
-
Handle incremental sync by tracking state internally (e.g., last sync timestamp)
Common provider implementations:
- REST API: Poll external API for changed users since last sync
- LDAP: Query directory for users modified after last sync time
- Database: Query users table with
updated_at > last_sync - Webhook: Accept push notifications of changed users (requires API endpoint)
Important: Field types cannot be changed after creation (Mattermost platform limitation). To change a field type:
- Delete the field (all user values will be lost). You can do this via the Mattermost API or by adding code to delete the field during plugin activation.
- Update the field definition in code
- Restart the plugin to recreate with new type
.
├── server/
│ ├── sync/
│ │ ├── field_sync.go # Field creation and schema management
│ │ ├── value_sync.go # User attribute value synchronization
│ │ ├── provider.go # AttributeProvider interface
│ │ └── file_provider.go # File-based provider implementation
│ ├── plugin.go # Plugin lifecycle (OnActivate/OnDeactivate)
│ └── job.go # Background job orchestration
├── data/
│ └── user_attributes.json # Example data file
└── README.md
make test # Run all unit tests
make check-style # Run linting
make all # Run check-style, test, and buildEnable local mode in your Mattermost server configuration:
{
"ServiceSettings": {
"EnableLocalMode": true,
"LocalModeSocketLocation": "/var/tmp/mattermost_local.socket"
}
}Then deploy automatically on changes:
make deployFor continuous deployment during development:
export MM_SERVICESETTINGS_SITEURL=http://localhost:8065
export MM_ADMIN_TOKEN=your_token_here
make watchSee LICENSE file for details.
This is a starter template meant to be customized for your specific use case. The code is designed to be read, understood, and modified. Start by exploring the server/sync/ directory to understand how each component works, then adapt it to your external system.
For Mattermost plugin development questions, see the plugin documentation.