trade-policy) is the dashboard’s go-to surface for live trade-policy context — what’s getting restricted, where tariffs are moving, how flows are shifting, what barriers exist, and how US customs revenue is tracking as a real-time signal of tariff pass-through.
What the panel shows
Six tabs across one data-dense panel (defaultRowSpan: 2):
TabId is the union 'restrictions' | 'tariffs' | 'flows' | 'barriers' | 'revenue' | 'comtrade' (src/components/TradePolicyPanel.ts:17).
Panel id is trade-policy; canonical component is src/components/TradePolicyPanel.ts. Title resolves from i18n (panels.tradePolicy).
How you reach it
- Cmd+K: type trade, tariff, or customs.
- Availability by variant: registered and enabled by default in the full/geopolitical (
priority: 1), finance (priority: 1), and commodity (priority: 1) variants. Not present in the tech or happy variants. Source:'trade-policy'entries inFULL_PANELS,FINANCE_PANELS,COMMODITY_PANELSofsrc/config/panels.ts.
Data sources
Six response shapes from@/services/trade, each backed by a generated sebuf REST RPC in TradeService. The panel shell is free; tariff trends and Comtrade calls use premiumFetch because those two paths are premium transport paths, while restrictions, flows, barriers, and customs revenue use the public client. Customs revenue also checks the bootstrap cache key customsRevenue before falling back to the RPC.
The
revenue tab’s US-Treasury feed is particularly sensitive — see the changelog entry for “US Treasury customs revenue in Trade Policy panel” (#1663).
Trade-flow coverage
get-trade-flows is served entirely from the Railway seed; it never calls WTO per request. What that seed contains is the whole supported space:
- Reporter — any 3-digit UN M49 code the WTO
/reportersendpoint advertises. Empty defaults to840(United States). - Partner —
000(World) only. The WTO indicators behind these rows,ITS_MTV_AXandITS_MTV_AM, publish a World total and answer HTTP 204 for every named partner, so no bilateral pair is obtainable. Empty defaults to000. - Years —
1–30, inclusive of both endpoints (years=10returns 11 calendar years). The seed holds the full 30-year window per reporter and the handler slices it, so every lookback is served from the same fetch.0selects the default of10.
years is rejected with HTTP 400 rather than silently replaced by a default. A well-formed request the seed cannot answer returns empty flows plus an unavailableReason from a closed vocabulary, so a coverage gap is distinguishable from a fault:
unavailableReason is the TradeFlowUnavailableReason enum, so the closed set is discoverable from the OpenAPI schema rather than only from prose. Values are prefixed TRADE_FLOW_UNAVAILABLE_REASON_:
Fleet-level coverage is published as the
tradeFlows entry in /api/health, whose records count is the number of reporter/World pairs actually seeded; a shortfall reads COVERAGE_PARTIAL, while a stopped seeder reads STALE_SEED.
Tariff-trend coverage
Without a partner or product group,get-tariff-trends is seed-backed (premium path). The Railway seeder pre-fetches WTO MFN applied averages (TP_A_0010) and this API serves slices of that history — it never calls WTO per request. A partner or product group selects a different series, described in Partner and product-group averages.
- Reporter — any 3-digit UN M49 code the WTO
/reportersendpoint advertises. Empty defaults to840(United States). - Partner — empty or
000(World) selects the MFN series. Any other code selects the partner series. - Product sector — empty or
allselects the All-products aggregate. A WITS product group selects the partner series; any other value is answeredNOT_COVERED. - Years —
1–30, inclusive of both endpoints (years=10returns 11 calendar years). The seed holds the full 30-year window per reporter and the handler slices it.0selects the default of10.
years is rejected with HTTP 400 rather than silently replaced by 840 / 10. A well-formed request the seed cannot answer returns empty datapoints plus an unavailableReason from the TariffTrendUnavailableReason enum (prefixed TARIFF_TREND_UNAVAILABLE_REASON_):
Fleet-level coverage is published as the
tariffTrendsUs entry in /api/health (the series name is historical; the probe is the fleet seed-meta:trade:tariffs record). A shortfall against the min-record floor reads COVERAGE_PARTIAL; a stopped seeder reads STALE_SEED.
Partner and product-group averages
Withpartner_country (other than 000) or product_sector set, get-tariff-trends answers what the reporter applies on goods from that partner, in that product group, or both. It reads the World Bank WITS tradestats-tariff dataset (UNCTAD TRAINS) live and caches each reporter, partner and group for seven days.
tariffRateis the effectively applied simple average (AHS-SMPL-AVRG): preferential rates count where the partner qualifies, otherwise MFN.weightedRateis the import-weighted average (AHS-WGHTD-AVRG). It is0where the reporter imported nothing in that group from that partner, and always0on the MFN series.partnerCountryechoes the requested code (Worldfor000or empty), andproductSectornames the group.- Product groups are WITS’s: the HS sections (
01-05_Animal,06-15_Vegetable,16-24_FoodProd,25-26_Minerals,27-27_Fuels,28-38_Chemicals,39-40_PlastiRub,41-43_HidesSkin,44-49_Wood,50-63_TextCloth,64-67_Footwear,68-71_StoneGlas,72-83_Metals,84-85_MachElec,86-89_Transport,90-99_Miscellan) and the aggregatesAgrRaw,Chemical,Food,Fuels,manuf,OresMtls,TextilesandTransp. Matching is case-insensitive. - Codes are 3-digit UN M49. WTO’s own codes for France (
251), Norway (579), India (699) and Switzerland (757) are accepted too. The EU (918) is a reporter but not a partner; name a member state instead. - MCP agents read these series, and the MFN series of any reporter, with
get_tariff_averages.
NOT_COVERED; an unreachable WITS is UPSTREAM_UNAVAILABLE.
Bilateral tariff lookup (API only)
GET /api/trade/v1/get-bilateral-tariff answers a question get-tariff-trends cannot: the applied tariff on one 6-digit HS product when one country imports it from another. It has no panel tab. It reads UNCTAD TRAINS live through the World Bank WITS API and caches each answer for seven days. One cached read per importer, product and year serves every partner.
The response returns
mfnRate, a partner-specific preferentialRate when one is on file, and appliedRate. Read basis before quoting appliedRate:
Rates are simple averages across the importer’s national tariff lines. When the MFN schedule has specific or compound duties, the reported average leaves them out (US wine, HS
220421, reads 0%); mfnAveRate then carries the WITS ad valorem equivalent and appliedRate uses it when MFN applies.
TRAINS does not record unilateral additional duties (US Section 301, 232 or IEEPA tariffs), anti-dumping or countervailing duties, or safeguards. For pairs subject to them, the applied rate here understates what importers pay.
unavailableReason is NOT_COVERED (upstreamUnavailable: false) when TRAINS has no row for that importer, product and year, and UPSTREAM_UNAVAILABLE (upstreamUnavailable: true) when WITS fails. The route is tier 1, so Pro and the API plans can call it. MCP agents call it as get_bilateral_tariff.
US import duty lookup (API only)
GET /api/trade/v1/get-us-import-duty answers the US case in full and for today: the current Harmonized Tariff Schedule (HTS) rates on one product from one country, plus the chapter 99 additional duties in force. WITS cannot do this. It has no US preferences after 2021, nothing at all after 2023, and no unilateral duties. It has no panel tab.
Each 8-digit line carries the three HTS rate columns and a
basis:
MFN(column 1 General).PREFERENTIAL: column 1 Special, under a trade agreement the exporter is party to.preferenceProgramnames it, for exampleSfor USMCA.COLUMN_2: Belarus, Cuba, North Korea and Russia. Russian goods on the U.S. note 30 lists take 35% or 70% instead of the printed column 2 rate, andbaseRatenames the heading (35% (9903.90.08)).
unresolvedPrograms. GSP is ignored, because it has been lapsed since 2021.
additionalDuties lists the chapter 99 duties for that line and exporter, each with a status:
The duties covered are:
- Section 301 China: Lists 1–4A, and the 2024 four-year-review increases. The review took its products off the lists, so the review rate replaces the list duty rather than adding to it.
- Section 301 China maritime and shipbuilding action (2025): +100% on intermodal chassis and their parts (
9903.91.12: statistical number8716.39.0090and subheadings8716.90.30and8716.90.50), and on ship-to-shore gantry cranes (9903.91.14, subheading8426.19.00). It is suspended until 2026-11-10 and reported asSCHEDULEDuntil then. It adds to any List duty on the same line, so chassis lines still on List 3 carry both. Because each heading covers only particular articles under its lines, the route then reports it asCONDITIONAL. - Section 301 forced-labor action: in force since 2026-07-24. It adds 10% or 12.5% on 60 trading partners, counting the EU as one. For the EU, Japan, South Korea, Switzerland and Taiwan, it raises the combined rate to a 10% or 12.5% floor instead (
topUpTo). It carries product, country and USMCA exemptions. - Section 301 Brazil: 25%.
- Section 232: steel, aluminum and copper; vehicles and parts; trucks and buses; lumber, furniture and cabinets; semiconductors; patented pharmaceuticals; and drones.
- IEEPA duties. The HTS still prints them, but they have not been collected since 2026-02-24, after Learning Resources v. Trump.
- The Section 122 surcharge, which expired on 2026-07-23.
- Anti-dumping and countervailing duties.
estimatedRate is the base rate plus the APPLIES duties. estimateComplete is false when the base rate is specific or compound, when any duty is CONDITIONAL or SCHEDULED, or when the chapter 99 index could not be read (additionalDutiesLoaded: false).
Product coverage for these duties lives in the chapter 99 U.S. notes, which run to about 5 MB, not in the product lines. seed-supply-chain-trade therefore parses the notes from the current HTS release every six hours. It publishes a catalog of measures, trade:us-hts:catalog:v1, plus one coverage shard per HS chapter, keyed by release. Each measure is located by an anchor phrase and validated. If any measure fails to resolve, the publish is skipped and the previous release stays in place. The route reads the product’s lines live from the HTS exportList service and caches them for a day under the catalog’s release, so a new release reads fresh lines. If the catalog is missing, it still serves the rates with additionalDutiesLoaded: false. Health is the usHtsDuties entry in /api/health.
unavailableReason is NOT_COVERED when the HTS has no line under the code, and UPSTREAM_UNAVAILABLE (upstreamUnavailable: true) when the HTS service fails. The route is tier 1. MCP agents call it as get_us_import_duty.
Refresh cadence
Per-tab fetches on mount and on tab switch; no polling. Upstream seeders run on varying cadences per dataset (tariffs daily, Comtrade monthly).Tier & gating
Panel shell is free. Nopremium flag in variant registrations. The generated REST service is mixed: restrictions, flows, barriers, and customs revenue are public; get-tariff-trends, list-comtrade-flows, and the API-only get-bilateral-tariff and get-us-import-duty are premium RPC paths and degrade to empty data without an entitled session or API key.
API reference
- Trade service — full schemas and query parameters for the six tab-backing RPCs.
