Skip to content

Replay trigger Open API ​

The endpoints, fields and error codes used by automatic replays and result notifications. For wiring them into a pipeline, see Replay after deployment; for setting up notifications, see Replay notifications.

No authentication — internal network only

These endpoints have no authentication. Anyone who can reach the backend can trigger replays and change notification channels, so only allow internal access and never expose them to the internet. They're available on self-hosted deployments only. On All-in-One, set SP_REPLAY_OPENAPI=true in the environment and restart; until then, requests get a 404, a 405 or an HTML page.

Endpoints ​

All paths are relative to the backend, for example http://sp-backend.internal:8090/openapi/v1/replay-triggers.

MethodPathPurpose
POST/openapi/v1/replay-triggersTrigger a replay
GET/openapi/v1/replay-runs/{planId}Get progress and findings
GET/openapi/v1/replay-runs/{planId}/diagnosisRead the stored result and deployment details
POST/openapi/v1/replay-runs/{planId}/diagnosisUsed by SoftProbe's analysis service to write back results; you don't need to call it
GET/openapi/v1/notification-channelsList notification channels
POST/openapi/v1/notification-channelsCreate or update a channel
DELETE/openapi/v1/notification-channels/{id}Delete a channel
POST/openapi/v1/notification-channels/{id}/testSend a sample card to a channel

Requests and responses are JSON. Business errors still return HTTP 200, with errorCode and errorMessage explaining why; a null errorCode means success. Network and gateway errors use HTTP status codes as usual.

Trigger a replay ​

POST /openapi/v1/replay-triggers

Returns immediately; the replay runs in the background.

FieldTypeRequiredDefaultDescription
appIdstringYesThe application's appId
targetEnvstringYesURL of the service under test, including http:// or https://
operationsstring[]NoWhole applicationReplay only these endpoints, by path as shown in the recording list, for example /order/create. Omit it or pass [] to replay the whole application
caseSourcestringNorollingrolling uses recent recordings; pinned uses pinned cases
caseSourceHoursintegerNo24How many hours of recordings to use; must be positive. Doesn't apply to pinned
caseCountLimitintegerNoServer settingMaximum number of cases to replay per endpoint. A server-side setting may override it; the replay result shows the actual count
caseTagsobjectNoFilter cases by recording tags, for example {"env": "prod"}. Tags are the raw values the agent reported: -Dsp.tags.env=prod becomes {"env": "prod"}
enableMockbooleanNotrueWith false, downstream calls are made for real instead of returning recorded data
passThresholdnumberNoPass-rate threshold between 0 and 1. Only affects verdict, never findings.state
attributesobjectNoDetails about this deployment, stored as-is; the keys below appear on notification cards

attributes keys follow the OpenTelemetry semantic conventions. Notification cards use these:

KeyShown on the card as
service.nameThe application name in the card title; defaults to the appId
deployment.environment.nameEnvironment
cicd.pipeline.name, cicd.pipeline.run.id, cicd.pipeline.run.url.fullPipeline, clickable when a URL is given
vcs.ref.head.nameBranch
vcs.ref.head.revisionCommit, first 7 characters

passThreshold and attributes are passed once, at trigger time, and can't be changed afterwards. attributes can be read back through Read the stored result; passThreshold is never returned by any query.

Example request:

bash
curl -X POST http://sp-backend.internal:8090/openapi/v1/replay-triggers \
  -H 'Content-Type: application/json' \
  -d '{
    "appId": "order-service",
    "targetEnv": "http://order-service.test:8080",
    "operations": ["/order/create", "/order/pay"],
    "caseTags": {"env": "prod"},
    "attributes": {
      "deployment.environment.name": "test",
      "vcs.ref.head.name": "release/2026-10",
      "vcs.ref.head.revision": "9c3e1f2",
      "cicd.pipeline.run.id": "1643",
      "cicd.pipeline.run.url.full": "https://jenkins.example.com/job/order/1643/"
    }
  }'

Success:

json
{
  "planId": "6abb8559e5eb34767296c557",
  "statusUrl": "/openapi/v1/replay-runs/6abb8559e5eb34767296c557",
  "errorCode": null,
  "errorMessage": null
}

Failure:

json
{
  "planId": null,
  "statusUrl": null,
  "errorCode": "UNKNOWN_OPERATION",
  "errorMessage": "these operations are not registered under appId=order-service: [/order/cancel] ..."
}

Error codes ​

errorCodeCause
MISSING_APP_IDappId is missing
MISSING_TARGET_ENVtargetEnv is missing
INVALID_CASE_SOURCE_HOURScaseSourceHours isn't a positive number
INVALID_CASE_SOURCEcaseSource isn't rolling or pinned
APP_NOT_REGISTEREDoperations was given, but the appId has no endpoints at all: the appId is wrong, or the application hasn't recorded traffic yet
UNKNOWN_OPERATIONoperations contains paths that were never recorded; errorMessage lists them
EMPTY_OPERATIONSoperations was given but contains only blank strings. To replay the whole application, leave the field out
REASON_<n>Creating the replay plan failed. Common values: 1 another replay is being created for the same application, try again later; 101 no endpoints found for the application; 200 no cases recorded in that time range
PLAN_RUNNING_<n>Creating the replay plan was refused; see errorMessage
CREATE_PLAN_FAILEDCreating the replay plan failed; see errorMessage
INTERNAL_ERRORServer error; check the server log

Get progress and findings ​

GET /openapi/v1/replay-runs/{planId}

Works for replays started from the web console too; those just don't send notifications.

FieldDescription
statusPENDING (not started), RUNNING, COMPLETED (finished); UNKNOWN when the planId doesn't exist
progressProgress, 0 to 1
verdictPASS or FAIL, based on the pass rate; see below
passRatePass rate, 0 to 1
totalCases, successCases, failedCasesTotal, passed and failed cases
reportUrlThe replay's report page
findingsThe replay findings; pipelines decide on its state, see below
analysisSummary of the AI cause analysis, see below
errorCodeNOT_FOUND when the planId doesn't exist
errorMessageError details; may also explain why a replay didn't finish normally

Until the replay finishes, only status and progress are set. A finished response, with some fields removed:

json
{
  "status": "COMPLETED",
  "progress": 1.0,
  "verdict": "FAIL",
  "passRate": 0.82,
  "totalCases": 50,
  "successCases": 41,
  "failedCases": 9,
  "reportUrl": "http://softprobe.internal/sp/workbench/order-service/runs/6aba9d1fe5eb34767296c3f9",
  "errorCode": null,
  "errorMessage": null,
  "findings": {
    "state": "NEEDS_ACTION",
    "scope": {
      "interfacesInScope": 3,
      "interfacesReplayed": 3,
      "interfacesUnchanged": 2,
      "requests": 50,
      "requestsWithoutResult": 0
    },
    "needsActionInterfaces": 1,
    "needsAction": [
      {
        "kind": "FIELD_VALUE",
        "subjects": ["payable"],
        "interfaces": [
          {"operationName": "/order/price", "affectedRequests": 9, "totalRequests": 22}
        ],
        "affectedRequests": 9,
        "totalRequests": 22
      }
    ],
    "toReviewInterfaces": 0,
    "toReview": [],
    "coverage": {"uncoveredInterfaces": 0, "mainOperationsConfigured": false, "uncoveredMainOperations": []},
    "excludedFieldCount": 3
  },
  "analysis": {
    "state": "DONE",
    "analyzedCases": 9,
    "failedCases": 9,
    "codeChangeCases": 9,
    "undeterminedCases": 0,
    "invalidCases": 0,
    "codeChanges": [
      {
        "shortTitle": "会员价改为向下取整,应付少 1 元",
        "operations": ["/order/price"],
        "affectedCases": 9,
        "location": {"file": "src/main/java/demo/PricingService.java", "line": 12},
        "consecutive": 8
      }
    ]
  }
}

findings.state ​

Evaluated from top to bottom; the first match wins:

OrderstateMeaning
1NO_CASESNo requests to replay
2INTERRUPTEDThe replay was interrupted or cancelled, or some requests produced no result
3ENVIRONMENT_FAILUREOne kind of failure (the same status code, or the same connection failure) hit more than half of the endpoints, and at least 3. Usually a test environment problem
4NEEDS_ACTIONThere are problems to fix
5REVIEW_ONLYThere are only differences for someone to review
6LOW_COVERAGENo problems found, but coverage is too low: with main endpoints registered, one of them wasn't replayed; without them, fewer than 10 endpoints or fewer than 30 requests were replayed
7CLEANVerified, no problems found

While the replay is running, the state is RUNNING. Only CLEAN counts as a pass.

Which differences need action and which need review:

GroupDifferences
Needs action (needsAction)Field value, presence, type or array length differs; many fields differ in one request; empty response; response format changed; fewer downstream calls; 5xx (except 504) or 404; a new sensitive field
To review (toReview)New fields; more downstream calls; unstructured content such as PDF or HTML differs; downstream request parameters differ; a 401, 403, 429, 504, timeout or connection failure on a few endpoints; differences that can't be classified; endpoints with failed cases but no difference details

Each endpoint counts only toward its most severe group. Field differences that AI noise reduction suggested ignoring, and that nobody reverted, move down from "needs action" to "to review" — except new sensitive fields. Differences AI noise reduction already ignored automatically go through ignore rules and aren't in either group.

findings is computed on every request, so it changes as ignore rules are added or cases are marked passed.

verdict and passRate ​

verdict looks only at the pass rate:

  • If the replay didn't finish normally, had no cases, or had cases without a result, it's always FAIL.
  • With passThreshold set at trigger time, it's PASS when the pass rate is at or above the threshold.
  • Without it, it's PASS only when nothing failed.

verdict is computed when the replay finishes and stored; later queries read the stored value. Re-running the same planId computes it again.

The pass rate says nothing about what the replay actually covered. Decide on findings.state.

analysis ​

A summary of the AI cause analysis — the same one shown in the report and on notification cards. It may be missing or null when there's no analysis. It never affects verdict or findings.state.

FieldDescription
stateRUNNING, DONE, PARTIAL (stopped partway) or SKIPPED (never started)
reasonWhy it's PARTIAL or SKIPPED, for example QUOTA_EXHAUSTED (today's automatic analyses are used up), NO_EXECUTOR (the analysis service isn't connected), MODEL_QUOTA (the AI service is out of quota), STALE (the analysis was interrupted)
analyzedCasesCases analyzed
failedCasesCases that didn't pass
codeChangeCases, undeterminedCases, invalidCasesCases in each tier: caused by code changes, cause not established, invalid. They add up to the failed cases
markedCasesHow many of those were marked passed by someone. Marking doesn't reduce the original counts
codeChangeInterfaces, undeterminedInterfaces, invalidInterfacesEndpoints in each tier; each endpoint counts only in its most severe tier
codeChangesEach difference caused by code changes, see below

Each item in codeChanges:

FieldDescription
findingIdID of this difference
title, shortTitleA one-sentence description, and the short title shown in the report (at most 20 characters). Written by the AI, in Chinese by default
operationsEndpoints involved
affectedCases, replayedCasesCases affected, and cases replayed on these endpoints in total
markedCases, markedAtHow many were marked passed, and when the last one was (millisecond timestamp)
sampleOperationAn example endpoint
locationCode location: file, line, symbol
inferredtrue when the cause was inferred from response data without locating the code: either no repository was read, or it was read but the code wasn't located
consecutiveHow many replays in a row it has appeared in; 1 means first seen

Read the stored result ​

GET /openapi/v1/replay-runs/{planId}/diagnosis

Reads the stored result for this replay: verdict, passRate, case counts, the attributes passed at trigger time, and the written-back analysis text summary. passThreshold is not returned.

errorCodeMeaning
nullA result is available
NO_CONCLUSION_YETTriggered but not finished. attributes can already be read
NOT_FOUNDNo stored result for this replay, for example because it wasn't triggered through the API

A POST to the same path is how SoftProbe's analysis service writes back its results. It doesn't start an analysis, and you don't need to call it. To have replays analyzed automatically, turn on Replays triggered by CI in flow settings.

Notification channels ​

List channels ​

GET /openapi/v1/notification-channels

Returns channels, plus supportedTypes, the channel types this backend supports. For security, URLs and secrets are never returned; you get a masked urlMasked and a secretConfigured flag instead.

Create or update a channel ​

POST /openapi/v1/notification-channels

FieldDescription
idPass it to update; leave it out to create
nameName
typefeishu_bot, dingtalk_bot or webhook
urlURL, http or https only
secretThe chat bot's signing secret; for webhook, sent as-is in the X-Webhook-Secret header
appIdsWhich applications' replays are sent here; an empty array means all
onlyOnFailureWhen true, sends only when the state isn't CLEAN
enabledWhether the channel is on

Updates change only the fields you send; fields left out or set to null keep their values. So renaming a channel doesn't require sending the URL and secret again.

bash
curl -X POST http://sp-backend.internal:8090/openapi/v1/notification-channels \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Dev team",
    "type": "feishu_bot",
    "url": "https://open.feishu.cn/open-apis/bot/v2/hook/<token>",
    "appIds": ["order-service"],
    "onlyOnFailure": true,
    "enabled": true
  }'

Delete a channel and send a test ​

  • DELETE /openapi/v1/notification-channels/{id} deletes the channel.
  • POST /openapi/v1/notification-channels/{id}/test sends a sample message with every section filled in. On failure it returns SEND_FAILED; errorMessage includes at most the receiver's HTTP status, never its response body — see the server log for details. Test sends count toward the send rate limit.

Error codes ​

errorCodeCause
MISSING_TYPEtype is missing
UNKNOWN_TYPEtype isn't in supportedTypes
MISSING_URLurl is missing
UNSUPPORTED_SCHEMEThe URL isn't http or https
NOT_FOUNDThe channel doesn't exist
RATE_LIMITEDToo many test sends; try again later
SEND_FAILEDThe test send failed

Notification events ​

Webhook channels receive POST requests in CloudEvents 1.0 format.

FieldDescription
specversion1.0
idEvent ID
source/softprobe/replay
typeai.softprobe.replay.run.completed (the result is in) or ai.softprobe.replay.run.diagnosed (the AI analysis has been written back)
subjectThe planId
timeWhen it was sent, in UTC
datacontenttypeapplication/json
dataSee below

Fields in data:

FieldDescription
appId, planIdThe application and the replay
verdict, passRate, totalCases, successCases, failedCasesAs in the polling endpoint
reasonWhy verdict is FAIL
summaryThe AI analysis text, when there is one
reportUrlThe report page
attributesThe deployment details passed at trigger time
findingsThe replay findings, as in the polling endpoint
analysisThe AI analysis summary, as in the polling endpoint

When the verdict is FAIL, a few extra difference statistics (clusterCount, topClusters and so on) are included, for webhooks only. Fields that aren't available are left out.

Headers: Content-Type: application/json; charset=utf-8, plus X-Webhook-Secret when the channel has a secret. Any 2xx response counts as delivered.

Capture Sessions · Review Steps · Improve what matters