{"openapi":"3.1.0","info":{"title":"QSO App public API","version":"1.1.0","description":"Read-only and anonymous: 120 requests a minute per address, CORS open, answers cacheable for a minute. AllStarLink node data from allstarlink.org; DMR repeater data from BrandMeister; place coordinates from the US Census Bureau gazetteer; licensing records from the FCC and ISED Canada. Sources and terms: https://github.com/pinzerbib/qsoapp/wiki/Data-API","license":{"name":"AGPL-3.0","url":"https://github.com/pinzerbib/qsoapp/blob/main/LICENSE"}},"servers":[{"url":"https://api.qsoapp.com","description":"Production"},{"url":"https://staging-api.qsoapp.com","description":"Staging"}],"tags":[{"name":"On the air","description":"Nodes, repeaters, DMR, presence and signal reports"},{"name":"Callsigns","description":"The licensing mirror"},{"name":"Clubs","description":"What a club publishes"},{"name":"First contact","description":"The guided path's own content"},{"name":"First year","description":"A shared milestone"}],"paths":{"/v1/nodes":{"get":{"tags":["On the air"],"summary":"AllStarLink nodes near a point, by place, or by callsign","description":"AllStarLink publishes no coordinate for a node, only the text its owner typed, so place is how most nodes are found.","parameters":[{"name":"near","in":"query","description":"lat,lon; nearest first, each row carries distanceKm and distanceMi","schema":{"type":"string"}},{"name":"km","in":"query","description":"Radius in km","schema":{"type":"number","default":50,"maximum":500}},{"name":"mi","in":"query","description":"Radius in miles, instead of km","schema":{"type":"number","maximum":311}},{"name":"limit","in":"query","description":"Rows","schema":{"type":"integer","default":50,"maximum":200}},{"name":"callsign","in":"query","description":"The callsign's rows instead of a radius","schema":{"type":"string"}},{"name":"place","in":"query","description":"Match the location text and description instead of a radius","schema":{"type":"string"}},{"name":"online","in":"query","description":"1 or true keeps only what the live feed still lists. A repeater somebody here contributed reports to no network and is never hidden by it.","schema":{"type":"boolean"}}],"responses":{"200":{"description":"nodes, with distanceKm when near is given"}}}},"/v1/nodes/{number}":{"get":{"tags":["On the air"],"summary":"One node: callsign, frequency, tone, status, location","parameters":[{"name":"number","in":"path","required":true,"description":"AllStarLink node number","schema":{"type":"integer"}}],"responses":{"200":{"description":"node"},"404":{"description":"Unknown"}}}},"/v1/repeaters":{"get":{"tags":["On the air"],"summary":"Repeaters near a point, or by callsign","description":"Submitted by trustees and operators, plus BrandMeister's DMR repeaters.","parameters":[{"name":"near","in":"query","description":"lat,lon; nearest first, each row carries distanceKm and distanceMi","schema":{"type":"string"}},{"name":"km","in":"query","description":"Radius in km","schema":{"type":"number","default":50,"maximum":500}},{"name":"mi","in":"query","description":"Radius in miles, instead of km","schema":{"type":"number","maximum":311}},{"name":"limit","in":"query","description":"Rows","schema":{"type":"integer","default":50,"maximum":200}},{"name":"callsign","in":"query","description":"The callsign's rows instead of a radius","schema":{"type":"string"}},{"name":"online","in":"query","description":"1 or true keeps only what the live feed still lists. A repeater somebody here contributed reports to no network and is never hidden by it.","schema":{"type":"boolean"}}],"responses":{"200":{"description":"repeaters, with distanceKm when near is given"}}}},"/v1/repeaters.chirp.csv":{"get":{"tags":["On the air"],"summary":"The same search as a CHIRP memory file","description":"Analogue FM only: CTCSS as Tone or TSQL, DCS as DTCS, digital-only machines left out. File then Import in CHIRP.","parameters":[{"name":"near","in":"query","description":"lat,lon; nearest first, each row carries distanceKm and distanceMi","schema":{"type":"string"}},{"name":"km","in":"query","description":"Radius in km","schema":{"type":"number","default":50,"maximum":500}},{"name":"mi","in":"query","description":"Radius in miles, instead of km","schema":{"type":"number","maximum":311}},{"name":"limit","in":"query","description":"Rows","schema":{"type":"integer","default":50,"maximum":200}}],"responses":{"200":{"description":"text/csv, as an attachment"}}}},"/v1/pota/parks":{"get":{"tags":["On the air"],"summary":"Parks on the Air near a point","description":"From POTA's public API, cached a day per area. The radius is capped at 200 km.","parameters":[{"name":"near","in":"query","description":"lat,lon; nearest first, each row carries distanceKm and distanceMi","schema":{"type":"string"}},{"name":"km","in":"query","description":"Radius in km","schema":{"type":"number","default":50,"maximum":500}},{"name":"mi","in":"query","description":"Radius in miles, instead of km","schema":{"type":"number","maximum":311}},{"name":"limit","in":"query","description":"Rows","schema":{"type":"integer","default":50,"maximum":200}}],"responses":{"200":{"description":"parks, nearest first"},"502":{"description":"POTA did not answer"}}}},"/v1/pota/spots":{"get":{"tags":["On the air"],"summary":"Activators spotted right now","description":"Cached a minute. Without near, every current spot.","parameters":[{"name":"near","in":"query","description":"lat,lon; nearest first, each row carries distanceKm and distanceMi","schema":{"type":"string"}},{"name":"km","in":"query","description":"Radius in km","schema":{"type":"number","default":50,"maximum":500}},{"name":"mi","in":"query","description":"Radius in miles, instead of km","schema":{"type":"number","maximum":311}},{"name":"limit","in":"query","description":"Rows","schema":{"type":"integer","default":50,"maximum":200}}],"responses":{"200":{"description":"spots: callsign, park, frequency, mode, time"},"502":{"description":"POTA did not answer"}}}},"/v1/satellites/passes":{"get":{"tags":["On the air"],"summary":"When an amateur satellite crosses your sky","description":"SGP4 over Celestrak's amateur element sets, cached twelve hours, for the FM satellites a handheld can work. Each pass carries where it rises, how high it climbs, where it sets, and the uplink, downlink and tone that go in the radio.","parameters":[{"name":"near","in":"query","description":"lat,lon; required","schema":{"type":"string"}},{"name":"hours","in":"query","description":"How far ahead to look","schema":{"type":"integer","default":24,"maximum":72}},{"name":"minElevation","in":"query","description":"Skip passes that stay lower than this, in degrees","schema":{"type":"integer","default":10,"maximum":60}}],"responses":{"200":{"description":"passes, soonest first"},"502":{"description":"Celestrak did not answer"}}}},"/v1/learn":{"get":{"summary":"The Learn articles: plain-words explanations of the hobby.","responses":{"200":{"description":"Every article, in reading order."}}}},"/v1/learn/{slug}":{"get":{"summary":"One Learn article.","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The article."},"404":{"description":"No such article."}}}},"/v1/radios":{"get":{"tags":["First contact"],"summary":"The handheld profiles behind \"pick your radio\"","description":"Authored in the repository under content/radios, CC BY-SA 4.0. Each profile carries the keys by their printed names, how this radio changes a setting, and the steps a new operator does in order.","responses":{"200":{"description":"radios, by name"}}}},"/v1/radios/{slug}":{"get":{"tags":["First contact"],"summary":"One radio profile","parameters":[{"name":"slug","in":"path","required":true,"description":"Radio slug, as listed by /v1/radios","schema":{"type":"string"}}],"responses":{"200":{"description":"radio"},"404":{"description":"Unknown"}}}},"/v1/path/prompts":{"get":{"tags":["First contact"],"summary":"The Part 97 prompts a first contact touches","description":"Authored in the repository at content/path/regulatory.md, CC BY-SA 4.0. One source, so the wording a new operator is held to is the same everywhere it is shown.","responses":{"200":{"description":"prompts, in the order they come up"}}}},"/v1/reach":{"post":{"tags":["On the air"],"summary":"Terrain line of sight from one point to up to 25 sites","description":"2 m at the person and 20 m at a site unless given, over 4/3-earth curvature. Its own rate limit: 30 calls a minute per address, not the 120 the rest of /v1 allows.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["from","to"],"properties":{"from":{"type":"object","description":"Where the person is standing","required":["lat","lon"],"properties":{"lat":{"type":"number","minimum":-90,"maximum":90},"lon":{"type":"number","minimum":-180,"maximum":180},"heightM":{"type":"number","default":2}}},"to":{"type":"array","maxItems":25,"items":{"type":"object","description":"A site, with an id you choose to match the answer by","required":["id","lat","lon"],"properties":{"id":{"type":"string"},"lat":{"type":"number","minimum":-90,"maximum":90},"lon":{"type":"number","minimum":-180,"maximum":180},"heightM":{"type":"number","default":20}}}}}}}}},"responses":{"200":{"description":"One result per target: clear, marginal (under 30 m in the way), blocked, or unknown when the elevation tiles are missing"},"400":{"description":"from and a non-empty to are both required"},"429":{"description":"Rate limited"}}}},"/v1/repeaters/{id}":{"get":{"tags":["On the air"],"summary":"One repeater","parameters":[{"name":"id","in":"path","required":true,"description":"Repeater id","schema":{"type":"string"}}],"responses":{"200":{"description":"repeater"},"404":{"description":"Unknown"}}}},"/v1/dmr/{callsign}":{"get":{"tags":["On the air"],"summary":"A callsign's DMR ids, from RadioID","parameters":[{"name":"callsign","in":"path","required":true,"description":"Callsign","schema":{"type":"string"}}],"responses":{"200":{"description":"callsign and ids"},"502":{"description":"RadioID unavailable"}}}},"/v1/presence":{"get":{"tags":["On the air"],"summary":"Who has said they are around today, by grid square","description":"Opt-in only, four-character Maidenhead grid, cleared nightly.","parameters":[{"name":"grid","in":"query","description":"Maidenhead grid, four characters (FN30)","schema":{"type":"string"}},{"name":"near","in":"query","description":"lat,lon, resolved to a grid square","schema":{"type":"string"}}],"responses":{"200":{"description":"callsigns present"},"400":{"description":"grid or near required"}}}},"/v1/reports/{kind}/{id}":{"get":{"tags":["On the air"],"summary":"Signal reports left on a repeater, a node or a callsign","parameters":[{"name":"kind","in":"path","required":true,"description":"repeater, node or callsign","schema":{"type":"string","enum":["repeater","node","callsign"]}},{"name":"id","in":"path","required":true,"description":"Repeater id, node number or callsign","schema":{"type":"string"}}],"responses":{"200":{"description":"reports"},"400":{"description":"Unknown kind"}}}},"/v1/lookup/{callsign}":{"get":{"tags":["Callsigns"],"summary":"Licensing record (US, Canada) and the callsign's nodes","parameters":[{"name":"callsign","in":"path","required":true,"description":"Callsign","schema":{"type":"string"}}],"responses":{"200":{"description":"records and nodes"},"404":{"description":"Unknown"}}}},"/v1/clubs":{"get":{"tags":["Clubs"],"summary":"Every club on QSO App","responses":{"200":{"description":"clubs"}}}},"/v1/clubs/{slug}":{"get":{"tags":["Clubs"],"summary":"A club's public page: name, callsign, where it meets, nets","parameters":[{"name":"slug","in":"path","required":true,"description":"Club slug or club callsign","schema":{"type":"string"}}],"responses":{"200":{"description":"club"},"404":{"description":"Unknown"}}}},"/v1/clubs/{slug}/events":{"get":{"tags":["Clubs"],"summary":"A club's upcoming events","parameters":[{"name":"slug","in":"path","required":true,"description":"Club slug","schema":{"type":"string"}}],"responses":{"200":{"description":"events"},"404":{"description":"Unknown"}}}},"/v1/clubs/{slug}/events.ics":{"get":{"tags":["Clubs"],"summary":"The same events as an iCalendar feed to subscribe to","parameters":[{"name":"slug","in":"path","required":true,"description":"Club slug","schema":{"type":"string"}}],"responses":{"200":{"description":"text/calendar"},"404":{"description":"Unknown"}}}},"/v1/clubs/{slug}/posts":{"get":{"tags":["Clubs"],"summary":"A club's public announcements, minutes and agendas","parameters":[{"name":"slug","in":"path","required":true,"description":"Club slug","schema":{"type":"string"}}],"responses":{"200":{"description":"posts"},"404":{"description":"Unknown"}}}},"/v1/clubs/{slug}/dues":{"get":{"tags":["Clubs"],"summary":"A club's dues plans","parameters":[{"name":"slug","in":"path","required":true,"description":"Club slug","schema":{"type":"string"}}],"responses":{"200":{"description":"plans"},"404":{"description":"Unknown"}}}},"/v1/clubs/{slug}/elections":{"get":{"tags":["Clubs"],"summary":"A club's elections and their results","parameters":[{"name":"slug","in":"path","required":true,"description":"Club slug","schema":{"type":"string"}}],"responses":{"200":{"description":"elections"},"404":{"description":"Unknown"}}}},"/v1/nets/open":{"get":{"tags":["Clubs"],"summary":"Every club net on the air right now","responses":{"200":{"description":"nets, each with its club, frequency and net control"}}}},"/v1/school-programs":{"get":{"tags":["Clubs"],"summary":"School programmes clubs run, with the club","responses":{"200":{"description":"programmes"}}}},"/v1/milestones/{id}":{"get":{"tags":["First year"],"summary":"A shared first, as its owner chose to share it","parameters":[{"name":"id","in":"path","required":true,"description":"Share id from a /m/ link","schema":{"type":"string"}}],"responses":{"200":{"description":"milestone"},"404":{"description":"Unknown"}}}}}}