api

API Endpoints

Complete reference for all brin API endpoints

Base URL: https://api.brin.sh
Auth: None required.


##Look up an artifact

GET/{origin}/{identifier}

Look up a single artifact and get its security score.

Bash
# Packages
curl https://api.brin.sh/npm/express
curl https://api.brin.sh/npm/express@4.18.0
curl https://api.brin.sh/pypi/requests
curl https://api.brin.sh/crate/tokio
 
# Repositories
curl https://api.brin.sh/repo/expressjs/express
 
# Web
curl https://api.brin.sh/domain/example.com
curl https://api.brin.sh/page/example.com/docs
 
# AI components
curl https://api.brin.sh/mcp/owner/repo
curl https://api.brin.sh/skill/owner/repo
 
# Contributors
curl https://api.brin.sh/contributor/homanp
 
# Commits
curl https://api.brin.sh/commit/owner/repo@abc123def

Supported origins:

OriginWhat it scansIdentifier formatExample
npmnpm packagespackage or package@version/npm/express
pypiPyPI packagespackage/pypi/requests
crateCargo cratespackage/crate/tokio
repoGitHub repositoriesowner/repo/repo/expressjs/express
domainDomainshostname/domain/example.com
pageWeb pageshostname/path/page/example.com/docs
mcpMCP serversowner/repo or registry name/mcp/owner/repo
skillAI agent skillsowner/repo or owner/repo/skill/skill/owner/repo
emailEmailsMessage-ID (returned after POST)/email/msg-id@example.com
commitGit commitsowner/repo@sha/commit/owner/repo@abc123
contributorGitHub contributorsusername or github:username/contributor/homanp

Query parameters:

ParamTypeDefaultDescription
formatstringjsonResponse format. Supported values: json, simple, badge.
detailsbooleanfalseInclude sub_scores in the response. Also forces threats to be included even for non-flagged results.
refreshbooleanfalseIf the existing record is older than 24 hours, enqueue a fresh scan and return the current record with pending_deep_scan: true.
webhookstringnoneOptional callback URL for scan progress events. Accepted values must start with https://, http://localhost, or http://127.0.0.1.
tolerancestringconservativeControls how the score maps to a verdict. Supported values: conservative, lenient, yolo. See Safety tolerance.
modestringnoneIf set to full, wait for the worker deep scan to complete and return a non-preliminary result. This does not force Tier 3/LLM; Tier 3 still follows each scanner's normal trigger rules.

Response:

JSON
{
  "score": 85,
  "verdict": "safe",
  "confidence": "high",
  "tolerance": "conservative",
  "url": "https://api.brin.sh/npm/express",
  "scanned_at": "2026-03-01T12:00:00Z"
}

With ?details=true:

JSON
{
  "origin": "npm",
  "name": "express",
  "score": 85,
  "verdict": "safe",
  "confidence": "high",
  "tolerance": "conservative",
  "scanned_at": "2026-03-01T12:00:00Z",
  "sub_scores": {
    "identity": 95.0,
    "behavior": 80.0,
    "content": 100.0,
    "graph": 70.0
  }
}

When verdict is suspicious or dangerous, a threats array is included:

JSON
{
  "score": 12,
  "verdict": "dangerous",
  "threats": [
    {
      "type": "install_attack",
      "severity": "critical",
      "detail": "postinstall script exfiltrates environment variables to external endpoint"
    }
  ]
}

##Scan a pull request

GET/pr/{owner}/{repo}/{number}

Scan a GitHub pull request by repository owner, repository name, and pull request number.

Brin stores the repository slug as name and the pull request number as version in the response.

Bash
curl "https://api.brin.sh/pr/owner/repo/123"

###Request modes

By default, a cache miss returns a provisional response immediately and queues the full pull request scan in the background:

Bash
curl "https://api.brin.sh/pr/owner/repo/123"

That fast response sets pending_deep_scan: true. The worker then fetches the pull request from GitHub and runs the full scanner asynchronously.

For CI or blocking workflows, use mode=full:

Bash
curl "https://api.brin.sh/pr/owner/repo/123?mode=full&details=true"

With mode=full, the API enqueues the job and polls until the worker has replaced the pending placeholder with the final pull request result.

###How the PR scanner works

The full scanner combines GitHub metadata, aggregate diff analysis, and conditional LLM review.

TierWhat it doesNotes
Tier 1Scores the PR author's identity using GitHub account age, contribution history, org memberships, prior commits to the target repo, email-domain reputation, and review status.Always runs
Tier 2Scores PR behavior and content using total change size, sensitive-file count, PR title/body quality, agent config changes, security tooling changes, CI workflow tampering, secret detection, comment injection, PR-description injection, and obfuscation heuristics.Always runs
Tier 3Prepares a workspace with pr-description.txt, diff.patch, and metadata.json, then asks OpenCode to look for backdoors, agent targeting, sabotage, and other semantic risk.Runs only when triggered

Tier 3 is triggered when any of these are true:

  • Agent config files were touched
  • A critical deterministic threat was found
  • Sensitive files were changed
  • Lower-trust authors need deeper review

###GitHub data used

The worker pulls these GitHub resources during the full scan:

  • GET /repos/{owner}/{repo}/pulls/{number} for PR metadata
  • GET /repos/{owner}/{repo}/pulls/{number}/reviews for approval state and reviewers
  • GET /repos/{owner}/{repo}/pulls/{number}/files for changed files and per-file patches
  • GET /repos/{owner}/{repo}/pulls/{number} with Accept: application/vnd.github.diff for the aggregate diff
  • Contributor profile and org lookups for the PR author
  • Repo-specific prior commit count for the PR author

###Response

Pass details=true to include sub-scores and all detected threats:

Bash
curl "https://api.brin.sh/pr/owner/repo/123?details=true&mode=full"
JSON
{
  "origin": "pr",
  "name": "owner/repo",
  "version": "123",
  "score": 68,
  "confidence": "medium",
  "verdict": "caution",
  "tolerance": "conservative",
  "scanned_at": "2026-03-26T12:00:00Z",
  "url": "https://api.brin.sh/pr/owner/repo",
  "sub_scores": {
    "identity": 74.0,
    "behavior": 55.0,
    "content": 72.0,
    "graph": null
  },
  "threats": [
    {
      "type": "agent_config_tampering",
      "severity": "high",
      "detail": "Agent config file modified: AGENTS.md"
    }
  ]
}

Notes:

  • pending_deep_scan: true means you are still looking at a provisional or stale-refresh result.
  • graph is currently null for pull requests. The scanner emits graph edges, but PR graph scoring is not active yet.

###Webhooks

For async integrations, pass a webhook URL:

Bash
curl "https://api.brin.sh/pr/owner/repo/123?webhook=https://your-server.com/brin-callback"

Accepted webhook URLs are:

  • https://...
  • http://localhost/...
  • http://127.0.0.1/...

Events arrive in this order:

EventDescription
tier1_completeAuthor identity scoring finished
tier2_completeDeterministic PR behavior and content checks finished
tier3_completeLLM review finished, when Tier 3 ran
scan_completeFinal stored result

Each event includes score, verdict, confidence, sub_scores, threats, and tiers_completed.

###Threat types

The PR scanner currently emits these deterministic threat types, plus any matching Tier 3 findings:

ThreatSeverityMeaning
first_time_contributorLowAuthor has no prior commits to the target repository
agent_config_tamperingHigh / CriticalAgent instruction files were modified or newly added
security_sabotageMediumSecurity tooling config was modified
supply_chain_modCriticalCI workflow patch contains remote script execution patterns
credential_exposureCriticalAdded lines contain secret or credential patterns
obfuscationHighHigh-entropy additions suggest hidden or encoded payloads
code_comment_injectionHighAdded comments target AI reviewers or agents
pr_injectionCriticalPR title or body contains AI-targeting instructions
scan_errorCriticalThe PR could not be fetched or parsed correctly

###CI example

Use mode=full in GitHub Actions when you want a single blocking response:

YAML
name: PR Security Scan
 
on:
  pull_request_target:
    types: [opened, reopened, synchronize, ready_for_review]
 
permissions:
  contents: read
  pull-requests: write
  issues: write
 
jobs:
  scan:
    runs-on: ubuntu-24.04
    timeout-minutes: 10
    concurrency:
      group: brin-pr-${{ github.event.pull_request.number }}
      cancel-in-progress: true
 
    steps:
      - name: Scan PR with Brin
        id: scan
        shell: bash
        env:
          REPO: ${{ github.repository }}
          PR_NUMBER: ${{ github.event.pull_request.number }}
        run: |
          set -euo pipefail
 
          RESPONSE=$(curl -sfL --max-time 300 \
            "https://api.brin.sh/pr/${REPO}/${PR_NUMBER}?mode=full&details=true&tolerance=conservative")
 
          SCORE=$(jq -r '.score' <<<"$RESPONSE")
          VERDICT=$(jq -r '.verdict' <<<"$RESPONSE")
          STATUS=clean
          SHOULD_FAIL=false
 
          if [ "$VERDICT" = "dangerous" ] || [ "$SCORE" -lt 30 ]; then
            STATUS=blocking
            SHOULD_FAIL=true
          elif [ "$VERDICT" = "suspicious" ]; then
            STATUS=review
          fi
 
          echo "score=${SCORE}" >> "$GITHUB_OUTPUT"
          echo "verdict=${VERDICT}" >> "$GITHUB_OUTPUT"
          echo "status=${STATUS}" >> "$GITHUB_OUTPUT"
          echo "should_fail=${SHOULD_FAIL}" >> "$GITHUB_OUTPUT"
 
          THREATS=$(jq -r '(.threats // [])[] | "- \(.type): \(.detail)"' <<<"$RESPONSE")
          {
            echo "threats<<BRIN_EOF"
            echo "$THREATS"
            echo "BRIN_EOF"
          } >> "$GITHUB_OUTPUT"

###Safety tolerance

Tolerance changes verdict thresholds without changing the raw score:

  • conservative — strictest, best for external or high-risk pull requests
  • lenient — treats scores 60+ as safe
  • yolo — treats scores 40+ as safe
Bash
curl "https://api.brin.sh/pr/owner/repo/123?mode=full&tolerance=lenient"

##Batch lookup

POST/bulk

Look up up to 100 artifacts in a single request.

Bash
curl -X POST https://api.brin.sh/bulk \
  -H "Content-Type: application/json" \
  -d '{
    "lookups": [
      {"origin": "npm", "identifier": "express"},
      {"origin": "pypi", "identifier": "requests"},
      {"origin": "domain", "identifier": "example.com", "webhook": "https://your-server.com/cb"}
    ],
    "tolerance": "lenient"
  }'

Response:

JSON
{
  "results": [
    {
      "score": 85,
      "verdict": "safe",
      "confidence": "high",
      "tolerance": "lenient",
      "url": "https://api.brin.sh/npm/express",
      "scanned_at": "2026-03-01T12:00:00Z"
    }
  ]
}

##List and search artifacts

GET/

List and search all scanned artifacts with filtering, sorting, and pagination.

Bash
# All dangerous npm packages
curl "https://api.brin.sh/?origin=npm&verdict=dangerous"
 
# Search by name
curl "https://api.brin.sh/?q=lodash"
 
# Only artifacts with threats, sorted by score ascending
curl "https://api.brin.sh/?has_threats=true&sort=score&order=asc"

Query parameters:

ParamDefaultDescription
originFilter by origin type (npm, pypi, crate, repo, mcp, skill, domain, page, email, commit, contributor)
verdictFilter by verdict (safe, caution, suspicious, dangerous)
confidenceFilter by confidence (low, medium, high)
min_scoreMinimum score (0–100)
max_scoreMaximum score (0–100)
has_threatstrue to only return artifacts with threats
qFull-text search on identifier
sortscanned_atSort field: scanned_at, score, origin, identifier
orderdescSort direction: asc, desc
limit50Results per page (max 200)
offset0Pagination offset

Response:

JSON
{
  "total": 142,
  "limit": 50,
  "offset": 0,
  "data": [
    {
      "origin": "npm",
      "identifier": "chalk",
      "version": "5.6.2",
      "score": 78,
      "verdict": "caution",
      "confidence": "medium",
      "scanned_at": "2026-02-26T13:44:34Z",
      "pending_deep_scan": false
    }
  ]
}

##Query the trust graph

GET/graph/{origin}/{identifier}

Query the artifact's neighborhood in the trust graph.

Bash
curl "https://api.brin.sh/graph/domain/vercel.com?depth=2"

Query parameters:

ParamDefaultDescription
depth2Traversal depth (1–5)

Returns nodes and edges arrays for graph visualization.


##Webhooks

When a webhook URL is provided, brin POSTs progress events as each scan tier completes. Events are delivered with up to 3 retries.

Events: tier1_complete, tier2_complete, tier3_complete, scan_complete

JSON
{
  "event": "scan_complete",
  "origin": "npm",
  "identifier": "express",
  "timestamp": "2026-03-01T12:00:30Z",
  "data": {
    "score": 85,
    "verdict": "safe",
    "confidence": "high",
    "sub_scores": {
      "identity": 95,
      "behavior": 80,
      "content": 100,
      "graph": 70
    },
    "threats": [],
    "tiers_completed": ["tier1", "tier2", "tier3"]
  }
}

##Response headers

Every response includes:

Text
x-brin-score:      85
x-brin-verdict:    safe
x-brin-confidence: high
x-brin-tolerance:  conservative

##Error responses

JSON
{ "error": "not found" }
StatusDescription
200Success
400Bad request
404Entity not found
500Server error