Logbook API

For authors of logging programs.

A logging program can send each contact to an operator's dxnode logbook as it is made, read the log back, and delete contacts. Requests are plain HTTPS, contacts travel as ADIF, and replies are JSON.

The key

Each logbook has one key. Its owner makes it on their logbook page, where it is shown once, and pastes it into your program. Send it with every request:

Authorization: Bearer dxl_…

A key opens one logbook and nothing else on the account. Making a new key stops the old one, and the owner can remove it at any time. Keep it out of logs, URLs and crash reports. Requests over plain HTTP are refused.

Add contacts

POST https://dxnode.dev/api/v1/logbook/contacts

The body is ADIF: one or more .adi records, each ending in <EOR>, or an .adx document. Send it as text/plain or application/xml. Up to 2,000 contacts and 1 MB per request.

curl https://dxnode.dev/api/v1/logbook/contacts \
  -H "Authorization: Bearer $DXNODE_KEY" \
  -H "Content-Type: text/plain" \
  --data-binary '<CALL:4>W1AW <QSO_DATE:8>20261023 <TIME_ON:6>142600
<BAND:3>20m <MODE:3>SSB <FREQ:6>14.265 <RST_SENT:2>59 <RST_RCVD:2>57 <EOR>'

The reply says what happened to each contact you sent:

201 Created
{
  "added": 1,
  "duplicates": 0,
  "rejected": 0,
  "ids": [48211],
  "problems": [],
  "contacts": 1205
}

A contact needs CALL, QSO_DATE, TIME_ON, a MODE and either a BAND or a FREQ. Every other field is kept exactly as sent, your program's own APP_ fields included, and comes back in downloads and reads. A contact with the same call, band, mode, date and time (to the minute) as one already in the log is skipped and counted under duplicates, so sending a contact twice is safe. The reply is 200 when nothing new was added. Contacts that can't be logged are counted under rejected, with the reasons in problems.

Read contacts

GET https://dxnode.dev/api/v1/logbook/contacts?after=0&limit=100

Contacts come in the order they were added, oldest first. after is the last id you already have; limit is 1 to 1,000, 100 if you leave it out. When next is a number, ask again with after set to it. When it is null, you have everything.

{
  "contacts": [
    {
      "id": 48211,
      "call": "W1AW",
      "date": "2026-10-23",
      "time": "14:26:00",
      "band": "20m",
      "mode": "SSB",
      "freq": "14.265",
      "confirmed": false,
      "added": "2026-10-23T14:26:09.511Z",
      "adif": "<CALL:4>W1AW <QSO_DATE:8>20261023 <TIME_ON:6>142600 <BAND:3>20m <MODE:3>SSB <FREQ:6>14.265 <RST_SENT:2>59 <RST_RCVD:2>57 <EOR>"
    }
  ],
  "next": null
}

adif is the whole record as stored. mode is the submode when the contact has one (FT4 rather than MFSK). confirmed is true when the record says a QSL card, LoTW or eQSL confirmation was received.

Delete a contact

DELETE https://dxnode.dev/api/v1/logbook/contacts/48211

Replies 204 with no body, or 404 when the logbook has no contact with that id.

Check the key

GET https://dxnode.dev/api/v1/logbook
{
  "callsign": "W1AW",
  "public": false,
  "contacts": 1205,
  "confirmed": 734,
  "latest": "2026-10-23T14:26:00Z"
}

A quick way to test a key before sending contacts. latest is the newest contact's time, in UTC.

Limits and errors

A key can make 1,200 requests an hour; send contacts in batches if you are catching up a backlog. After 20 wrong keys from one address, that address waits 15 minutes. When a limit is reached the reply is 429 with a Retry-After header in seconds.

Every error is JSON, {"error": "…"}, with a sentence you can show the operator. The status says what kind: 400 the request isn't right, 401 the key is missing, wrong or removed, 403 the request came over plain HTTP, 404 no such contact, 413 too much in one request, 415 the body was JSON or a form instead of ADIF.

Logbook API | dxnode