REST API · v1
Microwaveprop API
The Microwaveprop public REST API exposes the read-and-write surface a regular user of the website has access to: contact (QSO) submission, beacon submission, beacon-monitor management, propagation queries, and profile management. Admin-only operations are deliberately excluded.
Quickstart
Mint a long-lived bearer token once, then use it for every
subsequent call. Tokens are 32 random bytes, URL-base64 encoded
with an mwp_ prefix so leak scanners can spot them.
The interactive way to mint a token is the account settings page — the value is shown once. Keep it like a password.
curl -sS -X POST https://prop.w5isp.com/api/v1/auth/tokens \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"...","name":"laptop"}'
# => {"data": {...}, "token": "mwp_..."}
TOKEN=mwp_...
curl -sS -H "Authorization: Bearer $TOKEN" \
https://prop.w5isp.com/api/v1/me
Authentication
Every endpoint that mutates state, or returns the authenticated user's own data, requires a bearer token. Public read endpoints accept an optional bearer that unlocks the caller's private rows.
Endpoint auth matrix
| Endpoint | Auth | Notes |
|---|---|---|
POST /auth/tokens |
password | The only endpoint that accepts a password. |
GET / PATCH /me |
bearer | The user behind the bearer token. |
POST /contacts |
bearer | Regular user QSO submission. |
GET /contacts |
optional | Public; bearer reveals viewer-private rows. |
GET /beacons |
none | Approved beacons only. |
POST /beacons |
bearer | New beacons start unapproved. |
GET /scores |
none | Public read of propagation scores. |
GET /profiles/:call |
none | Public per-callsign profile. |
Token lifecycle
-
Tokens may carry an optional
expires_at(ISO 8601 UTC). Without one they live until revoked. -
GET /me/api-tokenslists every non-revoked token belonging to the authenticated user (without plaintext). -
DELETE /me/api-tokens/:idrevokes a token (soft delete — the row remains for audit). - Revoking the token used by the current request immediately invalidates every subsequent request from that token.
Authorization: Bearer mwp_AbCdEf123…
# 32 random bytes, URL-base64
mwp_<43 chars>
# Server stores SHA-256(token) only.
Errors
All error responses follow
RFC 9457
— a structured application/problem+json
body
with a stable title
field and per-field errors
on validation failures.
| Status | When |
|---|---|
| 400 | Missing or malformed query / body parameter. |
| 401 | No / invalid / revoked / expired bearer token. |
| 403 | Authenticated, but the action is forbidden for the caller. |
| 404 | Resource not found, or hidden by privacy. |
| 409 | Duplicate — an equivalent resource already exists. |
| 422 | Validation failed; errors carries per-field messages. |
| 429 |
Rate limit exceeded; check RateLimit-*
+ Retry-After.
|
| 5xx | Bug. Please open an issue. |
{
"type": "about:blank",
"title": "validation_failed",
"status": 422,
"detail": "One or more fields are invalid.",
"errors": {
"callsign": ["must be 3-10 letters and digits"]
}
}
Rate limiting
Every response carries the
RFC 9651
RateLimit-*
headers so clients can self-throttle
before hitting a 429.
| Header | Meaning |
|---|---|
RateLimit-Limit |
Requests permitted in the current window. |
RateLimit-Remaining |
Requests remaining in the current window. |
RateLimit-Reset |
Seconds until the window resets. |
Retry-After |
Sent on 429s; retry no sooner than this many seconds. |
Defaults
| Caller | Limit |
|---|---|
| Anonymous (per IP) | 60 req / minute |
| Authenticated (per token) | 600 req / minute |
POST /auth/tokens (per IP) |
30 req / minute |
RateLimit-Limit: 600
RateLimit-Remaining: 597
RateLimit-Reset: 42
Endpoint reference
/auth/tokens
Issue an API token
Prefer the account settings page for interactive use; this endpoint is here for scripts that need to mint a token without leaving the terminal.
Request body
-
emailstring required - Account email address.
-
passwordstring required - Account password.
-
namestring required - Human-readable label so you can identify the token later.
-
expires_atdatetime - ISO 8601 UTC. Omit for a token that lives until revoked.
curl -X POST https://prop.w5isp.com/api/v1/auth/tokens \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"password": "your password",
"name": "iPad",
"expires_at": "2027-01-01T00:00:00Z"
}'
{
"data": {
"id": "01HX...",
"name": "iPad",
"inserted_at": "2026-05-09T12:34:00Z",
"last_used_at": null,
"expires_at": "2027-01-01T00:00:00Z",
"revoked_at": null
},
"token": "mwp_AbCdEf..."
}
/me
The authenticated user
Returns the user behind the bearer token: callsign, name, email, home QTH, and admin flag.
PATCH /me
accepts any subset of home_grid, home_lat, home_lon, home_elevation_m. Grid is auto-derived from
lat/lon and vice versa.
curl -H "Authorization: Bearer $TOKEN" \
https://prop.w5isp.com/api/v1/me
curl -X PATCH \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"home_grid":"EM12kx"}' \
https://prop.w5isp.com/api/v1/me
/me/contacts
User-scoped collections
Two sibling endpoints return everything submitted under the authenticated user's account, newest first:
GET /me/contacts— every QSO submitted by the user.-
GET /me/beacons— every beacon (approved or pending) submitted by the user.
Both honour standard page + per_page pagination.
curl -H "Authorization: Bearer $TOKEN" \
"https://prop.w5isp.com/api/v1/me/contacts?per_page=20"
/me/api-tokens
Manage your API tokens
GET /me/api-tokens
lists every non-revoked token belonging to the authenticated
user. The plaintext value is never returned — only the
metadata (id, name, timestamps).
DELETE /me/api-tokens/:id
revokes a token. The row is soft-deleted (its revoked_at
is set) and any in-flight request using it is invalidated on
the next call.
curl -H "Authorization: Bearer $TOKEN" \
https://prop.w5isp.com/api/v1/me/api-tokens
curl -X DELETE \
-H "Authorization: Bearer $TOKEN" \
https://prop.w5isp.com/api/v1/me/api-tokens/01HX...
/me/beacon-monitors
Beacon monitor stations
Distributed beacon monitor stations that report SNR samples
back to Microwaveprop. Each monitor row has a token
field — the credential the monitor program uses to
identify itself when reporting.
Operations
GET /me/beacon-monitors— list.-
POST /me/beacon-monitors— create. Returns the new monitor's plaintext token (shown once). DELETE /me/beacon-monitors/:id— revoke and remove.
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"shack-pi","grid":"EM12kx"}' \
https://prop.w5isp.com/api/v1/me/beacon-monitors
/contacts
Public contact (QSO) feed
Paginated list of QSOs.
Query parameters
-
pageinteger -
1-based. Defaults to
1. -
per_pageinteger -
Defaults to
50; capped at200. -
searchstring - One or two callsigns; matches station1 or station2.
When called with a bearer token, the user's own private contacts are included in addition to the public set.
GET /contacts/:id
returns a single QSO. Private QSOs return 404 to non-owners.
curl "https://prop.w5isp.com/api/v1/contacts?search=W5XD&per_page=25"
/contacts
Submit a QSO
Create a new QSO under the authenticated user's account; their
email is recorded as submitter_email.
Required fields
-
station1string required - Submitter callsign.
-
station2string required - Other operator's callsign.
-
qso_timestampdatetime required - ISO 8601 UTC.
-
bandstring required -
MHz integer string (
"10000","24000"). -
grid1string required - 4- or 6-character Maidenhead.
-
grid2string required - Other end's grid.
-
modestring required -
Mode (e.g.
CW,SSB).
Optional fields
-
user_declared_prop_modestring -
e.g.
tropo,aircraft_scatter. -
height1_ftnumber - Antenna height above ground.
-
height2_ftnumber - Other end's antenna height.
-
privateboolean - Hide from the public feed.
-
notesstring - Free-form text.
A duplicate (same stations + same hour + same band) returns
409 Conflict
with the existing record in the existing
field.
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"station1": "W5XD",
"station2": "K5XD",
"qso_timestamp": "2026-05-08T12:34:00Z",
"band": "10000",
"grid1": "EM12",
"grid2": "EM13",
"mode": "CW"
}' \
https://prop.w5isp.com/api/v1/contacts
/beacons
Beacons
GET /beacons
lists approved beacons; GET /beacons/:id
reads a
single one.
POST /beacons
is unauthenticated — the new beacon is created in approved=false
state until an admin approves it via the website.
curl -X POST \
-H "Content-Type: application/json" \
-d '{
"callsign": "WB5LUA/B",
"frequency_mhz": 10368.025,
"grid": "EM13qe",
"power_w": 5
}' \
https://prop.w5isp.com/api/v1/beacons
/scores
Propagation score at a grid point
Returns the composite propagation score plus the per-factor breakdown (rain, humidity, refractivity, …) for one grid point at one band.
Query parameters
-
bandinteger required - MHz.
-
latnumber required - Decimal degrees.
-
lonnumber required - Decimal degrees.
-
valid_timedatetime - ISO 8601 UTC. Defaults to the latest scored hour.
GET /scores/bands
lists every band the engine scores for, with each band's
humidity-effect classification (beneficial / harmful).
curl "https://prop.w5isp.com/api/v1/scores?band=10000&lat=32.78&lon=-96.80"
/forecast
48-hour score timeline
Returns the score at one grid point across the available
forecast horizon (currently 48 hours). Same band
+ lat
+ lon
parameters as /scores.
curl "https://prop.w5isp.com/api/v1/forecast?band=10000&lat=32.78&lon=-96.80"
/profiles/:callsign
Public per-user profile
Public per-user profile: callsign, name, home QTH, all public contacts involving the callsign, plus all approved beacons submitted by the user. Email is not exposed.
curl https://prop.w5isp.com/api/v1/profiles/W5XD
Conventions
- All timestamps are ISO 8601 UTC (
...Z). - All identifiers are UUIDv7 (binary_id) strings.
- Bands are integer MHz strings (
"10000","24000"). - Latitude / longitude are decimal degrees.
-
Maidenhead grids are 4- or 6-character (
EM12,EM12kx).
Stability promise
- Adding new fields to existing responses is non-breaking.
- Removing or renaming fields will only happen in a new
/api/vN. - Adding new endpoints to
/api/v1is non-breaking. - Tightening validation may produce new 422s; called out in the changelog.
See also
- openapi.yaml — the machine-readable spec.
-
/.well-known/api-catalog— RFC 9727 service descriptor (public). - /algo — the scoring algorithm in detail.