NEWS

Cisco MINT Partner! Learn more →

Automation Services
2026-09-18
11 min read

Turning a Ticket Into an Approval Step

Creating the ticket was easy. Knowing which approval belonged to which paused response was the real integration problem.

ServiceNow
Cisco XDR
SOAR
Approval Workflow

A customer asked us to connect Cisco XDR response automation to the approval process their operators already used in ServiceNow. They did not want a second approval inbox, and they did not want analysts switching to a workflow editor to understand what they were authorizing. XDR could collect evidence and prepare an action, but a person in ServiceNow had to decide whether that action could proceed.

The requirement included an important ordering rule: the ticket had to exist before the response, and approval had to occur before execution. Creating a ServiceNow record after an automated action would provide an audit trail, but it would not provide control. The delivered integration needed to stop at a visible boundary, wait as long as policy allowed, and continue only after a decision tied to that exact proposal.

Both platforms already had the basic capabilities. XDR could pause a workflow and receive an external webhook. ServiceNow could present an approval, apply access controls, and send a REST request when state changed. The challenge was preserving one coherent state across them without making either platform pretend to be the other.

ServiceNow approval loop: create the ticket, wait for approval, and resume the response safely.

A concurrent test revealed the weak assumption

Our first implementation created the ticket, stored its number in the workflow, and waited. The approval callback included that number and a decision. With one incident in the test environment, the path looked correct: approve the ServiceNow record, send the callback, and resume the XDR response.

Then we opened two approval requests at once and approved the second one first.

The ticket number was readable and unique in ServiceNow, but it was not a sufficient identity for the pending response in XDR. A ticket could survive retries, be reopened, or eventually contain more than one proposed action. Searching workflow runs by free text or assuming that the oldest waiting run belonged to the next callback would fail under ordinary concurrency. The integration needed an identifier created specifically for the handoff.

That discovery gave the collaboration a useful focus. The ServiceNow team described how operators read incidents, where approval state belonged, and which updates could trigger business logic. The XDR team described the paused execution, the context required by the response connector, and the states that had to remain durable while no workflow code was running. Together, we separated three concepts that the first draft had blurred: the human-facing ticket, the approval decision, and the pending response action.

ServiceNow would own the approval record and the identity of the approver. XDR would own response eligibility and execution. A generated correlation value would connect the two. The ticket number would remain important for people and reporting, but it would not be used as a substitute for workflow identity.

We designed the workflow as explicit blocks

The final XDR flow was deliberately readable. Its blocks received the source incident, normalized identifiers, enriched the evidence, classified the asset, prepared the proposed response, created or located the ServiceNow record, stored the returned references, and paused for the decision. After the callback, separate blocks authenticated and validated the request, looked up the waiting state, branched on approved, rejected, or expired, and allowed only the approved branch to reach the response connector. A reconciliation block wrote the result back to ServiceNow.

That block structure was more than presentation. It made the execution boundary testable. There was no route from ticket creation or the waiting state directly to response execution. The callback block did not accept a response command; it accepted a decision about a known proposal. Validation had to finish before the branch selected any action.

The XDR side used its real Request Approval task to represent the pause. The task held the workflow at a human decision point after evidence preparation and before response. For this project, the human interaction took place in ServiceNow. An approved or rejected state transition there produced the external callback that allowed the matching approval path in XDR to resolve. If no valid decision arrived before expiry, the response did not run.

The external return path used an XDR Webhook Rule. XDR generated the endpoint associated with that automation rule; ServiceNow called that endpoint when the relevant approval state changed. The generated URL was stored as protected integration configuration, not pasted into the ticket. Reaching the endpoint did not itself grant permission to execute. It entered the validation workflow, which had to find and consume an eligible pending record before resuming anything.

The ServiceNow record had a clear job

The ServiceNow participants helped shape the record around the questions an approver and the next shift would actually ask. The description summarized what had happened, the affected asset class, the evidence available at request time, the proposed response, and why a human decision was required. It stayed concise enough to review without opening XDR.

Changing operational detail went into work notes: enrichment updates, integration status, the received decision, response submission, verification, and any reconciliation error. Additional comments remained available for human conversation rather than machine state. This avoided making every analyst comment look like an instruction to the automation.

Structured fields held the values that systems needed to query reliably. Those included the correlation value, approval reference, current integration state, and relevant XDR reference. We did not bury them in the description or search work notes to recover them. The correlation value was generated when the response proposal became pending, stored with XDR’s durable state, and written to the ServiceNow record during creation.

The record was therefore the readable approval surface, not the workflow database. XDR retained the complete pending proposal, its expiry, and whether its decision had been consumed. ServiceNow retained the record history, approval state, and human attribution. Each system exposed enough shared identifiers to reconcile the handoff without duplicating all of the other system’s state.

We also agreed on a small state model. A new record could be pending approval. A rejected or expired request ended without response execution. An approved request permitted XDR to attempt the response, but approval did not mean the action had succeeded. After execution, the record could show accepted, verified successful, or failed and requiring follow-up. That distinction prevented an approved ServiceNow record from masking a downstream connector failure.

The outbound callback stayed narrow

A ServiceNow rule watched the approval state transition. It sent a POST only when the relevant request moved to an approved or rejected state. Changes to assignment, description, additional comments, or work notes did not trigger the callback. This was important operationally: routine ticket maintenance could not become an accidental response instruction.

The payload contained references, not secrets or a serialized incident:

{
  "correlation_id": "<paused-run-correlation>",
  "decision": "approved",
  "ticket": "<ticket-number>",
  "approval_reference": "<approval-reference>"
}

The callback URL and its authentication material lived in ServiceNow’s protected integration configuration or credential storage. No XDR client password appeared in a business rule, ticket field, work note, or approval message. ServiceNow authenticated its request to the configured return path, and the XDR-side flow treated successful authentication as the beginning of validation rather than the end.

The receiver checked the allowed message shape and decision values. It looked up the pending action by correlation value, verified the associated approval reference and ticket relationship, confirmed that the request was still waiting and not expired, and compared the stored proposal context with the context eligible to execute. An authenticated request with an unknown correlation value could not select a workflow by guesswork. A callback for a closed or superseded request stopped before the response branch.

This design also covered the manual nature of the decision. An approver worked in ServiceNow using the customer’s normal controls. The person did not call XDR or copy a token. Their approval changed the ServiceNow state; the integration generated the authenticated callback; the XDR Webhook Rule received it; and validation resumed the one matching paused Request Approval path. Human authority and machine transport remained separate and auditable.

Retry and replay safety belonged on both sides

We assumed that either direction could be retried. If ServiceNow ticket creation timed out, XDR could not know from the timeout alone whether the record existed. A blind retry might create a duplicate approval. The creation request therefore carried the same correlation value on every attempt. Before creating again, the integration looked for a record with that structured value. If one existed, it reused or updated it rather than opening a second request.

The callback had the mirror-image problem. ServiceNow could retry after a timeout even when XDR had already processed the decision. A malicious or accidental replay could deliver the same valid request later. XDR stored the pending decision as a one-time state transition. The first valid callback consumed it and recorded the resulting outcome. A repeat with the same correlation and approval reference returned that outcome without calling the response connector again.

We made consumption atomic because sequential tests can hide races. Two copies arriving together were not allowed to read “pending” and both proceed. A conflicting callback, such as rejection after approval had already been consumed, did not overwrite history. It was recorded as a mismatch for investigation.

Reporting was independently idempotent. If the response ran but the ServiceNow work-note update failed, the workflow retried the reporting operation, not the response action. The existing correlation and execution reference let reconciliation update the right ticket later. A presentation failure could not turn into a second containment attempt.

The joint test plan followed the failure paths

The teams tested the ordinary story first. XDR prepared a proposal and created one ServiceNow record. The approver could read the evidence and requested action in that record. Approval emitted the callback, the XDR endpoint accepted and validated it, the correct waiting run resumed, and the test response connector was called once. The result then appeared in ServiceNow as a separate execution state.

Next came the concurrency case that had exposed the initial design. We opened two pending approvals and decided them in reverse order. Each callback contained its own correlation value, and each resumed only its matching run. We repeated the exercise with two proposals related to the same source incident so that the ticket context alone could not accidentally make the test pass.

We delivered the identical approved callback twice and confirmed a single response invocation. We delivered two copies concurrently with the same result. We sent a validly authenticated request with an unknown correlation value, the correct ticket with the wrong correlation value, an unsupported decision, and an approval reference that did not match the pending state. None reached execution.

The ServiceNow team tested its side of the boundary. Editing work notes and assignment did not send callbacks. Rejecting a request closed the response path without being reported as an automation error. Closing or cancelling the record before approval prevented later continuation. A callback arriving after expiry could not reopen the action.

We forced a timeout during ticket creation and verified that retry found the record by correlation value instead of creating another. We forced a timeout after callback delivery and verified that ServiceNow’s retry received the recorded result while XDR’s connector count stayed at one. We failed the final work-note update and confirmed that reconciliation repaired reporting without repeating execution.

Finally, we changed incident context while a request was pending. When new enrichment changed the target classification or made the proposed response obsolete, the previous approval no longer applied. XDR retained the pause, ServiceNow showed that a new decision was needed, and the workflow created a proposal tied to the updated context. The test demonstrated that an approval authorized what the person had reviewed, not whatever happened to be current later.

The delivered outcome respected both platforms

At handoff, the customer had one approval experience for operators and one authoritative response engine. ServiceNow showed the request, evidence, decision history, and execution updates in the place the team already worked. XDR retained control of whether a response was pending, eligible, consumed, executed, or in need of reconciliation. The correlation value provided a precise bridge without turning either system into a loose search index for the other.

The workflow could be explained block by block: receive and enrich, prepare the proposal, create or find the ticket, store shared references, pause at Request Approval, receive the ServiceNow callback through the XDR-generated Webhook Rule endpoint, authenticate and correlate it, branch by decision, execute only after approval, and reconcile the result. That sequence also gave auditors a clearer account than a single status called “approved.”

Most importantly, the ticket was no longer evidence of an action that automation had already taken. It was the actual human boundary. Authentication established the callback’s sender. Correlation selected the exact pending proposal. Expiry and context checks preserved the meaning of the decision. One-time consumption made retries and replay safe. Separate execution and reporting states showed what happened after approval without spending that approval twice.

The anonymized diagram retained with this story shows only that controlled loop. Real ticket numbers, approver identities, callback URLs, account labels, incident identifiers, and target details remain excluded. The useful result is the shape of the delivery: ServiceNow owned the human decision, XDR owned the response, and a small authenticated callback connected them without weakening either side.

ABOUT THE AUTHOR

Technoxi Security Engineering

Security Automation Team

We connect detection, case management, and response without hiding uncertainty.