--- title: "External Issue Linking - API" slug: "external-issue-linking" updated: 2026-08-18T22:48:56Z published: 2026-08-18T22:48:56Z canonical: "help.nucleussec.com/external-issue-linking" --- > ## Documentation Index > Fetch the complete documentation index at: https://help.nucleussec.com/llms.txt > Use this file to discover all available pages before exploring further. # External Issue Linking - API ## 1. Before you begin ### 1.1 What this guide covers Using the External Issue Linking API to bring tickets from another system - Jira, ServiceNow, or any tool with a ticket URL - into Nucleus and attach them to the findings they cover. ### 1.2 When to use this API This API is designed to give you full control over how tickets and findings are connected in Nucleus, independent of what Nucleus supports out of the box. If your ticketing process does not fit cleanly into a built-in connector or grouping option, this is the tool to reach for. Common examples: - Onboarding with existing ticket history. Import the tickets your teams have already been working in another tool so they show up on the matching findings from day one. - Custom ticketing integrations. Wire up a ticketing system Nucleus does not have a connector for, or layer workflow logic on top of a system it does. - Custom ticket-to-finding grouping. Group finding instances under a single ticket in whatever way your process requires, beyond the grouping options Nucleus provides natively. If you instead want Nucleus to create tickets upstream, or to keep a ticket in sync with its upstream counterpart (upstream auto-close in Nucleus, comments flow, etc.), use a connector-based ticketing integration. ### 1.3 What you'll need - A Nucleus API key with access to the target project. - The `create-external-issues` permission on that project (or org-admin rights). - The Nucleus `project_id` for the project you're importing into. - Ticket data from your source system, at minimum, a URL and a key (e.g. OPS-1234 ) per ticket. - An HTTP client you're comfortable with. All examples use JSON. ### 1.4 Key terms External ticket - A ticket that lives in another system. This API brings a reference to it into Nucleus; the ticket itself stays upstream. API-link ticket - What Nucleus calls a ticket record created through this API. Section 8 covers how they behave differently from other tickets in Nucleus. --- ## 2. How it works at a glance ### 2.1 The five endpoints The API has five endpoints, grouped by purpose: | Purpose | Method | Endpoint | | --- | --- | --- | | Register tickets | POST | `/api/projects/{project_id}/issues/bulk ` | | Link tickets to findings | POST | `/api/projects/{project_id}/issues/link/bulk ` | | Update ticket details | PUT | `/api/projects/{project_id}/issues/bulk ` | | Unlink instances from a ticket | POST | `/api/projects/{project_id}/issues/unlink/bulk ` | | Delete tickets | POST | `/api/projects/{project_id}/issues/delete/bulk ` | All five accept an array of items in one call, up to 1000 rows per request. ### 2.2 The register, link, surface flow Getting an external ticket to appear on a Nucleus finding is a two-step process: 1. Register the ticket. You give Nucleus the ticket's URL and key, and Nucleus returns an `external_issue_id`. At this point the ticket exists in Nucleus but is not yet visible in the UI. 2. Link the ticket to one or more finding instances using the `external_issue_id` from step 1. Once linked, the ticket surfaces on Finding Details, All Vulnerabilities, and the Tickets page. Updates, unlinks, and deletes all reference the `external_issue_id` from step 1, so hold onto it. ### 2.3 Async job model: submit, poll, read results Every endpoint runs asynchronously: 1. You submit the request. Nucleus responds with 202 Accepted and a job_id . 2. You poll `GET /api/projects/{project_id}/jobs/{job_id}` until the job finishes. 3. The completed job returns a per-row result for every item you submitted, each marked success: true or success: false with an error message. Row-level failures do not fail the job. As long as the job ran, the poll returns `200` , and your integration can inspect each row individually to see what succeeded. --- ## 3. Registering tickets Registering tells Nucleus "this external ticket exists." It does not attach the ticket to anything yet, but it returns the `external_issue_id` your integration will need for every step after this one. ### 3.1 Building the request Your integration sends a POST to `/api/projects/{project_id}/issues/bulk` with an array of tickets. Each ticket requires `issue_url` and `issue_key`. Everything else is optional but recommended if you have it, since these values are what your users will see in Nucleus. ```json [  {    "issue_url":   "https://jira.example.com/browse/OPS-1234",    "issue_key":   "OPS-1234",    "title":       "Patch OpenSSL on prod fleet",    "description": "Rolling out CVE-2024-12345 remediation this sprint.",    "priority":    "High",    "assignee":    "jsmith",    "status":      "In Progress"  } ] ``` You can submit up to 1000 tickets in a single request. ### 3.2 Submitting and getting your external_issue_id The call returns `202 Accepted` with a `job_id` . Your integration should poll `GET /api/projects/{project_id}/jobs/{job_id}` until the job completes, then read the per-row results: ```json {  "success": true,  "external_issue_id": 1003421,  "issue_url": "https://jira.example.com/browse/OPS-1234",  "issue_key": "OPS-1234" } ``` The `external_issue_id` is the identifier used for every subsequent link, update, and delete call for this ticket. Persist it in your integration's system of record alongside the source ticket. Re-posting the same `issue_url` + `issue_key` will return the same `external_issue_id` (see below), but relying on that as a lookup mechanism is slower and less reliable than storing it once at registration time. If you re-submit the same `issue_url` with the same `issue_key` , Nucleus returns the existing `external_issue_id` and does not create a duplicate. If you re-submit the same URL with a different key, that row fails with an error and the existing ticket is left untouched. ### 3.3 Why the ticket isn't visible yet A ticket that has been registered but not linked to any finding is invisible in the Nucleus UI. It won't show up on the Tickets page, on Finding Details, or on the All Vulnerabilities page. This is intentional: Nucleus surfaces tickets in the context of the findings they cover, and until you link one, there is no context to surface it in. --- ## 4. Linking tickets to findings Linking attaches a registered ticket to one or more finding instances. This is the step that makes tickets appear in Nucleus. ### 4.1 Identifying the finding instance (the 4-tuple) In Nucleus ticketing, a linked finding instance is uniquely identified by four values used together: | Field | What it is | | --- | --- | | scan_type | The scanner or source that produced the finding (for example, NESSUS, QUALYS). | | asset_id | The Nucleus asset ID the finding is on. | | finding_number | The finding identifier (for example, a CVE or plugin ID). | | finding_justification_key | The finding justification key. | The findings search endpoint returns all four fields on every result, so the recommended pattern is to query it, collect it and pass it into the link request. Endpoint: `POST /api/projects/{project_id}/findings/search` ### 4.2 Building and submitting the link request Your integration sends a POST to `/api/projects/{project_id}/issues/link/bulk` with an array. Each row combines an `external_issue_id` from section 3 with a 4-tuple from section 4.1: ```json [  {    "external_issue_id": 1003421,    "scan_type": "NESSUS",    "asset_id": 12345,    "finding_number": "CVE-2024-12345",    "finding_justification_key": "987654321"  } ] ``` What to Know: - One ticket, many instances. Linking a ticket to 50 instances is 50 rows referencing the same `external_issue_id` . - Re-linking is safe. A row that is already linked returns `success: true` and changes nothing, so batches can be retried without tracking which rows already succeeded. The response follows the async pattern from section 3: `202 Accepted` with a `job_id`, then your integration polls for per-row results. ### 4.3 Verifying the ticket appears in Nucleus Once the link job completes, the ticket surfaces everywhere a Nucleus generated ticket would appear, such as Vulnerabilities page or on the Tickets page. Ticket-count metrics and filters like `has_no_tickets` update accordingly. --- ## 5. Updating tickets Once a ticket is registered, use the update endpoint to change its header fields - title, description, priority, assignee, status, or the URL/key themselves. Only fields the caller explicitly sends are changed; anything omitted is left as-is. ### 5.1 Building and submitting the update Your integration sends a PUT to `/api/projects/{project_id}/issues/bulk` with an array. Each row must include the external_issue_id (which identifies the ticket) plus at least one mutable field: ```json [  {    "external_issue_id": 1003421,    "title":       "Patch OpenSSL on prod fleet - escalated",    "priority":    "Critical",    "assignee":    "asmith",    "status":      "In Review"  } ] ``` Mutable fields: `title` , `description` , `priority` , `assignee`, `status`, `issue_url`, `issue_key`. The `external_issue_id` itself is immutable. The response follows the async pattern from section 3: `202 Accepted` with a `job_id` , then your integration polls for per-row results. Re-sending the same payload is a no-op and returns success: true , so retries are safe. ### 5.2 Common update patterns - Keeping header fields in sync with your source system. When your integration observes a change upstream (assignee reassigned, priority raised, status transitioned), submit an update row with just the changed fields. There is no need to resend fields that haven't changed. - Moving a ticket to a different finding instance. Updates do not touch links. To move a ticket, unlink the wrong instance (section 6) and link the correct one (section 4). ### 5.3 What this endpoint will not do - It won't modify tickets that weren't created through this API. Rule-driven, connector-driven, and UI-manual tickets return a per-row error. This endpoint only operates on API-link tickets. - Status is opaque text. Updating `status` changes the label your users see in Nucleus but does not trigger any automation, close linked instances, or sync back to an upstream system. --- ## 6. Removing links and tickets Cleanup has two levels: unlink individual instances from a ticket, or delete the ticket entirely. Both follow the same async pattern as previous sections. ### 6.1 Unlinking specific instances Unlinking removes the connection between a ticket and one or more finding instances. The ticket header stays intact and can be re-linked or linked to different instances later. Your integration sends a POST to `/api/projects/{project_id}/issues/unlink/bulk` with an array. Each row is the same 4-tuple + `external_issue_id` shape used for linking: ```json [  {    "external_issue_id": 1003421,    "scan_type": "NESSUS",    "asset_id": 12345,    "finding_number": "CVE-2024-12345",    "finding_justification_key": "987654321"  } ] ``` Common use cases: moving a ticket to a different instance (unlink the wrong one, then link the correct one via section 4), or narrowing an over-inclusive link batch without discarding the ticket record. ### 6.2 Deleting tickets Deleting removes the ticket header and any remaining links atomically. Use this to retire tickets your integration no longer needs. Your integration sends a POST to `/api/projects/{project_id}/issues/delete/bulk` with an array of `external_issue_id` values: ```json [  { "external_issue_id": 1003421 },  { "external_issue_id": 1003422 } ] ``` ### 6.3 What these endpoints will not do Both endpoints only operate on API-link tickets. Attempts to unlink or delete rule-driven, connector-driven, or UI-manual tickets return a per-row error, and the ticket is left untouched. --- ## 7. How API-link tickets behave in Nucleus API-linked tickets are first-class ticket records - they appear in the UI, count toward metrics, and can be filtered on - but they are intentionally isolated from Nucleus's built-in ticketing automation at this time. This section is what integration owners need to know so their users aren't surprised. ### 7.1 Where they surface Once a ticket has at least one link, users will see it in these places: - **Vulnerability Details** (the finding detail window that opens from any findings grid): on the Tickets tab, alongside any other tickets on that finding. The tab title shows the total ticket count for the finding. - **Tickets** (the project-level page under the project menu): listed among the project's tickets with its title, key, URL, priority, assignee, and status. - **Active Findings** and **All Findings** (project-level findings pages): rows for linked instances reflect the ticket in their ticket-count column, and filters like "has no tickets" respect the link. - **Asset Details**: the finding rows for that asset show the same ticket presence as the findings pages. A ticket with no links is registered but hidden - see section 3.3. ### 7.2 What automation and connectors will not do to them API-link tickets are deliberately outside the scope of Nucleus's connector-driven ticket lifecycle. This is what keeps your integration authoritative: - **Ticketing automation rules do not create, update, or close them**. Rules only operate on tickets they created. - **Connector sync does not touch them**. Jira, ServiceNow, and other connectors will not overwrite the header fields, mutate status, or close the ticket when linked instances are remediated. - **Auto-close on remediation does not fire**. When every linked instance transitions to mitigated, Nucleus will not close the ticket. Your integration decides when to update `status` or delete the ticket. - **Webhooks and outbound notifications (bot comments) for ticket lifecycle events do not fire** for actions taken through these endpoints. Users can still see and interact with the ticket in the UI, but the source of truth for its fields is your external system and custom integration. ### 7.3 What the ticket detail view does and does not show On the Tickets tab and Tickets page, API-link tickets display the fields your integration submitted: title , description, priority, assignee, status, `issue_url` , and `issue_key`. The URL renders as a clickable link out to your source system. Because there is no connector behind them, the following are not populated: - Comments / activity history from the source system. Nucleus does not poll your ticketing system, so comments made in Jira or ServiceNow will not appear here. - Sync status indicators. There is no "last synced" timestamp because there is no sync. If your users need to see comments or full history, the intended path is the issue_url link out to the source system.