Translating Alert Triage Payloads
If you already POST Alert Triage rules, this endpoint converts those request bodies. POST /orgs/{org_slug}/alert-policies/migration/translate takes the same alertTriage array you send to POST /orgs/{org_slug}/triage/alerts and returns the new alert-resolution or policy-rule requests.
Nothing is created. The endpoint does not start a migration, does not evaluate policies, and does not write resolutions or rules. You send the returned requests yourself.
You can create alert resolutions before you confirm the migration. Policy and rule writes return 409 until you confirm. Translate the payloads now, POST the resolution bodies now, and dry-run the rule bodies now (?dry_run=true). POST the rule bodies for real after you confirm. See Updating API Integrations for when each write path actually switches.
Request
curl -u "$SOCKET_API_TOKEN:" -X POST \
-H "Content-Type: application/json" \
-d '{
"alertTriage": [
{
"packageType": "npm",
"packageName": "lodash",
"alertType": "criticalCVE",
"note": "accepted risk",
"state": "ignore"
}
]
}' \
https://api.socket.dev/v0/orgs/$ORG/alert-policies/migration/translateThe body is the same contract as POST /triage/alerts: an alertTriage array of 1–100 entries. Each entry is validated as a triage write first. A malformed entry (missing state, a bad uuid) fails the whole request with 400. Entries that are valid triage writes but have no equivalent land in untranslatable instead, and do not fail the batch.
Auth is any one of triage:alerts-update, alert-policy:read, or alert-resolution:create. Repository-restricted tokens can call translate, but the returned requests are organization-wide. Posting them needs a token with organization-wide repository access, plus alert-resolution:create for a resolution or alert-policy:create for a rule. The endpoint consumes 1 unit of quota.
Response
A successful call is always 200 and always returns both arrays. 200 means the request was valid, not that every rule converted. Check untranslatable for the ones that did not.
{
"translations": [
{
"index": 0,
"target": "alert_resolution",
"method": "POST",
"path": "/v0/orgs/acme/alerts/resolutions",
"body": {
"reason": "other",
"comment": "accepted risk",
"vigil_selector": {
"finding.alertType": "criticalCVE",
"artifact.type": "npm",
"artifact.name": "lodash"
}
}
}
],
"untranslatable": []
}index is the zero-based position in the submitted alertTriage array. path already contains your org slug. Send body to it as-is. Every translation is a POST. A legacy uuid is not a destination id, and resolutions have no PUT. For an alert_resolution target, you can send that request before you confirm the migration; matching alerts leave the open list the next time Socket refreshes your organization's alerts.
What Each Entry Becomes
| Legacy entry | target | Notes |
|---|---|---|
ignore whose filters a resolution can express (alert type, package identity, version, CVE/GHSA) | alert_resolution | reason is always other. Add reason_text on the body before you POST if you want a short label. Resolutions cannot be updated; delete and recreate one you already sent. |
ignore that also filters on fixAvailable, cvssScoreCmp, reachability, or kevs | alert_policy_rule | Those filters cannot be evaluated as a resolution, so the entry becomes an Ignore rule instead. |
block, warn, monitor | alert_policy_rule | block becomes the error action. |
inherit | untranslatable | Not a write. Delete the matching rule, or change the policy baseline. |
An entry carrying alertKey | untranslatable | Recreate it from the alert type and package: as a resolution for ignore, a rule otherwise. |
An ignore is a resolution only when every filter on it is one a resolution can evaluate. How the old fields map, including version prefixes, CVSS, reachability, and KEV, is on Alert Triage API. The selector itself is a Vigil selector.
Policy-rule translations always target the Default policy (/alert-policies/default/rules), which covers repositories that have no policy label. Labeled policies do not inherit those rules. If the rule belongs on another policy, POST the same body to /alert-policies/{policy_id}/rules instead.
Example: A Monitor Entry Becomes a Policy Rule
{
"alertTriage": [
{
"packageType": "npm",
"packageName": "react",
"packageVersion": "19.2.8",
"state": "monitor"
}
]
}{
"translations": [
{
"index": 0,
"target": "alert_policy_rule",
"method": "POST",
"path": "/v0/orgs/acme/alert-policies/default/rules",
"body": {
"name": "in [email protected]",
"action": "monitor",
"note": null,
"vigil_selector": {
"artifact.type": "npm",
"artifact.name": "react",
"artifact.version": "19.2.8"
}
}
}
],
"untranslatable": []
}Hold the real POST until after you confirm the migration. Posting it before then returns 409. You can dry-run it now by sending the same body with ?dry_run=true.
Example: When Only Some Rules Convert
{
"alertTriage": [
{
"packageName": "lodash",
"alertType": "criticalCVE",
"state": "ignore"
},
{
"alertKey": "QGxvZGFzaA",
"state": "ignore"
}
]
}{
"translations": [
{
"index": 0,
"target": "alert_resolution",
"method": "POST",
"path": "/v0/orgs/acme/alerts/resolutions",
"body": {
"reason": "other",
"comment": null,
"vigil_selector": {
"finding.alertType": "criticalCVE",
"artifact.name": "lodash"
}
}
}
],
"untranslatable": [
{
"index": 1,
"code": "alert_key_not_translatable",
"reason": "alertKey cannot be translated. POST /v0/orgs/{org_slug}/alerts/resolutions with the alert type and package, or resolve it in the dashboard."
}
]
}The first entry is ready to send. The second is identified by index so you can fix it without dropping the rest of the batch.
Entries That Cannot Be Translated
A rule that cannot be converted lands in untranslatable with one of these code values.
code | Why |
|---|---|
inherit_not_translatable | inherit is not a policy write. Delete the matching rule, or change the policy baseline. |
alert_key_not_translatable | alertKey has no selector field. Recreate the entry from the alert type and package: as a resolution for an ignore, or as a policy rule for block / warn / monitor. |
matches_all_alerts | Every filter is a wildcard, so the entry matches every alert. Change the policy baseline instead. |
filter_not_translatable | A filter has no equivalent. The common case is patchAvailable, which tested patch entitlement, a different question from finding.cvePatchStatus. See Alert Triage API. |
note_too_long | The note is longer than the destination allows: 1024 characters for a resolution comment, 256 for a rule note. Shorten it. The note is not trimmed automatically. |
Notes, Names, and Selectors
- Empty or whitespace-only notes become
null. Creating a resolution with an empty comment is not allowed. - A note that fits a resolution can still be too long for a rule.
- Scoped npm names that were stored whole in
packageName(for example@scope/pkgwith a null namespace) are split intoartifact.namespaceandartifact.name. Resolutions evaluate the split form; sending the combined name matches nothing. - Generated rule names come from the entry's filters (
in [email protected],criticalCVE in lodash, …). Rename them when youPOSTif you want something else.
The selector language is Vigil. Full request and response schemas for this endpoint are in the API reference.
Related Pages
- Updating API Integrations — when to switch your scripts, and which endpoints replace which.
- Dry-Run Policy and Resolution Writes — try a translated request with
?dry_run=true. - Alert Triage API — how the old fields map.
- Vigil Selectors — the selector language in
vigil_selector. - Resolve Alerts — translated ignores become resolutions.
- Policies — translated pattern rules become policy rules.
Updated about 18 hours ago