Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.7.0] - 2026-05-12

### Removed
- **Subject-based webhook subscriptions.** Mirrors the Tango API deprecation of subject-based subscriptions (see [makegov/tango#2267](https://github.com/makegov/tango/issues/2267)). Removed:
- `TangoClient.list_webhook_subscriptions()`, `get_webhook_subscription()`, `create_webhook_subscription()`, `update_webhook_subscription()`, `delete_webhook_subscription()`.
- `WebhookSubscription` and `WebhookSubjectTypeDefinition` dataclasses (and their `tango` package re-exports).
- `subject_types` and `subject_type_definitions` fields from `WebhookEventTypesResponse`; `default_subject_type` from `WebhookEventType`.
- `tango webhooks subscriptions` CLI group (`list` / `get` / `create` / `delete`).
- Endpoint CRUD, signing helpers, `WebhookReceiver`, `test_webhook_delivery`, `get_webhook_sample_payload`, and `list_webhook_event_types` are unaffected.

### Notes
- SemVer-major bump (still pre-1.0). Users who were calling `*_webhook_subscription` against an already-migrated tango will see `AttributeError`; against an older tango, the methods were removed before tango's API was. If you need to reach `/api/webhooks/subscriptions/` directly during a migration window, call `client._get` / `_post` / `_patch` / `_delete` with the path explicitly.

## [0.6.0] - 2026-05-07

### Added
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -353,9 +353,8 @@ tango webhooks simulate --secret $SECRET --event-type entities.updated # sign +
tango webhooks simulate --secret $SECRET --event-type entities.updated \
--to http://127.0.0.1:8011/tango/webhooks # also POST

# Manage real subscriptions and endpoints
# Manage delivery endpoints
tango webhooks endpoints create|list|get|delete
tango webhooks subscriptions create|list|get|delete

# Force a real test delivery from Tango
tango webhooks trigger
Expand Down
56 changes: 4 additions & 52 deletions docs/API_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1288,67 +1288,19 @@ for code in naics.results:

## Webhooks

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

> **For testing, signing, and a CLI tool**, see [`docs/WEBHOOKS.md`](WEBHOOKS.md). This section covers SDK method signatures only.

### list_webhook_event_types()

Discover supported `event_type` values and subject types.
Discover supported `event_type` values.

```python
info = client.list_webhook_event_types()
print(info.event_types[0].event_type)
```

### list_webhook_subscriptions()

```python
subs = client.list_webhook_subscriptions(page=1, page_size=25)
```

Notes:

- This endpoint uses `page` + `page_size` (tier-capped) rather than `limit`.

### get_webhook_subscription()

```python
sub = client.get_webhook_subscription("SUBSCRIPTION_UUID")
```

### create_webhook_subscription()

```python
sub = client.create_webhook_subscription(
"Track specific vendors",
{
"records": [
{"event_type": "awards.new_award", "subject_type": "entity", "subject_ids": ["UEI123ABC"]},
{"event_type": "awards.new_transaction", "subject_type": "entity", "subject_ids": ["UEI123ABC"]},
]
},
)
```

Notes:

- Prefer v2 fields: `subject_type` + `subject_ids`.
- Legacy compatibility: `resource_ids` is accepted as an alias for `subject_ids` (don’t send both).
- Catch-all: `subject_ids: []` means “all subjects” for that record and is **Enterprise-only**. Large tier users must list specific IDs.

### update_webhook_subscription()

```python
sub = client.update_webhook_subscription("SUBSCRIPTION_UUID", subscription_name="Updated name")
```

### delete_webhook_subscription()

```python
client.delete_webhook_subscription("SUBSCRIPTION_UUID")
```

### list_webhook_endpoints()

List your webhook endpoint(s).
Expand Down Expand Up @@ -1384,7 +1336,7 @@ print(result.success, result.status_code)

### get_webhook_sample_payload()

Fetch Tango-shaped sample deliveries (and sample subscription request bodies).
Fetch Tango-shaped sample deliveries.

```python
sample = client.get_webhook_sample_payload(event_type="awards.new_award")
Expand All @@ -1396,7 +1348,7 @@ print(sample["event_type"])
The API does not currently expose a public `/api/webhooks/deliveries/` or redelivery endpoint. Use:

- `test_webhook_delivery()` for connectivity checks
- `get_webhook_sample_payload()` for building handlers + subscription payloads
- `get_webhook_sample_payload()` for building handlers

### Receiving webhooks (signature verification)

Expand Down
47 changes: 9 additions & 38 deletions docs/WEBHOOKS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Webhooks Guide

This guide covers everything `tango-python` provides for **building, testing, and operating webhook integrations against the Tango API**: signing helpers, a local receiver, a command-line tool, and management commands for the underlying endpoints and subscriptions.
This guide covers everything `tango-python` provides for **building, testing, and operating webhook integrations against the Tango API**: signing helpers, a local receiver, a command-line tool, and management commands for delivery endpoints.

If you only need the SDK method signatures, see [`API_REFERENCE.md` § Webhooks](API_REFERENCE.md#webhooks). For the API-level contract (signing scheme, event taxonomy, retry behavior), see the [Tango Webhooks Partner Guide](https://docs.makegov.com/webhooks-user-guide/).

Expand All @@ -18,7 +18,6 @@ If you only need the SDK method signatures, see [`API_REFERENCE.md` § Webhooks]
- [`tango webhooks fetch-sample`](#tango-webhooks-fetch-sample)
- [`tango webhooks list-event-types`](#tango-webhooks-list-event-types)
- [`tango webhooks endpoints`](#tango-webhooks-endpoints)
- [`tango webhooks subscriptions`](#tango-webhooks-subscriptions)
- [Programmatic use](#programmatic-use)
- [Signature verification in your handler](#signature-verification-in-your-handler)
- [`WebhookReceiver` in pytest fixtures](#webhookreceiver-in-pytest-fixtures)
Expand Down Expand Up @@ -54,26 +53,24 @@ tango webhooks --help

## Concepts in 60 seconds

Tango webhooks have three pieces of state:
Tango webhooks have two pieces of state:

| Concept | What it is | Tango term |
|---|---|---|
| **Endpoint** | The URL Tango POSTs to, plus a generated signing secret | `WebhookEndpoint` |
| **Subscription** | A filter saying *which events* you want delivered to that endpoint | `WebhookSubscription` |
| **Delivery** | A single signed POST Tango makes when a matching event fires | (the request itself) |
| **Delivery** | A single signed POST Tango makes when an event fires | (the request itself) |

A typical setup:

1. **Create an endpoint** (`POST /api/webhooks/endpoints/`) with the public URL of your handler. Tango returns a `secret` — save it; it's used to sign every delivery.
2. **Create one or more subscriptions** (`POST /api/webhooks/subscriptions/`) describing the events your handler cares about (e.g. `entities.updated` for specific UEIs).
3. **Tango POSTs** to your endpoint when matching events fire. The body is JSON; the header `X-Tango-Signature: sha256=<hex>` is the HMAC-SHA256 of the raw body bytes keyed by your endpoint's secret.
4. **Your handler verifies the signature**, parses the body, and acts on it.
2. **Tango POSTs** to your endpoint when events fire. The body is JSON; the header `X-Tango-Signature: sha256=<hex>` is the HMAC-SHA256 of the raw body bytes keyed by your endpoint's secret.
3. **Your handler verifies the signature**, parses the body, and acts on it.

---

## Quickstart: zero to receiving

Assumes you have a `TANGO_API_KEY` and want to receive entity-update webhooks for a specific UEI.
Assumes you have a `TANGO_API_KEY` and want to receive webhooks.

### 1. See what you can subscribe to

Expand Down Expand Up @@ -121,12 +118,6 @@ When you're ready for end-to-end testing against Tango itself, expose your local
# Use the public URL the tunnel gave you.
tango webhooks endpoints create --url https://<your-tunnel>.ngrok.io/tango/webhooks
# Save the `secret` from the response — that's what your handler uses to verify.

tango webhooks subscriptions create \
--name "watch UEI ABC123" \
--event-type entities.updated \
--subject-type entity \
--subject-id ABC123
```

To force a real test delivery from Tango (without waiting for an actual event):
Expand Down Expand Up @@ -194,7 +185,7 @@ Three sources for the payload (mutually exclusive):

| Flag | Source | When to use |
|---|---|---|
| `--event-type X` | Fetches the canonical sample for `X` from Tango | You want a realistic body without setting up a subscription |
| `--event-type X` | Fetches the canonical sample for `X` from Tango | You want a realistic body without waiting for a real delivery |
| `--payload-file PATH` | Reads a JSON file | You're testing a specific shape (regression, edge case) |
| *(neither)* | A built-in placeholder envelope | Smoke-testing the wiring |

Expand Down Expand Up @@ -239,23 +230,6 @@ tango webhooks endpoints delete ENDPOINT_ID [--yes]

`create` returns the generated `secret` once — save it. `delete` prompts for confirmation; `--yes` skips. `--inactive` registers the endpoint disabled (no deliveries until you re-enable it).

### `tango webhooks subscriptions`

Manage **what Tango delivers**.

```bash
tango webhooks subscriptions list [--page N] [--page-size N]
tango webhooks subscriptions get SUBSCRIPTION_ID
tango webhooks subscriptions create \
--name "watch UEI ABC123" \
--event-type entities.updated \
--subject-type entity \
--subject-id ABC123
tango webhooks subscriptions delete SUBSCRIPTION_ID [--yes]
```

`create` builds a single-record subscription (one event type, one subject type, one or more subject IDs). For multi-record subscriptions, call `client.create_webhook_subscription(...)` directly with a hand-crafted `payload` dict.

---

## Programmatic use
Expand Down Expand Up @@ -346,20 +320,17 @@ with WebhookReceiver(secret="s").run() as rx:

## Common workflows

### "I'm starting fresh — set me up to receive entity updates"
### "I'm starting fresh — set me up to receive webhooks"

```bash
export TANGO_API_KEY=...
# 1. Confirm event types
tango webhooks list-event-types
# 2. Stand up a tunnel so Tango can reach you
ngrok http 8011 &
# 3. Register your endpoint and subscription
# 3. Register your endpoint
tango webhooks endpoints create --url https://<id>.ngrok.io/tango/webhooks
# (save the `secret` from the response into TANGO_WEBHOOK_SECRET)
tango webhooks subscriptions create \
--name "entities" --event-type entities.updated \
--subject-type entity --subject-id <UEI>
# 4. Run the listener pointed at your downstream handler
tango webhooks listen --port 8011 --secret $TANGO_WEBHOOK_SECRET \
--forward-to http://localhost:4242/wh
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "tango-python"
version = "0.6.0"
version = "0.7.0"
description = "Python SDK for the Tango API"
readme = "README.md"
requires-python = ">=3.12"
Expand Down
6 changes: 1 addition & 5 deletions tango/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
WebhookEndpoint,
WebhookEventType,
WebhookEventTypesResponse,
WebhookSubjectTypeDefinition,
WebhookSubscription,
WebhookTestDeliveryResult,
)
from .shapes import (
Expand All @@ -37,7 +35,7 @@
)
from .webhooks.receiver import Delivery, WebhookReceiver

__version__ = "0.6.0"
__version__ = "0.7.0"
__all__ = [
"TangoClient",
"TangoAPIError",
Expand All @@ -56,8 +54,6 @@
"WebhookEndpoint",
"WebhookEventType",
"WebhookEventTypesResponse",
"WebhookSubscription",
"WebhookSubjectTypeDefinition",
"WebhookTestDeliveryResult",
"ShapeParser",
"ModelFactory",
Expand Down
Loading
Loading