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/translate

The 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 entrytargetNotes
ignore whose filters a resolution can express (alert type, package identity, version, CVE/GHSA)alert_resolutionreason 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 kevsalert_policy_ruleThose filters cannot be evaluated as a resolution, so the entry becomes an Ignore rule instead.
block, warn, monitoralert_policy_ruleblock becomes the error action.
inherituntranslatableNot a write. Delete the matching rule, or change the policy baseline.
An entry carrying alertKeyuntranslatableRecreate 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.

codeWhy
inherit_not_translatableinherit is not a policy write. Delete the matching rule, or change the policy baseline.
alert_key_not_translatablealertKey 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_alertsEvery filter is a wildcard, so the entry matches every alert. Change the policy baseline instead.
filter_not_translatableA 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_longThe 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/pkg with a null namespace) are split into artifact.namespace and artifact.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 you POST if you want something else.

The selector language is Vigil. Full request and response schemas for this endpoint are in the API reference.

Related Pages


Did this page help you?