Tintero Developers

Manifest reference

All 19 fields Tintero reads from plugin.json, with the exact rules the installer applies.

On this page

plugin.json sits at the root of your plugin. The authoritative version of this page is the PluginManifest interface in the type definitions. Type your manifest against it and a mistake becomes a red squiggle instead of a failed install.

A complete manifest

{
  "id": "com.example.word-count",
  "name": "Word Count",
  "version": "1.0.0",
  "description": "A live word count for the document you are writing",
  "author": {
    "name": "Your Name",
    "url": "https://example.com"
  },
  "license": "MIT",
  "type": "sidebar-panel",
  "surfaces": [
    "sidebar"
  ],
  "main": "plugin.js",
  "icon": "icon.svg",
  "scopes": [
    "ui.sidebar",
    "editor.read"
  ],
  "ui": {
    "sidebar": {
      "label": "Words",
      "tooltip": "Live word count",
      "width": 280
    }
  }
}

Required fields

Missing any of these and the install fails with a message naming the field.

FieldTypeNotes
id string Reverse-DNS by convention. It is also your plugin's directory name and its storage bucket, which is why the installer is strict about it.
name string Shown to users, in the sidebar and the plugin manager.
version string A free-form string; semver is the convention, and nothing parses it.
description string One line. The install dialog shows it while the user decides.
author PluginManifestAuthor An object. Only "name" is required; "url" is shown as a link.
type PluginType The legacy single-slot field, still required. See surfaces below.
main string Your entry-point JavaScript, relative to the plugin root.
scopes PluginScopeString[] The permissions you request. Everything the API does is gated on these.

Optional fields

FieldTypeNotes
license string Not required, but its absence is a warning at install.
minTinteroVersion string Advisory. Declare it if you rely on a recently added method.
surfaces PluginSurface[] Contributions model (optional). `surfaces` = where the plugin presents (sidebar/app/background/dialog); `capabilities` = what it contributes (importers/exporters). When omitted, both are derived from `type` so pre-existing single-`type` plugins keep working unchanged.
capabilities PluginCapability[] What your plugin contributes: importers and exporters.
icon string SVG only. See the trap below.
shortcut string Suggested launch shortcut, e.g. "Ctrl+Shift+W" (the user can override it).
ui PluginManifestUI Where your surfaces get their labels and their HTML: sidebar, dialog, settings.
settings { schema: Record<string, PluginSettingDefinition>; } A schema Tintero renders a settings form for, readable via tintero.settings.
import PluginManifestImport Format metadata for importer plugins: extensions and a display name.
export PluginManifestExport Format metadata for exporter plugins: extension, mime type, display name.
network PluginManifestNetwork The host allow-list for tintero.net.fetch. Required if you request net.fetch.

The ui block

Where each surface gets its label and its markup.

ui.sidebar

FieldTypeRequired
panel string no
label string yes
tooltip string no
width number no

ui

FieldTypeRequired
sidebar PluginManifestSidebarUI no
settings string no
dialog string no

Settings schema

Describe your settings and Tintero draws the form, keeps the values, and hands them back through tintero.settings. Each field is one of these: string , number , boolean , select .

FieldTypeRequired
type PluginSettingType yes
label string yes
description string no
required boolean no
secret boolean no
default any no
min number no
max number no
options string[] no

Paths

Every path you write gets stuck onto the end of your own plugin folder, so all of them have to stay inside it. These are checked when you install: main , icon , ui.sidebar.panel , ui.settings , ui.dialog .

Rejected outright:

  • Empty or whitespace-only paths
  • Paths containing a null byte
  • Absolute POSIX paths (leading /)
  • Paths starting with ~
  • Windows drive letters (C:)
  • Any .. segment
  • Any . segment
  • Empty segments (a//b)

Backslashes are turned into / before any of that happens, so Windows paths are covered by the same rules.

What the installer rejects

These are the installer's own words, straight from validateManifest. If your plugin will not install, the reason is one of them.

  • Missing or invalid "id"
  • "id" must contain only lowercase letters, numbers, dots and hyphens, and start and end with a letter or number
  • Missing or invalid "name"
  • Missing or invalid "version"
  • Missing or invalid "description"
  • Missing or invalid "author" (requires at least "name")
  • Missing or invalid "main" entry point
  • Invalid "type": "…". Must be one of: …
  • Missing or invalid "scopes" array
  • Invalid scopes: …
  • Plugins with a "sidebar" surface must include the "ui.sidebar" scope
  • Importer plugins must include "import" configuration

And the warnings, which do not block an install but the user still sees:

  • No license specified
  • No icon specified; a default icon will be used
  • Exporter plugins should include "export" configuration

Your plugin id has to match /^[a-z0-9]([a-z0-9.-]*[a-z0-9])?$/: lowercase letters, numbers, dots and hyphens, starting and ending with a letter or a number.