Skip to content

Vendor watchlist — track new contracts from specific UEIs

Get a webhook every time a vendor on your watchlist wins a new contract.

The 1-line answer

Create one filter alert per vendor UEI on query_type=contract. Tango re-evaluates each filter against /api/contracts/ and POSTs alerts.contract.match events when matches appear.

curl -X POST -H "X-API-KEY: $TANGO_API_KEY" \
  -H "Content-Type: application/json" \
  "https://tango.makegov.com/api/webhooks/alerts/" \
  -d '{
    "name": "Vendor watchlist — ACME Corp",
    "query_type": "contract",
    "filters": { "uei": "ABC123XYZ4567" },
    "frequency": "realtime"
  }'

That's it. Repeat for each vendor in your watchlist.

One alert per vendor — uei is single-value

The /api/contracts/ API's uei filter is exact-match, single-value only. There is no | multi-value support on this filter today. To watch N vendors, create N alerts — one per UEI.

Future: If multi-value uei lands on /api/contracts/, this recipe collapses to a single alert per watchlist with uei: "UEI1|UEI2|UEI3". Tracked as a backlog enhancement; no ETA.

Step 1 — Verify each UEI returns data

For each vendor on your list, prove the filter works against the contracts API first.

curl -H "X-API-KEY: $TANGO_API_KEY" \
  "https://tango.makegov.com/api/contracts/?uei=ABC123XYZ4567&ordering=-award_date&limit=5"

If this returns the recent contracts you expect, the alert will fire on new ones. If it returns nothing, double-check the UEI is correct and the vendor has FPDS history.

Step 2 — Create one alert per vendor

for uei in ABC123XYZ4567 DEF456UVW7890 GHI789RST0123; do
  curl -X POST -H "X-API-KEY: $TANGO_API_KEY" \
    -H "Content-Type: application/json" \
    "https://tango.makegov.com/api/webhooks/alerts/" \
    -d "{
      \"name\": \"Vendor watchlist — $uei\",
      \"query_type\": \"contract\",
      \"filters\": { \"uei\": \"$uei\" },
      \"frequency\": \"realtime\"
    }"
done
import os
from tango import TangoClient

client = TangoClient(api_key=os.environ["TANGO_API_KEY"])

vendors = [
    ("ACME Corp",      "ABC123XYZ4567"),
    ("Beta Industries", "DEF456UVW7890"),
    ("Gamma LLC",      "GHI789RST0123"),
]

for label, uei in vendors:
    alert = client.create_webhook_alert(
        name=f"Vendor watchlist — {label}",
        query_type="contract",
        filters={"uei": uei},
        frequency="realtime",
    )
    print(alert.alert_id, alert.status, label)
import { TangoClient } from "@makegov/tango-node";

const client = new TangoClient({ apiKey: process.env.TANGO_API_KEY! });

const vendors = [
  { label: "ACME Corp",       uei: "ABC123XYZ4567" },
  { label: "Beta Industries", uei: "DEF456UVW7890" },
  { label: "Gamma LLC",       uei: "GHI789RST0123" },
];

for (const { label, uei } of vendors) {
  const alert = await client.createWebhookAlert({
    name: `Vendor watchlist — ${label}`,
    query_type: "contract",
    filters: { uei },
    frequency: "realtime",
  });
  console.log(alert.alert_id, alert.status, label);
}

Each alert returns 201 Created on first run, 200 OK on subsequent runs (dedup on query_type + filters).

Step 3 — Receive alerts.contract.match events

Tango POSTs a signed JSON batch to your endpoint. The match summary uses the delivered shape (richer than the API's sample-payload echo):

{
  "timestamp": "2026-05-12T18:20:14Z",
  "delivery_id": "8c5e3f6a-...-9b21",
  "events": [
    {
      "event_type": "alerts.contract.match",
      "alert_id": "e4c4...-...-...",
      "query_type": "contract",
      "filters": { "uei": "ABC123XYZ4567" },
      "matches": {
        "new_count": 1,
        "modified_count": 0,
        "new": [
          {
            "id": "CONT_AWD_W15QKN24C1234_9700_-NONE-_-NONE-",
            "piid": "W15QKN24C1234",
            "obligated": 2450000,
            "recipient_uei": "ABC123XYZ4567"
          }
        ],
        "modified": []
      },
      "checked_at": "2026-05-12T18:20:12.000Z"
    }
  ]
}

Use matches.new and matches.modified for the per-record summary. Pull full contract details from /api/contracts/{id}/ when you need more than the summary fields.

For the full receiver implementation (signature verification, idempotency, fast 2xx, error handling), see Stream contract awards in real time.

Step 4 — Manage the watchlist

# List your alerts
curl -H "X-API-KEY: $TANGO_API_KEY" \
  "https://tango.makegov.com/api/webhooks/alerts/"

# Pause a vendor without deleting
curl -X PATCH -H "X-API-KEY: $TANGO_API_KEY" \
  -H "Content-Type: application/json" \
  "https://tango.makegov.com/api/webhooks/alerts/<alert_id>/" \
  -d '{"is_active": false}'

# Remove a vendor permanently
curl -X DELETE -H "X-API-KEY: $TANGO_API_KEY" \
  "https://tango.makegov.com/api/webhooks/alerts/<alert_id>/"

name, frequency, cron_expression, and is_active are updatable. query_type and filters (which include uei) are immutable — to change the watched UEI, delete and recreate.

Limitations

  • uei is single-value on /api/contracts/. One alert per vendor. See the warning at the top of this page.
  • Tier caps apply. Free tier gets 1 alert total; Micro 3; Small 5; Medium 10; Large 25. A 5-vendor watchlist requires Small or higher.
  • Only contracts. This recipe covers query_type=contract. To watch a vendor's registration changes (DSBS updates, address changes, etc.) instead, use query_type=entity with uei — same N-alerts pattern applies.
  • No multi-vendor consolidation. All alerts deliver to the same endpoint, but each vendor's matches arrive in their own event (one per alert per dispatch). Group on the receiver side via alert_id if you need to fan out by vendor in your downstream system.