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
- In Jira, open Nexus tab → gear → Project Settings → CI.
- Create an API key and copy it. Nexus shows the key once.
- Copy the Nexus API URL, Results URL or CI API URL for the integration you are building.
- Store the URL and key in your CI system's secret store.
- Send the key in the
Authorizationheader. 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.
Upload JUnit, Cucumber, Playwright and other framework reports.
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
202with{"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.partialalso 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 samebuild, the upload is treated as the same one for 6 hours. Withoutbuild, an identical report sent more than 5 minutes after the first one finished counts as a new run. curl --retrydoes not resend a202. For large suites, use a packaged integration (they resendpartialfor 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 with400. The same rule applies toupdate-test-caseand 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 logsidempotent-hit, orin-progressif 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.
400withjob=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.partialagain and again: make sure every resend is the identical file with the identical parameters. A changedbuildorenvironmentstarts 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.