API Reference

List Supply-Chain Incidents

Return recent software supply-chain incidents — compromised packages, registry attacks, typosquats, and maintainer takeovers — newest first, with optional filters for severity and a published-date window.

GET
/v1/supply-chain/incidents

Cost: 1 credit per successful call

Query parameters

severitystringoptionaldefault: (all)

Filter by incident severity. One of low, medium, high, or critical (case-insensitive). Omit to return incidents of any severity.

daysintegeroptionaldefault: (no floor)

Only return incidents published within the last N days. Accepted range: 1–365. Omit for no date floor (all incidents up to limit).

limitintegeroptionaldefault: 50

Maximum number of incidents to return. Accepted range: 1–200.

Request

curl "https://api.precursorintelligence.com/functions/v1/supply-chain/incidents?severity=critical&days=30&limit=50" \
  -H "Authorization: Bearer $PRECURSOR_API_KEY"

Response

200 OK
{
  "data": {
    "count": 2,
    "items": [
      {
        "id": "npm-2026-06-17-mastra-easy-day-js",
        "title": "Mastra AI npm scope takeover via `easy-day-js` typosquat dropper",
        "summary": "A dormant contributor account, never revoked from the @mastra scope, was used to mass-publish ~144 trojanised @mastra/* releases, each declaring a new easy-day-js dependency whose postinstall hook drops a cross-platform crypto-wallet stealer / RAT.",
        "severity": "critical",
        "published": "2026-06-17",
        "last_updated": "2026-06-18",
        "threat_actor": null,
        "tags": ["account-takeover", "typosquat", "infostealer", "crypto-wallet-drain"],
        "reference": "https://socket.dev/blog/mastra-npm-packages-compromised"
      },
      {
        "id": "npm-2026-06-11-atomic-arch-aur-hijack",
        "title": "Atomic Arch: 400+ AUR packages hijacked to ship npm-delivered Rust stealer with eBPF rootkit",
        "summary": "Attackers hijacked 400+ orphaned Arch User Repository packages and rewrote their PKGBUILDs to pull three malicious npm dependencies, each dropping a Rust ELF credential stealer with an eBPF rootkit and systemd persistence.",
        "severity": "critical",
        "published": "2026-06-11",
        "last_updated": "2026-06-12",
        "threat_actor": null,
        "tags": ["maintainer-takeover", "account-takeover", "credential-theft", "infostealer"],
        "reference": "https://www.sonatype.com/blog/atomic-arch-npm-campaign-adds-malicious-dependency"
      }
    ],
    "generated_at": "2026-05-18T10:24:31.118Z"
  },
  "meta": {
    "request_id": "8b16efb8-9a6c-4ace-b817-11cbee626d08",
    "generated_at": "2026-05-18T10:24:31.118Z",
    "credits": { "charged": 1, "remaining": 9998 }
  }
}

Response fields

data.countintegeroptional

Number of incidents returned (equals items.length).

data.itemsarrayoptional

The matching incidents, ordered by published descending. Each object contains the fields below.

data.items[].idstringoptional

Stable slug-like identifier for the incident. Pass this to GET /v1/supply-chain/incidents/{id} for the full record.

data.items[].titlestringoptional

Short human-readable incident title.

data.items[].summarystringoptional

One-paragraph summary of the incident.

data.items[].severitystringoptional

Incident severity: low, medium, high, or critical.

data.items[].publishedstringoptional

ISO 8601 date (YYYY-MM-DD) the incident was first published.

data.items[].last_updatedstring | nulloptional

ISO 8601 date of the most recent update, or null if never updated.

data.items[].threat_actorstring | nulloptional

Attributed threat actor, or null when unattributed.

data.items[].tagsarrayoptional

String tags categorising the incident (e.g. typosquat, infostealer).

data.items[].referencestringoptional

Primary reference URL for the incident.

data.generated_atstringoptional

ISO 8601 UTC timestamp indicating when this response was produced.

Errors

StatuserrorWhen
400invalid_severityseverity is not one of low, medium, high, critical.
400invalid_daysdays is not an integer, or is outside the 1–365 range.
400invalid_limitlimit is not an integer, or is outside the 1–200 range.
401invalid_authorizationThe Authorization header is missing or malformed.
401unauthorizedThe key is invalid, revoked, or the request IP is not on the allowlist.
402insufficient_creditsThe organisation's credit balance is below 1.
404not_foundThe route or resource does not exist.
405method_not_allowedOnly GET and OPTIONS are accepted.
500internalAn unexpected server error occurred.

See Errors for the full error reference.