Marketplace Submission Checklist
You can develop apps for your own use or for use within your company. If you choose to publish your app on JetBrains Marketplace, use this checklist to prepare it for review.
The YouTrack team reviews every app submitted to JetBrains Marketplace before publication. This checklist describes the review criteria. Use it before submitting your app to identify issues that may delay approval.
Pre-submission Checklist
Use this summary before each Marketplace submission. You can also add these checks to your release checklist.
Listing
The listing includes a description and at least one in-app screenshot. Change notes are recommended.
If setup goes beyond installation, a Getting Started section provides instructions that match the current app version.
The product name is spelled "YouTrack" throughout the listing and interface.
The compatibility range is correct, with
minYouTrackVersionset to 2026.2 or later if backend scripts make outbound HTTP calls or use async functions.If the app sends data outside YouTrack, the listing discloses the processing and links to a privacy policy.
For details, see Marketplace Listing and UI and User Experience.
Package
Backend
.jsfiles are at the package root.Paths in
manifest.jsonmatch the ZIP contents.settings.jsonsaves without validation errors and contains no placeholder content.Widget and endpoint
permissionsmatch the intended users.The package declares no unused extension properties.
The package contains no obfuscated code, debug endpoints, development URLs, or internal files.
For details, see Package and Manifest.
Security
The package contains no hardcoded credentials.
All secrets use
"format": "secret"insettings.json.Extension properties contain no secrets, and no endpoint returns a secret.
Any leaked secrets have been revoked or rotated.
Every
globaloruserendpoint declarespermissionsor checks access explicitly in code.Write endpoints require write permissions.
Documented roles are enforced on the server.
The app checks the requester's permissions for entities referenced by supplied IDs.
Open endpoints don't expose license data, settings, or email addresses.
GETrequests never write data.Error responses contain no stack traces or raw exceptions.
Custom MCP tools receive the same access and input checks as HTTP handlers.
Endpoints have been tested with a low-privilege user's token.
For details, see Secrets and Credentials, HTTP Handlers and Access Control, and Test Endpoint Access.
Backend Behavior
All outbound HTTP calls from backend scripts use
*Asyncmethods with matchingasyncFunctionshandlers.onSchedulerules use focused searches, leave enough time between runs, and finish within the default 3-minute limit.onChangeguards are precise and read-only, and no resource-intensive work runs in the transaction.Extension property values are small and written infrequently. Data is stored per entity, or shared values are protected against concurrent updates.
For details, see Outbound HTTP Calls (Async Required), On-schedule Rules, On-change Rules, and Storing Data.
Frontend and UI
External requests and content delivery network (CDN) assets are essential to the app and disclosed in the listing.
Widgets don't use
eval,new Function, or dynamic imports of remote code.User content is sanitized before HTML rendering.
Links to YouTrack pages include
contextPath.Every REST call a widget makes works for the users who see it.
Widgets use Ring UI or match YouTrack's look and feel, fit their expected size, and work in the dark theme.
For details, see Frontend (Widget) Code and UI and User Experience.
Functional Quality
The app works for regular users.
Project-scoped widgets work after the app is attached to a project.
Configuration changes require the administrator's consent.
Updates preserve existing data and settings.
For details, see Functional Quality.
How the Review Works
The YouTrack team reviews the listing and code, installs the app, and tests its behavior. Each submission goes through the following checks:
Step | Review activity |
|---|---|
Listing review | The team reviews the Marketplace listing: description, screenshots, Getting Started instructions, and change notes. |
Code audit | The team downloads the submitted ZIP archive and reviews every widget bundle and backend script. |
Installation | The team installs the app on a clean YouTrack Server and attaches it to a test project. |
Functional testing | The team tests every advertised feature as an administrator and as a regular user. |
UI check | The team checks every widget in light and dark themes. |
Feedback | If changes are needed, you receive specific change requests. The app is published once it meets the review requirements. |
Severity Levels
Each change request is marked with a severity:
Severity | Meaning | Must be fixed before approval? |
|---|---|---|
Blocker | A security vulnerability, data exposure, risk to YouTrack's stability, or a core feature that doesn't work. | Yes. |
Major | A significant problem, such as a missing check on a low-sensitivity endpoint or a broken secondary feature. | Yes, unless the currently published version has the same issue. In that case, fix it in the next version. Synchronous HTTP calls are an exception: they must always be fixed before approval (Outbound HTTP Calls (Async Required)). |
Minor | A quality improvement or a low-risk issue. | No. A fix in the next version is recommended. |
Ten Common Reasons Apps Are Sent Back
These issues come up often in reviews. Checking them first can save you another round of reviews.
Tokens stored in cleartext. Declare them in
settings.jsonwith"format": "secret". Don't put them in extension properties. For details, see Secrets and Credentials.globaloruserendpoints without access checks. Declarepermissionsor check access explicitly in code. For details, see HTTP Handlers and Access Control.Access rules enforced only in the UI. Hiding a button doesn't stop someone from calling the endpoint directly. For details, see HTTP Handlers and Access Control.
Trusting IDs from the request body. Accepting an entity ID from a request without checking the caller's access can expose data they aren't allowed to see. For details, see HTTP Handlers and Access Control.
Synchronous outbound HTTP (
postSync,getSync, and so on), especially calls to AI services, including large language model (LLM) APIs. Use the*Asyncmethods instead. For details, see Outbound HTTP Calls (Async Required).Obfuscated code. The review team needs readable code to verify what the app does. For details, see Package and Manifest.
Undisclosed calls to third-party servers, such as license servers, analytics, CDNs, or hosted editors. For details, see Marketplace Listing.
Links that ignore the YouTrack context path. These break when YouTrack Server is hosted at
/youtrack. For details, see Frontend (Widget) Code.Widgets that are unreadable in dark theme. Check every widget with YouTrack's dark theme turned on. For details, see UI and User Experience.
Error responses that leak stack traces or internal details. Return a short message and log the details on the server. For details, see HTTP Handlers and Access Control.
Marketplace Listing
The listing is the first thing administrators see. The review team also starts with the listing.
Write a clear description. Explain what the app does, who it is for, and its main features.
Include at least one screenshot. Show the app in action inside YouTrack. Screenshots should show the key features, not just a logo or a settings form.
Add a Getting Started section if setup goes beyond installation. Explain any extra steps: configuration, permissions, attaching the app to projects, and setting up external accounts or tokens. Make sure the instructions match the current app version, including file names, menu names, and setting labels.
Add change notes for each version (recommended). Describe what actually changed. Make sure the submitted code includes the fixes you mention.
Include a vendor name. For details, see
vendorin the manifest.Set the correct compatibility range. If backend scripts make outbound HTTP calls or use async functions, set
minYouTrackVersioninmanifest.jsonto"2026.2"or later. This version requirement doesn't apply to requests that widgets make from the browser. For details, see the manifest schema and Outbound HTTP Calls (Async Required).Disclose third-party data processing. If the app sends any data outside YouTrack, say so in the listing and link to a privacy policy. This applies to license checks, telemetry, user counts, hosted editors, AI services, and CDNs. Say what is sent, to whom, and why.
Package and Manifest
For the basic package layout, see Launch Checklist.
Backend scripts are at the package root. YouTrack registers HTTP handlers and workflow rules from
.jsfiles in the app's root folder. If they sit in a subfolder such asbackend/, the handlers are never registered, and only the widgets work. For details, see App Package Overview.File paths in
manifest.jsonmatch the files in the ZIP package. CheckindexPath,iconPath, andsettingsSchemaPath. A wrong path can cause a broken icon or a missing settings dialog. For details, see widgets in the manifest.settings.jsonis a valid schema for YouTrack. If you include"$schema", usehttp://json-schema.org/draft-07/schema#: thehttps://form fails validation when the settings are saved. Open the settings in YouTrack and save them once to confirm. Remove leftover boilerplate, such as a demo$idor a placeholder title. For details, see App Settings.Permission restrictions match the intended users. The
permissionsproperty doesn't grant permissions to the app. It limits who can see a widget or call an endpoint. An empty list means no restriction. Make sure each restriction matches the permissions needed for the action. For details, see App Permissions.The app declares no unused extension properties. Remove properties the app declares but doesn't use. For details, see Extension Properties.
Backend scripts and widget bundles contain no obfuscated code. Standard bundler minification is fine. Output from tools like
javascript-obfuscator(string arrays, self-defending wrappers, disabled console) is not.License checks use readable code. If you need license enforcement, sign license data on your server and verify the signature in readable code.
Remove development leftovers and use production service URLs. Remove debug endpoints and panels, verbose logging of server responses, and internal documentation. Replace any staging, sandbox, or development URLs for license, payment, or API services with production URLs.
Secrets and Credentials
Check secret handling carefully before submission. These issues can block approval and may require several review cycles to resolve.
The package contains no hardcoded credentials. This includes API keys, bearer tokens, custom authentication headers, and YouTrack permanent tokens. Hardcoded credentials count as a Blocker issue, even if they're obfuscated or used only for a trial.
Tokens, passwords, and client secrets use
"format": "secret"insettings.json.{ "type": "object", "properties": { "apiToken": { "type": "string", "format": "secret", "title": "API token" } } }Secret values are masked (
<***>) in the administration interface, in REST responses, and inconsole.log()output. A setting declared only as"type": "string"leaves its value readable in cleartext by administrators and through the REST API. Secret fields also work in arrays of objects. For details, see Working with Settings for Secrets.Secrets are never stored in extension properties. Extension properties (
AppGlobalStorage,User,Project,Issue) have no secret type. Their values are stored in cleartext. This covers OAuth access and refresh tokens, webhook secrets, and tokens copied from app settings. For details, see Extension Properties.Custom encryption isn't a substitute for secret storage. If you use XOR or another reversible scheme and ship the key in the package, anyone with the ZIP can recover the secret. Use
"format": "secret"insettings.json.No endpoint returns a secret. This also applies to responses sent to the app's own widgets. If a widget needs an operation that uses a secret, run it in a backend handler and return only the result.
Keep service account permissions and token scopes to the minimum needed. If the app creates a service account or asks for a token, request only the services and permissions it needs. For example, don't grant access to the Hub (user management) service if the app only calls the YouTrack API.
The app reuses only accounts it created. Before reusing an existing service account, check that the app created it. One way is to store the account's ID when the app creates it and check that ID before reuse. Otherwise, the app may issue a token for an account controlled by someone else.
Leaked secrets are revoked or rotated. If a secret ever shipped in a package or was exposed through an endpoint, revoke or rotate it. Removing it from the next version isn't enough because old packages are still available.
HTTP Handlers and Access Control
YouTrack enforces visibility differently depending on endpoint scope. Make sure you understand which protections YouTrack provides automatically and which you need to add. For details, see Scopes for HTTP Handlers and the HTTP handler reference.
All endpoints are subject to app activation, handler visibility, and any declared permissions checks.
Scope | What YouTrack checks before your code runs | What you must add |
|---|---|---|
| The caller can see the entity. If not, the response is | Declare |
| The scope adds no entity visibility check. | Always declare |
Access Control Checklist
Every
globaloruserendpoint declarespermissionsor checks access explicitly in code.exports.httpHandler = { endpoints: [ { method: 'POST', path: 'config', scope: 'global', permissions: ['ADMIN_UPDATE_APP'], handle: (ctx) => { /* ... */ } } ] };List permission keys in an array. A nonempty array requires at least one listed permission. For details, see endpoint properties and permission keys.
Write endpoints require write permissions. An endpoint that changes data must not be callable with
READ_ISSUEorREAD_PROJECTalone. Project settings should requireUPDATE_PROJECT. App-wide settings should requireADMIN_UPDATE_APP.Role restrictions described in your app documentation are enforced on the server. If the listing says "only managers can edit" or "only app administrators can rename columns", the backend must check this. Hiding controls in the widget is not access control.
Never trust entity IDs from the request. If a handler loads
Issue.findById(body.issueId)or a project frombody.projectId, check the caller's permissions for that entity. Anissue,article, orprojectendpoint checks visibility only for its scope entity. Check access separately for any other entity identified in the request, and add the permissions needed to change data. A common bug is aprojectendpoint that accepts another project's ID in the body.Respect visibility when returning lists. Don't return all issues, attachments, or records in a project when the caller can only see some of them. Check per-item visibility, such as attachment visibility groups and issue visibility.
Open endpoints don't expose sensitive data. License data (license holder, user counts, and expiration details), app settings, administrators' email addresses, and YouTrack installation IDs must not be readable by regular users or guests.
GETrequests have no side effects. Don't write data or change configuration in response to a read request or just because someone opens an administration page.Validate input. Check the structure and data types of request bodies, limit their size, and reject unsupported values. Escape user content before you embed it in HTML, XML, or Markdown.
Return meaningful HTTP status codes. Set
ctx.response.code = 403(or400,404) explicitly. Don't send200withok: false. Don't set a code and then throw, because the runtime turns that into a500. For details, see response object.Don't expose internal details in error responses. Return a short, user-facing message. Don't return
e.stack, raw exception text, or file paths. Log details on the server instead.Custom MCP tools validate access and input. An MCP tool is another way to reach your app's data. Check the caller's access, validate every argument (for example, that an issue reference exists and is visible to the caller), and don't return cached data the caller couldn't see otherwise. For details, see Custom MCP Tools.
Before submitting your app, test endpoint access with an account that has minimal permissions. The YouTrack team performs these checks during review.
Test Endpoint Access
Create a test user with minimal permissions, for example a user who has only the Observer role.
Call each endpoint directly using that user's token.
Try reading another project's data, writing settings, and changing license state. Check that the endpoints reject operations the user isn't permitted to perform.
Outbound HTTP Calls (Async Required)
Marketplace apps must use async HTTP for all outbound calls from backend scripts. The backend *Async methods are available from YouTrack 2026.2, so if your backend calls external services, set minYouTrackVersion to "2026.2" or later. This doesn't apply to requests that widgets make from the browser.
For details about Asynchronous Functions, see supported script types, execution limits, sharing state, and HTTP calls and chaining. For the HTTP methods, see Send Requests after Transaction Completion.
Synchronous calls keep the current transaction or request thread open while waiting for a response. The synchronous methods are doSync, getSync, postSync, putSync, patchSync, deleteSync, and headSync. Use the corresponding *Async methods. For HEAD requests, use doAsync('HEAD', ...).
In
onChangeandactionrules, they keep the write transaction open and can block other users who are updating the same entities.In
onSchedulerules, they count toward the default 3-minute execution limit (see Workflow Rules and Performance).In HTTP handlers, they hold a request thread.
Async functions are supported in onChange, action, and onSchedule rules, HTTP handlers, and custom MCP tools.
If more than one condition in the following table applies, use the highest severity.
Issue | Severity |
|---|---|
A | Blocker |
A | Blocker |
A | Major |
| Major |
Example: An Async HTTP Call from an On-change Rule
Async Checklist
The handler name matches the key in
asyncFunctions. The string passed to*Async(..., 'onResponse')orctx.invokeAsync('name')must be a key inasyncFunctionsof the same rule or handler.ctx.invokeAsync()throws an error if the handler name doesn't match. For an HTTP response handler, a mismatch only produces a warning in the app log, and the response is never processed. Inline callbacks aren't supported.asyncFunctionsis in the right place. In a workflow rule or MCP tool, put it at the same object level asactionorexecute. In an HTTP handler, put it onexports.httpHandlerat the same level asendpoints, outside individual endpoints.Each execution schedules at most one async call. A function can schedule either one
ctx.invokeAsync()call or one*AsyncHTTP request. A second call throws an error.Chains stay within 10 steps (the default limit), and every self-scheduling chain has a stop condition.
Don't use the return value of an async HTTP method.
*Asyncreturns nothing. Read the result fromctx.responseinside the handler.Pass state only through
ctx.store()/ctx.load(). Only primitives,null, and entity references are supported. Convert objects and arrays to JSON strings withJSON.stringify()before storing them.Don't rely on the transaction state in the handler.
isChanged()andoldValue()don't work there. Capture any values you need from them before scheduling the async call.Check
ctx.response.isSuccessand handle failures. Never throw errors. An unhandled error stops the chain. Let users know when a call fails, especially if a service rejects it because of a rate limit.minYouTrackVersionis2026.2or later inmanifest.json.
Workflow Rules and Performance
Your app runs inside the customer's YouTrack. Slow rules can make YouTrack less responsive for other users.
On-schedule Rules
For details, see On-schedule Rules.
Cron expressions use the Java Quartz format (6 fields: sec min hour day month weekday). 0 * * * * ? means every minute, not every second.
Narrow
searchqueries. The rule runs once for every issue the query matches. An empty query or#Unresolvedin YouTrack can match tens of thousands of issues. Narrow it down with a project, a tag, or a date range such asupdated: {Last week}.Leave enough time between runs. If a run hasn't finished when the next one is due, the next run is skipped, not queued.
Keep each run below the default 3-minute limit. If a run reaches the limit, YouTrack stops it and skips the remaining issues.
Handle per-issue errors. Each issue runs in its own transaction. A failure for one issue doesn't stop the others and is recorded only in the workflow log. Make sure users also get feedback about failures they need to act on.
searchonly selects issues. Ifsearchis a function, it should only build the query. Don't send requests or write data from it.
On-change Rules
For details, see On-change Rules.
Keep resource-intensive work out of the transaction. Large searches and outbound HTTP calls might freeze the transaction for all users. Use async functions instead.
Use a precise
guard. If the guard always returnstrue, the rule runs on every save.Keep
guardfree of side effects. Guards must only read data.Avoid cycles between rules. A change made by a rule can trigger on-change rules again. If repeated changes exceed the iteration limit, YouTrack rejects the whole transaction with a workflow-cycle error, and the user's change isn't saved.
General Performance Checks
Keep loops and data fetches bounded. Don't load every user just to count them or page through every issue on each request. Set a limit on the number of results.
Limit parallel requests from widgets. Dozens of REST API calls at once when a widget loads can slow down YouTrack.
Storing Data
Use extension properties for small amounts of app data. Keep values small, avoid frequent writes, and plan for concurrent updates. For details, see App Global Storage.
No secrets in extension properties. For details, see Secrets and Credentials.
Avoid frequent writes. Each write updates the YouTrack database. Writing a property for every matching issue in a frequently scheduled
onSchedulerule can put extra load on YouTrack.Keep values small. Don't store logs, large JSON blobs, or whole datasets. Cap the size of anything users can submit.
Plan for concurrent edits. Concurrent updates to the same extension property can overwrite each other. If two users save the same JSON blob, the last write wins. Store data per entity rather than in one global blob, or add a revision check.
Keep user data private. Keep each user's settings separate, and return only the settings the caller is allowed to see. Don't keep every user's settings in one shared global blob that every handler reads and returns.
Use settings for configuration. For administrator-configured values,
settings.json(global or project scope) gives you validation, a UI, and secret masking. For details, see App Settings.
Frontend (Widget) Code
Widgets run in sandboxed iframes. The review covers every bundle, including minified ones.
External requests are essential to the app and disclosed in the listing. This includes CDN stylesheets and scripts. Bundle Ring UI and other libraries instead of loading them from
unpkgor similar at runtime.Widgets don't evaluate strings as code or dynamically import remote code. Don't use
eval,new Function, or dynamicimport()of remote code.The app doesn't generate undisclosed background traffic. Don't use
sendBeacon,WebSocketconnections to external hosts, tracking pixels, ordocument.cookieunless they are essential and disclosed.The app doesn't use long encoded strings to hide URLs, code, or credentials. Check long base64-encoded, hexadecimal, and similarly encoded strings.
postMessagetargets a specific origin, not'*', when it carries data such as OAuth codes.User content is sanitized before it reaches
innerHTMLordangerouslySetInnerHTML. This includes Markdown rendered with HTML enabled and document previews. Blockjavascript:links.The widget doesn't cache sensitive data in
localStorage. Issue summaries and descriptions cached in the browser can remain accessible after the user loses access to those issues.Links respect the context path. Self-hosted YouTrack can run under a path such as
https://host/youtrack. Root-absolute links starting with/, such as/issue/ABC-1, skip that context path. Relative links can also point to the wrong place because the widget's base URL is the app's resource folder. Fetch the context path and build your links from it:const host = await YTApp.register(); const { contextPath } = await host.fetchYouTrack('config?fields=contextPath'); // contextPath is the configured path prefix, such as "/youtrack", or "" at the root URL. const issueUrl = `${contextPath}/issue/${issueId}`;Set
target="_top"on these links so they open in the YouTrack page, outside the widget iframe. For example:<a href={issueUrl} target="_top">. For details, see theYTAppobject and thefetchYouTrackmethod.Widget requests use the scope and entity ID required by the endpoint. For example, an issue-menu widget must not use an issue ID where a project ID is expected. A widget can call a
globalendpoint regardless of the widget's scope. For details, see HTTP endpoint scopes.Every REST call a widget makes works for the users who see it. Some endpoints and fields return
403for regular users, for example/api/groups?fields=users. You can't tell from the path alone: some/api/admin/...endpoints work for regular users. Test each endpoint and field set with an account that has only the permissions your widget's intended users need. If an operation needs more access, put it in a backend handler that checks whether the caller is allowed to perform it.
UI and User Experience
Apps should feel like part of YouTrack.
Use Ring UI or match YouTrack's look and feel. Use Ring UI (
@jetbrains/ring-ui) for buttons, inputs, loading indicators, alerts, and dialogs. If you don't use it, match YouTrack's colors, spacing, and fonts. For details, see Ring UI.Widgets work with the dark theme enabled. Switch YouTrack to the dark theme (click your avatar, then select ) and check every widget: text, backgrounds, dialogs, and disabled states. Follow YouTrack's theme, not the operating system's
prefers-color-schemesetting.Widgets fit their intended size. Check that content isn't clipped, no inner scrollbars appear at the default size, and there are no large empty areas. When specifying dimensions in the manifest, use
defaultDimensionsfor dashboard and Markdown widgets andexpectedDimensionsfor other supported extension points. For details, see widget size and widget properties in the manifest.Widgets use the system font stack. Avoid decorative fonts.
Write the product name as "YouTrack" throughout the listing, UI text, setting labels, dialogs, and error messages. Don't use
Youtrack,youtrack, orYOUTRACK.Give empty values meaningful labels. Don't display
[object Object],undefined, or?. Use YouTrack's wording, such as "Unassigned" or "No value," where appropriate.Show widgets only to users with the required permissions. If a menu item would lead to an access error, hide it with widget
permissionsor a guard. For details, see App Permissions and Conditional Widgets.The interface is localized consistently. Don't mix languages in your interface text, and make sure every translation key exists for every language your app supports.
Field values and labels created by the app contain no spelling mistakes. These values become part of customers' data.
Functional Quality
The review covers every feature described in your listing. Check the following before submission:
Every advertised feature works on a clean YouTrack Server. Test outside your development environment.
Project-scoped widgets work after the app is attached to a project. Issue, article, project-settings, and helpdesk widgets appear only in projects the app is attached to. Test this and explain the attachment step in Getting Started. For details, see Extension Points for Widgets.
The app works for regular users. Test with an account that has only the permissions the app's intended users need.
Built-in groups work in permission checks. Check membership with
user.isInGroup(groupName)rather than searchinguser.groups, which may not include built-in groups such as "All Users" or "Registered Users". For details, seeUser.isInGroup.The app handles edge cases. Test empty or optional fields, missing work item types, special characters in user input and queries (
&,#,+), and URLs with query strings.Multi-step operations don't lose or duplicate data. If an operation has several steps, such as copying and then deleting an item, order the steps or roll them back so a failure halfway through doesn't cause data loss or duplication.
The app makes configuration changes only with the administrator's consent. Opening an administration page must not create fields, change colors, or edit bundles. Ask the administrator to confirm these changes before applying them.
Updates keep existing data and settings. Check that the updated app can still read and use data and settings from the previous version.