Algia API
The Algia API lets your own tools read and add to your own pain diary: a script, a spreadsheet,
a home automation or an agent you run yourself. It reaches one account's data, with a key that
account made.
It is for personal automation, not for building a service for other people. It never changes an
account, a password or a second factor, never erases an account and never downloads everything
at once; those stay in the application. It returns what was recorded and interprets none of it.
- Base address
https://api.algia.be/v1
- Format
- JSON in UTF-8
- Version
- 1, in the path
Who can use it
The API is part of Plus, and it is off for every account until it is asked for and granted.
-
An account on Plus asks for access in the application, under Security, API, with a sentence
or two on what it is for.
The API page in the application.
- The operator reads the request and grants it by hand. Until then the page shows the request and its date.
- Once access is granted, the same page shows the form for personal access tokens.
If the account leaves Plus, every token stops working until the account is on Plus again. If the
operator revokes access, every token is revoked with it, for good.
Personal access tokens
- A token starts with
algia_pat_ and is shown once, when it is made. Algia keeps only a SHA-256 fingerprint of it, so a lost token cannot be shown again: revoke it and make a new one.
- Making a token asks for the password and the second-factor code again, unless both were given in the last 5 minutes.
- A token is valid for 90 days unless another period is chosen, and for at most 365 days. It can be revoked at any moment on the same page, which also shows when each token was last used.
- A token carries the scopes chosen for it, and nothing more.
A token is a password to the diary. Keep it in a secret store or an environment variable, never in
a web page, a shared document or a repository. The API sends no cross-origin headers, so a web
page cannot call it from a browser.
Scopes
| Scope | Allows | Endpoints |
entries:read |
Read scores, places and answers |
GET /entries |
entries:write |
Add and delete entries |
POST /entries DELETE /entries/{id} |
diary-text:read |
Read diary notes |
Adds the diary text and each place's note to GET /entries
|
medication:read |
Read medication and reminders |
GET /medications GET /intakes |
medication:write |
Answer reminders |
POST /intakes/{id}/answer |
Diary text is a scope of its own, so a dashboard that reads scores never receives free text. A token
with entries:read alone gets scores, places and answers, without the diary or the notes.
Making a request
Every request carries the token in the Authorization header, over HTTPS:
curl "https://api.algia.be/v1/entries?from=2026-10-01&to=2026-10-06" \
-H "Authorization: Bearer $ALGIA_TOKEN" \
-H "Accept: application/json"
- A body is JSON, sent with
Content-Type: application/json.
- Dates in a query are
YYYY-MM-DD; a value in another shape is ignored rather than refused.
recorded_at is ISO 8601 in UTC. date and time in an answer are the local day and time where the entry was recorded.
- Every request needs a current consent for health data on the account, given in the application. Without it every endpoint answers
403 consent_required.
- When processing is paused on the account, reads still work and writes answer
403 processing_restricted.
Rate limits
Each token may make 60 reads and 20 writes a minute. Past either, the answer is
429 rate_limited with a Retry-After header in seconds. Writing or deleting an
entry also counts towards the account's own limit, which the application shares.
Retries and the Idempotency-Key
A tool that retries after a timeout must not record the same entry twice. Send an
Idempotency-Key header of up to 120 characters with POST /entries. A repeat
with the same key from the same token within 24 hours returns the first answer, with
Idempotent-Replay: true, and writes nothing. The body of the repeat is not compared, so
use a new key for every new entry.
curl -X POST "https://api.algia.be/v1/entries" \
-H "Authorization: Bearer $ALGIA_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7f3c2a90-entry-2026-10-06-0830" \
-d '{"overall_score": 4, "regions": ["neck_back"]}'
Deleting an entry and answering a reminder take no key: repeating them changes nothing. A second
delete answers 404, a second taken or skipped answers 409.
Errors
An error is a short JSON object with one code, and nothing that names the account or its data:
{"error": "insufficient_scope"}
An invalid entry also lists the fields that failed:
{"error": "invalid", "fields": ["overall_score", "region_detail.neck_back.score"]}
| Code | Status | Meaning |
unauthenticated |
401 |
No token, or one that is unknown, expired or revoked, or an account without API access. |
insufficient_scope |
403 |
The token does not carry the scope this endpoint needs. |
consent_required |
403 |
The account has no current consent for health data. |
processing_restricted |
403 |
Processing is paused on the account, so writes are refused. Reads still work. |
rate_limited |
429 |
Past the minute's limit. Wait the seconds in Retry-After. |
not_found |
404 |
No entry with this id belongs to the account. |
invalid |
422 |
A field failed the same checks as the form; the fields list names each one. |
not_answerable |
409 |
The reminder is already answered, the answer is not one of the three, or it is not the account's. |
Endpoints
Paths are relative to https://api.algia.be/v1. Every endpoint can also answer
401, 403 and 429 as described above.
GET
/entries
The account's entries, oldest first, optionally between two local days.
Every entry, oldest first. With from and to, only the local days between them, both included. Diary text and notes appear only when the token also carries diary-text:read.
Scope entries:read
Parameters
| Name | In | Type | Description |
from |
query |
string (YYYY-MM-DD) |
First local day to include, YYYY-MM-DD. A value in another shape is ignored. |
to |
query |
string (YYYY-MM-DD) |
Last local day to include, YYYY-MM-DD. A value in another shape is ignored. |
Answers
200
-
The entries.
{
"data": [
{
"id": "01JABCDEF0123456789ABCDEFG",
"recorded_at": "2026-10-05T18:30:00+00:00",
"date": "2026-10-05",
"time": "20:30",
"overall_score": 4,
"sleep": 3,
"mood": 3,
"activity": 2,
"fatigue": 4,
"noted_later": false,
"edited": false,
"regions": [
{
"code": "lower_back_left_back",
"score": 5,
"pain_type": "aching"
}
]
}
]
}
401
-
No token, or a token that is unknown, expired, revoked, or whose account no longer has API access.
403
-
The token lacks the scope, the account has no current health-data consent, or processing is paused (writes only).
429
-
More than 60 reads in a minute for this token.
POST
/entries
Records an entry, with the same checks as the form in the application.
The same rules as the form: a score from 0 to 10, scales from 1 to 5, known region codes, and a moment no more than 7 days back and no more than 5 minutes ahead. The answer never carries diary text, whatever the token's scopes.
Scope entries:write
Parameters
| Name | In | Type | Description |
Idempotency-Key |
header |
string, at most 120 characters |
Up to 120 characters, chosen by the client. A repeat with the same key from the same token within 24 hours returns the first answer with Idempotent-Replay set to true, and writes nothing. The body of the repeat is not compared. |
Request body
| Field | Type | Description |
overall_score required |
integer from 0 to 10 |
|
recorded_at |
string (ISO 8601) |
Up to 7 days back; now when absent. |
sleep |
integer from 1 to 5 |
|
mood |
integer from 1 to 5 |
|
activity |
integer from 1 to 5 |
|
fatigue |
integer from 1 to 5 |
|
diary |
string |
Free text, stored encrypted. |
regions |
list of strings |
Region codes. |
region_detail |
object |
Per region code in regions, optional detail. |
region_detail.<code>.score |
integer from 0 to 10 |
|
region_detail.<code>.pain_type |
one of aching, burning, stabbing, tingling, cramping, throbbing, numb |
|
region_detail.<code>.radiates_to |
string |
Another region code. |
region_detail.<code>.note |
string |
Free text, stored encrypted. |
Example request
{
"overall_score": 4,
"recorded_at": "2026-10-06T06:30:00Z",
"sleep": 3,
"regions": [
"lower_back_left_back",
"neck_back"
],
"region_detail": {
"lower_back_left_back": {
"score": 5,
"pain_type": "aching"
}
}
}
Answers
201
-
Recorded. The entry as it was saved, without diary text.
{
"data": {
"id": "01JABCDEF0123456789ABCDEFH",
"recorded_at": "2026-10-06T06:30:00+00:00",
"date": "2026-10-06",
"time": "08:30",
"overall_score": 4,
"sleep": 3,
"mood": null,
"activity": null,
"fatigue": null,
"noted_later": false,
"edited": false,
"regions": [
{
"code": "lower_back_left_back",
"score": 5,
"pain_type": "aching"
},
{
"code": "neck_back",
"score": null,
"pain_type": null
}
]
}
}
401
-
No token, or a token that is unknown, expired, revoked, or whose account no longer has API access.
403
-
The token lacks the scope, the account has no current health-data consent, or processing is paused (writes only).
422
-
A field is not valid.
{
"error": "invalid",
"fields": [
"overall_score"
]
}
429
-
More than 20 writes in a minute for this token. Entry writes also count towards the account's 60 a minute, shared with the application.
DELETE
/entries/{id}
Deletes an entry. It can be restored from the diary in the application for 90 days.
Scope entries:write
Parameters
| Name | In | Type | Description |
id required |
path |
string |
|
Answers
204
-
Deleted. No body.
401
-
No token, or a token that is unknown, expired, revoked, or whose account no longer has API access.
403
-
The token lacks the scope, the account has no current health-data consent, or processing is paused (writes only).
404
-
No entry with this id belongs to the account.
{
"error": "not_found"
}
429
-
More than 20 writes in a minute for this token. Entry writes also count towards the account's 60 a minute, shared with the application.
GET
/medications
The account's current medication list with its reminder times. Archived medication is left out.
Scope medication:read
Answers
200
-
The medication list.
{
"data": [
{
"id": "01JABCDEF0123456789ABCDEFJ",
"name": "Medicine A",
"dose": "1 tablet",
"reminders": [
{
"time": "08:00",
"days": 127
}
]
}
]
}
401
-
No token, or a token that is unknown, expired, revoked, or whose account no longer has API access.
403
-
The token lacks the scope, the account has no current health-data consent, or processing is paused (writes only).
429
-
More than 60 reads in a minute for this token.
GET
/intakes
Reminders that came due and their answers, oldest first, optionally between two local days.
Scope medication:read
Parameters
| Name | In | Type | Description |
from |
query |
string (YYYY-MM-DD) |
First local day to include, YYYY-MM-DD. A value in another shape is ignored. |
to |
query |
string (YYYY-MM-DD) |
Last local day to include, YYYY-MM-DD. A value in another shape is ignored. |
Answers
200
-
The reminders.
{
"data": [
{
"id": "01JABCDEF0123456789ABCDEFK",
"date": "2026-10-06",
"time": "08:00",
"medication": "Medicine A",
"answer": null
}
]
}
401
-
No token, or a token that is unknown, expired, revoked, or whose account no longer has API access.
403
-
The token lacks the scope, the account has no current health-data consent, or processing is paused (writes only).
429
-
More than 60 reads in a minute for this token.
POST
/intakes/{id}/answer
Answers a reminder with taken or skipped, or asks for it again in 15 minutes with later.
Scope medication:write
Parameters
| Name | In | Type | Description |
id required |
path |
string |
|
Request body
| Field | Type | Description |
answer required |
one of taken, skipped, later |
|
Example request
{
"answer": "taken"
}
Answers
204
-
Answered. No body.
401
-
No token, or a token that is unknown, expired, revoked, or whose account no longer has API access.
403
-
The token lacks the scope, the account has no current health-data consent, or processing is paused (writes only).
409
-
Already answered, an answer other than the three, or not the account's reminder.
{
"error": "not_answerable"
}
429
-
More than 20 writes in a minute for this token. Entry writes also count towards the account's 60 a minute, shared with the application.
Region codes
A place on the body is named by its code in regions and region_detail.
The pain types are aching, burning, stabbing,
tingling, cramping, throbbing and numb.
Head and neck (6)
| Code | Place |
head_front | Face |
jaw_right_front | Jaw, right |
jaw_left_front | Jaw, left |
neck_front | Front of the neck |
head_back | Back of the head |
neck_back | Back of the neck |
Torso (14)
| Code | Place |
chest_right_front | Chest, right |
chest_left_front | Chest, left |
abdomen_upper_front | Upper belly |
abdomen_lower_front | Lower belly |
hip_right_front | Hip, right |
hip_left_front | Hip, left |
groin_right_front | Groin, right |
groin_left_front | Groin, left |
upper_back_left_back | Upper back, left |
upper_back_right_back | Upper back, right |
lower_back_left_back | Lower back, left |
lower_back_right_back | Lower back, right |
buttock_left_back | Buttock, left |
buttock_right_back | Buttock, right |
Arms (16)
| Code | Place |
shoulder_right_front | Shoulder, right, front |
shoulder_left_front | Shoulder, left, front |
upper_arm_right_front | Upper arm, right, front |
upper_arm_left_front | Upper arm, left, front |
elbow_right_front | Elbow, right, inside |
elbow_left_front | Elbow, left, inside |
forearm_right_front | Forearm, right, inside |
forearm_left_front | Forearm, left, inside |
shoulder_left_back | Shoulder, left, back |
shoulder_right_back | Shoulder, right, back |
upper_arm_left_back | Upper arm, left, back |
upper_arm_right_back | Upper arm, right, back |
elbow_left_back | Elbow, left, outside |
elbow_right_back | Elbow, right, outside |
forearm_left_back | Forearm, left, outside |
forearm_right_back | Forearm, right, outside |
Hands (6)
| Code | Place |
wrist_right_front | Wrist, right |
wrist_left_front | Wrist, left |
hand_right_front | Palm, right |
hand_left_front | Palm, left |
hand_left_back | Back of the hand, left |
hand_right_back | Back of the hand, right |
Legs (12)
| Code | Place |
thigh_right_front | Thigh, right, front |
thigh_left_front | Thigh, left, front |
knee_right_front | Knee, right, front |
knee_left_front | Knee, left, front |
shin_right_front | Shin, right |
shin_left_front | Shin, left |
thigh_left_back | Thigh, left, back |
thigh_right_back | Thigh, right, back |
knee_left_back | Knee, left, back |
knee_right_back | Knee, right, back |
calf_left_back | Calf, left |
calf_right_back | Calf, right |
Feet (10)
| Code | Place |
ankle_right_front | Ankle, right, front |
ankle_left_front | Ankle, left, front |
foot_right_front | Top of the foot, right |
foot_left_front | Top of the foot, left |
ankle_left_back | Ankle, left, back |
ankle_right_back | Ankle, right, back |
heel_left_back | Heel, left |
heel_right_back | Heel, right |
foot_left_back | Sole, left |
foot_right_back | Sole, right |