# World Radio League API > Log amateur radio contacts into a World Radio League logbook, and read them back. > Version 1.0.0. Full machine-readable spec: https://api.worldradioleague.com/v1/openapi.json ## Getting started 1. The operator generates a key in World Radio League under Integrations > Developer API. It is shown once and stored hashed. One key covers the whole account, not per logbook. A paid membership is required. 2. Send it as `Authorization: Bearer wrl_live_...` (or `X-API-Key`). 3. Call `GET /v1/me` first to confirm the key works and see the rate limits. Base URL: https://api.worldradioleague.com Without the custom domain: https://ebuveblscfmqpztairyk.supabase.co/functions/v1/public-api ## Rules that will trip you up - Keep the key server-side. These endpoints send no CORS headers, so a browser cannot call them. An API key in a front-end bundle is a leaked key. - `POST /v1/contacts` takes ONE contact. An array is rejected with 413. For bulk uploads, the operator should use the ADIF import inside World Radio League. - `logbookId` is OPTIONAL when creating a contact. Omit it and the contact goes to the operator's default logbook, so no setup call is needed. Send it when you know which logbook you want; it must belong to the key's owner. - If the operator has several logbooks and no default, omitting `logbookId` returns 422 LOGBOOK_REQUIRED. Do not retry blindly — read `GET /v1/me` (`defaultLogbook`) at setup and ask the operator to choose, or send `logbookId` yourself. - A contact cannot be moved between logbooks. `logbookId` is rejected on PATCH. - Field names are ADIF names, camelCased: `call`, `freq`, `txPwr`, `rstRcvd`, `stationCallsign`, `gridsquare`, `qth`, `dxcc`. If you have written an ADIF importer, you already know them. Do NOT guess `callsign`, `frequency` or `power` — unknown fields are rejected. - `timestamp` is the one deliberate departure from ADIF: one ISO-8601 UTC instant instead of ADIF's QSO_DATE + TIME_ON pair. The pair is also accepted, under ADIF's own names: `"timestamp": {"qsoDate": "20260805", "timeOn": "1435"}`. - Timestamps are UTC. An ISO-8601 string with no timezone is treated as UTC, not local. - `mode` carries what ADIF splits across MODE and SUBMODE: WRL stores a single mode field, so send the submode when you want to be specific. Send "FT8", not "MFSK" plus a submode. For JS8, send "JS8" — WRL understands that as MODE = MFSK, SUBMODE = JS8. - `state` is the contacted station's state or province. Send the abbreviation where possible: "TX", not "Texas". - `gridsquare` is the contacted station's Maidenhead locator; `myGridsquare` is yours. - `name` is the contacted station operator's name. - `stationCallsign` (the station callsign) and `operator` (the callsign of whoever is operating the station) both default to the account callsign when omitted. Send them when the operating callsign differs from the account holder's. - `band` is in metres: "20m", "20" or 20; "70cm" becomes 0.7. If band and frequency disagree, the frequency wins and a warning is returned in `meta.warnings`. - Unknown request fields are REJECTED, not ignored. `grid_square` is not `gridsquare` and you will get a 400 saying so. - `programId` is REQUIRED on every contact. Send a short name for your software, e.g. "Ham2k" — it is the equivalent of ADIF PROGRAMID and records which application logged the QSO. It is not returned in responses. - A logbook that still contains contacts cannot be deleted (409). - Asking for another account's resource returns 404, never 403. ## Enrichment Enrichment is the process by which World Radio League derives missing information from the data you provide. A new contact returns `enrichment: "pending"`; within a few seconds we derive `dxcc` and `distance` from the callsign. Re-read the contact with `GET /v1/contacts/{id}` to see them. Anything you supplied yourself is never overwritten. Country, continent and flag are NOT returned — all three are derivable from `dxcc`, so they are not duplicated on every contact. ## Responses Every response is `{ "data": ..., "meta": ..., "error": ... }`. On failure `error.code` is a stable string — branch on it, never on `error.message`. Every response carries `X-Request-Id`; quote it in support requests. ## Rate limits 60 writes/min, 120 reads/min, 5,000 writes/day, 20,000 reads/day. `429` carries `Retry-After` in seconds, computed from the window that actually blocked. Honour it rather than retrying on a fixed timer. ## Endpoints ### GET /v1/me Identity and quota for the current key ### GET /v1/contacts List your contacts ### POST /v1/contacts Log one contact ### GET /v1/contacts/{id} Fetch one contact ### PATCH /v1/contacts/{id} Correct a contact ### DELETE /v1/contacts/{id} Delete a contact ### GET /v1/logbooks List your logbooks ### POST /v1/logbooks Create a logbook ### GET /v1/logbooks/{id} Fetch one logbook ### PATCH /v1/logbooks/{id} Update a logbook ### DELETE /v1/logbooks/{id} Delete an empty logbook ## Minimal example ```bash curl -X POST "https://api.worldradioleague.com/v1/contacts" \ -H "Authorization: Bearer $WRL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "logbookId": "6f1c0b7e-6c2a-4c4e-9d1f-2a5b3c4d5e6f", "programId": "YourAppName", "call": "W1AW", "timestamp": "2026-08-05T14:35:00Z", "freq": 14.074, "band": "20m", "mode": "FT8" }' ```