Skip to content

Tango Node SDK – API Reference

This document provides the full API reference for the Node.js / TypeScript version of the Tango SDK. It is a translation of the Python SDK documentation, rewritten for JavaScript runtime semantics, async/await, and the TypeScript type system.

Importing

import { TangoClient, ShapeConfig } from "@makegov/tango-node";
// Models (optional)
import type { Contract } from "@makegov/tango-node/models";

All methods are async and return Promises.


Agencies

listAgencies(options?)

List federal departments and subagencies.

const resp = await client.listAgencies({ page: 1, limit: 25 });

Parameters

Name Type Description
page number Page number (default 1).
limit number Max results per page (default 25, max 100).

Returns (Agencies)

PaginatedResponse<AgencyLike>


getAgency(code)

Fetch a single agency by its code.

const agency = await client.getAgency("2000");

Returns a shaped Agency object. Responses are materialized via the dynamic model pipeline (dates parsed, nested objects built).


Contracts

listContracts(options)

Search and list contract records.

const resp = await client.listContracts({
  keyword: "cloud",
  naics_code: "541511",
  shape: ShapeConfig.CONTRACTS_MINIMAL,
  flat: true,
});

Search / Filter Parameters

These mirror the Python SDK:

Filter Maps to API param
keyword search
naics_code naics
psc_code psc
recipient_name recipient
recipient_uei uei
set_aside_type set_aside

Sorting:

sort: "award_date",
order: "desc"   // -> ordering="-award_date"

Pagination + shaping options:

shape: string,
flat: boolean,
flatLists: boolean,
page: number,
limit: number,
cursor: string,  // mutually exclusive with `page` — if provided, `page` is ignored

Contracts support both page-based and cursor-based pagination. Use cursor for deep pagination (faster and more stable on large result sets); use page for small offsets or when you need to jump to a specific page. page and cursor are mutually exclusive — if you pass cursor, the SDK ignores page.

Returns (Contracts)

PaginatedResponse<Contract> materialized according to the requested shape. Date/datetime fields are parsed, decimals normalized to strings, nested recipients, agencies, and locations are objects.


Vehicles

Vehicles provide a solicitation-centric grouping of related IDVs.

listVehicles(options)

const resp = await client.listVehicles({
  search: "GSA schedule",
  shape: ShapeConfig.VEHICLES_MINIMAL,
  page: 1,
  limit: 25,
});

Supported parameters:

  • search (vehicle-level full-text search)
  • page, limit (max 100)
  • shape, flat, flatLists

getVehicle(uuid, options?)

const vehicle = await client.getVehicle("00000000-0000-0000-0000-000000000001", {
  shape: ShapeConfig.VEHICLES_COMPREHENSIVE,
});

Notes:

  • On vehicle detail, search filters expanded awardees(...) when included in your shape (it does not filter the vehicle itself).
  • When using flat: true, you can override the joiner with joiner (default ".").

listVehicleAwardees(uuid, options?)

const awardees = await client.listVehicleAwardees("00000000-0000-0000-0000-000000000001", {
  shape: ShapeConfig.VEHICLE_AWARDEES_MINIMAL,
});

IDVs

IDVs (indefinite delivery vehicles) are the parent “vehicle award” records that can have child awards/orders under them.

listIdvs(options)

const idvs = await client.listIdvs({
  limit: 25,
  cursor: null,
  shape: ShapeConfig.IDVS_MINIMAL,
  awarding_agency: "4700",
});

Notes:

  • This endpoint uses keyset pagination (cursor + limit) rather than page.

getIdv(key, options?)

const idv = await client.getIdv("SOME_IDV_KEY", {
  shape: ShapeConfig.IDVS_COMPREHENSIVE,
});

listIdvAwards(key, options?)

Lists child awards (contracts) under an IDV.

const awards = await client.listIdvAwards("SOME_IDV_KEY", { limit: 25 });

listIdvChildIdvs({ key, ...options })

const children = await client.listIdvChildIdvs({ key: "SOME_IDV_KEY", limit: 25 });

listIdvTransactions(key, options?)

const tx = await client.listIdvTransactions("SOME_IDV_KEY", { limit: 100 });

getIdvSummary(identifier) / listIdvSummaryAwards(identifier, options?)

Deprecated. These methods wrap the /api/idvs/{identifier}/summary/ and /api/idvs/{identifier}/summary/awards/ routes, which were removed server-side and now return 404. The methods will be removed from the SDK in a future release. For solicitation-grouped views, query /api/vehicles/ instead (see Vehicles).

const summary = await client.getIdvSummary("SOLICITATION_IDENTIFIER");
const awards = await client.listIdvSummaryAwards("SOLICITATION_IDENTIFIER", { limit: 25 });

Entities

listEntities(options)

const resp = await client.listEntities({
  search: "Acme",
  shape: ShapeConfig.ENTITIES_MINIMAL,
});

Filters:

  • search
  • any field names supported by the API

getEntity(uei, options?)

Fetch a single entity by UEI or CAGE.

Returns a shaped entity object with nested addresses/fields based on the shape.


Forecasts

listForecasts(options)

Forecast search, with optional shaping.


Opportunities

listOpportunities(options)

Search SAM.gov opportunities with shaping.


Notices

listNotices(options)


Grants

listGrants(options)


Organizations / Offices / Departments

listOrganizations(options?)

The canonical agency/department/office hierarchy. level filters by hierarchy depth: 1 = department, 2 = agency, 3 = sub-agency, and so on.

const orgs = await client.listOrganizations({
  level: 1,                // 1 = department, 2 = agency, 3 = sub-agency, …
  include_inactive: false,
  search: "Defense",
  limit: 25,
});

getOrganization(identifier)

const org = await client.getOrganization("ORG_KEY");

listOffices(options?)

const offices = await client.listOffices({ search: "acquisitions" });

getOffice(code)

const office = await client.getOffice("4732XX");

listDepartments(options?)

Deprecated. Use listOrganizations({ level: 1 }) instead. The standalone departments endpoint is retained for backward compatibility and will be removed in a future API version.

const depts = await client.listDepartments({ page: 1, limit: 25 });

getDepartment(code)

const dept = await client.getDepartment("097");

OTAs

Other Transaction Agreements — non-FAR-based awards.

listOtas(options?)

Uses keyset pagination (cursor + limit).

const otas = await client.listOtas({ limit: 25, awarding_agency: "4700" });

getOta(key)

const ota = await client.getOta("OTA_KEY");

OTIDVs

Other Transaction IDVs — umbrella OT agreements with child awards.

listOtidvs(options?)

Uses keyset pagination (cursor + limit).

const otidvs = await client.listOtidvs({ limit: 25 });

getOtidv(key)

const otidv = await client.getOtidv("OTIDV_KEY");

listOtidvAwards(key, options?)

const awards = await client.listOtidvAwards("OTIDV_KEY", { limit: 25 });

Subawards

listSubawards(options?)

const subs = await client.listSubawards({ prime_uei: "ABC123DEF456", limit: 25 });

GSA eLibrary Contracts

listGsaElibraryContracts(options?)

const contracts = await client.listGsaElibraryContracts({ schedule: "MAS", limit: 25 });

Protests

listProtests(options?)

const protests = await client.listProtests({ source_system: "gao", limit: 25 });

getProtest(caseNumber)

const protest = await client.getProtest("CASE_UUID");

IT Dashboard

listItDashboard(options?)

const investments = await client.listItDashboard({ search: "cloud", limit: 25 });

getItDashboard(uii)

const investment = await client.getItDashboard("023-000001234");

LCATs

listLcats(options)

Requires either { uei } (entity LCATs) or { idvKey } (IDV LCATs) — throws TangoValidationError if neither is provided.

const lcats = await client.listLcats({ uei: "ABCDEF123456" });
// or:
const lcats = await client.listLcats({ idvKey: "GS-00F-XXXX" });

listIdvLcats(key, options?)

Labor Categories (/api/idvs/{key}/lcats/) attached to an IDV.

const lcats = await client.listIdvLcats("GS-00F-XXXX", { limit: 25 });

Metrics

listMetrics(options)

List metrics for a NAICS code, PSC code, or entity. ownerType, ownerId, months, and periodGrouping are all required.

const metrics = await client.listMetrics({
  ownerType: "naics",
  ownerId: "541511",
  months: 12,
  periodGrouping: "month",
});

getNaicsMetrics(code, months, periodGrouping)

const m = await client.getNaicsMetrics("541511", 12, "month");

getPscMetrics(code, months, periodGrouping)

const m = await client.getPscMetrics("D302", 12, "month");

getEntityMetrics(uei, months, periodGrouping)

const m = await client.getEntityMetrics("ABCDEF123456", 12, "month");

Reference Lookups

listNaics(options?) / getNaics(code)

const naics = await client.listNaics({ search: "software" });
const code = await client.getNaics("541511");

listPsc(options?) / getPsc(code)

const psc = await client.listPsc();
const code = await client.getPsc("D302");

listMasSins(options?) / getMasSin(sin)

const sins = await client.listMasSins();
const sin = await client.getMasSin("54151S");

listAssistanceListings(options?) / getAssistanceListing(number)

const listings = await client.listAssistanceListings();
const listing = await client.getAssistanceListing("10.310");

listBusinessTypes(options?) / getBusinessType(code)

const types = await client.listBusinessTypes();
const bt = await client.getBusinessType("A6");

Resolve / Validate

resolve(input)

Resolve a free-text name to ranked entity or organization candidates.

const result = await client.resolve({ name: "Lockheed Martin", target_type: "entity" });
// result.candidates[0].display_name, result.count

Required fields: name, target_type ("entity" | "organization").

validate(input)

Validate the format of a PIID, solicitation number, or UEI.

const result = await client.validate({ type: "uei", value: "ABCDEF123456" });

Required fields: type ("piid" | "solicitation" | "uei"), value.


Entity Sub-resources

listEntityContracts(uei, options?)

const contracts = await client.listEntityContracts("ABCDEF123456", { limit: 25 });

listEntityIdvs(uei, options?) / listEntityOtas(uei, options?) / listEntityOtidvs(uei, options?)

const idvs = await client.listEntityIdvs("ABCDEF123456");

listEntitySubawards(uei, options?) / listEntityLcats(uei, options?)

const subawards = await client.listEntitySubawards("ABCDEF123456");

Agency Sub-resources

listAgencyAwardingContracts(code, options?)

const contracts = await client.listAgencyAwardingContracts("4700", { limit: 25 });

listAgencyFundingContracts(code, options?)

const contracts = await client.listAgencyFundingContracts("4700", { limit: 25 });

Opportunities (attachments)

searchOpportunityAttachments(options)

Semantic search over opportunity attachments. q is required.

const results = await client.searchOpportunityAttachments({
  q: "cybersecurity",
  topK: 10, // max results (optional)
  includeExtractedText: false, // include raw extracted text (optional)
});
Name Type Description
q string Required. Search query.
topK number Maximum number of results to return.
includeExtractedText boolean Whether to include raw extracted text.

Async Iteration

All list methods can be iterated page-by-page via the generic iterate() helper or the named convenience wrappers.

iterate(method, options?)

for await (const contract of client.iterate("listContracts", { awarding_agency: "9700" })) {
  console.log(contract.piid);
}

Named wrappers: iterateContracts, iterateEntities, iterateOpportunities, iterateNotices, iterateGrants, iterateForecasts, iterateIdvs, iterateVehicles.


Utility

getVersion()

const v = await client.getVersion();

listApiKeys()

const keys = await client.listApiKeys();

Webhooks (v2)

Webhook APIs let Large / Enterprise users manage subscription filters for outbound Tango webhooks.

listWebhookEventTypes()

Discover supported event_type values.

const info = await client.listWebhookEventTypes();

Webhook endpoints

In production, MakeGov provisions the initial endpoint for you. These methods are most useful for dev/self-service.

const endpoints = await client.listWebhookEndpoints({ page: 1, limit: 25 });
const endpoint = await client.getWebhookEndpoint("ENDPOINT_UUID");

createWebhookEndpoint accepts the canonical snake_case shape (callback_url, is_active, name) or the legacy camelCase aliases (callbackUrl, isActive). If name is not provided, the SDK falls back to the URL host.

// Create (canonical snake_case)
const created = await client.createWebhookEndpoint({
  name: "Prod receiver",
  callback_url: "https://example.com/tango/webhooks",
  // is_active defaults to true on create
});

// Legacy camelCase still works:
const created2 = await client.createWebhookEndpoint({
  callbackUrl: "https://example.com/tango/webhooks",
  isActive: true,
});

// Update
await client.updateWebhookEndpoint(created.id, { is_active: false });

// Delete
await client.deleteWebhookEndpoint(created.id);

testWebhookEndpoint(endpointId)

Send an immediate test webhook to a specific endpoint. endpointId is required. The SDK sends { endpoint: <id> } in the request body (canonical post-tango#2252 cleanup; the API also accepts endpoint_id as a deprecated alias).

const result = await client.testWebhookEndpoint("ENDPOINT_UUID");
console.log(result.success, result.status_code);

testWebhookDelivery(options?) (legacy alias)

Legacy wrapper around testWebhookEndpoint. endpointId may be omitted, in which case the API auto-resolves the user's only endpoint (404 if 0, 400 if >1). Prefer testWebhookEndpoint for new code.

const result = await client.testWebhookDelivery({ endpointId: "ENDPOINT_UUID" });

getWebhookSamplePayload(options?)

Fetch Tango-shaped sample deliveries.

const sample = await client.getWebhookSamplePayload({ eventType: "alerts.contract.match" });

Webhook Alerts

The Alerts API is a filter-subscription convenience layer on top of subscriptions. The SDK uses cleaner field names than the underlying API: name (vs subscription_name), filters (vs filter_definition), and singular query_type values.

// Create
const alert = await client.createWebhookAlert({
  name: "New IT cloud contracts",                  // vs subscription_name on the wire
  query_type: "contract",                          // SINGULAR — not "contracts"
  filters: { naics: "541511" },                    // vs filter_definition on the wire
  frequency: "realtime",                           // realtime | daily | weekly | custom
  cron_expression: undefined,                      // required if frequency === "custom"
});

// List
const alerts = await client.listWebhookAlerts({ page: 1, pageSize: 25 });

// Get / Update / Delete
const got = await client.getWebhookAlert("ALERT_UUID");
await client.updateWebhookAlert("ALERT_UUID", { name: "Updated name" });
await client.deleteWebhookAlert("ALERT_UUID");

Notes:

  • name and query_type are required on create. query_type is singular (e.g. "contract", not "contracts").
  • Only name, frequency, cronExpression, and isActive are writable via updateWebhookAlertquery_type and filters are read-only after creation.

Deliveries / redelivery

The API does not currently expose a public /api/webhooks/deliveries/ or redelivery endpoint. Use:

  • testWebhookEndpoint(endpointId) for connectivity checks
  • getWebhookSamplePayload() for building handlers + alert payloads

Receiving webhooks (signature verification)

Every delivery includes an HMAC signature header:

  • X-Tango-Signature: sha256=<hex digest>

Use the SDK's verifySignature helper — do not hand-roll HMAC. Verify against the raw request body bytes (not a re-serialized parsed body). Arg order is (body, header, secret).

import { verifySignature } from "@makegov/tango-node";

// Express — use express.raw() to get the body as a Buffer before JSON parsing
app.post("/tango/webhooks", express.raw({ type: "application/json" }), (req, res) => {
  const rawBody = req.body; // Buffer
  const signatureHeader = req.headers["x-tango-signature"];

  if (!verifySignature(rawBody, signatureHeader, process.env.TANGO_WEBHOOK_SECRET)) {
    return res.status(401).json({ error: "invalid_signature" });
  }

  const payload = JSON.parse(rawBody.toString("utf8"));
  // ... handle payload.events ...
  res.json({ ok: true });
});

verifySignature signature:

function verifySignature(body: string | Buffer, header: string | null | undefined, secret: string): boolean;

Returns false for missing, malformed, or mismatched headers — never throws on mismatch. Uses timingSafeEqual internally. See WEBHOOKS.md § Signature verification for Fastify and framework-agnostic examples.


Error Types

All thrown by async methods:

  • TangoAPIError
  • TangoAuthError
  • TangoNotFoundError
  • TangoRateLimitError
  • TangoValidationError
  • ShapeError
  • ShapeParseError
  • ShapeValidationError
  • TypeGenerationError
  • ModelInstantiationError

Pagination

All list endpoints return:

interface PaginatedResponse<T> {
  count: number;
  next: string | null;
  previous: string | null;
  pageMetadata: Record<string, unknown> | null;
  results: T[];
}

You can follow next / previous manually or use your own wrapper.