Skip to content

Template Publishing

Publishing a template to the Community Gallery is how you make a reusable template available to thousands of GTM practitioners. The process is more structured than most people expect: it requires a specific repository layout, a metadata.yaml file that powers the Gallery listing, passing tests, and an Apache 2.0 license. Done correctly, the template becomes a maintainable artifact with automatic update notifications for everyone who installs it.

This article covers the complete submission workflow from repository setup to Gallery listing, plus the common reasons templates get rejected and how to avoid them.

Every Gallery template lives in a GitHub repository. The repository can contain a single template or multiple templates (one per subdirectory). For a single-template repository, the structure is:

my-gtm-template/
├── template.tpl # Required: the template file
├── metadata.yaml # Required: Gallery listing metadata
├── LICENSE # Required: Apache 2.0 license
└── README.md # Strongly recommended: setup instructions

For a multi-template repository (common for organizations):

my-gtm-templates/
├── tag-linkedin-insight/
│ ├── template.tpl
│ ├── metadata.yaml
│ └── README.md
├── variable-url-params/
│ ├── template.tpl
│ ├── metadata.yaml
│ └── README.md
└── LICENSE # Can be at root if all templates share the same license

Google’s indexer looks for metadata.yaml files and treats the containing directory as a template. A LICENSE file must be present in the same directory as metadata.yaml or in the repository root.

The .tpl file is a JSON-like format that GTM exports when you download a template from the editor. It is not standard JSON — it uses a custom syntax with sections delimited by ___INFO___, ___TEMPLATE_PARAMETERS___, ___SANDBOXED_JS_FOR_TAG_TEMPLATE___, ___WEB_PERMISSIONS___, and ___TESTS___ markers.

A complete .tpl file structure:

___INFO___
{
"displayName": "My Tag Template",
"description": "What this template does",
"securityGroups": [],
"id": "cvt_XXXXXXXX_XXXX", // Generated by GTM — do not edit manually
"type": "TAG", // TAG, VARIABLE, or CLIENT
"version": 1,
"containerContexts": ["WEB"] // WEB, SERVER, or both
}
___TEMPLATE_PARAMETERS___
[
{
"type": "TEXT",
"name": "partnerId",
"displayName": "Partner ID",
"simpleValueType": true,
"valueValidators": [
{
"type": "NON_EMPTY"
}
]
},
{
"type": "SELECT",
"name": "eventType",
"displayName": "Event Type",
"macrosInSelect": false,
"selectItems": [
{
"value": "PageView",
"displayValue": "Page View"
},
{
"value": "Conversion",
"displayValue": "Conversion"
}
],
"simpleValueType": true
}
]
___SANDBOXED_JS_FOR_TAG_TEMPLATE___
// Your template code here
const injectScript = require('injectScript');
const data = require('data');
// ... implementation
___WEB_PERMISSIONS___
[
{
"instance": {
"key": {
"publicId": "inject_script",
"versionId": "1"
},
"param": [
{
"key": "urls",
"value": {
"type": 2,
"listItem": [
{
"type": 1,
"string": "https://snap.licdn.com/li.lms-analytics/insight.min.js"
}
]
}
}
]
},
"clientAnnotations": {
"isEditedByUser": true
},
"isRequired": true
}
]
___TESTS___
[
{
"name": "Successful load",
"code": "// test code here\nrunCode({partnerId: '12345', eventType: 'PageView'});\nassertApi('gtmOnSuccess').wasCalled();"
}
]

Never edit the ___INFO___ section’s id field. GTM generates this UUID when you create the template. If you change it, existing installations lose their update link. Export the template from GTM and commit the exported file — do not reconstruct the .tpl manually.

The correct way to get a .tpl file:

  1. Open your template in GTM’s template editor.

  2. Click the three-dot menu in the top right corner.

  3. Select Export.

  4. GTM downloads a .tpl file with your template’s current state — code, fields, permissions, and tests all included.

  5. Rename the file to template.tpl and commit it to your repository.

Every update you publish should start with a new export from the GTM editor. Do not maintain the .tpl file by hand — GTM’s internal format changes between versions.

The metadata.yaml file powers your Gallery listing. GTM’s indexer reads this to generate the listing title, description, category filter, and documentation link.

homepage: "https://github.com/your-org/my-gtm-template"
documentation: "https://github.com/your-org/my-gtm-template/blob/main/README.md"
# One of: "tag", "variable", "client"
templateType: tag
# Display name shown in Gallery search
displayName: "My Vendor Tag"
# Short description (appears in Gallery search results)
description: "Implements the My Vendor pixel with consent mode support and proper queue initialization."
# Categories — choose from the official list
categories:
- "ADVERTISING" # See full list below
# GitHub repository URL
repository: "https://github.com/your-org/my-gtm-template"
# Changelog (optional but strongly recommended)
changelog:
- version: "1.0.1"
changes: "Added Enhanced Conversions support"
- version: "1.0.0"
changes: "Initial release"

The Gallery uses a fixed set of category identifiers. Use these exact strings:

Category displayYAML value
AnalyticsANALYTICS
AdvertisingADVERTISING
Affiliate MarketingAFFILIATE_MARKETING
AttributionATTRIBUTION
ChatCHAT
Consent ManagementCONSENT_MANAGEMENT
CRMCRM
Customer SuccessCUSTOMER_SUCCESS
Data TransformationDATA_TRANSFORMATION
Email MarketingEMAIL_MARKETING
Heatmap & RecordingHEAT_MAP_AND_RECORDING
Marketing AutomationMARKETING_AUTOMATION
PersonalizationPERSONALIZATION
RemarketingREMARKETING
SearchSEARCH
SEOSEO
SocialSOCIAL
Tag ManagementTAG_MANAGEMENT
UtilityUTILITY
VideoVIDEO

A template can belong to multiple categories. Most vendor pixel templates should use ADVERTISING. Generic utility templates should use UTILITY or DATA_TRANSFORMATION.

Every Gallery template must be licensed under Apache 2.0. This is non-negotiable — Google will reject templates with other licenses.

Create a LICENSE file in the template directory with the standard Apache 2.0 text:

Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
...

The full text is available at apache.org/licenses/LICENSE-2.0.txt. Copy the complete text — do not truncate it or link to it from the file.

The Apache 2.0 license allows users to use, modify, and distribute your template, including commercially. It requires attribution. This is why Gallery template modifications sever the update link — the user has created a derivative work they now own.

Tests are required for Gallery submission. A template with no tests will be rejected during review.

The minimum test requirement:

  • At least one test that exercises the primary success path
  • At least one test that exercises a failure path
  • All tests must pass before submission

Tests are stored inside the .tpl file in the ___TESTS___ section. GTM exports them automatically when you export the template. Write your tests in the GTM editor’s Tests tab, run them until they all pass, then export.

A practical minimum test set for a tag template:

// Test 1: Successful execution
(function() {
mock('injectScript', function(url, onSuccess, onFailure) {
onSuccess();
});
// Mock all other required APIs
runCode({
// All required fields with valid values
pixelId: '12345',
eventType: 'PageView'
});
assertApi('gtmOnSuccess').wasCalled();
assertApi('gtmOnFailure').wasNotCalled();
})();
// Test 2: Script load failure
(function() {
mock('injectScript', function(url, onSuccess, onFailure) {
onFailure();
});
runCode({
pixelId: '12345',
eventType: 'PageView'
});
assertApi('gtmOnFailure').wasCalled();
assertApi('gtmOnSuccess').wasNotCalled();
})();
// Test 3: Missing required field
(function() {
runCode({
pixelId: '', // Missing
eventType: 'PageView'
});
assertApi('gtmOnFailure').wasCalled();
})();

See Testing Custom Templates for the complete testing API.

  1. Create a public GitHub repository. The repository must be public — GTM’s indexer cannot access private repositories. The repository name doesn’t matter, but use something descriptive (gtm-linkedin-insight-tag, gtm-cookie-writer, etc.).

  2. Commit your template files. Commit template.tpl, metadata.yaml, LICENSE, and README.md to the main branch. The commit you make here becomes the first “version” users install.

  3. Verify the repository structure. The metadata.yaml must be at the root (for single-template repos) or in a subdirectory (for multi-template repos). The .tpl file must be in the same directory as metadata.yaml.

  4. Submit via the GTM Gallery submission form. Go to tagmanager.google.com/gallery/submit and complete the form. You’ll need:

    • Your GitHub repository URL
    • The template type (tag, variable, client)
    • A brief description of what the template does
    • Contact email for review correspondence
  5. Wait for Google’s review. Review typically takes 2–4 weeks for first submissions. Google’s team checks: license compliance, permissions appropriateness, code quality, test coverage, documentation adequacy, and no malicious or obfuscated code.

  6. Respond to review feedback. If Google requests changes, you’ll receive feedback via email. Make the requested changes, commit them, and reply to the review thread with the updated commit SHA.

  7. Template appears in the Gallery. Once approved, your template appears in Gallery search results. Users who install it will receive update notifications when you push new commits.

How the version and update mechanism works

Section titled “How the version and update mechanism works”

The Gallery update system is built on Git commit SHAs. When a user installs your template:

  1. GTM records the exact commit SHA of the .tpl file at the time of installation
  2. GTM periodically checks your repository for new commits that change the .tpl file
  3. When a new commit is detected, GTM shows “Update available” in the user’s Templates list
  4. Users see a diff of the changes (permissions changes are highlighted prominently)
  5. Users can accept or defer the update

This means every change you commit to the .tpl file becomes a version. There is no separate versioning system — commit history is the version history. Keep your commit messages meaningful:

git commit -m "Add Enhanced Conversions support for email hashing"
git commit -m "Fix: Update SDK URL to v4.2 (v4.1 deprecated)"
git commit -m "Add consent_update event trigger support"

Users see these commit messages in the update diff view. Good commit messages help users decide whether to accept an update.

To publish an update to an installed template:

  1. Make your changes in the GTM editor.
  2. Run all tests — every test must pass.
  3. Export the template (template.tpl).
  4. Replace the old template.tpl in your repository with the new export.
  5. Update metadata.yaml changelog section with what changed.
  6. Commit with a descriptive message.
  7. Push to your main branch.

GTM’s indexer will detect the new commit within a few hours and trigger update notifications for all users who have the template installed.

The README is the documentation that Gallery users see when they click “More Info” on your template. It is the primary documentation for your template — do not skip it.

A complete README for a Gallery template:

# My Vendor Tag Template for GTM
Implements the My Vendor tracking pixel with:
- Automatic queue initialization
- Consent Mode v2 support
- Custom event tracking
## Requirements
- A My Vendor account with a Pixel ID
- (Optional) Google Consent Mode v2 if using consent-aware tracking
## Setup
1. Add the template from the Community Gallery
2. Create a new tag using this template
3. Enter your **Pixel ID** from your My Vendor account
## Fields
| Field | Required | Description |
|-------|----------|-------------|
| Pixel ID | Yes | Your 8-digit My Vendor Pixel ID |
| Event Type | Yes | Standard or custom event name |
| Custom Event Name | Only if Event Type = Custom | The custom event name to fire |
## Events
The template fires the pixel on every tag trigger. For standard events,
use the built-in event types. For custom events, select "Custom Event"
and enter the event name.
## Consent Mode
This template respects Google Consent Mode v2. If `ad_storage` is denied,
the pixel fires with limited data mode enabled. Behavioral modeling is
supported.
## Permissions
- `inject_script`: Loads the My Vendor pixel SDK from cdn.myvendor.com
- `access_globals`: Reads and writes the `myVendorQ` queue
- `send_pixel`: Sends pixel requests to px.myvendor.com
## License
Apache 2.0

The Permissions section is particularly important. Users reviewing your template want to understand why each permission is needed. A clear explanation of each permission reduces review friction and builds trust.

Google’s review team rejects a significant fraction of first-time submissions. These are the most common reasons:

Missing or wrong license. The LICENSE file is absent, or it contains a license other than Apache 2.0. Fix: copy the complete Apache 2.0 license text into a LICENSE file.

No tests. The .tpl file has an empty ___TESTS___ section. Fix: write tests in the GTM editor, verify they pass, re-export.

Overly broad permissions. A inject_script permission with https://* (any URL) or access_globals for all globals (.*). Fix: narrow every permission to the minimum required. The review team reads permissions declarations carefully.

Hardcoded account values. The template has an account ID, API key, or pixel ID hardcoded in the template code rather than in a user-facing field. Fix: all account-specific values must be template fields that users configure.

Obfuscated or minified code. The template code is minified, base64-encoded, or otherwise obfuscated. Fix: the code section must be readable source code. The review team reads every line.

No documentation. The metadata.yaml has no documentation link, or the linked README is empty or a placeholder. Fix: write a complete README before submitting.

Template does not work. The template fires gtmOnFailure on basic inputs, or the injected script URL is wrong. Fix: test in GTM Preview mode before submitting.

Missing metadata.yaml fields. The displayName or description is absent or is the default generated text. Fix: write real content for all required fields.

Once a template is live in the Gallery, you take on ongoing maintenance responsibility. Users who rely on your template expect:

Compatibility updates. When the vendor SDK you inject changes its URL, initialization API, or behavior, the template needs to be updated. Users with outdated templates will see broken tracking until they update.

Security responses. If a vulnerability is discovered in your template or the SDK it loads, update promptly. The template update system is fast — users receive notification within hours of your commit.

Responding to issues. Gallery listing pages often link to the GitHub repository. Users will open issues when they find bugs. Responding and releasing fixes is part of the maintenance contract.

Deprecating templates you can no longer maintain. If you cannot maintain a template, contact Google to remove it from the Gallery rather than leaving an unmaintained template installed on thousands of sites. An abandoned template is a security risk for everyone who uses it.

Before submitting to the Gallery, verify each of these:

  • Repository is public on GitHub
  • template.tpl exported from GTM (not hand-edited)
  • All tests pass in GTM editor
  • metadata.yaml has correct displayName, description, templateType, categories
  • LICENSE file contains complete Apache 2.0 text
  • README.md explains fields, setup steps, and permissions
  • Template has no hardcoded account IDs or API keys
  • All permissions are narrowed to the minimum required
  • Template code is readable (no minification or obfuscation)
  • Template tested in GTM Preview mode with real data
  • Submission form completed at tagmanager.google.com/gallery/submit