feat: tango-python 1.0.0 — API parity, webhook overhaul, docs auto-pull, packaging guardrails#25
Merged
Merged
Conversation
Internal HTTP helpers previously accepted only positional `json_data`.
Docs examples and some user code (and a few SDK callers) pass `json=`,
which raised `TypeError`. Make both work — `json_data=` (positional or
keyword) and `json=` (keyword-only) both supply the request body. Empty
body defaults to `{}` for callers that POST/PATCH with no payload.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
These endpoints all accept `?ordering=` server-side (per OpenAPI), but the SDK methods didn't expose it. Add explicit `ordering: str | None` to: - list_forecasts - list_grants - list_subawards - list_gsa_elibrary_contracts - list_opportunities - list_notices - list_protests Pattern matches the existing `ordering` on list_otas / list_otidvs / list_contracts / list_idvs / list_vehicles / list_vehicle_orders. Prefix with `-` for descending. list_entities, list_vehicle_awardees, and list_idv_transactions intentionally NOT updated — those endpoints don't expose `ordering` in their OpenAPI parameters. list_idv_awards / list_idv_child_idvs already accept `**kwargs`, so `ordering=` flows through unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Tango webhooks API requires `name` on WebhookEndpoint create (the server enforces unique(user, name)), and `endpoint` UUID on WebhookSubscription create when a user owns more than one endpoint. The SDK's create methods previously didn't accept either, forcing callers to fall back to raw `_post` or use the `/api/webhooks/alerts/` convenience API. Changes: - create_webhook_endpoint: add keyword-only `name=`. Currently optional for backward compatibility; emits DeprecationWarning when omitted and will become required in a future major version. Documents the server-side 400 callers see today when they skip it. - create_webhook_subscription: add keyword-only `endpoint=`, `subscription_type=`, `query_type=`, `filter_definition=`, `frequency=`, `cron_expression=`, `is_active=`. Mirrors the full WebhookSubscriptionSerializer surface. - update_webhook_subscription: add `frequency=`, `cron_expression=`, `is_active=` so filter subscriptions can be paused / re-frequencied via the canonical PATCH route. - update_webhook_endpoint: add `name=` so endpoints can be renamed. All additions are keyword-only and backward compatible — existing call sites continue to work unchanged. The single in-tree test that constructs a webhook endpoint is updated to pass `name="primary"` so it doesn't trip the new DeprecationWarning. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Surveyed the OpenAPI schema and added the methods that were missing
from the SDK. Existing list_/get_ patterns preserved throughout —
shape-aware where the underlying endpoint supports shaping, plain
dict-returning where it doesn't.
Webhook alerts (convenience layer over filter subscriptions):
- list_webhook_alerts / get_webhook_alert / create_webhook_alert
/ update_webhook_alert / delete_webhook_alert
- New WebhookAlert dataclass
Resolve / validate (POST-based identity helpers):
- resolve(name, target_type, ...) — entity/org name disambiguation
- validate(identifier_type, value) — PIID/solicitation/UEI format check
- New ResolveCandidate, ResolveResult, ValidateResult dataclasses
Reference data:
- list_departments, get_department
- list_psc, get_psc, get_psc_metrics
- get_naics, get_naics_metrics
- get_business_type
- list_assistance_listings, get_assistance_listing
- list_mas_sins, get_mas_sin
Entity sub-resources (all under /api/entities/{uei}/...):
- list_entity_contracts, list_entity_idvs
- list_entity_otas, list_entity_otidvs
- list_entity_subawards, list_entity_lcats
- get_entity_metrics
IDV sub-resources:
- list_idv_lcats
Agency sub-resources:
- list_agency_awarding_contracts
- list_agency_funding_contracts
Misc:
- search_opportunity_attachments(q, top_k, include_extracted_text)
- get_version()
- list_api_keys()
Decisions:
- Kept `uuid=` for vehicle methods to match `get_vehicle(uuid)` and the
existing list_vehicle_awardees / list_vehicle_orders surface — these
match the API path param. IDVs use `key=`, vehicles use `uuid=`, and
the SDK stays consistent.
- Stayed away from /api/accounts/usage/ admin endpoints, /api/events/,
/api/news/, and /api/company/rag/ — niche internal surfaces. Easy to
add when there's user demand.
- list_entities, list_vehicle_awardees, and list_idv_transactions are
not getting `ordering` because their server-side parameters don't
advertise it (verified against OpenAPI).
Backward compatible — all additions, no existing call sites changed.
Existing tests still pass (238/238).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two pieces:
1. tests/test_api_parity.py — 40 mock-driven unit tests covering:
- `_post`/`_patch` json kwarg aliases
- resolve / validate (POST endpoints) request shape + response parsing
- WebhookAlert CRUD (create / update / list)
- WebhookEndpoint create/update with name= (and DeprecationWarning
when name= is omitted)
- WebhookSubscription create with endpoint=, subscription_type=,
filter fields
- ordering= threading on the seven affected list_* methods
- Reference-data and entity/agency sub-resource URL construction
2. scripts/smoke_api_parity.py — live smoke test that hits every method
the branch added or changed against a running Tango. Skips
gracefully when the local server can't service a particular
endpoint (404 for attachment-search without the RAG index; 504 from
funding-contracts aggregation; alerts can't auto-resolve endpoint
when the test user owns multiple). Creates webhook endpoints +
subscriptions, verifies them, and tears them down. Local run on
2026-05-11: 45/45 PASS (with three SKIPs for the environmental
reasons above).
Also includes a trivial ruff-format collapse in models.py
(VEHICLE_ORDERS_MINIMAL one-liner).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…inst actual surface Audit pass on the parity branch's internal docs vs `tango/client.py`, `tango/models.py`, and `tango/webhooks/`. README.md: - Fixed unclosed code block in Quick Start (was causing `## Authentication` to render inside the code block). - Added examples for PSC, `get_naics`, MAS SINs, assistance listings, departments, resolve/validate, IT Dashboard, and entity sub-resources. docs/API_REFERENCE.md: - Added sections for previously undocumented methods: PSC, MAS SINs, Assistance Listings, Departments, Business Types (by code), IT Dashboard, Entity Sub-resources, IDV LCATs, Agency Sub-resources, Resolve/Validate, Opportunity Attachments, Webhook Alerts, Utility, `get_naics`/`get_naics_metrics`. - Removed two stale phantom methods from the ShapeConfig table: `search_contracts` and `list_organization_offices` (neither exists in source). - Added `VEHICLE_ORDERS_MINIMAL` and `ITDASHBOARD_INVESTMENTS_MINIMAL` rows to the ShapeConfig table. - Fixed `list_agencies` Parameters list (missing `search` param). - Fixed `search_opportunity_attachments` signature (had nonexistent `limit`; actual interface is `(q, top_k=None, include_extracted_text=None)`). - `VEHICLES_MINIMAL` ShapeConfig row updated to match the 17-field constant in `models.py` (was a stale 8-field list). - Added missing `ITDASHBOARD_INVESTMENTS_COMPREHENSIVE` ShapeConfig row. docs/DEVELOPERS.md: - Removed phantom `use_dynamic=True` parameter from all 7 code examples (the param doesn't exist on any client method). - Removed phantom ShapeConfig constants from examples: `CONTRACTS_SUMMARY`, `CONTRACTS_COMPREHENSIVE`, `CONTRACTS_FOR_ANALYSIS`, `ENTITIES_STANDARD`. Replaced with constants that actually exist. - Corrected `ENTITIES_MINIMAL` field list (`display_name` -> `legal_business_name`) and `ENTITIES_COMPREHENSIVE` (removed ~9 fields not in the actual constant). docs/SHAPES.md / docs/WEBHOOKS.md: - Updated `WebhookReceiver` import paths — it's exported from the top-level `tango` package, not `tango.webhooks`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors `tango-node/docs/DYNAMIC_MODELS.md` so the two SDK repos have matching internal-docs structure. Translated entirely to Python idioms. Sections: - Overview - Components — ShapeParser / SchemaRegistry / TypeGenerator / ModelFactory - Full Shaping Pipeline (manual) - Attribute Access — Python-specific addition covering `__getattr__` and its helpful error messages - Type Safety - Caching - Nested Models - Predefined Shape Constants Key Python divergences from the Node version (corrected on this side rather than copied): - `SchemaRegistry.get_schema(ModelClass)` returns a full dict (not Node's `getField()`). - TypeGenerator uses LRU eviction, not the FIFO the Node doc claims. - No `ShapedListModel` — that class doesn't exist in the Python codebase. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Folds in the work from PR #27 (issue-2275-drop-subjects) plus three cleanup-batch carry-overs from tango's `dogpile/api-cleanup-batch`: - #2256: add `endpoint` kwarg to create_webhook_alert (multi-endpoint accounts can now use the convenience wrapper) - #2254: remove `ordering` kwarg from list_notices and list_protests (server rejects every value) - #2252: switch test_webhook_delivery to canonical `endpoint` body key Closes makegov/tango#2275 — part of makegov/tango#2267. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Receivers copy-pasting `generate_signature(...)` into their handler used
to produce a bare hex digest like `"abc123..."` and had to remember to
wrap it as `f"sha256={sig}"` to match the dispatcher's wire format.
Forgetting the wrap silently produced malformed signatures that failed
verification on the other side.
`generate_signature(body, secret)` now returns the full
`"sha256=<lowercase hex>"` form, ready to be assigned to the
`X-Tango-Signature` header directly. `verify_signature` continues to
accept both the prefixed wire form and the bare-hex form so callers
that pre-strip the prefix keep working — added an explicit regression
test for that.
Audited and updated:
- `tango/webhooks/simulate.py` — `sign()` builds the header from the
prefixed form directly; `SignedRequest.signature` field stays bare hex
- `tango/webhooks/cli.py` — uses `SIGNATURE_PREFIX` for the printed
signature value (via the SimulationResult.signature bare-hex field)
- `tests/test_webhooks_signing.py` — vectors updated to prefixed form;
added explicit dual-form acceptance test
- `tests/test_webhooks_receiver.py` — `_post_signed` no longer
double-wraps the header
- `docs/WEBHOOKS.md` and `docs/API_REFERENCE.md` — receiver examples
drop the manual `f"sha256={...}"` wrap
Breaking change for direct callers of `generate_signature` who relied
on bare-hex output — pass through `parse_signature_header()` to recover
the previous form. Folded into the v0.7.0 release.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Aligns examples with the post-dogpile event-type taxonomy
(only alerts.{opportunity,contract,entity,grant,forecast}.match
emit now).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The CLI was missing the `--name` option entirely. Since tango#2267, the server enforces `unique(user, name)` on webhook endpoints and `name` is a required API field — omitting it causes a 400 from the server. Pass `--name` through to `client.create_webhook_endpoint(name=...)`. Test: updated `test_cli_endpoints_create_returns_secret` to supply `--name default`; verified `--help` output includes the option. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After tango#2275 (migration 0019_drop_subject_webhooks), all legacy subject-based rows with null query_type / filter_definition were deleted. The model fields are now non-nullable at the DB level. - query_type: str | None → str - filters: dict | None → dict[str, Any] - status: str → Literal["active", "paused"] (server serializer maps is_active=True→"active", False→"paused") Adds docstring note explaining the migration context and referencing the tango migration file. Aligns Python with Node's WebhookAlert interface in tango-node/src/models/Webhooks.ts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nion
Parity with `tango-node`'s `WebhookSamplePayloadResponse` discriminated union.
The endpoint can return one of two response shapes depending on whether
`event_type` is supplied:
- Single-event: `{event_type, sample_delivery, signature_header, note}`
- All-events: `{samples, usage, signature_header, note}`
Added `WebhookSampleDelivery`, `WebhookSamplePayloadSingleResponse`,
`WebhookSamplePayloadAllResponse`, and the union alias
`WebhookSamplePayloadResponse` as TypedDict classes (structural, no runtime
conversion). Exported from the top-level `tango` package and used as the
return annotation on `client.get_webhook_sample_payload()`. No behavior
change — just better static typing.
Verified against the live API:
- single (`alerts.contract.match`): returns 4 keys matching the Single shape
- all: returns 4 keys matching the All shape
All 285 unit tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… language After the `endpoints create --name` CLI fix, three examples still showed the old `tango webhooks endpoints create --url URL` shape. Without `--name` the CLI now errors out (Click marks it required). Updated all three call sites (lines 123, 243, 373) to include `--name`. Also fixed the stale "one endpoint per user" wording in the troubleshooting section. Endpoint uniqueness is on `(user, name)` post-tango#2256 — users can have multiple endpoints with distinct names. The 400 case is name collision, not a user-level cap. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tango server (makegov/tango#2259) now accepts both spellings as expand aliases: - `naics(code,description)` — canonical - `naics_code(code,description)` — alias rewritten to `naics` at parse time - Same pair for `psc(...)` / `psc_code(...)`. Pre-fix the SDK's ShapeParser rejected the canonical server form and demanded the `_code` alias — exactly opposite to the server. Mirror the server's `_EXPAND_ALIASES` map locally so both spellings are accepted client-side when used as expansions (with parens or wildcards). Bare scalar leaves `naics_code` / `psc_code` are untouched, matching server semantics. Also added explicit `naics` / `psc` expand entries to Contract, Forecast, Opportunity, Notice, and Vehicle schemas in `tango/shapes/explicit_schemas.py` so the canonical form validates locally too (IDV already had them). Tests: 11 new cases in `TestShapeParserExpandAliases`. 261/261 unit tests pass on Python 3.12 and 3.13. (Python 3.14 in the worktree's default venv has pre-existing typing-related failures unrelated to this change.) Refs: makegov/tango#2266, makegov/tango#2259 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous SUBAWARD_SCHEMA declared `id` and `amount` (rejected by the server with `unknown_field`) and was missing every real field on the resource. Replace it with a schema derived from `awards.serializers.subawards.SubawardSerializer` plus the runtime `available_fields` payload: `key` / `award_key` / `piid` / `usaspending_permalink`, the denormalized `prime_awardee_*` / `recipient_*` lookup columns, and expandable objects for `awarding_office`, `funding_office`, `prime_recipient`, `subaward_recipient`, `place_of_performance`, `subaward_details`, `fsrs_details`, and `highly_compensated_officers`. New nested schemas `SubawardDetails`, `FsrsDetails`, `SubawardPlaceOfPerformance`, and `HighlyCompensatedOfficer` back the expansions. Also update the `Subaward` dataclass in `tango/models.py` to match. Conformance and unit tests pass.
Adds three SDK-doc files that previously lived only in makegov/docs under docs/sdks/python/. This is the pre-cutover content port for the auto-pull pipeline (makegov/docs#15), so the docs-site versions can be deleted at cutover without losing content. - docs/ERRORS.md — exception hierarchy, recovery patterns, and shape-error classes (ShapeValidationError, ShapeParseError, TypeGenerationError, ModelInstantiationError) that have no dedicated entry in API_REFERENCE.md - docs/PAGINATION.md — page-based vs cursor-based strategies, iteration patterns, PaginatedResponse field reference - docs/CLIENT.md — TangoClient constructor reference, rate_limit_info and last_response_headers properties, retry-semantics note Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…gov/docs#15) Fires on push to main when docs/, README.md, or CHANGELOG.md changes and dispatches `external_updated` to makegov/docs so the public docs site rebuilds. Required so the auto-pull pipeline picks up SDK doc changes without waiting for someone to push to the docs composer. Requires `DOCS_DISPATCH_TOKEN` secret (contents:write on makegov/docs). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
First stable release. The previously-pending 0.7.0 changeset (API parity, subject-based webhook removal, shape-validator alias support) plus the docs-only content port (makegov/docs#16) and the docs-dispatch CI wiring for the makegov/docs#15 auto-pull pipeline now ship together as 1.0.0. From here on, breaking changes will require a major bump. - pyproject.toml: 0.7.0 → 1.0.0 - tango/__init__.py: __version__ 0.7.0 → 1.0.0 - CHANGELOG: rename [Unreleased] → [1.0.0] - 2026-05-13; rewrite intro framing; add CI section for the new docs-dispatch workflow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Hatchling's default sdist behavior packs everything in the project root, which is fine in CI (clean checkout) but ships per-developer state from a laptop: .mg-tools/, .claude/, .claude.bak.*/, CLAUDE.md, ROADMAP.md, work diaries, scratch reports, .coverage, etc. None of those have ever been on PyPI because every release has shipped via the publish.yml workflow's clean GitHub Actions checkout — but a single accidental `uv publish` from a dev machine would have leaked all of them. This adds an explicit `[tool.hatch.build.targets.sdist]` include list plus a small exclude list for the internal docs that live inside included dirs (docs/DEVELOPERS.md, docs/quick_start.ipynb). Even from a developer's laptop, only the listed paths can now end up on PyPI. Verified locally: `uv build` produces a sdist whose top-level entries are .github, .gitignore, CHANGELOG.md, docs, LICENSE, PKG-INFO, pyproject.toml, README.md, scripts, tango, tests. No more .mg-tools, .claude, CLAUDE.md, .coverage, DEVELOPERS.md, or quick_start.ipynb. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI fix
- `tests/test_webhooks_receiver.py::test_receiver_404s_on_unknown_path`
was failing on windows-latest with `httpx.ReadError WinError 10053`.
The receiver's do_POST returned a 404 before draining the request body
on unknown paths. On Linux/macOS that's absorbed silently; on Windows
the abrupt socket close abort the still-pending request. do_POST now
drains the body first, then branches on path.
Behavior changes
- `_post` / `_patch` now raise `TangoValidationError` when both
`json_data=` and `json=` are passed instead of silently preferring
one. Single-kwarg usage is unchanged.
- `create_webhook_endpoint(name=...)` is now **required** (was a
`DeprecationWarning` in 0.7.0 — never publicly released). The server
enforces unique(user, name), so omitting `name` returned a 400
anyway. Raising client-side gives a clear error and avoids the
wasted round-trip. Updated `test_create_endpoint_without_name_warns`
→ `..._raises`.
Docs/example fixes
- `get_psc_metrics` / `get_naics_metrics` / `get_entity_metrics`
docstrings — `period_grouping` values are "month"/"quarter"/"year"
(path-segment values), not "monthly"/"quarterly".
- `docs/API_REFERENCE.md#get_agency` — consistent `get_agency("GSA")`
example + note that the parameter accepts CGAC / FPDS / short code /
abbreviation / canonical name.
- `README.md` Quick Start — `agency.name` instead of `agency['name']`;
`get_agency()` returns an `Agency` dataclass.
- `scripts/smoke_api_parity.py` — `list_business_types(limit=1)` is
now wrapped in the `run(...)` helper so a failure there records FAIL
instead of crashing the whole script.
CHANGELOG updated to reflect the behavior changes.
Closes the open Copilot comments on PR #25.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
1.0.0 release notes (additions since this PR was opened)
This PR was originally scoped to the API parity work captured below. Over the last week it absorbed three additional changesets and a packaging hardening pass, which collectively justify the major-version bump from 0.6.x → 1.0.0:
224878e, tango#2252) —generate_signature(body, secret)now returns the canonical"sha256=<hex>"wire form instead of bare hex. Breaking for receivers that consumed the bare hex directly.verify_signaturestill accepts both forms.7ede733, tango#2275) —list_webhook_subscriptions/get_webhook_subscription/create_webhook_subscription/update_webhook_subscription/delete_webhook_subscriptionand theWebhookSubscription/WebhookSubjectTypeDefinitiondataclasses are gone. Subject filtering migrates tocreate_webhook_alert(...)and the alerts API.naics(...)/psc(...)expansions (7b233c5, tango#2266) —ShapeParser.validate()now mirrors the server's_EXPAND_ALIASEStable;naics_code(...)andpsc_code(...)aliases are rewritten to the canonical form at parse time. Closes the parser-vs-server divergence flagged in tango#2266.create_webhook_endpoint(name=...)is now required, not deprecated (132641f, addresses Copilot review). 0.7.0 was going to emit aDeprecationWarning; since 0.7.0 never publicly released, 1.0.0 makes it a hardTangoValidationErrorinstead. The server enforcesunique(user, name)so the warn-then-400 path was strictly worse DX._post/_patchraise onjson_data=+json=collision (132641f) — previously silently preferredjson_data. Now raisesTangoValidationErrorto surface caller bugs.42d760d, makegov/docs#16) — newdocs/ERRORS.md,docs/PAGINATION.md,docs/CLIENT.mdported fromdocs.makegov.com/sdks/python/so the docs-site auto-pull (makegov/docs#15) can replace the hand-maintained pages.docs-dispatchCI workflow (401207e, makegov/docs#15) — fires on push tomainfordocs/**,README.md,CHANGELOG.md. Triggers a rebuild of docs.makegov.com when the SDK content changes.06e7feb) — explicit[tool.hatch.build.targets.sdist]whitelist. Even from a developer laptop,uv publishcan no longer bundle.mg-tools/,.claude/,CLAUDE.md,ROADMAP.md, work diaries, scratch reports, or.coverageto PyPI. CI publishing was already clean; this closes the local-publish foot-gun.132641f) —do_POSTnow drains the request body before returning a 404, fixingWinError 10053onwindows-latestrunners.CHANGELOG
[1.0.0]section reflects everything above.[Unreleased]is empty.Summary
Brings
tango-pythonto full API parity with Tango: 30+ new methods, signature fixes for write paths,orderingkwargs on list endpoints that were silently missing them, and four new typed response models. 278 unit tests pass, 45/45 smoke checks pass against a running Tango instance, 80% line coverage across the package.Branch: 5 commits on
feat/api-parityoffmain.What's new
Resolve / Validate utilities (typed wrappers)
Two new top-level methods replace the prior
client._post("/api/resolve/", ...)workaround:resolve(name, target_type, ...)— POST/api/resolve/. Returns a typedResolveResultwithResolveCandidateentries (identifier,display_name,match_tieron Pro+). Optional context:state,city,contextstring. Backed by new dataclasses exported from the package root.validate(identifier_type, value)— POST/api/validate/. ReturnsValidateResult.Webhook alerts CRUD parity
The convenience layer over
/api/webhooks/alerts/(filter subscriptions) was previously create-only. Full CRUD now:list_webhook_alerts(...)get_webhook_alert(id)create_webhook_alert(name=, query_type=, filters=, frequency=, cron_expression=)update_webhook_alert(id, ...)delete_webhook_alert(id)WebhookAlertdataclass exported fromtango.Webhook write-method signatures, completed
The SDK's write methods were missing fields that the Tango API has required since multi-endpoint support landed. Catching up:
create_webhook_endpointnow acceptsname=(keyword-only). Required by the server; omitting it emits aDeprecationWarningand will become an error in a future major version.create_webhook_subscriptionacceptsendpoint=,subscription_type=,query_type=,filter_definition=,frequency=,cron_expression=,is_active=— covers both subject and filter subscription patterns through the canonical/api/webhooks/subscriptions/API.update_webhook_subscriptionacceptsfrequency=,cron_expression=,is_active=.update_webhook_endpointacceptsname=for renames.Reference data
list_departments,get_department,list_psc,get_psc,get_psc_metrics,get_naics,get_naics_metrics,get_business_type,list_assistance_listings,get_assistance_listing,list_mas_sins,get_mas_sin.Entity sub-resources
list_entity_contracts,list_entity_idvs,list_entity_otas,list_entity_otidvs,list_entity_subawards,list_entity_lcats,get_entity_metrics. All shape-aware where the underlying endpoint supports shaping.IDV + agency sub-resources
list_idv_lcatslist_agency_awarding_contracts,list_agency_funding_contractsMisc
search_opportunity_attachments(q, top_k, include_extracted_text)for/api/opportunities/attachment-search/get_version()for/api/version/list_api_keys()for/api/api-keys/orderingparameter where the API supports itSeven list methods now accept
ordering=and pass it through:list_forecasts,list_grants,list_subawards,list_gsa_elibrary_contracts,list_opportunities,list_notices,list_protests. Prefix with-for descending. This closes parity gaps where the API documented?ordering=but the SDK silently rejected the kwarg.Fixed
TangoClient._post()and_patch()now accept bothjson_data=(positional, original) andjson=(kwarg). Internal callers and docs examples that usejson=no longer fail withTypeError.Testing
pytest: 412 passed, 31 skipped — was 238 before this branch; +40 new tests intests/test_api_parity.py.80% line coverageacrosstango/(pytest --cov).mypyclean on changed code;ruffclean.scripts/smoke_api_parity.pyagainsthttp://localhost:8000): 45/45 pass. Three blocks SKIPped for environmental reasons (/attachment-search/404 locally,/funding-contracts/504 on the test agency,create_webhook_alertskipped when the test account has multiple endpoints — see makegov/tango#2256).Decisions worth flagging
uuid=on vehicle methods (list_vehicle_awardees,list_vehicle_orders). Matches the existingget_vehicle(uuid)path-param convention. SDK convention: IDVs usekey=, vehicles useuuid=.Known issues uncovered during parity work (tracked separately on tango)
/subscriptions/POST uses fieldendpoint;/endpoints/test-delivery/usesendpoint_id.orderingon/notices/,/protests/,/subawards/but viewsets reject most values./api/webhooks/alerts/; backend needs to accept an explicitendpointfield on the alert create serializer before the SDKs can paper over the limitation.Risks
DeprecationWarningoncreate_webhook_endpointwithoutname=may surface in user logs — intentional, since the API will eventually rejectname-less requests outright.Sibling work
Companion PR in
tango-nodecovers the same parity work for the Node SDK. Docs are updated to reflect both SDKs' new surfaces inmakegov/docs#9.– Hal 🤖