Skip to Content
SimulatorsJiraOverview

Simulators

Jira simulator

jira-twin is a local, stateful simulator of the Jira Cloud REST API for testing code that files, works and searches issues: issues with their comments, worklogs, attachments, links, watchers and change history; projects with their versions and components; and Jira Software’s boards, sprints and epics all keep their records between calls. It never calls Jira or Atlassian, sends no email or notification and accepts any email address and API token.

Quickstart

Install the CLI, sign in and boot the simulator with RYSTIC_URL pointing at it.

On Pro you sign in with an API key from your account .

curl -fsSL https://www.rystic.ai/install.sh | sh
rystic login rys_…   # your API key
export RYSTIC_URL=$(rystic run jira-twin -d --port 0 --scenario jira_sprint_board --json | jq -r .data.url)
curl -s "$RYSTIC_URL/_rystic/version"
# -> {..."product":"jira-twin"...}

On Enterprise, the licensed edition signs in with a license file in place of the key: rystic login ./rystic-license.json (licensing ).

First calls

The default scenario is a software project, PLAT, in the middle of a sprint. Read its newest issue, file a new one and read it back:

curl -s -G "$RYSTIC_URL/rest/api/3/search/jql" -u me@example.com:token --data-urlencode 'jql=project = PLAT ORDER BY created DESC' -d fields=summary,status -d maxResults=1 | jq -c '.issues[0] | {key, summary: .fields.summary, status: .fields.status.name}'
# -> {"key":"PLAT-6","summary":"Checkout returns 500 on expired payment token","status":"Blocked"}
curl -s "$RYSTIC_URL/rest/api/3/issue" -u me@example.com:token -H 'Content-Type: application/json' -d '{"fields":{"project":{"key":"PLAT"},"issuetype":{"name":"Task"},"summary":"Retry failed webhooks"}}' | jq -c '{id, key}'
# -> {"id":"10107","key":"PLAT-7"}
curl -s "$RYSTIC_URL/rest/api/3/issue/PLAT-7?fields=summary,status" -u me@example.com:token | jq -c '{key, summary: .fields.summary, status: .fields.status.name}'
# -> {"key":"PLAT-7","summary":"Retry failed webhooks","status":"To Do"}

A search returns only issue ids unless it names fields, as Jira’s does.

Connect your client

Point a Jira library at $RYSTIC_URL as its site URL with any email address and API token, and the rest of your code stays as it is. Atlassian publishes no official Jira SDK, so these are the most used libraries. Python (jira):

import os
from jira import JIRA

jira = JIRA(server=os.environ["RYSTIC_URL"], basic_auth=("me@example.com", "token"))
print(jira.myself()["accountId"])

Node (jira.js):

import { createCloudClient } from "jira.js";

const jira = createCloudClient({
  host: process.env.RYSTIC_URL,
  auth: { type: "basic", email: "me@example.com", apiToken: "token" },
});
const me = await jira.myself.getCurrentUser();

Go (go-jira):

import (
	"os"

	jira "github.com/andygrunwald/go-jira"
)

func main() {
	tp := jira.BasicAuthTransport{Username: "me@example.com", Password: "token"}
	client, _ := jira.NewClient(tp.Client(), os.Getenv("RYSTIC_URL"))
	client.User.GetSelf()
}

A client that reads its site from a config file, such as jira-cli, takes the simulator’s URL as the server in that file.

Retries

The simulator does not rate-limit, so no request is answered 429 and a client’s backoff stays idle. Jira has no idempotency keys and the simulator reads none: a POST sent twice makes two records.

Each client’s quickstart was run against this simulator in each way it connects, with the vendor’s own hosts blocked.

ClientVersionKindWorks withFails withCheckedKnown gap
atlassian-python-api (PyPI)v5.0.5sdkbase URL—2026-11-06—
github.com/andygrunwald/go-jira (Go)v1.17.0sdkbase URL—2026-11-06—
ankitpokhrel/jira-cli (GitHub Releases)v1.7.0clibase URL—2026-11-06—
jira.js (npm)v6.4.0sdkbase URL—2026-11-06—
jira (PyPI)v3.10.5sdkbase URL—2026-11-06—
mcp-atlassian (PyPI)v0.23.1mcp-serverproxy—2026-11-06—

Auth

The simulator takes your credential where Jira Cloud does, as HTTP Basic with an email address and an API token (curl -u me@example.com:token), and checks no value: any non-empty user name works, with any token or none.

A request acts as one default user whatever token it carries, unless it names another user as below. Each scenario names one of its own users as that default, so GET /rest/api/3/myself answers that user and the comments, worklogs and attachments you create are authored by it. To act as another seeded user, send X-Rystic-Actor: <accountId>, or bind the email address you send to its account id with POST /_rystic/credentials and {"bindings":{"<email>":"<accountId>"}} (the control plane); loading a scenario or a reset clears those bindings. With no scenario, the default is the user you seed under the key member, and myself answers account id tb-member-account-id until you seed one. A request whose Basic header has an empty user name, whose header is malformed or whose scheme is Bearer is refused with 401 and Client must be authenticated to access this resource. on a route that checks credentials. Not every route checks one: serverInfo, issue and project reads and the field and issue type lists answer a request with no credential, where Jira refuses some of them. Send a credential on every request so the code you test is the code that reaches Jira.

API version

The simulator serves Jira Cloud’s REST API versions 2 and 3 under /rest/api/2 and /rest/api/3, and the Jira Software API under /rest/agile/1.0; the version is part of the path, so you pin it by the URL you call.

Both REST versions read and write the same records: an issue created through /rest/api/2 is read back through /rest/api/3, and a description is stored as sent and returned in the form it arrived in, plain text or Atlassian Document Format, on either version. A comment follows Jira’s own split: version 2 takes a plain-text body and version 3 takes Atlassian Document Format, and a create with the other form answers 400. A path under any other version, such as /rest/api/latest or /rest/api/4, answers 404 No route in twin for GET /rest/api/4/issue/PLAT-1. GET /rest/api/3/serverInfo reports a Cloud deployment.

Coverage

Project, user and workflow administration, custom field setup, dashboards, groups, permission schemes, webhooks and Jira Service Management are out of scope; Limitations lists, by family, the published operations it does not serve, and GET /_rystic/routes lists the routes a running simulator serves. On 2026-11-06, the simulator matched Jira Cloud on 835 of the 1035 tests that compare the two (80.7%), and 17 of them gave no verdict.

170 routes, counted from the simulator’s route table.

Show all operations
ResourceOperations
Attachment settings (1)GET /rest/api/3/attachment/meta
Board (7)GET, POST /rest/agile/1.0/board; GET, DELETE /rest/agile/1.0/board/{boardId}; GET /rest/agile/1.0/board/{boardId}/backlog; GET /rest/agile/1.0/board/{boardId}/configuration; GET /rest/agile/1.0/board/{boardId}/epic
Bulk operation task (1)GET /rest/api/3/bulk/queue/{taskId}
Dynamic webhook (5)GET, POST, DELETE /rest/api/3/webhook; GET /rest/api/3/webhook/failed; PUT /rest/api/3/webhook/refresh
Filter (9)POST /rest/api/3/filter; GET /rest/api/3/filter/favourite; GET /rest/api/3/filter/my; GET /rest/api/3/filter/search; GET, PUT, DELETE /rest/api/3/filter/{id}; PUT, DELETE /rest/api/3/filter/{id}/favourite
Issue (45)POST /rest/agile/1.0/backlog/issue; GET /rest/agile/1.0/board/{boardId}/issue; POST /rest/agile/1.0/epic/none/issue; GET, POST /rest/agile/1.0/epic/{epicIdOrKey}; GET, POST /rest/agile/1.0/epic/{epicIdOrKey}/issue; PUT /rest/agile/1.0/epic/{epicIdOrKey}/rank; PUT /rest/agile/1.0/issue/rank; GET /rest/agile/1.0/issue/{issueIdOrKey}; GET, PUT /rest/agile/1.0/issue/{issueIdOrKey}/estimation; GET, POST /rest/agile/1.0/sprint/{sprintId}/issue; POST /rest/api/2/issue; POST /rest/api/2/issue/bulk; GET, PUT, DELETE /rest/api/2/issue/{issueIdOrKey}; PUT /rest/api/2/issue/{issueIdOrKey}/assignee; GET, POST /rest/api/2/issue/{issueIdOrKey}/transitions; GET, POST /rest/api/2/search/jql; POST /rest/api/3/bulk/issues/move; POST /rest/api/3/issue; POST /rest/api/3/issue/bulk; GET /rest/api/3/issue/picker; GET, PUT, DELETE /rest/api/3/issue/{issueIdOrKey}; PUT /rest/api/3/issue/{issueIdOrKey}/assignee; GET /rest/api/3/issue/{issueIdOrKey}/editmeta; GET, POST, DELETE /rest/api/3/issue/{issueIdOrKey}/remotelink; GET, PUT, DELETE /rest/api/3/issue/{issueIdOrKey}/remotelink/{linkId}; GET, POST /rest/api/3/issue/{issueIdOrKey}/transitions; GET /rest/api/3/label; POST /rest/api/3/search/approximate-count; GET, POST /rest/api/3/search/jql
Issue attachment (5)GET /rest/api/3/attachment/content/{id}; GET /rest/api/3/attachment/thumbnail/{id}; GET, DELETE /rest/api/3/attachment/{id}; POST /rest/api/3/issue/{issueIdOrKey}/attachments
Issue changelog (4)POST /rest/api/2/changelog/bulkfetch; POST /rest/api/3/changelog/bulkfetch; GET /rest/api/3/issue/{issueIdOrKey}/changelog; POST /rest/api/3/issue/{issueIdOrKey}/changelog/list
Issue comment (10)GET, POST /rest/api/2/issue/{issueIdOrKey}/comment; GET, PUT, DELETE /rest/api/2/issue/{issueIdOrKey}/comment/{id}; GET, POST /rest/api/3/issue/{issueIdOrKey}/comment; GET, PUT, DELETE /rest/api/3/issue/{issueIdOrKey}/comment/{id}
Issue field (4)GET /rest/api/2/field; GET /rest/api/3/field; GET /rest/api/3/field/search; POST /rest/api/3/jql/parse
Issue link (5)POST /rest/api/2/issueLink; DELETE /rest/api/2/issueLink/{linkId}; POST /rest/api/3/issueLink; GET, DELETE /rest/api/3/issueLink/{linkId}
Issue link type (3)GET /rest/api/2/issueLinkType; GET /rest/api/3/issueLinkType; GET /rest/api/3/issueLinkType/{issueLinkTypeId}
Issue priority (1)GET /rest/api/3/priority/search
Issue type (3)GET /rest/api/2/issue/createmeta/{projectIdOrKey}/issuetypes/{issueTypeId}; GET /rest/api/3/issue/createmeta/{projectIdOrKey}/issuetypes/{issueTypeId}; GET /rest/api/3/issuetype
Issue vote (3)GET, POST, DELETE /rest/api/3/issue/{issueIdOrKey}/votes
Issue watcher (6)GET, POST, DELETE /rest/api/2/issue/{issueIdOrKey}/watchers; GET, POST, DELETE /rest/api/3/issue/{issueIdOrKey}/watchers
Issue worklog (10)GET, POST /rest/api/2/issue/{issueIdOrKey}/worklog; GET, PUT, DELETE /rest/api/2/issue/{issueIdOrKey}/worklog/{id}; GET, POST /rest/api/3/issue/{issueIdOrKey}/worklog; GET, PUT, DELETE /rest/api/3/issue/{issueIdOrKey}/worklog/{id}
Project (10)GET /rest/agile/1.0/board/{boardId}/project; GET /rest/api/2/issue/createmeta; GET /rest/api/2/issue/createmeta/{projectIdOrKey}/issuetypes; GET /rest/api/2/project; GET /rest/api/2/project/search; GET /rest/api/2/project/{projectIdOrKey}; GET /rest/api/3/issue/createmeta/{projectIdOrKey}/issuetypes; GET /rest/api/3/issuetype/project; GET /rest/api/3/project/search; GET /rest/api/3/project/{projectIdOrKey}
Project component (7)GET /rest/api/2/project/{projectIdOrKey}/components; POST /rest/api/3/component; GET, PUT, DELETE /rest/api/3/component/{id}; GET /rest/api/3/project/{projectIdOrKey}/component; GET /rest/api/3/project/{projectIdOrKey}/components
Project version (10)GET /rest/agile/1.0/board/{boardId}/version; GET /rest/api/2/project/{projectIdOrKey}/versions; POST /rest/api/2/version; PUT /rest/api/2/version/{id}; GET /rest/api/3/project/{projectIdOrKey}/version; GET /rest/api/3/project/{projectIdOrKey}/versions; POST /rest/api/3/version; GET, PUT /rest/api/3/version/{id}; POST /rest/api/3/version/{id}/removeAndSwap
Server information (2)GET /rest/api/2/serverInfo; GET /rest/api/3/serverInfo
Sprint (7)GET /rest/agile/1.0/board/{boardId}/sprint; POST /rest/agile/1.0/sprint; GET, POST, PUT, DELETE /rest/agile/1.0/sprint/{sprintId}; POST /rest/agile/1.0/sprint/{sprintId}/swap
User (8)GET /rest/api/2/myself; GET /rest/api/2/user; GET /rest/api/2/user/assignable/search; GET /rest/api/2/user/search; GET /rest/api/3/myself; GET /rest/api/3/user; GET /rest/api/3/user/assignable/search; GET /rest/api/3/user/search
Workflow status (4)GET /rest/api/2/status; GET /rest/api/3/project/{projectIdOrKey}/statuses; GET /rest/api/3/status; GET /rest/api/3/statuses/search

Scenarios

The simulator ships 4 scenarios; the quickstart boots jira_sprint_board.

ScenarioWhat it is forWhat it seedsClock
jira_fresh_siteA site at its floorattachment_settings 1, field 9, issue_link_type 4, issue_type 5, priority 5, project 1, server_info 1, status 4, user 12026-02-02T00:00:00Z
jira_release_trainA release project whose four versions stand in every lifecycle state from boot — archived, released, overdue-unreleased and planned — with issues pinned to each fix version, a saved filter over the patch scope, a webhook watching that scope and two bulk edit tasks, one finished and one still running.attachment 1, attachment_settings 1, bulk_task 2, changelog 3, comment 2, component 2, field 9, filter 1, issue 4, issue_link 1, issue_link_type 4, issue_type 5, priority 5, project 1, remote_issue_link 1, server_info 1, status 4, user 2, version 4, vote 2, watcher 5, webhook 1, worklog 12026-02-02T00:00:00Z
jira_sprint_board (default)A lived-in software project mid-sprintattachment 2, attachment_settings 1, board 1, changelog 4, comment 4, component 2, field 9, filter 2, issue 6, issue_link 2, issue_link_type 4, issue_type 5, priority 5, project 1, remote_issue_link 1, server_info 1, sprint 3, status 4, user 5, version 2, vote 3, watcher 10, webhook 2, worklog 22026-02-02T00:00:00Z
jira_triage_inboxFive unassigned customer reports sitting in To Do on a kanban board backed by one saved filter and one inbound webhookattachment 3, attachment_settings 1, board 1, changelog 2, comment 3, field 9, filter 1, issue 5, issue_link 1, issue_link_type 4, issue_type 5, priority 5, project 1, remote_issue_link 2, server_info 1, status 4, user 4, vote 3, watcher 6, webhook 12026-02-02T00:00:00Z

What each scenario holds:

  • jira_fresh_site: One admin, one empty project and the full catalog of issue types, statuses, priorities, fields and link types — every issue listing returns nothing, so a first write’s effect is unmistakable.
  • jira_sprint_board: Six issues across To Do, In Progress, Blocked and Done — epic, story, subtask, bug and task — on a scrum board whose three sprints run from closed through active to future, with two saved filters, comments, worklogs, watchers, votes, attachments, links, change history, both a released and an open version and two registered webhooks, one already lapsed.
  • jira_triage_inbox: Attachments, remote links back to the originating tickets, watchers, three votes on the loudest report, reporter comments and a duplicate link, including one report from a deactivated reporter.

Boot one by name, or load one into a running simulator:

rystic run jira-twin -d --port 0 --scenario jira_sprint_board
curl -s -X POST "$RYSTIC_URL/_rystic/scenario" -d '{"name":"jira_sprint_board"}'

What the scenarios do not hold

  • Each scenario holds one project, with Jira’s default issue types, statuses and priorities. Create more projects by seeding them (Seeding state): the simulator serves no project create.
  • Without --scenario, the simulator boots with no project, issue type, status or priority, so an issue create has nothing to name. Seed those first or boot a scenario.

Seeding state

Each resource is one register, named state_<resource> (state_issue, state_project, state_sprint and so on), holding records keyed by their id; GET /_rystic/registers lists every register and its fields. Seed a project the scenario lacks, read it back and file an issue in it:

curl -s -X POST "$RYSTIC_URL/_rystic/seed" -d '{"state_project": {"10500": {"id": "10500", "key": "OPS", "name": "Operations", "projectTypeKey": "software"}}}'
curl -s "$RYSTIC_URL/rest/api/3/project/OPS" -u me@example.com:token | jq -c '{key, name}'
# -> {"key":"OPS","name":"Operations"}
curl -s "$RYSTIC_URL/rest/api/3/issue" -u me@example.com:token -H 'Content-Type: application/json' -d '{"fields":{"project":{"key":"OPS"},"issuetype":{"name":"Task"},"summary":"Rotate keys"}}' | jq -c '{id, key}'
# -> {"id":"10108","key":"OPS-1"}

A record is served as stored, with its self link filled in when you leave it out. An issue number counts per project from the seeded issues and an issue id counts across the site.

Scenarios and seeding covers seed files, refusals and the seed gate.

Features

Each feature has its own page:

  • Boards, sprints and epics: Jira Software’s Agile API: boards with a backlog, sprints you run from create to complete and epics that group issues.
  • Issue attachments: Upload files to an issue as Jira’s multipart form does, download them through a signed redirect and delete them.
  • Versions and components: A project’s release versions and components: create, release, archive and swap versions; add, rename and remove components.

Determinism and reset

On jira_sprint_board the next issue is PLAT-7 with id 10107, and a comment or an attachment continues its own seeded numbering.

The clock moves one millisecond each time a request stamps or reports a time: creating an issue, comment, worklog, attachment or sprint, editing an issue, comment or worklog, or a serverInfo read. GET /rest/api/3/serverInfo reports it as serverTime.

Nothing moves on its own. Move the clock with POST /_rystic/clock and every later created, updated and sprint completeDate reads the new time. Reset and determinism covers reset, the clock and the RNG.

Limitations

Known issues.

  • A version, component or attachment seeded by a scenario can render differently from one created through the API: a seeded version carries release and start date fields a created one lacks, no seeded component uses the project-default assignee, and a seeded attachment’s author shows an email address and avatars a created one lacks.
  • A JSON write sent with the wrong Content-Type (form-urlencoded) gets a 400 error collection where Jira returns 415 with a problem document. This holds for moving issues to the backlog or into or out of an epic, swapping sprints, setting an estimate and a v2 bulk create or transition. It holds too for editing an issue, creating a component, creating or editing a version, editing a v3 comment and creating or editing a remote link. Removing a version with a swap and editing a v3 component accept the write where Jira returns 415, and registering a webhook returns 403 where Jira returns 415.
  • A request with no credential can get a success where Jira refuses it. Listing labels and the bulk changelog fetch return 200 where Jira returns 401. A v3 issue create returns 201 where Jira returns 400, and the create-metadata issue types read returns 200 where Jira returns 404. Deleting a board deletes it where Jira returns 401. Other requests with no credential get a different refusal. A v2 issue create or bulk create and adding a vote return 401 where Jira returns 400, and the webhook routes return 403 where Jira returns 401.
  • User search with an invalid parameter (v2 and v3) and the v3 user lookup without an account id return 400 where Jira returns 403. The v3 user lookup with an unknown parameter returns 403 where Jira returns 400, and the v2 user lookup returns 404 where Jira returns 400.
  • GET /rest/api/2/field, GET /rest/api/2/project and a v2 issue read’s 404 return Content-Type application/json, where Jira returns application/json;charset=UTF-8.
  • An issue’s watchers list can leave out a watcher’s emailAddress, where Jira returns an address.
  • Issue edit metadata leaves out the site’s custom fields, and an invalid remote-link read answers 200 where Jira answers 403.
  • Assigning an unknown assignee answers 400 where Jira answers 404, and deleting a comment without permission could not be reproduced against Jira, so the simulator’s answer there is unchecked.
  • The simulator does not model Jira’s linking-disabled state, where reads of issue link types answer 404.
  • GET /rest/api/2/myself omits avatarUrls for an account stored without them, where Jira returns them for every account.
  • Listing a project’s components through API version 2 returns each component’s leadAccountId, a write-only field that Jira leaves out of the response; version 3 returns the component the way Jira does.
  • Reading an issue with expand=changelog returns no changelog, and the expanded field names and schema leave out the site’s custom fields. Workaround: Read the history from GET /rest/api/3/issue/{key}/changelog.
  • A project read doesn’t list versions or components created after the simulator started. Workaround: List them through the project’s versions and components routes.
  • A board created through the API has no columns or estimation, and its issue list ignores the board’s filter.
  • An issue’s sprint fields stay out of date after its sprint starts, closes or is deleted.
  • An issue’s votes field doesn’t follow the votes route: a vote added through the route isn’t counted in the issue.
  • Creating a filter with invalid JQL succeeds where Jira answers 400, and the saved filter’s search URL names a route Jira removed.
  • Following a self link from a v2 answer for an attachment, its content, an issue link, an issue type, a status or a priority answers 404. Workaround: Read these records through /rest/api/3.
  • The v3 project search includes nextPage on its last page. Workaround: Stop paging when isLast is true.
  • Creating, editing or deleting a worklog adds no item to the issue’s changelog, where Jira records one.
  • After a v2 worklog is logged on an issue with no estimate, timetracking omits the remaining estimate, which Jira reports as 0m. Workaround: Log worklogs through /rest/api/3, or treat an absent remaining estimate as 0.
  • A version create that names its project by key, without projectId, is refused with “You must specify a valid project id.”, where Jira creates the version. Workaround: Send the project’s numeric projectId.
  • A component create that carries the read-only isAssigneeTypeValid member is refused, where Jira ignores it and creates the component. Workaround: Leave isAssigneeTypeValid out of the create body.
  • A newly created issue has no watcher entry for its reporter: a v3 read shows watchCount 0 and isWatching false, where Jira shows the reporter watching.
  • A v2 bulk create accepts an issue with no summary or an unknown parent, which Jira refuses, and can give a new issue the key of a deleted one. Workaround: Create issues one at a time, or through the v3 bulk create route.
  • A transition or edit that sets a resolution by a name the site does not have stores it: a v2 transition under Done’s id, and a v3 transition or any edit as the bare name. What Jira returns is unconfirmed. Workaround: Set only the resolutions the site lists.

Beyond the known issues, 1112 of the 1282 operations in the published spec are not served, 209 documented responses have no test yet and 221 behaviors are not modeled. The details follow.

Show all limitations

Not served. 1112 of the 1282 operations in the published spec are not served.

  • Backlog, 1 of 2 served: POST /rest/agile/1.0/backlog/{boardId}/issue
  • Board, 11 of 26 served: GET /rest/agile/1.0/board/filter/{filterId}, GET /rest/agile/1.0/board/{boardId}/epic/none/issue, GET /rest/agile/1.0/board/{boardId}/epic/{epicId}/issue, GET /rest/agile/1.0/board/{boardId}/features, PUT /rest/agile/1.0/board/{boardId}/features, POST /rest/agile/1.0/board/{boardId}/issue, GET /rest/agile/1.0/board/{boardId}/project/full, GET /rest/agile/1.0/board/{boardId}/properties, … and 7 more
  • Epic, 6 of 7 served: GET /rest/agile/1.0/epic/none/issue
  • Filters, 9 of 26 served: POST /rest/api/2/filter, GET /rest/api/2/filter/favourite, GET /rest/api/2/filter/my, GET /rest/api/2/filter/search, GET /rest/api/2/filter/{id}, PUT /rest/api/2/filter/{id}, DELETE /rest/api/2/filter/{id}, GET /rest/api/2/filter/{id}/columns, … and 9 more
  • Issue attachments, 6 of 16 served: GET /rest/api/2/attachment/content/{id}, GET /rest/api/2/attachment/meta, GET /rest/api/2/attachment/thumbnail/{id}, GET /rest/api/2/attachment/{id}, DELETE /rest/api/2/attachment/{id}, GET /rest/api/2/attachment/{id}/expand/human, GET /rest/api/2/attachment/{id}/expand/raw, POST /rest/api/2/issue/{issueIdOrKey}/attachments, … and 2 more
  • Issue bulk operations, 2 of 9 served: POST /rest/api/3/bulk/issues/delete, GET /rest/api/3/bulk/issues/fields, POST /rest/api/3/bulk/issues/fields, GET /rest/api/3/bulk/issues/transition, POST /rest/api/3/bulk/issues/transition, POST /rest/api/3/bulk/issues/unwatch, POST /rest/api/3/bulk/issues/watch
  • Issue comments, 10 of 12 served: POST /rest/api/2/comment/list, POST /rest/api/3/comment/list
  • Issue fields, 3 of 22 served: POST /rest/api/2/field, GET /rest/api/2/field/search, GET /rest/api/2/field/search/trashed, PUT /rest/api/2/field/{fieldId}, GET /rest/api/2/field/{fieldId}/association/project, GET /rest/api/2/field/{fieldId}/contexts, DELETE /rest/api/2/field/{id}, POST /rest/api/2/field/{id}/restore, … and 11 more
  • Issue link types, 3 of 10 served: POST /rest/api/2/issueLinkType, GET /rest/api/2/issueLinkType/{issueLinkTypeId}, PUT /rest/api/2/issueLinkType/{issueLinkTypeId}, DELETE /rest/api/2/issueLinkType/{issueLinkTypeId}, POST /rest/api/3/issueLinkType, PUT /rest/api/3/issueLinkType/{issueLinkTypeId}, DELETE /rest/api/3/issueLinkType/{issueLinkTypeId}
  • Issue links, 5 of 6 served: GET /rest/api/2/issueLink/{linkId}
  • Issue priorities, 1 of 16 served: GET /rest/api/2/priority, POST /rest/api/2/priority, PUT /rest/api/2/priority/default, PUT /rest/api/2/priority/move, GET /rest/api/2/priority/search, GET /rest/api/2/priority/{id}, PUT /rest/api/2/priority/{id}, DELETE /rest/api/2/priority/{id}, … and 7 more
  • Issue remote links, 6 of 12 served: GET /rest/api/2/issue/{issueIdOrKey}/remotelink, POST /rest/api/2/issue/{issueIdOrKey}/remotelink, DELETE /rest/api/2/issue/{issueIdOrKey}/remotelink, GET /rest/api/2/issue/{issueIdOrKey}/remotelink/{linkId}, PUT /rest/api/2/issue/{issueIdOrKey}/remotelink/{linkId}, DELETE /rest/api/2/issue/{issueIdOrKey}/remotelink/{linkId}
  • Issue search, 6 of 14 served: GET /rest/api/2/issue/picker, POST /rest/api/2/jql/match, GET /rest/api/2/search, POST /rest/api/2/search, POST /rest/api/2/search/approximate-count, POST /rest/api/3/jql/match, GET /rest/api/3/search, POST /rest/api/3/search
  • Issue types, 2 of 16 served: GET /rest/api/2/issuetype, POST /rest/api/2/issuetype, GET /rest/api/2/issuetype/project, GET /rest/api/2/issuetype/{id}, PUT /rest/api/2/issuetype/{id}, DELETE /rest/api/2/issuetype/{id}, GET /rest/api/2/issuetype/{id}/alternatives, POST /rest/api/2/issuetype/{id}/avatar2, … and 6 more
  • Issue votes, 3 of 6 served: GET /rest/api/2/issue/{issueIdOrKey}/votes, POST /rest/api/2/issue/{issueIdOrKey}/votes, DELETE /rest/api/2/issue/{issueIdOrKey}/votes
  • Issue watchers, 6 of 8 served: POST /rest/api/2/issue/watching, POST /rest/api/3/issue/watching
  • Issue worklogs, 10 of 20 served: DELETE /rest/api/2/issue/{issueIdOrKey}/worklog, POST /rest/api/2/issue/{issueIdOrKey}/worklog/move, GET /rest/api/2/worklog/deleted, POST /rest/api/2/worklog/list, GET /rest/api/2/worklog/updated, DELETE /rest/api/3/issue/{issueIdOrKey}/worklog, POST /rest/api/3/issue/{issueIdOrKey}/worklog/move, GET /rest/api/3/worklog/deleted, … and 2 more
  • Issues, 26 of 48 served: GET /rest/api/2/events, POST /rest/api/2/issue/archive, PUT /rest/api/2/issue/archive, POST /rest/api/2/issue/bulkfetch, GET /rest/api/2/issue/limit/adf/report, GET /rest/api/2/issue/limit/report, PUT /rest/api/2/issue/unarchive, GET /rest/api/2/issue/{issueIdOrKey}/changelog, … and 14 more
  • JQL, 1 of 14 served: GET /rest/api/2/invalid-jqls-access-log, GET /rest/api/2/jql/autocompletedata, POST /rest/api/2/jql/autocompletedata, GET /rest/api/2/jql/autocompletedata/suggestions, POST /rest/api/2/jql/parse, POST /rest/api/2/jql/pdcleaner, POST /rest/api/2/jql/sanitize, GET /rest/api/3/invalid-jqls-access-log, … and 5 more
  • Labels, 1 of 2 served: GET /rest/api/2/label
  • Myself, 2 of 12 served: GET /rest/api/2/mypreferences, PUT /rest/api/2/mypreferences, DELETE /rest/api/2/mypreferences, GET /rest/api/2/mypreferences/locale, PUT /rest/api/2/mypreferences/locale, GET /rest/api/3/mypreferences, PUT /rest/api/3/mypreferences, DELETE /rest/api/3/mypreferences, … and 2 more
  • Project components, 7 of 16 served: GET /rest/api/2/component, POST /rest/api/2/component, GET /rest/api/2/component/{id}, PUT /rest/api/2/component/{id}, DELETE /rest/api/2/component/{id}, GET /rest/api/2/component/{id}/relatedIssueCounts, GET /rest/api/2/project/{projectIdOrKey}/component, GET /rest/api/3/component, … and 1 more
  • Project versions, 9 of 30 served: GET /rest/api/2/project/{projectIdOrKey}/version, GET /rest/api/2/version/{id}, DELETE /rest/api/2/version/{id}, PUT /rest/api/2/version/{id}/mergeto/{moveIssuesTo}, POST /rest/api/2/version/{id}/move, GET /rest/api/2/version/{id}/relatedIssueCounts, GET /rest/api/2/version/{id}/relatedwork, POST /rest/api/2/version/{id}/relatedwork, … and 13 more
  • Projects, 6 of 26 served: POST /rest/api/2/project, GET /rest/api/2/project/recent, PUT /rest/api/2/project/{projectIdOrKey}, DELETE /rest/api/2/project/{projectIdOrKey}, POST /rest/api/2/project/{projectIdOrKey}/archive, POST /rest/api/2/project/{projectIdOrKey}/delete, POST /rest/api/2/project/{projectIdOrKey}/restore, GET /rest/api/2/project/{projectIdOrKey}/statuses, … and 12 more
  • Sprint, 8 of 12 served: GET /rest/agile/1.0/sprint/{sprintId}/properties, GET /rest/agile/1.0/sprint/{sprintId}/properties/{propertyKey}, PUT /rest/agile/1.0/sprint/{sprintId}/properties/{propertyKey}, DELETE /rest/agile/1.0/sprint/{sprintId}/properties/{propertyKey}
  • Status, 1 of 18 served: GET /rest/api/2/statuses, POST /rest/api/2/statuses, PUT /rest/api/2/statuses, DELETE /rest/api/2/statuses, GET /rest/api/2/statuses/byNames, GET /rest/api/2/statuses/search, GET /rest/api/2/statuses/{statusId}/project/{projectId}/issueTypeUsages, GET /rest/api/2/statuses/{statusId}/projectUsages, … and 9 more
  • User search, 4 of 16 served: GET /rest/api/2/user/assignable/multiProjectSearch, GET /rest/api/2/user/permission/search, GET /rest/api/2/user/picker, GET /rest/api/2/user/search/query, GET /rest/api/2/user/search/query/key, GET /rest/api/2/user/viewissue/search, GET /rest/api/3/user/assignable/multiProjectSearch, GET /rest/api/3/user/permission/search, … and 4 more
  • Users, 2 of 26 served: POST /rest/api/2/user, DELETE /rest/api/2/user, GET /rest/api/2/user/bulk, GET /rest/api/2/user/bulk/migration, GET /rest/api/2/user/columns, PUT /rest/api/2/user/columns, DELETE /rest/api/2/user/columns, GET /rest/api/2/user/email, … and 16 more
  • Webhooks, 5 of 10 served: GET /rest/api/2/webhook, POST /rest/api/2/webhook, DELETE /rest/api/2/webhook, GET /rest/api/2/webhook/failed, PUT /rest/api/2/webhook/refresh
  • Workflow statuses, 2 of 4 served: GET /rest/api/2/status/{idOrName}, GET /rest/api/3/status/{idOrName}
  • Not served at all: Agents (2), Announcement banner (4), App data policies (4), App migration (3), App properties (8), Application roles (4), Audit records (2), Avatars (14), Classification levels (2), Dashboards (34), Dynamic modules (3), Field schemes (30), Filter sharing (12), Group and user picker (2), Groups (16), Issue comment properties (8), Issue custom field associations (4), Issue custom field configuration (apps) (6), Issue custom field contexts (30), Issue custom field options (14), … and 54 more

Not tested. 1035 tests compare the simulator with the real service. 209 documented responses have no test yet, and 160 of them need a credential our test account doesn’t hold (401 ×100, 403 ×60). The other 49:

OperationStatusWhy
DELETE /rest/api/2/issue/{issueIdOrKey}/comment/{id}405no known request reaches it
DELETE /rest/api/3/issue/{issueIdOrKey}/comment/{id}405no known request reaches it
DELETE /rest/api/3/issue/{issueIdOrKey}/remotelink204no known request reaches it
DELETE /rest/api/3/issue/{issueIdOrKey}/remotelink415no known request reaches it
GET /rest/api/2/issue/createmeta/{projectIdOrKey}/issuetypes200nothing the simulator serves creates the record it needs
GET /rest/api/2/issue/createmeta/{projectIdOrKey}/issuetypes/{issueTypeId}200nothing the simulator serves creates the record it needs
GET /rest/api/2/project/{projectIdOrKey}200nothing the simulator serves creates the record it needs
GET /rest/api/2/project/{projectIdOrKey}/components200nothing the simulator serves creates the record it needs
GET /rest/api/2/project/{projectIdOrKey}/versions200nothing the simulator serves creates the record it needs
GET /rest/api/2/user404no known request reaches it
GET /rest/api/2/user/assignable/search404no known request reaches it
GET /rest/api/2/user/assignable/search429no request a client sends provokes it
GET /rest/api/2/user/search429no request a client sends provokes it
GET /rest/api/3/attachment/content/{id}303no known request reaches it
GET /rest/api/3/attachment/thumbnail/{id}303no known request reaches it
GET /rest/api/3/bulk/queue/{taskId}400nothing the simulator serves creates the record it needs
GET /rest/api/3/issue/{issueIdOrKey}/remotelink413no known request reaches it
GET /rest/api/3/issueLinkType/{issueLinkTypeId}200nothing the simulator serves creates the record it needs
GET /rest/api/3/issuetype/project200no known request reaches it
GET /rest/api/3/issuetype/project404no known request reaches it
… and 29 more

Not modeled. Behavior the simulator does not model, by operation:

  • Documented statuses not reproduced: 401 (3 operations).
  • The captured 204 carried Content-Type: text/html;charset=UTF-8 on a bodiless answer; no header policy is declared because one observation does not establish which headers a verdict should hold. (DELETE /rest/agile/1.0/board/{boardId})
  • The permission arm likewise needs an account without Jira/board admin rights, which the run’s single administrator credential cannot present. (DELETE /rest/agile/1.0/board/{boardId})
  • The bodies of the 401, 403 and 404 answers are undocumented and unmeasured; only their statuses are claimed. The two measured 204s carry Content-Type text/html;charset=UTF-8 with an empty body, so no response headers are declared for comparison. (DELETE /rest/agile/1.0/sprint/{sprintId})
  • The description states that deleting a sprint moves all open issues in the sprint to the backlog. (DELETE /rest/agile/1.0/sprint/{sprintId})
  • Whether deleting a comment restricted by visibility to a group or role draws 404 or 400 for a caller outside that group is undocumented here. (DELETE /rest/api/2/issue/{issueIdOrKey}/comment/{id})
  • The 403 and 404 response bodies are undocumented (no content declared in the spec); only the statuses are modelled. (DELETE /rest/api/3/attachment/{id})
  • The 400 conflates two triggers (filter not found, filter used by a board). Without a measured body they cannot be split into discriminated branches. (DELETE /rest/api/3/filter/{id})
  • Whether an anonymous request (the spec lists an empty security alternative) answers 400 for a private filter or a credential refusal is undocumented. (DELETE /rest/api/3/filter/{id}/favourite)
  • The spec’s third security alternative is anonymous ({}), and the description states the operation can be accessed anonymously; what an anonymous caller is answered on a visible issue is not documented here. (DELETE /rest/api/3/issue/{issueIdOrKey})
  • Which answer a credential-less request draws on this route is unmeasured: the spec documents 401 for missing or incorrect credentials and 405 for an anonymous call, and the security list admits the empty alternative. (DELETE /rest/api/3/issue/{issueIdOrKey}/comment/{id})
  • Issue linking must be active on the site; the answer when it is disabled is not documented. (DELETE /rest/api/3/issue/{issueIdOrKey}/remotelink)
  • Deletion by globalId (DELETE /rest/api/3/issue/{issueIdOrKey}/remotelink?globalId=) is a separate operation and is not modeled here. (DELETE /rest/api/3/issue/{issueIdOrKey}/remotelink/{linkId})
  • The deprecated username query parameter’s behaviour (ignored, or refused) is undocumented. (DELETE /rest/api/3/issue/{issueIdOrKey}/watchers)
  • The operation requires the site-wide ‘Allow users to watch issues’ option to be ON; no in-scope operation reads or sets that option, so the refusal when it is OFF is not modeled. (DELETE /rest/api/3/issue/{issueIdOrKey}/watchers)
  • The spec lists a bare ‘200 response’ beside the 204 with no body schema and no statement of what selects it; no branch is declared for it because neither its request selector nor its body is documented. (DELETE /rest/api/3/issueLink/{linkId})
  • Whether an empty webhookIds array is treated as a missing list (400) or as an accepted no-op (202) is not documented. (DELETE /rest/api/3/webhook)
  • Whether the service refuses unknown body members beside webhookIds is not documented; the request shape is left open. (DELETE /rest/api/3/webhook)
  • The expand parameter (admins, permissions) and the resulting response members are not modeled as separate branches; no capture shows the expanded shape. (GET /rest/agile/1.0/board)
  • The spec’s type query parameter schema is an empty object with additionalProperties false; the wire form (a comma-separated string) is taken from its description, not from a capture. (GET /rest/agile/1.0/board)
  • The selection semantics of the fields parameter and the inert expand parameter are unresolved beyond the prose. (GET /rest/agile/1.0/board/{boardId}/backlog)
  • … and 200 more

By design.

  • Few permission checks. A request can act as any seeded user (Auth), but few routes refuse a user for lack of permission. Reading a saved filter by id is refused to a user it is not shared with, though a filter search and favouriting or unfavouriting by id reach every filter, and only a filter’s owner may delete it; editing a filter you do not own answers 501. The webhook routes and the issue-edit override flags are open only to a user whose accountType is app, except the failed-webhooks list, edit metadata with an override flag and the override flag on adding a v2 worklog or updating any worklog, which refuse every user. Removing another user as a watcher through /rest/api/2/ is refused when your own user record denies MANAGE_WATCHER_LIST. Any other test of what a second user may not do has nothing to run against.
  • Search is a subset of JQL. Each of the four search routes, GET and POST on /rest/api/2/search/jql and on /rest/api/3/search/jql, understands its own fixed set of fields, operators and sort fields. A clause outside it answers 400 with a short message or matches nothing and answers an empty page, where Jira refuses an unknown field. GET /rest/api/3/search/jql takes key, project, issuetype, status, statusCategory, assignee, reporter and summary and has the most sort fields (created, updated, key and Rank); a clause on labels, priority, resolution, a date or text ~ matches nothing there, while GET /rest/api/2/search/jql also takes labels and id.

Release notes: Changelog

Last updated on