# HTTP Handlers

HTTP handlers let you make YouTrack data accessible from custom HTTP endpoints. These handlers extend the REST API so clients can call these endpoints like any other YouTrack REST API endpoint, not just the frontend code for a specific widget. This means you can use HTTP handlers to provision webhooks that can be accessed by third-party services.

YouTrack supports custom HTTP handlers written in JavaScript. The current YouTrack JavaScript implementation is compatible with the latest ECMAScript specification.

## Sample HTTP Handler

Here is a sample script that creates an endpoint for handling HTTP `GET` requests:

```JAVASCRIPT
exports.httpHandler = {
    endpoints: [
        {
            scope: "issue",
            method: "GET",
            path: "demo",
            permissions: ['READ_ISSUE', 'READ_ARTICLE'],
            handle: function (ctx) {
                ctx.response.json({message: "Hello World"});
            }
        }
    ]
}
```

Here's a configuration for an additional endpoint that handles `POST` requests:

```JAVASCRIPT
{
      scope: "global",
      method: "POST",
      path: "demo",
      handle: function (ctx) {
        const body = ctx.request.json();
        ctx.globalStorage.extensionProperties.globalIssuesSet = body;
        ctx.response.json({receiveBody: body});
}
```

This HTTP handler accepts a JSON payload from the client. The payload is stored in global storage that has been provisioned for the app. It then returns a JSON response to confirm that the payload was received and stored properly.

To learn how to provision global storage for an app, see [App Global Storage](apps-extension-properties.html#global-storage).

## Use Workflow API Modules in HTTP Handlers

HTTP handlers can use modules from the YouTrack workflow API package `@jetbrains/youtrack-scripting-api`. For example, use the `@jetbrains/youtrack-scripting-api/http` module to send requests to external services.

The following sample handler accepts a JSON payload from the client, forwards it to an external service, then returns a value from the service response to the client:

```JAVASCRIPT
const http = require('@jetbrains/youtrack-scripting-api/http');

const SERVICE_URL = 'https://api.example.com';

exports.httpHandler = {
    endpoints: [
        {
            method: 'POST',
            path: '/forward',
            handle: function (ctx) {
                const requestJson = ctx.request.json();
                const connection = new http.Connection(SERVICE_URL);

                connection.addHeader('Content-Type', 'application/json');

                const serviceResponse = connection.postSync(
                    '/transform',
                    null,
                    JSON.stringify(requestJson)
                ).json();

                ctx.response.json({
                    transformedValue: serviceResponse.transformedValue
                });
            }
        }
    ]
};
```

For details about the HTTP module, see [http](v1-http.html). When the external service requires credentials, store them as secret app settings. For details, see [Working with Settings for Secrets](app-settings.html#working-with-secrets).

## Schedule Async Functions from HTTP Handlers

> **Note:**
> Available since YouTrack version 2026.2.

HTTP handlers can also schedule asynchronous functions. Use this when the handler should return a response to the caller immediately and continue work after the handler transaction is complete. For example, you can call an external service with [postAsync](v1-Connection.html#postAsync) and process its response in a named async function:

```JAVASCRIPT
const http = require('@jetbrains/youtrack-scripting-api/http');

const SERVICE_URL = 'https://api.example.com';

exports.httpHandler = {
    endpoints: [
        {
            scope: 'project',
            method: 'POST',
            path: '/schedule-sync',
            handle: function (ctx) {
                const connection = new http.Connection(SERVICE_URL);
                connection.postAsync('/sync', null, ctx.request.body, 'onSyncResponse');

                ctx.response.json({status: 'scheduled'});
            }
        }
    ],
    asyncFunctions: {
        onSyncResponse: function (ctx) {
            if (!ctx.response.isSuccess) {
                console.warn('External sync failed: ' + ctx.response.body);
            }
        }
    }
};
```

For details about declaring async functions and sharing state across async calls, see [Asynchronous Functions](async-functions.html).

## HTTP Handler API

Here is the reference for the script that implements a custom HTTP handler.

Each script exports one HTTP handler to the `exports.httpHandler` property. The HTTP handler is declared as an object containing the array of `endpoints`. It can also contain an optional [asyncFunctions](async-functions.html) object.

### Handler Context

The `handle` function receives a context object as its only argument. This object provides access to the incoming request, the outgoing response, the current user, app settings, app storage, and scope-specific YouTrack entities.

| Property | Description |
| --- | --- |
| `ctx.currentUser` |    The current user who calls the endpoint. This property is an alias for `entities.User.current`.     You can use `ctx.currentUser.isInGroup(groupName)` to check membership in a specific group. Starting with YouTrack 2025.3, the `groups` property contains the user's groups.    |
| `ctx.settings` | The app settings configured according to the app's settings schema. |
| `ctx.globalStorage` |    The global storage object provisioned for the app.     For details, see [App Global Storage](apps-extension-properties.html#global-storage).    |
| `ctx.request` | The HTTP request object. For details, see [HTTP Request](#request). |
| `ctx.response` | The HTTP response object. For details, see [HTTP Response](#response). |
| `ctx.issue`, `ctx.project`, `ctx.article`, `ctx.user` |    The scope-specific entity for endpoints that use the corresponding scope.     For example, an endpoint with `scope: "issue"` receives `ctx.issue`.    |

For the full list of properties and methods available for YouTrack entities in app scripts, see the [User](v1-User.html) and related JavaScript API reference pages.

### Endpoints

Each endpoint contains the following properties:

| Property | Description |
| --- | --- |
| `scope` |    The scope entity of the endpoint. Setting the scope guarantees you that the scope entity will be available in the context of the [handle](#handle) function.     YouTrack supports the following scopes: `issue`, `project`, `article`, `user`, `global`.     Default value: `global`.     For more details, see [Scope](#scope).    |
| `method` |    The HTTP method that the endpoint implements.     YouTrack supports GET, POST, PUT, and DELETE methods.    |
| `path` |    The relative path for accessing the endpoint.     To use the endpoint in an app, append this path to the name of the handler file and invoke the endpoint via `host.fetchApp()`.     Example: `const appResponse = await host.fetchApp('backend/demo', {scope: true});`    |
| `permissions` | The list of permissions to check when someone calls the endpoint with the given method. As long as the user who attempts to access the endpoint has at least one of the specified permissions, the request is authorized.     Permissions are listed as an array. Each permission is referenced using its key. For a complete list of permissions and key values, see [App Permissions](app-permissions.html).    |
| `handle` | The function that YouTrack invokes when someone calls the endpoint with the given method. |

### Scope

Here you can learn more about available `scope` values for the HTTP endpoints.

Setting the scope guarantees that the scope entity will be available in the context of the [handle](#handle) function.

If you don't set the scope explicitly, the default value is `global`.

> **Note: Connection between Custom Endpoints and Widgets**
> If you use custom HTTP endpoints in the [widgets](apps-reference-extension-points.html) of your app, be aware that there is a connection between the endpoint scope and the scope of certain widgets.
>
>
>
> When you define a certain entity as the scope of an endpoint, it means that only widgets of the same scope may use this endpoint.
>
>
>
> For example, if the endpoint scope is set to `issue`, only issue-related widgets will be able to use it.

| Scope | Description | Related Extension Points |
| --- | --- | --- |
| `issue` |    Adds the `ctx.issue` entity to the context of the `handle` function.    |      * `ISSUE_ABOVE_ACTIVITY_STREAM`    * `ISSUE_BELOW_SUMMARY`    * `ISSUE_FIELD_PANEL_LAST`    * `ISSUE_FIELD_PANEL_FIRST`    * `ISSUE_OPTIONS_MENU_ITEM`    |
| `project` |    Adds the `ctx.project` entity to the context of the `handle` function.    |      * `HELPDESK_CHANNEL`    * `PROJECT_SETTINGS`    * `PROJECT_TAB`    |
| `article` |    Adds the `ctx.article` entity to the context of the `handle` function.    |      * `ARTICLE_OPTIONS_MENU_ITEM`    * `ARTICLE_BELOW_SUMMARY`    * `ARTICLE_ABOVE_ACTIVITY_STREAM`    |
| `user` |    Adds the `ctx.user` entity to the context of the `handle` function.    |      * `USER_CARD`    * `USER_PROFILE_SETTINGS`    |
| `global` | The default scope. | Endpoints with this scope are accessible for all frontend extensions. |

### HTTP Request

HTTP request is an object (`ctx.request`) that the endpoint receives.

#### Properties

Here are the properties of the HTTP request object.

| Property | Type | Description |
| --- | --- | --- |
| `body` | string | The request body. |
| `bodyAsStream` | Object | A byte stream representation of the request body. |
| `headers` | Array.<{name: String, value: String}> | A collection of request headers. |
| `path` | string | The relative path to the endpoint. Equals `endpoint.path`. |
| `fullPath` | string | The full path to the endpoint. |
| `method` | string | The HTTP method that the request used. Can be either GET, POST, PUT, or DELETE. |
| `parameterNames` | Array.<String> | An array of the URL parameter names |

#### Functions

Here are the functions that you can use to work with HTTP requests. These are JavaScript functions that are assigned to the `handle` property defined for each endpoint. These functions are executed whenever a matching HTTP request is received by the app.

| Property | Return Type | Description |
| --- | --- | --- |
| `json()` | JSON | Returns the request body in JSON format. |
| `getParameter(name)` | string | Returns the URL parameter by its name. |
| `getParameters(name)` | Array.<String> | Returns all URL parameters by the name as an array of strings. |

### HTTP Response

The HTTP response is an object (`ctx.response`) that the handler returns in response to the request from the client.

#### Properties

Here are the properties of the HTTP response object.

| Property | Type | Description |
| --- | --- | --- |
| `body` | string | The response body. If an exception occurs during processing, the response body is empty (`null`). |
| `bodyAsStream` | Object | A byte stream representation of the response body. If an exception occurs during processing, the property is empty (`null`). |
| `code` | number | The HTTP status code that is assigned to the response. If an exception occurs during processing, the property is empty. 200 by default. |

#### Functions

Here are the functions that you can use to work with HTTP responses. These are referenced by accessing the `ctx.response` object provided in the `handle` function of an HTTP handler. These functions let you format responses in JSON or plain text and customize the response header.

| Function | Return type | Description |
| --- | --- | --- |
| `json(object)` |   | Adds the `Content-Type: application/json` HTTP header to the response that the handler returns to the client. The response is presented in the format of a JSON string.  |
| `text(string)` | string | Adds the `Content-Type: text/plain` HTTP header to the response that the handler returns to the client. The response is presented in the format of a string.  |
| `addHeader(header, value)` | Response object | This function adds an HTTP header to the response. If you pass `null` as the value, the corresponding header will be removed from the response. If you pass more than one header with the same name, only the last one persists. |

## Requirements

You can include additional requirements for the HTTP handler objects. An HTTP handler object has the `requirements` field that you may use to define the requirements for the script.

For details about how requirements work for JavaScript scripts in YouTrack in the workflow context, see [Requirements](requirements.html).

## Accessing Custom REST Endpoints

When you call a custom REST endpoint, you invoke its corresponding HTTP handler. The endpoints used are based on the scope property assigned to the handler.

| Scope | URL |
| --- | --- |
| `issue` | <host>/api/issues/<issueId>/extensionEndpoints/app/handler/endpoint |
| `article` | <host>/api/articles/<articleId>/extensionEndpoints/app/handler/endpoint |
| `project` | <host>/api/admin/projects/<projectId>/extensionEndpoints/app/handler/endpoint |
| `user` | <host>/api/users/<userId>/extensionEndpoints/app/handler/endpoint |
| `global` | <host>/api/extensionEndpoints/app/handler/endpoint |

Set the following variables to match your app:

* `app` — the name of your app.

* `handler` — the name of the file that contains the HTTP handler script in the app package without the `.js` file extension.

* `endpoint` — the path from the declaration. For example, `"path": "/endpoint"`

Each API request requires the same permissions as the scope entity. For example, the endpoint /api/issues/DEMO-1/extensionEndpoints/app/handler/endpoint is accessible to any user who has permission to access the issue with the ID DEMO-1. Global endpoints are accessible to all users except the guest account.

