Skip to content

Commit a84ba89

Browse files
committed
fix(cohorts): restore the response envelope Amplitude's production parser reads
1 parent e252a36 commit a84ba89

3 files changed

Lines changed: 23 additions & 10 deletions

File tree

api/cohorts/sync_views.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@
2222
)
2323

2424
_LIST_RESPONSE = inline_serializer(
25-
"AmplitudeListResponse", {"listId": serializers.UUIDField()}
25+
"AmplitudeListResponse",
26+
{
27+
"list_id": serializers.UUIDField(),
28+
"response": inline_serializer(
29+
"AmplitudeListResponseEnvelope", {"list_id": serializers.UUIDField()}
30+
),
31+
},
2632
)
2733

2834
_MIXPANEL_RESPONSE = inline_serializer(
@@ -73,10 +79,15 @@ def create(self, request: Request) -> Response:
7379
name=serializer.validated_data["name"],
7480
source_type=CohortSourceType.AMPLITUDE,
7581
)
76-
# Amplitude's production sync worker reads the list ID from its
77-
# documented default key, camelCase "listId", ignoring the response
78-
# path configured in the Integration Portal.
79-
return Response({"listId": str(cohort.uuid)})
82+
# Amplitude reads the list ID from this body at the path configured
83+
# in the Integration Portal, which their portal requires to start
84+
# with "response." — but the two sides of Amplitude disagree on what
85+
# that path is applied to. Their Testing tab wraps this body in a
86+
# {"response": ...} envelope first, so it finds the flat copy; their
87+
# production sync worker applies the same path to the raw body, so
88+
# it needs the nested copy. Verified against both, 2026-08-28.
89+
list_id = str(cohort.uuid)
90+
return Response({"list_id": list_id, "response": {"list_id": list_id}})
8091

8192
@action(detail=True, methods=["POST"])
8293
def add(self, request: Request, pk: str) -> Response:

api/tests/unit/cohorts/test_sync_views.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ def test_amplitude_create_list__valid_key__creates_amplitude_cohort(
5050

5151
# Then
5252
assert response.status_code == status.HTTP_200_OK
53-
cohort = Cohort.objects.get(uuid=response.json()["listId"])
53+
cohort = Cohort.objects.get(uuid=response.json()["list_id"])
54+
# Amplitude's Testing tab reads the flat copy, production the nested one.
55+
assert response.json()["response"]["list_id"] == response.json()["list_id"]
5456
assert cohort.environment == key.environment
5557
assert cohort.source_type == CohortSourceType.AMPLITUDE
5658
assert cohort.segment.name == "[Amplitude] Beta users: 1234"
@@ -74,7 +76,7 @@ def test_amplitude_create_list__postgres_environment__creates_cohort(
7476

7577
# Then
7678
assert response.status_code == status.HTTP_200_OK
77-
cohort = Cohort.objects.get(uuid=response.json()["listId"])
79+
cohort = Cohort.objects.get(uuid=response.json()["list_id"])
7880
assert cohort.environment == environment
7981

8082

@@ -350,7 +352,7 @@ def test_amplitude_create_list__valid_key__audits_and_queues_environment_update(
350352
# Then - the audit record carries no user, names the source, and is the
351353
# hook that rebuilds the environment document.
352354
assert response.status_code == status.HTTP_200_OK
353-
cohort = Cohort.objects.get(uuid=response.json()["listId"])
355+
cohort = Cohort.objects.get(uuid=response.json()["list_id"])
354356
audit_log = AuditLog.objects.get(related_object_id=cohort.segment_id)
355357
assert audit_log.author is None
356358
assert audit_log.master_api_key is None
@@ -377,7 +379,7 @@ def test_amplitude_create_list__valid_key__history_records_no_user(
377379
# Then - a machine caller leaves no user on historical records; stamping
378380
# one would fail, since the sync key is not a Flagsmith user.
379381
assert response.status_code == status.HTTP_200_OK
380-
cohort = Cohort.objects.get(uuid=response.json()["listId"])
382+
cohort = Cohort.objects.get(uuid=response.json()["list_id"])
381383
history_record = cohort.segment.history.get()
382384
assert history_record.history_user is None
383385
assert history_record.master_api_key is None

docs/docs/deployment-self-hosting/observability/_events-catalogue.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ Attributes:
166166
### `cohorts.sync_webhook.rejected`
167167

168168
Logged at `warning` from:
169-
- `api/cohorts/sync_views.py:217`
169+
- `api/cohorts/sync_views.py:228`
170170

171171
Attributes:
172172
- `action`

0 commit comments

Comments
 (0)