HTTP Endpoints
Use HTTP endpoints to define custom API endpoints for Jira. They enable you to integrate Jira with external systems by exposing custom logic over HTTP or by consuming external services from within Jira. You could use an HTTP endpoint to:
- Provide an API that returns specific work item details Expose a custom HTTP endpoint that returns exactly the work item fields and data you need. Use this to power internal tools, dashboards, or integrations without giving direct access to Jira's full API.
- Trigger Jira updates (such as transitions or field changes) based on external events Define an HTTP endpoint that, when called by an external system, performs actions in Jira, such as transitioning a work item, updating fields, or adding comments. Use this to keep Jira in sync with CI/CD pipelines, monitoring tools, or other business systems.
- Aggregate and expose data from Jira and other systems in a single response Create an HTTP endpoint that calls Jira and other external APIs, combines the results, and returns a unified response. Use this to provide a single, simplified data source for reporting, dashboards, or downstream integrations.
HTTP endpoints are exposed as URLs that external systems can call, or that you can invoke from your own scripts and automations. All endpoints are mounted at /api/** and support GET, POST, PUT, DELETE, and PATCH.
Request object
When an HTTP endpoint is called, your script receives an ApiRequest object as its first argument. This object contains everything you need to handle the request; the HTTP method, any path or query parameters, headers, and the request body.
import type { ApiRequest, ApiResponse } from "@scriptrunnerhq/types/api-endpoints";| Field | Type | Description |
|---|---|---|
url | string | The request URL (after the /api prefix) |
method | string | HTTP method: GET, POST, PUT, DELETE, PATCH |
pathParams | Record<string, string | undefined> | Named parameters from the path pattern |
queryParams | Record<string, string[]> | Query string parameters |
headers | Record<string, string[]> | Request headers |
body | string (optional) | Request body (for POST, PUT, PATCH) |
context | object (optional) | Forge context: cloudId, moduleKey, and userAccess flags |
A common pattern is to destructure the fields you need at the top of your script:
const { method, pathParams, queryParams, body } = request;Response object
Your script must return an ApiResponse object. This tells ScriptRunner what HTTP status code and body to send back to the caller.
interface ApiResponse {
statusCode: number
body: string
}statusCode— a standard HTTP status code, such as200for success,201for created,400for a bad request, or404for not found. Use appropriate codes to make your endpoint behave like a well-formed API.body— must always be a string. If you want to return a JSON object, serialise it first withJSON.stringify().
A minimal valid response looks as follows:
return {
statusCode: 200,
body: JSON.stringify({ message: "Success" }),
};ApiResponse from every code path in your script, including error cases. If your script throws an unhandled error, the endpoint will return a 500 response.Path patterns
The path you enter when creating an endpoint defines the URL route it responds to. Paths can include named parameters and wildcards, which are automatically extracted and made available in pathParams inside your script.
There are two types of dynamic segments:
- Named parameters (
:paramName) — match a single path segment and capture its value. For example,:issueKeyin/issues/:issueKey/linkscaptures the issue key from the URL. - Wildcards (
:paramName*) — match one or more path segments and capture them as a single string. For example,:path*in/search/:path*captures everything after/search/.
| Pattern | Matches | pathParams |
|---|---|---|
/issues/:issueKey/links | /issues/ISSUE-42/links | { issueKey: "ISSUE-42" } |
/issues/:issueKey/links/:linkId | /issues/ISSUE-42/links/123 | { issueKey: "ISSUE-42", linkId: "123" } |
/projects/:projectKey/issues | /projects/PROJ/issues | { projectKey: "PROJ" } |
/repos/:owner/:repo/issues | /repos/atlassian/AUI/issues | { owner: "atlassian", repo: "AUI" } |
/search/:path* | /search/a/b/c | { path: "a/b/c" } |
Output and logging
When you run a script, the output panel shows:
- Anything logged with
console.log(...) - The final value returned by your script with
return
Use console.log(...) for messages, intermediate values, or debugging, and include a return statement for the main result you want to display.
Activity history
Saved HTTP endpoints keep activity history (under Output), so you can review when they were run, and what the output was, making it easier to audit and troubleshoot.
Calling an endpoint externally
Once your endpoint is published, any external system or tool can call it over HTTP. Calls are authenticated with an OAuth 2.0 access token, so before you can call your endpoint you need to create an OAuth 2.0 app in the Atlassian developer console, give it the right scopes, and exchange a user's consent for a token.
This section covers the parts specific to ScriptRunner. For the full OAuth flow, see the Atlassian documentation Access REST APIs exposed by a Forge app.
Finding your endpoint URL
Your endpoint's full URL is displayed in the editor, under the path field, with a copy button. Endpoint URLs follow one of these patterns:
https://<site-name>.atlassian.net/gateway/api/svc/jira/apps/<appId>_<envId>/api/<path>
https://api.atlassian.com/svc/jira/<cloudId>/apps/<appId>_<envId>/api/<path>Both resolve to the same handler. We recommend you use whichever fits your integration.
Required scopes
Your OAuth 2.0 app needs these scopes. Which ones you need depends on how the calling system authenticates:
| How your integration authenticates | Required scopes |
|---|---|
| As a Jira user, with that user granting consent (OAuth 2.0 three-legged, or 3LO) | invoke:user-script:custom and read:forge-app:jira |
| Any other method | invoke:user-script:custom |
invoke:user-script:custom scope, requests will be rejected with a 403 Forbidden response.Authentication
HTTP endpoints use OAuth 2.0 bearer token authentication. Include your access token in the Authorization header of every request:
Authorization: Bearer <access_token>To get an access token, send the user to an authorization URL containing your app's client ID and the scopes above. For example:
https://auth.atlassian.com/authorize
?audience=api.atlassian.com
&client_id=<your_client_id>
&scope=invoke%3Auser-script%3Acustom%20read%3Aforge-app%3Ajira
&redirect_uri=<your_redirect_uri>
&state=<your_state>
&response_type=code
&prompt=consentExchange the returned code for an access token, then call your endpoint with it:
curl --request GET \
--url 'https://api.atlassian.com/svc/jira/<cloudId>/apps/<appId>_<envId>/api/<path>' \
--header 'Authorization: Bearer <access_token>'For the full flow, including exchanging the code for a token, see the Atlassian documentation Access REST APIs exposed by a Forge app.
Staying authenticated
Access tokens expire. To avoid sending your users through the consent screen every time one does, request a refresh token by adding offline_access to your scopes:
&scope=offline_access%20invoke%3Auser-script%3Acustom%20read%3Aforge-app%3Ajiraoffline_access on its own is not enough. You must also request read:forge-app:jira, or the authorization request fails. This applies even if your integration would not otherwise need that scope.You can then exchange the refresh token for a new access token without further user interaction. See the Atlassian documentation Implementing refresh tokens.
Create an HTTP endpoint
Follow these steps to create a new HTTP endpoint:
Edit an HTTP endpoint
Use these steps to modify an existing HTTP endpoint:
Enable and disable an HTTP endpoint
To enable or disable your HTTP endpoint:
- In Jira administration, select Marketplace apps.
- Under Apps, select ScriptRunner.
- In ScriptRunner, select HTTP endpoints.
- Locate the HTTP endpoint you want to enable or disable.
- Open the ellipsis (…) menu for that HTTP endpoint and select Disable or Enable.
Delete an HTTP endpoint
Remove HTTP endpoints you no longer need to keep your list organized and reduce clutter. When you delete an HTTP endpoint, it's removed from your active HTTP endpoints list and will no longer be callable, but it isn't permanently removed. You can still access and restore it from ScriptRunner if needed.
To delete an HTTP endpoint:
- In Jira administration, select Marketplace apps.
- Under Apps, select ScriptRunner.
- Select HTTP endpoints.
- Find the HTTP endpoint you want to remove.
- Open the ellipsis (…) menu for that HTTP endpoint.
- Select Delete.
- When prompted, confirm that you want to delete the HTTP endpoint.
Restore a deleted HTTP endpoint
You can restore deleted HTTP endpoints along with their activity history. To restore a deleted HTTP endpoint: