Skip to content

Commit ccc74ff

Browse files
ispyisailclaude
andcommitted
Update docs with verified API findings from Hello Club support
All fixes verified against live API (Mar 2026, scripts/verify_fixes.py): - Pagination: sort=-updatedAt,id eliminates duplicates (83 dupes -> 0) - Incremental sync: ?updatedAt=<date> filter works (1,381 members, 100% correct) - /event/upcoming: confirmed removed (not a bug), returns 400 - checkInLog/emailLog: dates confirmed required (not a spec bug), returns 422 - All behaviours confirmed to carry over to V2 - Added verification script: scripts/verify_fixes.py Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e90ce94 commit ccc74ff

3 files changed

Lines changed: 302 additions & 16 deletions

File tree

docs/endpoints.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# API Endpoints
22

3-
> **Tested:** Mar 2026 | **67 endpoint variations tested** | **20 endpoints working, 1 broken**
3+
> **Tested:** Mar 2026 | **67 endpoint variations tested** | **20 endpoints working, 1 removed**
44
55
## Endpoint Status
66

@@ -9,7 +9,7 @@
99
| Method | Path | Status | Description |
1010
|--------|------|--------|-------------|
1111
| GET | `/event` | **Working** | Query events (supports fromDate, toDate, many filters) |
12-
| GET | `/event/upcoming` | **Broken (400)** | Returns 400 for all params. Use `/event` with date range. |
12+
| GET | `/event/upcoming` | **Removed** | Returns 400. Confirmed removed by Hello Club — use `/event` with date range. |
1313
| GET | `/event/{id}` | **Working** | Get single event by ID |
1414
| POST | `/event` | Documented | Create event |
1515
| DELETE | `/event/{id}` | Documented | Delete event |
@@ -88,7 +88,7 @@ These parameters are supported across most GET list endpoints:
8888
|-----------|------|-------------|
8989
| `limit` | integer (0-100) | Results per page (default: 100) |
9090
| `offset` | integer | Pagination offset |
91-
| `sort` | string | Sort field (prefix `-` for descending, e.g. `-startDate`) |
91+
| `sort` | string | Sort field (prefix `-` for descending, e.g. `-startDate`). **Append `,id` for stable pagination** (e.g. `-updatedAt,id`). |
9292
| `fields` | string | Comma-separated field list to restrict response |
9393
| `search` | string | Free text search |
9494

@@ -106,7 +106,7 @@ These parameters are supported across most GET list endpoints:
106106
|-----------|------|-------------|
107107
| `membership` | string | Filter by membership type ID |
108108
| `group` | string | Filter by group ID |
109-
| `updatedAt` | ISO 8601 | Filter by last update date |
109+
| `updatedAt` | ISO 8601 | Filter members updated since this date (recommended for incremental sync) |
110110

111111
### Event Attendee Parameters
112112

@@ -132,7 +132,7 @@ These parameters are supported across most GET list endpoints:
132132
| `fromDate` | ISO 8601 | **Required** — start date for log range |
133133
| `toDate` | ISO 8601 | **Required** — end date for log range |
134134

135-
> **Note:** The spec says `fromDate`/`toDate` are optional for checkInLog and emailLog, but the API returns 422 without them.
135+
> **Note:** The spec says `fromDate`/`toDate` are optional for checkInLog and emailLog, but the API returns 422 without them. Hello Club have confirmed this is intentional (the spec is outdated). Same behaviour in V2.
136136
137137
## Response Structure
138138

docs/gotchas.md

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Gotchas & Known Issues
22

3-
Common pitfalls when working with the Hello Club API, discovered through live testing.
3+
Common pitfalls when working with the Hello Club API, discovered through live testing. Items marked **Verified** have been confirmed against the live API and corroborated by Hello Club support (Mar 2026).
44

55
## Removed Endpoint: `/event/upcoming`
66

7-
`GET /event/upcoming` returns `400 BadRequestError: "Invalid request"` for **all parameter combinations**. Hello Club have confirmed this is not a bug — the endpoint has been removed, though it remains in the outdated OpenAPI spec. The same behaviour applies in V2.
7+
**Verified.** `GET /event/upcoming` returns `400 BadRequestError: "Invalid request"` for all parameter combinations. Hello Club have confirmed this is not a bug — the endpoint has been removed, though it remains in the outdated OpenAPI spec. The same behaviour applies in V2.
88

99
**Workaround:** Use `GET /event` with `fromDate` and `toDate`:
1010

@@ -23,7 +23,7 @@ events = client.get("/event", params={
2323
2424
## Date-Required Endpoints (Spec Says Optional)
2525

26-
The spec says `fromDate`/`toDate` are optional for these endpoints, but the API returns **422** without them. Hello Club have confirmed this is intentional — the spec is outdated, not the API. The same behaviour applies in V2.
26+
**Verified.** The spec says `fromDate`/`toDate` are optional for these endpoints, but the API returns **422** (`ValidationError: "query.fromDate" is required`) without them. Hello Club have confirmed this is intentional — the spec is outdated, not the API. The same behaviour applies in V2.
2727

2828
- `GET /checkInLog` — returns 422 without dates
2929
- `GET /emailLog` — returns 422 without dates
@@ -161,20 +161,31 @@ Always check for both `null` and empty values when parsing.
161161

162162
## Unstable Pagination with Non-Default Sort Orders
163163

164-
**Severity: High** — causes silent data loss when paginating through members.
164+
**Verified. Fix confirmed.**
165165

166166
Sorting `GET /member` by anything other than the default (`-lastOnline`) produces **duplicate records** across pages, causing other members to be silently skipped. The offset-based pagination uses an unstable sort when records share the same sort value, so the same member can appear at different offsets on subsequent pages.
167167

168168
**Root cause (confirmed by Hello Club, Mar 2026):** The sort is unstable when multiple records share the same value for the sort field. To ensure stable sorting, append `,id` to your sort specifier (e.g. `sort=-updatedAt,id`). Hello Club may change the API to do this automatically in future.
169169

170-
### Test Results (Mar 2026, 2,143 total members)
170+
### Test Results
171+
172+
**Original test (Mar 2026, full dataset — 2,143 total members):**
171173

172174
| Sort | Records Returned | Unique Members | Duplicates | Missing Members |
173175
|------|----------------:|---------------:|-----------:|----------------:|
174176
| `-lastOnline` (default) | 2,143 | 2,115 | 28 | 0 |
175177
| `-updatedAt` | 2,143 | 1,423 | 720 | 697 |
176178
| `updatedAt` (ascending) | 2,143 | 1,060 | 1,083 | 1,060 |
177179

180+
**Verification test (Mar 2026, 3 pages of 100 members):**
181+
182+
| Sort | Records | Unique | Duplicates | Page 1-2 Overlap |
183+
|------|--------:|-------:|-----------:|-----------------:|
184+
| `-updatedAt` (broken) | 300 | 217 | 83 | ~28% |
185+
| `-updatedAt,id` (fixed) | 300 | 300 | **0** | **0** |
186+
187+
The `,id` tiebreaker completely eliminates the pagination bug.
188+
178189
### How to Reproduce
179190

180191
Fetch page 1 and page 2 with `sort=-updatedAt` and compare member IDs:
@@ -201,20 +212,24 @@ Any code that paginates through all members using `sort=-updatedAt` (e.g., for i
201212
2. Process ~33% of members multiple times
202213
3. Return a "complete" result set that is actually incomplete
203214

204-
### Workaround
215+
### Fix: Append `,id` to Sort
205216

206-
**Fix (confirmed by Hello Club, Mar 2026):** Append `,id` to your sort to make pagination stable:
217+
**Verified fix (confirmed by Hello Club, Mar 2026).** Append `,id` to any sort specifier to make pagination stable:
207218

208219
```python
209-
# Stable sort — no duplicates or missing records
220+
# Stable sort — 0 duplicates, 0 missing records (verified)
210221
page = client.get("/member", params={
211222
"limit": 100,
212223
"offset": 0,
213224
"sort": "-updatedAt,id", # id tiebreaker ensures stable ordering
214225
})
215226
```
216227

217-
**For incremental sync**, Hello Club also recommends filtering by `updatedAt` instead of sorting by it:
228+
This works with any sort field, not just `updatedAt`. Always append `,id` when paginating with non-default sort orders.
229+
230+
### Alternative: Filter by `updatedAt`
231+
232+
**Verified.** For incremental sync, Hello Club recommends filtering by `updatedAt` instead of sorting by it. Tested with 1,381 members matching a 7-day window — all returned members had correct `updatedAt` dates:
218233

219234
```python
220235
# Fetch only members updated since your last sync
@@ -234,7 +249,9 @@ while True:
234249
offset += 100
235250
```
236251

237-
**For full dataset fetches**, use the default sort (`-lastOnline`) and deduplicate by member ID:
252+
### Fallback: Default Sort + Dedup
253+
254+
For full dataset fetches, use the default sort (`-lastOnline`) and deduplicate by member ID:
238255

239256
```python
240257
members = {}
@@ -250,4 +267,4 @@ while True:
250267

251268
> **Note:** Even the default sort has ~28 duplicates out of 2,143 (1.3%), likely from members coming online during the paginated fetch. Always deduplicate by ID.
252269
253-
> **Tested:** Mar 2026 against a club with 2,143 members. Test script: [`scripts/test_api_sort_bug.py`](https://github.com/ispyisail/hc-group-fixer/blob/master/scripts/test_api_sort_bug.py)
270+
> **Tested:** Mar 2026 against a club with 2,143 members. Verification script: [`scripts/verify_fixes.py`](scripts/verify_fixes.py). Original test: [`scripts/test_api_sort_bug.py`](https://github.com/ispyisail/hc-group-fixer/blob/master/scripts/test_api_sort_bug.py)

0 commit comments

Comments
 (0)