Join a family group with an invite code
curl --request POST \
--url https://web-api.soxai.site/api/groups/join \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"groupId": "group_123",
"code": "ABC123",
"displayName": "Jane",
"sharedFields": {
"sleep_score": true
}
}
'import requests
url = "https://web-api.soxai.site/api/groups/join"
payload = {
"groupId": "group_123",
"code": "ABC123",
"displayName": "Jane",
"sharedFields": { "sleep_score": True }
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
groupId: 'group_123',
code: 'ABC123',
displayName: 'Jane',
sharedFields: {sleep_score: true}
})
};
fetch('https://web-api.soxai.site/api/groups/join', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));{
"uid": "user456",
"groupId": "b7d1f0c9a2e4d6f8b0c1",
"role": "member",
"status": "active",
"displayName": "Dad",
"joinedAt": "2026-08-03T09:30:00+00:00",
"masterHidden": false,
"sharedFields": {
"sleep_score": true,
"health_score": true,
"activity_score": false,
"sleep_total_time_exclude_awake": false,
"activity_steps": true,
"heat_stroke_severity": true,
"last_sync_time": true
},
"sharingScope": "group_only",
"alreadyJoined": false
}{
"error": "bad-request",
"error_msg": "displayName must not be empty"
}{
"error": "http-error",
"error_msg": "Invalid authentication credentials. <reason>"
}{
"error": "forbidden",
"error_msg": "Not enough permission"
}{
"error": "not-found",
"error_msg": "group not found"
}{
"error": "conflict",
"error_msg": "invite code is expired"
}{
"error": "unprocessable-entity",
"error_msg": "body.<field>: <validation message>"
}{
"error": "rate-limited",
"error_msg": "Rate limit exceeded. Retry after <n> seconds."
}{
"error": "internal-server-error",
"error_msg": "Failed to fetch data: <error_message>"
}Family Groups
Join a family group with an invite code
Join a group using the groupId and code carried by an invite deep
link.
Joining again with the same account is idempotent: the existing membership is returned and no invite use is consumed, so a re-tapped link does not exhaust the code.
Parameters
- Body (
GroupJoin):groupId,codeanddisplayNameare required.sharedFieldsis the initial per-metric sharing selection; omit it to share nothing until the member chooses to.
Response
200returns the caller’s membership in that group, asmember.- Joining again returns the existing membership and consumes no further use of the invite code.
Errors
| Status | Condition |
|---|---|
| 400 | displayName is empty |
| 404 | The group does not exist |
| 409 | The invite code is unknown, expired or exhausted; the caller already belongs to 20 groups; or the group already holds 50 members |
Authorization
- Scheme:
Authorization: Bearer <idToken>orsoxai-api-key: <api key>. - Any authenticated caller. Group membership is not involved, because this call is what creates or joins one.
POST
/
api
/
groups
/
join
Join a family group with an invite code
curl --request POST \
--url https://web-api.soxai.site/api/groups/join \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"groupId": "group_123",
"code": "ABC123",
"displayName": "Jane",
"sharedFields": {
"sleep_score": true
}
}
'import requests
url = "https://web-api.soxai.site/api/groups/join"
payload = {
"groupId": "group_123",
"code": "ABC123",
"displayName": "Jane",
"sharedFields": { "sleep_score": True }
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
groupId: 'group_123',
code: 'ABC123',
displayName: 'Jane',
sharedFields: {sleep_score: true}
})
};
fetch('https://web-api.soxai.site/api/groups/join', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));{
"uid": "user456",
"groupId": "b7d1f0c9a2e4d6f8b0c1",
"role": "member",
"status": "active",
"displayName": "Dad",
"joinedAt": "2026-08-03T09:30:00+00:00",
"masterHidden": false,
"sharedFields": {
"sleep_score": true,
"health_score": true,
"activity_score": false,
"sleep_total_time_exclude_awake": false,
"activity_steps": true,
"heat_stroke_severity": true,
"last_sync_time": true
},
"sharingScope": "group_only",
"alreadyJoined": false
}{
"error": "bad-request",
"error_msg": "displayName must not be empty"
}{
"error": "http-error",
"error_msg": "Invalid authentication credentials. <reason>"
}{
"error": "forbidden",
"error_msg": "Not enough permission"
}{
"error": "not-found",
"error_msg": "group not found"
}{
"error": "conflict",
"error_msg": "invite code is expired"
}{
"error": "unprocessable-entity",
"error_msg": "body.<field>: <validation message>"
}{
"error": "rate-limited",
"error_msg": "Rate limit exceeded. Retry after <n> seconds."
}{
"error": "internal-server-error",
"error_msg": "Failed to fetch data: <error_message>"
}Authorizations
HTTPBearerSoxaiApiKey
Body
application/json
Response
The caller's membership in that group. Joining again returns the existing membership with alreadyJoined true and consumes no further use of the code.