Nexus · test management for Jira
Menu
Get started

Nexus API reference

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

API v1

Use two project-specific endpoints: one receives framework result files; the other accepts bounded JSON operations for tests, cycles, runs, environments and test data.

Start here

  1. In Jira, open Nexus Project Settings → API Keys & CI.
  2. Create an API key and copy it. Nexus shows the key once.
  3. Copy the Results URL or CI API URL shown for the project.
  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, or a combination. Revoking one key does not affect the others. Nexus stores only a salted hash and never returns the key again.

HTTPS only. Both 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.

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 Ingestion log and Unmatched results on Project Settings → API Keys & 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 has a loop example.

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 dispatched. Body: {"status":"accepted"}. Check the Ingestion log for result-level outcomes.
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.

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 → API Keys & CI, and a key with the runner scope.

  • Runner automation is a Beta feature. It is off by default for a project with no automation history; turn it on 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 Ingestion log. Never send an API key.