Clubstations
A clubstation lets several operators share one club or special callsign. API v2 supports that, but a token created inside a clubstation session behaves differently from a personal one — it is bound to the permission level the member holds on the club.
Note
None of this applies to a personal token, and none of it applies at all when
the instance has the clubstation feature (special_callsign) switched off.
If you only ever use your own account, you can skip this page.
Personal tokens vs. club tokens
Every token records two users:
| Personal token | Club token | |
|---|---|---|
| Owner (whose data it reaches) | you | the clubstation |
| Creator (who acts through it) | you | the member who created it |
A club token is created by switching into the clubstation in the web UI and then minting a token from the API page as usual. Everything the token reaches belongs to the club; everything it writes is attributed to the member.
Use the Token resource to tell them apart — owner is the
clubstation's callsign for a club token:
Permission levels
The level is set per member on the club's permission page and is re-read on every API request — a change takes effect immediately, with no need to recreate the token.
| Level | Name | Through the API |
|---|---|---|
| 3 | Club Member | Log and manage your own QSOs; read station locations |
| 6 | Club Member ADIF | Same as level 3 (the extra ADIF rights are a UI distinction) |
| 9 | Club Officer | Unrestricted access to the whole club logbook |
Levels 3 and 6 are treated identically by the API. The difference between them in the web UI is about which ADIF screens a member may open; over the API both are simply "not an officer".
Wavelog administrators
A club token created by a Wavelog administrator works even when they hold no permission level on that club — they are treated as an officer, the same way the web UI lets them manage every clubstation. An administrator who does have an explicit level is treated by that level instead, so setting yourself to Club Member stays meaningful.
What a member below officer level may do
Their own QSOs only
For levels 3 and 6 every QSO operation is scoped to the member's own callsign
(OPERATOR):
GET /api/v2/qsolists only their QSOs.meta.totalcounts only those, too.GET /api/v2/qso?format=adifexports exactly the same set — the format does not widen the result.GET,PATCHandDELETEon/api/v2/qso/{id}of another operator's QSO return404 not_found, not403. A QSO the token may not see is reported as if it did not exist, rather than confirming its existence.
An officer sees and edits every operator's QSO.
The operator is assigned, not chosen
When a member creates a QSO, operator is always set to their own callsign.
Sending a different one has no effect — the value is overwritten, on all three
create paths (single JSON, bulk qsos[] and import_type=adif):
# Sent by a level-3 member of HB9CLUB whose own callsign is HB9ABC
curl -X POST https://<WAVELOG_URL>/index.php/api/v2/qso \
-H "Authorization: Bearer wl2_club_token" \
-H "Content-Type: application/json" \
-d '{"station_profile_id": 4, "call": "N9EAT", "band": "20m",
"mode": "SSB", "qso_date": "2026-06-16", "time_on": "1706",
"operator": "HB9XYZ"}'
# -> the QSO is stored with OPERATOR = HB9ABC
An officer may set operator freely, which is how QSOs are logged on behalf of
another operator.
Station locations are officer-only
Station locations are shared club infrastructure, and deleting one removes all of its QSOs along with it. Creating, updating and deleting them therefore requires level 9:
| Request | Level 3 / 6 | Level 9 |
|---|---|---|
GET /api/v2/station |
✅ | ✅ |
GET /api/v2/station/{id} |
✅ | ✅ |
POST /api/v2/station |
403 |
✅ |
PATCH /api/v2/station/{id} |
403 |
✅ |
DELETE /api/v2/station/{id} |
403 |
✅ |
The refusal carries the codes and levels involved:
{
"error": {
"code": "insufficient_club_permission",
"message": "This operation requires clubstation permission level 9",
"details": { "required_level": 9, "granted_level": 3 }
}
}
Radios belong to their operator
Each member registers their own rigs into the shared club account, so
Radio is scoped the same way: a member below officer level lists,
reads and deletes only the radios they registered themselves. Another member's
radio returns 404 not_found.
Lookups follow the same boundary
A lookup answers "have I worked this before" out of the logbook, so it honours the same restriction — otherwise it would hand back the name, QTH and locator recorded in a QSO the member is not allowed to list:
?callsign=reportsworkedBefore: false(andcall_worked: falsewithdetail=full) when the only matching QSO belongs to another operator.?grid=and?grid=allonly consider the member's own QSOs.
Club membership is officer-only
The whole Club resource stays officer-only — both reading the member list (no disclosure of member information to non-officers) and managing permissions:
| Request | Level 3 / 6 | Level 9 |
|---|---|---|
GET /api/v2/club |
403 |
✅ |
GET /api/v2/club/{user_id} |
403 |
✅ |
POST /api/v2/club |
403 |
✅ |
PATCH /api/v2/club/{user_id} |
403 |
✅ |
DELETE /api/v2/club/{user_id} |
403 |
✅ |
The refusal is a plain forbidden rather than the
insufficient_club_permission used elsewhere: the resource turns away everything
that is neither an officer nor a Wavelog administrator, and a personal token
gets the same code with a message naming the other missing role.
An officer cannot change or remove their own membership through the API, so they cannot lock the club out of its permission management. See Club.
A Wavelog administrator reaches the same resource from the outside, with a
personal token and ?club_id=, and is not bound by that rule — see
Wavelog administrators. The club:* scopes
are only offered to sessions that match one of the two roles, so a regular user
is not shown them at all.
Statistics are club-wide
One deliberate exception: Statistic reports club-wide figures for every member, regardless of level. The counters are aggregates, not QSO data, and they are shared with the dashboard.
This means the numbers can legitimately disagree with what the QSO resource returns to the same token:
GET /api/v2/statistic?profile=qso -> total: 412 (the whole club)
GET /api/v2/qso -> total: 57 (this member's QSOs)
Do not use the statistic totals to paginate or reconcile a QSO list.
Losing membership
Removing a member from the club deletes every club token they created for it — including tokens that were set to never expire. This is the same cleanup the v1 API keys have always had, and it takes effect at once. Any call made with such a token afterwards is refused:
The status is 401: the token no longer exists. Re-adding the member restores
their permission, but not the token — they have to create a new one.
Two things are not affected:
- The member's personal tokens (
owner == creator). Those belong to their own account, not to the club. - Tokens other members created for the same club. The cleanup is per member.
Handling this in a client
Treat 401 invalid_token as permanent and stop retrying — unlike
rate_limited it will not resolve on its own. Surface it to the user: they
need a new token, and only the club can grant the membership behind it.
club_access_revoked still exists
A second, independent check re-reads the membership on every request. It
guards the cases the cleanup above cannot reach — a membership removed
directly in the database, for instance — and answers 403
club_access_revoked with the token left intact. A client should handle both
codes; in day-to-day use it will see the 401.
Summary
| Personal | Level 3 / 6 | Level 9 | |
|---|---|---|---|
| Read own QSOs | ✅ | ✅ | ✅ |
| Read other operators' QSOs | — | 404 |
✅ |
Choose the operator field |
✅ | overwritten | ✅ |
| Read station locations | ✅ | ✅ | ✅ |
| Write/delete station locations | ✅ | 403 |
✅ |
| Own radios | ✅ | ✅ | ✅ |
| Other operators' radios | — | 404 |
✅ |
| Lookups across all operators | ✅ | own only | ✅ |
| Club member list | — | 403 |
✅ |
| Manage club permissions | — | 403 |
✅ (not your own) |
| Statistics | own | club-wide | club-wide |