API reference

Endpoints

Eight calls cover everything. All paths are relative to the base URL, for example https://dyb5ahy4siv91.cloudfront.net/partner/v1 on staging. Codes that any call can get are on Errors and retries.
POST/users

Creates an Active Giving account (or finds the existing one), adds the person to your team and sends them a welcome email.

Body

emailstringrequired

Unique across Active Giving. The person signs into the app with it later.

namestringrequired

Up to 100 characters. Shown to the team and in the welcome email.

partnerUserIdstringoptional

Your own ID for this person, up to 128 characters: letters, numbers and - _ . :. Lets you log workouts and look them up without storing ours. If you first add someone without it, a later retry can set it once; after that it does not change.

language"EN" | "DE" | "FR"optional

The language of the welcome email and the app, in capitals. Defaults to EN.

Request

curl -X POST "https://dyb5ahy4siv91.cloudfront.net/partner/v1/users" \
  -H "Authorization: Bearer $AG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "maria.lopez@example.com",
  "name": "Maria Lopez",
  "partnerUserId": "emp-1042",
  "language": "DE"
}'

Response

201 Created
{
  "userId": "3f6c2a1e-8b4d-4e7a-9c15-2d8e6f0a7b93",
  "partnerUserId": "emp-1042",
  "email": "maria.lopez@example.com",
  "name": "Maria Lopez",
  "language": "DE",
  "teamId": "7a1d9e40-5c2b-4f8e-a6d3-1b0c9e8f2a57",
  "linkedAt": "2026-10-14T08:20:11Z"
}

Responses

StatusWhen
201 + the personNew email. The welcome email goes out.
200 + the personThe email is already in your team. Safe to retry: nothing changes and no second email goes out.
409already_in_teamThe email belongs to someone in another team.
409team_fullYour team has reached its maximum number of members.
409person_unlinkedThe person's link to your team ended. It is final: they cannot be added again.
POST/activities

Records one finished workout and turns it into impact in a single call. The response says straight away whether it counted. Nothing is left pending.

Body

userIdstringsometimes required

Our user ID, the userId from Add a person. Send exactly one of userId, userEmail and partnerUserId.

userEmailstringsometimes required

The email you added the person with.

partnerUserIdstringsometimes required

Your own ID for the person, as you sent it in Add a person.

externalIdstringrequired

Your ID for this workout, up to 128 characters: letters, numbers and - _ . :. It never changes and is also the retry key: the same workout sent twice counts once.

typestringrequired

A key from activityTypes in Get your team.

startedAtISO 8601required

From the start of yesterday, in your team's time zone, until now. The workout must end no more than 5 minutes in the future.

durationSecondsintegerrequired

60 to 86,400.

distanceMetersintegersometimes required

Required when the type has earnsBy: "DISTANCE". Above 0, up to 1,000,000.

kilocaloriesintegersometimes required

Required, above 0, when the type has earnsBy: "CALORIES". Up to 20,000.

Request

curl -X POST "https://dyb5ahy4siv91.cloudfront.net/partner/v1/activities" \
  -H "Authorization: Bearer $AG_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "partnerUserId": "emp-1042",
  "externalId": "wk-20261014-0007",
  "type": "RUNNING",
  "startedAt": "2026-10-14T05:30:00Z",
  "durationSeconds": 2700,
  "distanceMeters": 8200
}'

Response

200 with the same body when the same workout was already received.

201 Created
{
  "id": "2b4eb032-c972-4360-a140-b46ad7a08805",
  "externalId": "wk-20261014-0007",
  "userId": "3f6c2a1e-8b4d-4e7a-9c15-2d8e6f0a7b93",
  "type": "RUNNING",
  "startedAt": "2026-10-14T05:30:00Z",
  "durationSeconds": 2700,
  "distanceMeters": 8200,
  "kilocalories": null,
  "status": "COUNTED",
  "impact": {
    "unit": "TREE",
    "amount": 0.4,
    "reducedBy": null
  },
  "teamId": "7a1d9e40-5c2b-4f8e-a6d3-1b0c9e8f2a57",
  "createdAt": "2026-10-14T06:16:02Z"
}

Responses

StatusWhen
404user_not_foundNo person in your team matches the one you named.
409person_unlinkedThe person's link to your team ended. Stop sending workouts for them.
409duplicate_external_idA workout with this externalId and different values was sent before.
409daily_limit_reachedThe person reached today's limit. error.resetsAt says when it resets.
409overlapping_activityThe person already has a workout at the same time.
422activity_not_availableYour team does not accept this type.
422started_at_out_of_rangestartedAt is before yesterday, or the workout ends more than 5 minutes from now.
GET/users/{userId}

Checks whether someone you added is still linked to your team, by the userId that Add a person answered.

Path

userIdstringrequired

The userId from Add a person, in its full 36-character form.

Request

curl "https://dyb5ahy4siv91.cloudfront.net/partner/v1/users/3f6c2a1e-8b4d-4e7a-9c15-2d8e6f0a7b93" \
  -H "Authorization: Bearer $AG_API_KEY"

Response

status is LINKED or ENDED. ENDED is final and covers every way a link ends; who ended it and why is not shared. LINKED only means the link exists, not that the person opened the app or got the welcome email. endedAt is null while the person is linked, and partnerUserId is null if you never sent one. There is no name, email or workout data.

200 OK
{
  "userId": "3f6c2a1e-8b4d-4e7a-9c15-2d8e6f0a7b93",
  "partnerUserId": "emp-1042",
  "status": "LINKED",
  "teamId": "7a1d9e40-5c2b-4f8e-a6d3-1b0c9e8f2a57",
  "linkedAt": "2026-10-14T08:20:11Z",
  "endedAt": null
}

Responses

StatusWhen
404user_not_foundYou did not add this person. That includes ids that do not exist and other partners' people, so the answer never tells whether an account exists.
400invalid_requestuserId is not in the full 36-character form we gave out. fields names it.
GET/users

The same lookup, by the partnerUserId you sent when you added the person.

Query

partnerUserIdstringrequired

Your own ID for the person, up to 128 characters: letters, numbers and - _ . :. It must match exactly, case included.

Request

curl "https://dyb5ahy4siv91.cloudfront.net/partner/v1/users?partnerUserId=emp-1042" \
  -H "Authorization: Bearer $AG_API_KEY"

Response

status is LINKED or ENDED. ENDED is final and covers every way a link ends; who ended it and why is not shared. LINKED only means the link exists, not that the person opened the app or got the welcome email. endedAt is null while the person is linked, and partnerUserId is null if you never sent one. There is no name, email or workout data.

200 OK
{
  "userId": "3f6c2a1e-8b4d-4e7a-9c15-2d8e6f0a7b93",
  "partnerUserId": "emp-1042",
  "status": "LINKED",
  "teamId": "7a1d9e40-5c2b-4f8e-a6d3-1b0c9e8f2a57",
  "linkedAt": "2026-10-14T08:20:11Z",
  "endedAt": null
}

Responses

StatusWhen
404user_not_foundYou did not add this person. That includes ids that do not exist and other partners' people, so the answer never tells whether an account exists.
400invalid_requestpartnerUserId is missing, or not up to 128 characters: letters, numbers and - _ . :. fields names it.
GET/activities/{externalId}

Reads one workout you sent, with its outcome and impact.

Path

externalIdstringrequired

The externalId you sent the workout with.

Request

curl "https://dyb5ahy4siv91.cloudfront.net/partner/v1/activities/wk-20261014-0007" \
  -H "Authorization: Bearer $AG_API_KEY"

Response

200 OK
{
  "id": "2b4eb032-c972-4360-a140-b46ad7a08805",
  "externalId": "wk-20261014-0007",
  "userId": "3f6c2a1e-8b4d-4e7a-9c15-2d8e6f0a7b93",
  "type": "RUNNING",
  "startedAt": "2026-10-14T05:30:00Z",
  "durationSeconds": 2700,
  "distanceMeters": 8200,
  "kilocalories": null,
  "status": "COUNTED",
  "impact": {
    "unit": "TREE",
    "amount": 0.4,
    "reducedBy": null
  },
  "teamId": "7a1d9e40-5c2b-4f8e-a6d3-1b0c9e8f2a57",
  "createdAt": "2026-10-14T06:16:02Z"
}

Responses

StatusWhen
404activity_not_foundYou sent no counted workout with this externalId.
GET/team

Everything about your team in one call: whether it is running, places and budget left, the workout types it accepts, the daily limit and the totals. Cache it for a few minutes.

Request

curl "https://dyb5ahy4siv91.cloudfront.net/partner/v1/team" \
  -H "Authorization: Bearer $AG_API_KEY"

Response

200 OK
{
  "id": "7a1d9e40-5c2b-4f8e-a6d3-1b0c9e8f2a57",
  "name": "Acme Move Challenge",
  "state": "RUNNING",
  "startsAt": "2026-10-01T00:00:00Z",
  "endsAt": "2026-12-31T23:59:59Z",
  "placesLeft": 412,
  "budgetLeft": true,
  "impactUnit": "TREE",
  "activityTypes": [
    {
      "key": "RUNNING",
      "label": "Running",
      "earnsBy": "DISTANCE"
    },
    {
      "key": "CYCLING",
      "label": "Cycling",
      "earnsBy": "DISTANCE"
    },
    {
      "key": "YOGA",
      "label": "Yoga",
      "earnsBy": "CALORIES"
    }
  ],
  "totals": {
    "members": 88,
    "activities": 1204,
    "kilometers": 6311.2,
    "kilocalories": 512400,
    "impact": 214.5
  },
  "dailyLimit": {
    "mode": "COUNT",
    "max": 5,
    "unit": null,
    "resetsAt": "2026-10-14T22:00:00Z"
  }
}