Ding Practice Export API
This API lets a community tracker — below, the tracker — read a member's Ding practice log directly. The log is served as an Insight Timer compatible CSV, over three HTTP calls, and only for members who have switched it on.
Getting a key
Write to support.ding@gmail.com with the Discord server ID and who maintains the tracker. We issue a key bound to that server. The tracker sends it as a bearer token — never in a URL; we never log it.
The three calls
Base URL https://syntony.is/ding. {guild_id} is the tracker's Discord server,
{user_id} is the member's Discord user ID. All three take
Authorization: Bearer KEY.
POST /exports/{guild_id}/{user_id} → 204 member enabled it with the tracker
GET /exports/{guild_id}/{user_id}/practice → 200 the CSV
DELETE /exports/{guild_id}/{user_id} → 204 member turned it off
With the key in $KEY:
# 1. ENABLE — the member turned the export on with you.
# Until this call everything below answers 404, and the phone uploads nothing.
# -> 204, empty body. Safe to repeat: enabling twice is not an error.
curl -i -X POST -H "Authorization: Bearer $KEY" \
https://syntony.is/ding/exports/{guild_id}/{user_id}
# 2a. FETCH (Sync now, then on your own schedule) — the whole log, every time.
# -> 200: the CSV into DingLogs.csv, the headers with the ETag into headers.txt.
# -> 404 while the phone has not uploaded yet — normal for minutes or days after 1.
curl -sD headers.txt -o DingLogs.csv -H "Authorization: Bearer $KEY" \
https://syntony.is/ding/exports/{guild_id}/{user_id}/practice
# 2b. FETCH WHEN YOU ALREADY HAVE IT — the same request plus one header. Optional and
# cheap: the file changes only after the member sits, so most polls have nothing new.
# -> 304 and an empty body when nothing moved; 200 with the full file when it did.
ETAG=$(awk 'tolower($1)=="etag:"{print $2}' headers.txt | tr -d '\r')
curl -i -H "Authorization: Bearer $KEY" -H "If-None-Match: $ETAG" \
https://syntony.is/ding/exports/{guild_id}/{user_id}/practice
# 3. DISABLE — the member turned it off with you.
# -> 204. If no other tracker is still enabled, we delete the file too:
# keeping practice nobody is left to read has no reason.
curl -i -X DELETE -H "Authorization: Bearer $KEY" \
https://syntony.is/ding/exports/{guild_id}/{user_id}
POST answers 404 as readily as GET does, and on a first integration that is the
likely answer: the member also has to tick your community in Ding, on the «Together» screen.
Consent is asked for in two places on purpose — with the tracker, and in the app — and this 404 is the one
thing worth passing back to them: open Ding → Together → tick the community.
Our server does not see all of a member's practice — the complete log exists only on their phone. So the
file has to come from the phone: it goes up the next time Ding is open with a network, which means the
first one can arrive minutes or days after POST.
POST and DELETE are idempotent. Until POST is called,
GET returns 404 even for a member who uses Ding daily — no consent, no data, and the phone
uploads nothing either. GET has no side effects, so it is safe on a schedule and safe to
repeat; passing the previous ETag back as If-None-Match returns 304 when nothing
changed, which is most of the time.
Responses
| Code | Meaning |
|---|---|
| 200 | GET. The log. CSV body, strong ETag, attachment named DingLogs-YYYY-MM-DD.csv. |
| 204 | POST, DELETE. Accepted. Empty body, and a repeat answers the same — enabling twice is not an error, and we do not say whether it was already on. |
| 304 | GET. Unchanged since the ETag the tracker sent. |
| 404 | All three. Nothing here for this tracker — deliberately one answer for every reason: unknown member, export not enabled, community unchecked in the app, nothing uploaded yet, malformed ID. We do not tell an API key who our users are. |
| 429 | All three. Over 120 requests per minute on the tracker's key. Retry-After says when to come back. |
| 401 | All three. Key not recognized. |
The file
A header line and one line per session, newest first.
Started At,Duration,Preset,Activity
06/22/2026 07:13:26,1:0:0,Morning,Meditation
06/21/2026 08:30:00,0:12:5,Together,Meditation
- Started At — local wall-clock time,
MM/DD/YYYY HH:MM:SS, no zone. Frozen in the zone the session happened in, so a member who travels does not silently rewrite their whole history. - Duration —
H:M:S, not zero-padded. - Preset — the practice the member chose. Names of people they sat with are stripped before the file leaves our server: we have that member's consent, not their partners'.
- Activity — always
Meditation.
The header and the four columns are frozen. Anything new appears inside Preset.
How a member stops it
Either by turning the export off with the tracker, or by unchecking that community in Ding's "Together" screen. Either one stops both the delivery and any further upload from the phone. What we share, and how to stop it, is also in our privacy policy.
Questions
support.ding@gmail.com. Questions and objections are equally welcome. The API is new and its shape is still open — if a different one suits the tracker better, we would rather build that.