Nexus · test management for Jira
Menu
Get Nexus

Nexus API reference

Connect test automation, CI pipelines and deployment systems to the quality record inside Jira.

API v1

Use the scoped Nexus API v1 to read and update project test records, upload framework results, or send bounded CI operations.

Start here

  1. In Jira, open Nexus tab → gear → Project Settings → CI.
  2. Create an API key and copy it. Nexus shows the key once.
  3. Copy the Nexus API URL, Results URL or CI API URL for the integration you are building.
  4. Store the URL and key in your CI system's secret store.
  5. Send the key in the Authorization header. Never put it in a URL or pipeline file.
Authorization: Bearer <NEXUS_API_KEY>

Keys belong to one Jira project, and a project can have up to 10 active keys. A project administrator can restrict a key to sending results, using the CI API, using the automation runner, reading test data, or a combination. Read access is a separate opt-in permission, off by default and never added to existing keys. Revoking one key does not affect the others. Nexus stores only a salted hash and never returns the key again.

HTTPS only. Nexus endpoint URLs are https:// addresses on Atlassian's platform. Send the key only over HTTPS, and never let a client follow a redirect with the key attached (with curl, leave out -L). The Nexus reporters, the nexus-upload CLI, the GitHub Action and the automation runner refuse a plain http:// Nexus URL before sending anything, and they never follow redirects. The one exception is for local tests: NEXUS_ALLOW_INSECURE_LOCALHOST=1 allows http to localhost, 127.0.0.1 or [::1] only.

POST Results URL

Upload JUnit, Cucumber, Playwright and other framework reports.

POST CI API URL

Send one or more JSON operations, with a maximum work budget of 25.

Nexus API v1

The Nexus API URL on the project's API keys screen is the installation-specific base URL. Append the documented /v1/ path. It is separate from the Results URL and CI API URL; do not guess an endpoint from your Jira site address.

The Nexus API v1 OpenAPI reference describes each route, request, response and required scope. It includes test cases, versions, branches, steps and typed step fields; reusable test sets; cycles, runs and step results; Test Plans and dynamic membership; shared preconditions; coverage; data sets; Cucumber feature sync; and stored Playwright scripts. Features switched off by an administrator remain unavailable through the API.

Choose the smallest scopes

An administrator grants scopes on a project API key. Read access is opt-in. Enable only the permissions your integration needs:

Scope Access
read Read the project's non-restricted test records
datasets:read Additional permission for reading data sets
cases:write Create and update test cases and their designs
cycles:write Manage cycles and reusable test sets
plans:write Manage Test Plans
results:write Record run and step results
preconditions:write Manage shared preconditions
datasets:write Manage eligible data sets

Write permissions require an explicit administrator choice. Records protected by Jira issue security are excluded from reads. Sensitive values are masked, and sensitive data sets cannot be written through this API. Review key usage in the project's Nexus API log and revoke or narrow a key when it is no longer needed.

Read a page of test cases

Copy the Nexus API URL into NEXUS_API_URL and keep the key in your secret store:

curl --fail --silent --show-error \
  "$NEXUS_API_URL/v1/test-cases?project=ND&limit=50" \
  -H "Authorization: Bearer $NEXUS_API_KEY"

Replace ND with your Jira project key; it must match the API key's project. Follow the returned pagination cursor using the operation's documented parameters. Each key belongs to one project. Read limits are 60 requests a minute and 2,000 an hour per key; honour Retry-After when throttled.

Write safely

Follow the OpenAPI contract for each operation, including its project parameter and operation-specific headers. Use idempotency keys where documented to retry without creating duplicate work. Step and feature updates use If-Match revisions: a missing revision returns 428, and a stale revision returns 412. Read the current record and reconcile changes before retrying. Bulk requests have operation-specific limits; split larger jobs into bounded requests.

Responses include Nexus-Api-Version and Nexus-Request-Id. Record the request ID when asking for support. Within v1, integrations should tolerate additional optional response fields. The original query-based read API below remains available for existing integrations; use v1 for new integrations.

Read test data (legacy query API)

A project administrator enables Read test data on a key and copies the Read API URL from the API keys screen. The key can read only its own project's test cases, cycles, runs, step results and coverage as paged JSON.

curl --fail --silent --show-error \
  "$NEXUS_READ_URL?project=ND&op=testCases&limit=50" \
  -H "Authorization: Bearer $NEXUS_API_KEY"

Available operations are testCases, testCase, cycles, cycle, runs, runSteps and coverage. Use limit (1–100) and the returned nextCursor to request the next page with cursor. See the Read API OpenAPI file for operation parameters and responses.

Records with Jira issue security are never returned. Sensitive test-data values are always masked, and people appear only as Atlassian account IDs. Each key is limited to 60 requests a minute and 2,000 an hour. Store the URL and key in your secret store, just as you do for CI uploads.

Send automated results

Post the result file as the request body. Nexus detects the format when possible and matches each result to the Test Case whose Automation Key starts the test name. Unknown keys are reported as unmatched; Nexus never guesses.

curl --fail --silent --show-error -X POST \
  "$NEXUS_RESULTS_URL?project=NEX&environment=staging&build=4821&commit=a1b2c3d" \
  -H "Authorization: Bearer $NEXUS_API_KEY" \
  -H "Content-Type: application/xml" \
  --data-binary @results.xml

Supported formats

format value Common producers
junit-xml JUnit, PyTest, NUnit, TestNG, Robot Framework, Playwright JUnit reporter
cucumber-json Cucumber
playwright-json Playwright JSON reporter
mocha-json Mocha
mochawesome-json Mochawesome
xunit-xml xUnit.net v2
robot-xml Robot Framework native output
nunit-xml NUnit native output
testng-xml TestNG native output
newman-json Postman collections run with Newman (-r json)
json Nexus plain JSON format

For Newman, each request is one result. A failed assertion, a script error or a request that could not be sent is a failure. A request whose tests were all skipped is Blocked.

Result query parameters

Parameter Purpose
project Required Jira project key. It must match the API key's project.
environment Environment name or configured alias such as stg.
build Build or pipeline identifier.
commit Source revision.
component Component or service under test.
testSet Record results against a particular execution cycle.
configuration Nexus Test Configuration id.
dataSet Dataset id for results named with a row key such as login [cust-3].
format Explicit parser when detection is not suitable.
systemOut comment posts each JUnit test's own <system-out> as a plain-text comment on its run, cut to 8,000 characters, for at most 50 results an upload. The comment mentions and notifies no one. Off by default; JUnit XML only.
stepMatching off records one overall result per Test Case and leaves its design steps unmarked. By default (on), the overall outcome is spread over the case's steps.
job Automation-runner job id. The supplied Nexus runner sets this value; don't set it yourself. A job id that is malformed, unknown, expired, finished, or held by another runner key is refused with 400. It never falls back to ordinary ingestion.

The Results endpoint accepts up to 5 MB and 120 requests a minute per project. It responds with 202, 400, 401, 413 or 500 and a small JSON status body; see Responses. Result-level detail and unmatched names appear in the Delivery log and Unmatched results on Project Settings → CI.

Resending and large uploads

Resending the same upload is always safe. Nexus fingerprints each upload: the file plus the parameters that change what it does (project, format, testSet, configuration, environment, build, commit, component, dataSet, job, systemOut). It never records a result of the same upload twice.

  • Nexus processes up to 500 results per request within the platform's time limit. If it runs out of time part-way, it answers 202 with {"status":"partial","reason":"time budget reached; send the same upload again to continue"}. Send the same file with the same parameters again, and repeat until the answer is {"status":"accepted"}. Each resend carries on where the last one stopped. partial also means another send of the same upload is still running.
  • Parallel shards that post results for the same Test Case land on one Test Execution.
  • To record a deliberate re-run of an identical report, change build (for example, a new pipeline number). With the same build, the upload is treated as the same one for 6 hours. Without build, an identical report sent more than 5 minutes after the first one finished counts as a new run.
  • curl --retry does not resend a 202. For large suites, use a packaged integration (they resend partial for you) or loop. The CI guide shows the packaged upload step.

Use the CI API

The CI API receives one JSON object containing an operations array. Each operation's top-level fields use a strict allow-list. An unknown operation field or invalid shape rejects the whole request before work starts.

curl --fail --silent --show-error -X POST \
  "$NEXUS_CI_API_URL?project=NEX" \
  -H "Authorization: Bearer $NEXUS_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
    "operations": [
      {
        "op": "create-test-case",
        "summary": "Login with valid credentials",
        "automationKey": "AUTH-LOGIN-01",
        "externalKey": "build-4821-login"
      },
      {
        "op": "report-result",
        "automationKey": "AUTH-LOGIN-01",
        "status": "passed",
        "environment": "staging"
      }
    ]
  }'

Operations

Operation Required fields Purpose
create-test-case summary Create a Jira Test Case, optionally with steps, preconditions and an Automation Key.
update-test-case testCaseKey Replace supplied design fields on an existing Test Case.
add-to-cycle testSetKey, testCaseKey Add one Test Case to an execution cycle.
add-cases-to-cycle testSetKey, testCaseKeys Add up to 25 Test Cases and log an outcome for each case.
set-run-assignee runKey, accountId Assign a Test Execution, or use null to unassign it.
report-result automationKey, status Record one automated result using the same execution path as file ingestion.
record-deployment environment, version, status, externalId Record an idempotent deployment.
record-environment-event environment, type, externalId Record an outage, refresh or other environment event.
record-configuration environment, entries, externalId Capture an environment configuration snapshot.
reserve-data-rows execution, dataSet, externalId Reserve consumable test-data rows for a pipeline.
consume-data-rows execution, dataSet, externalId Mark reserved rows as used.
release-data-rows execution, dataSet, externalId Return reserved rows to the available pool.

Test design operations

create-test-case accepts:

  • summary: required, up to 255 characters.
  • steps: up to 100 { action, data, expected } rows; each cell is bounded to 4,000 characters.
  • preconditions: up to 4,000 characters.
  • automationKey: up to 200 characters. Use only letters, digits and _ . : @ / -, with no spaces, and don't start it with -. Any other key fails the whole request with 400. The same rule applies to update-test-case and to Jira's own edit screens.
  • externalKey: up to 200 characters. Reusing it makes retries idempotent. This holds even when two deliveries of the same batch arrive at the same time: only one creates the Test Case, and the other logs idempotent-hit, or in-progress if the first is still creating it.

update-test-case accepts testCaseKey plus steps, preconditions or automationKey. When supplied, steps replaces the complete step table; it does not merge rows.

{
  "operations": [
    {
      "op": "create-test-case",
      "summary": "Customer completes checkout",
      "automationKey": "CHECKOUT-01",
      "externalKey": "catalog-checkout-v3",
      "preconditions": "A customer has one product in the basket",
      "steps": [
        {
          "action": "Submit the saved delivery address",
          "data": "Standard delivery",
          "expected": "The payment step opens"
        }
      ]
    }
  ]
}

Cycle and run operations

add-cases-to-cycle.testCaseKeys contains 1–25 unique Jira issue keys. Each key counts as one unit of the request's 25-operation work budget. Obsolete Test Cases and closed cycles are refused without changing Jira.

set-run-assignee.accountId is an Atlassian account id or null. The run must be a Test Execution in the authenticated project and its cycle must be open.

report-result.status is one of passed, failed, skipped or blocked. Optional fields are actualResult, environment, testSetKey and configurationId.

Deployments and environment evidence

{
  "operations": [
    {
      "op": "record-deployment",
      "environment": "staging",
      "version": "2.5.0",
      "status": "success",
      "externalId": "deploy-4821",
      "build": "4821",
      "commit": "a1b2c3d",
      "component": "checkout-api"
    },
    {
      "op": "record-environment-event",
      "environment": "staging",
      "type": "refresh",
      "invalidatesResults": true,
      "externalId": "refresh-2026-09-27",
      "note": "Synthetic customer accounts refreshed"
    }
  ]
}

Deployment status is success, failed or rolled_back. Optional fields are release, component, build, commit, deployedAt and evidenceUrl. Use an ISO-8601 timestamp for deployedAt; it cannot be more than five minutes in the future. evidenceUrl, when supplied, must use HTTPS.

Environment-event type is refresh, outage_start, outage_end, config_change or health. A health event requires healthState, which is healthy, degraded, down or unknown; other event types must omit healthState. Other optional fields are invalidatesResults, occurredAt and note. occurredAt is an ISO-8601 timestamp and cannot be more than five minutes in the future.

record-configuration.entries is a string-to-string object with up to 100 entries. Keys contain up to 64 letters, numbers, dots, underscores, colons, slashes or hyphens, and must begin with a letter or number. Values contain 1–256 printable, single-line characters. Optional fields are merge, capturedAt and note. capturedAt is an ISO-8601 timestamp and cannot be more than five minutes in the future. A merge requires at least one entry. Nexus rejects keys or values that look like secrets.

Consumable test data

Use the same execution and externalId across reserve, consume and release operations. execution must be a Test Execution in the authenticated project. A reservation belongs to that execution and externalId: another pipeline or execution cannot consume or release its rows, and results posted to the Results URL use up only reservations made for the same Test Execution. A reservation requires exactly one of count (1–200) or rowKeys (up to 200). environment is optional and selects the correct pool for an environment-scoped dataset.

Retrying a reservation with the same externalId renews the rows the pipeline still holds. It does not take back rows whose lease already ran out. Those can be reserved again only if nobody else has reserved them in the meantime.

{
  "operations": [
    {
      "op": "reserve-data-rows",
      "execution": "NEX-246",
      "dataSet": "4ed07d8c-4418-4c3a-8a42-9b11cdd8d4cb",
      "externalId": "build-4821",
      "count": 5,
      "environment": "staging"
    }
  ]
}

Limits and processing

Limit Value
CI API request body 512 KB
Work budget per request 25 operations
Concurrent operation workers 3
Stored CI operation history Up to 100 batches per project
Result-ingestion body 5 MB

Operations run independently after request validation. One operation failing does not stop the remaining valid operations. The HTTP response confirms only that the batch was accepted. Nexus records each operation's outcome (ok, idempotent-hit or failed with a short failure code) in its CI operation log for the project. A project admin reads it in Project Settings → CI → CI API log: the last 100 calls, newest first, with each operation's outcome, failure code, externalKey and the Test Case it created or updated, and a failures-only filter. The failure codes include in-progress (another delivery of the same externalKey is still creating the Test Case; retry later), reconcile-uncertain and idempotency-unavailable. With these last two, nothing was created; retry later.

externalKey and externalId make retried writes idempotent. The endpoint's responses are fixed and cannot return created Jira issue keys. When a pipeline must find a Test Case it created, use your own Jira REST credentials. Every Test Case created with an externalKey carries it in the ciExternalKey issue property from the moment it exists. The property can't be searched with JQL. Search the project's Test Cases with properties=ciExternalKey and match the value that comes back.

Responses and troubleshooting

Status Meaning What to do
202 Accepted The request was authenticated, validated and fully processed. Body: {"status":"accepted"}. Check the Delivery log for result-level outcomes. {"status":"partial"} means send the same upload again.
202 Accepted (partial, Results URL only) Nexus kept what it recorded but has not finished this upload, because its time limit ran out or another send of the same upload is still running. Body: {"status":"partial","reason":"time budget reached; send the same upload again to continue"}. Send the same file with the same parameters again until the answer is accepted. See Resending and large uploads.
400 Bad Request JSON, report format, query parameters or an operation shape is invalid. Body: {"status":"rejected","reason":"invalid payload"}. Correct the request before retrying.
401 Unauthorized The key is missing, invalid, revoked or belongs to another project. Body: {"status":"rejected","reason":"unauthorized"}. Check the secret and project key.
413 Payload Too Large The byte limit, parsed-result limit, operation work budget or per-project rate guard was exceeded. Body: {"status":"rejected","reason":"payload too large"}. Split oversized payloads or batches. If the request rate is high, wait and retry with bounded backoff.
500 Internal Server Error Nexus could not accept the request. Body: {"status":"error"}. Retry with bounded backoff, then include the timestamp when contacting support.
400 for a method other than POST The Results URL and CI API URL are static web triggers: they answer a GET or PUT with the same 400 body, and cannot send 405 or Allow. Send POST.
Rate limit Answered as 413 payload too large (a dedicated 429 needs a manifest change and waits for the next major version). The runner endpoint (a dynamic trigger) answers a real 429 {"error":"rate-limited"}, 503 {"error":"busy"} with Retry-After when Forge storage is busy, and 405 with Allow: POST for a wrong method, always as application/json. Wait and retry with bounded backoff.
413 or 500 with a body that is not Nexus's A body the Forge platform itself refuses (over its request-payload limit) is answered by Atlassian with Atlassian's own status and body. The app's own limit is 5 MiB (5,242,880 bytes) and answers the 413 above. Keep uploads under 5 MiB; split larger ones. Retry a bare 500 with backoff.

Common result-ingestion problems:

  • Unmatched test: put the Test Case's Automation Key at the start of the test name.
  • Unknown environment: use the configured environment name or alias.
  • No result after a failed test job: make the upload step run even when tests fail.
  • Accepted CI API batch with a failed operation: the HTTP response cannot carry per-operation detail. Check the result in Jira, or contact support with the request time.
  • 400 with job= set: the runner job has finished, expired or belongs to another runner key. Let the runner queue the work again; don't reuse an old job id.
  • partial again and again: make sure every resend is the identical file with the identical parameters. A changed build or environment starts a new upload.

Automation runner

The customer-hosted nexus-runner (download it from the Integrations page) takes automation runs queued from a cycle and runs the tests on your own machine or CI agent. Tests never run inside Jira or Forge. The runner uses a third project URL, shown on Project Settings → CI, and a key with the runner scope.

  • Runner automation is on by default for every project. A project admin can turn it off in Project Settings → Modules → Runner automation; while it is off, the runner URL refuses the project. Posting results from CI (the two URLs above) keeps working either way.
  • The runner posts its results to the Results URL with job=<id>, using the same API key that claimed the job. Heartbeats and completion from any other key are refused (409).
  • Nexus never sends a Test Case whose Automation Key breaks the rule above (letters, digits and _ . : @ / -) to a runner. Those runs are flagged instead.
  • The runner's own API is managed by the runner and not listed here. Keep the runner up to date: newer Nexus releases can require a minimum runner version.

Compatibility and support

Nexus may add optional fields and new operation kinds without breaking existing clients. Removing or renaming a published field, operation or response status requires a documented major-version change.

For copy-paste GitHub Actions, GitLab CI, Jenkins, Azure Pipelines and Bitbucket examples, use the CI guide. For reporter downloads and the Nexus automation runner, use the Integrations page.

For help, email nexus-support@resync.co.nz with the Jira site, project key, UTC/NZ timestamp and, for a results upload, the reason shown in the Delivery log. Never send an API key.