Achievements

Achievements are badges employees earn or are given: employee of the month, a completion badge for finishing linked content (courses, handbooks, journeys), an employment badge after a number of months of tenure, or a custom badge handed out by hand. Each achievement has a type, an emoji, a visibility (public — celebrated with department colleagues in the app — or personal, shown only to the employee and their managers) and, for the automatic types, a trigger config. An award is one achievement given to one user on a date — repeat awards (employee of the month twice) are separate awards. New awards are also announced by the monotree.achievement.awarded outgoing webhook.

Required scope: read:achievements to read, write:achievements to create, update and award. The achievements feature must be enabled for the company — otherwise every endpoint returns 403.

Achievements and awards cannot be deleted through the API.

Endpoints

GET
/api/open/v1/achievements

List achievements (paginated)

GET
/api/open/v1/achievements/{id}

Get a single achievement

POST
/api/open/v1/achievements

Create an achievement (draft by default)

PATCH
/api/open/v1/achievements/{id}

Update an achievement

GET
/api/open/v1/achievements/{id}/awards

List every award of an achievement, newest first (paginated)

POST
/api/open/v1/achievements/{id}/awards

Award an achievement to users

GET
/api/open/v1/users/{id}/achievements

List the awards a user holds on published achievements

List

curl "https://customer.monotree.com/api/open/v1/achievements?is_published=true&type=employee_of_the_month" \
  -H "Authorization: Bearer mono_your_token_here"
Query paramNotes
is_publishedtrue returns published achievements only, false returns drafts only. Omit for all.
typeemployee_of_the_month, completion, employment or custom.
visibilitypublic or personal.
sinceISO 8601 — filter by created_at.
page, per_pageStandard pagination — per_page max 50.

Achievements are ordered newest first. Drafts are returned too — is_published tells.

Create

An achievement is created as a draft by default. Pass is_published: true to publish it immediately. The response is 201 with the achievement object.

curl -X POST https://customer.monotree.com/api/open/v1/achievements \
  -H "Authorization: Bearer mono_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "custom",
    "title": "Team player",
    "description": "Always there for colleagues.",
    "emoji": "🤝",
    "visibility": "public",
    "translations": {
      "title": { "da": "Holdspiller" }
    },
    "is_published": true
  }'

Request body

FieldTypeNotes
typestringRequired. employee_of_the_month, completion, employment or custom. Cannot be changed later.
titlestringRequired, except for employee_of_the_month: without a title it is named "Employee of the month" in every active language. Written in the default content language.
descriptionstringOptional.
emojistringOptional, max 16 characters. Defaults to 👑 for employee of the month, 🏆 for completion, ⭐ for employment and 🤩 for custom.
visibilitystringOptional. public or personal. Defaults to public for employee of the month and employment, personal for completion and custom.
trigger_configobjectCompletion and employment only — see Triggers. Sending it for another type returns 422.
translationsobjectOptional. { "title": { "da": "..." }, "description": { "da": "..." } }. Every locale must be an active language (see Locales); writing one activates it on the achievement. The title in the default content language cannot be emptied here.
is_publishedbooleanOptional. true publishes immediately. Defaults to false (draft).

Triggers

Completion and employment achievements are awarded automatically, so they need a complete trigger_config before they can be published:

Typetrigger_configAwarded
completion{ "completable_type": "course", "completable_ids": [42] }handbook, course or journey. Every id must be something the CMS lets you link: a published handbook or course, or a published onboarding or custom journey (offboarding journeys can't be linked)When an employee completes the linked content
employment{ "months": 12 } — at least 1On the daily check, once an employee has been employed that many months

A trigger_config you send has to be complete; an incomplete one returns 422. Publishing without a complete trigger config also returns 422, and nothing is created or changed.

Update

curl -X PATCH https://customer.monotree.com/api/open/v1/achievements/12 \
  -H "Authorization: Bearer mono_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{ "description": "Chosen by the department managers every month.", "is_published": true }'

PATCH and PUT accept the same fields as create, all optional, and only change what you send. The response is the achievement object.

  • title and description are written in the default content language. translations are merged: languages you leave out keep their text.
  • type cannot be changed. Sending a different type returns 422; sending the current one is accepted, so you can send back an achievement you read.
  • is_published toggles the published state. A published achievement's trigger config has to stay complete, so clearing it or sending an incomplete one returns 422.
  • An unknown id returns 404.

Response

{
  "data": {
    "id": 12,
    "title": "Employee of the month",
    "description": "Chosen by the department managers.",
    "translations": {
      "title": { "en": "Employee of the month", "da": "Månedens medarbejder" },
      "description": { "en": "Chosen by the department managers." }
    },
    "emoji": "👑",
    "type": "employee_of_the_month",
    "visibility": "public",
    "trigger_type": null,
    "trigger_config": null,
    "awarded_users_count": 37,
    "is_published": true,
    "published_at": "2026-06-01T10:00:00+00:00",
    "created_at": "2026-05-20T09:00:00+00:00",
    "updated_at": "2026-06-01T10:00:00+00:00"
  }
}
FieldNotes
typeemployee_of_the_month, completion, employment or custom.
visibilitypublic — celebrated with department colleagues in the app — or personal, shown only to the employee and their managers.
trigger_typecompletion or tenure for the automatic types, null for the manual ones (employee of the month, custom).
trigger_config{ "completable_type": "handbook" | "course" | "journey", "completable_ids": [...] } for completion, { "months": 12 } for tenure, null otherwise.
awarded_users_countNumber of awards handed out.
translationsEvery locale of the title and description.

An achievement's awards

GET /achievements/{id}/awards returns every award, newest first, with the holder embedded as the same user object as /users. department is the department the award was given in (employee of the month for Copenhagen) or null; assigned_by is the CMS user who handed it out, the company's own user for awards given through the API, and null for automatic awards. Awards of deleted users are left out. since filters by the award's created_at; standard pagination, per_page max 50.

{
  "data": [
    {
      "id": 4711,
      "awarded_at": "2026-08-01T00:00:00+02:00",
      "is_manually_added": true,
      "department": { "id": 41, "name": "Copenhagen", "type": "department" },
      "assigned_by": { "id": 5, "name": "Sam Doe", "email": "sam@example.com", "avatar_url": null, "is_manager": true, "groups": [ ... ], ... },
      "user": { "id": 501, "name": "Jane Doe", "email": "jane@example.com", "avatar_url": null, "is_manager": false, "groups": [ { "id": 41, "name": "Copenhagen", "type": "department", "is_manager": false } ], ... },
      "created_at": "2026-08-03T09:12:41+00:00",
      "updated_at": "2026-08-03T09:12:41+00:00"
    },
    {
      "id": 4690,
      "awarded_at": "2026-07-01T00:00:00+02:00",
      "is_manually_added": true,
      "department": { "id": 42, "name": "Aarhus", "type": "department" },
      "assigned_by": { "id": 5, "name": "Sam Doe", "email": "sam@example.com", ... },
      "user": { "id": 502, "name": "Alex Doe", "email": "alex@example.com", ... },
      "created_at": "2026-07-02T08:00:00+00:00",
      "updated_at": "2026-07-02T08:00:00+00:00"
    }
  ],
  "meta": { "current_page": 1, "per_page": 25, "total": 37 }
}

Award users

POST /achievements/{id}/awards gives the achievement to one or more users, the same way an admin hands it out in the CMS. Requires write:achievements.

curl -X POST https://customer.monotree.com/api/open/v1/achievements/12/awards \
  -H "Authorization: Bearer mono_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "awards": [
      { "user_id": 501, "awarded_at": "2026-09-01", "department_id": 41 },
      { "user_id": 502, "awarded_at": "2026-09-01" }
    ]
  }'
FieldTypeNotes
awardsarrayRequired, at least one row.
awards[].user_idintegerRequired. An employee from /users — kiosk, bot and unregistered users return 422.
awards[].awarded_atdateRequired. The day the award is for, e.g. 2026-09-01 (the first of the month for employee of the month). A time sent along is dropped. Can be back-dated.
awards[].department_idintegerThe department the award is given in — it must be one of the user's departments. Required for employee of the month, optional for the other types.

The achievement must be published — a draft returns 422, because an award on a draft would never be announced. The response is 201 with one award object per row, in the order sent — the same object as GET /achievements/{id}/awards, with the user embedded:

{
  "data": [
    {
      "id": 4712,
      "awarded_at": "2026-09-01T00:00:00+02:00",
      "is_manually_added": true,
      "department": { "id": 41, "name": "Copenhagen", "type": "department" },
      "assigned_by": { "id": 8, "name": "Acme", "type": "customer", ... },
      "user": { "id": 501, "name": "Jane Doe", "email": "jane@example.com", ... },
      "created_at": "2026-09-14T11:30:02+00:00",
      "updated_at": "2026-09-14T11:30:02+00:00"
    },
    {
      "id": 4713,
      "awarded_at": "2026-09-01T00:00:00+02:00",
      "is_manually_added": true,
      "department": null,
      "assigned_by": { "id": 8, "name": "Acme", "type": "customer", ... },
      "user": { "id": 502, "name": "Alex Doe", "email": "alex@example.com", ... },
      "created_at": "2026-09-14T11:30:02+00:00",
      "updated_at": "2026-09-14T11:30:02+00:00"
    }
  ]
}
  • assigned_by is always the company's own user (type: customer, named after the company), whichever user the token acts as — so employees see the award as given by the company, not by a colleague. On the rare platform without a company user it is the system user (type: system).
  • The request is all or nothing: if any row is invalid, the response is 422 naming the row (e.g. awards.1.user_id) and nobody is awarded.
  • A row identical to an existing award — same user and same awarded_at — returns that award instead of creating another, so a retried request does not hand it out twice. The same user on a different day is a new award (employee of the month twice).
  • Each new award on a published achievement is announced like a CMS award: the employee gets a push notification, public awards are shown to their department colleagues, and the monotree.achievement.awarded webhook fires.

A user's achievements

GET /users/{id}/achievements returns the awards a user holds on published achievements, newest first, with the achievement embedded. Not paginated. Awards on draft achievements are left out — this is what the employee sees in-app. Returns 404 for kiosk, bot and shadow users.

{
  "data": [
    {
      "id": 4711,
      "awarded_at": "2026-08-01T00:00:00+02:00",
      "is_manually_added": true,
      "department": { "id": 41, "name": "Copenhagen", "type": "department" },
      "assigned_by": { "id": 5, "name": "Sam Doe", "email": "sam@example.com", ... },
      "achievement": { "id": 12, "title": "Employee of the month", "emoji": "👑", "type": "employee_of_the_month", "visibility": "public", "awarded_users_count": 37, "is_published": true, ... },
      "created_at": "2026-08-03T09:12:41+00:00",
      "updated_at": "2026-08-03T09:12:41+00:00"
    },
    {
      "id": 3980,
      "awarded_at": "2026-07-14T09:30:12+02:00",
      "is_manually_added": false,
      "department": null,
      "assigned_by": null,
      "achievement": { "id": 8, "title": "Food safety certified", "emoji": "🏆", "type": "completion", "visibility": "personal", "awarded_users_count": 112, "is_published": true, ... },
      "created_at": "2026-07-14T07:30:12+00:00",
      "updated_at": "2026-07-14T07:30:12+00:00"
    }
  ]
}

Award fields

FieldMeaning
awarded_atThe day the award is for, as midnight in the customer server's timezone with its offset — see above.
is_manually_addedtrue for awards handed out in the CMS or through the API, false for awards earned through a trigger.
departmentThe department the award was given in, or null.
assigned_byThe CMS user who handed it out, the company's own user (type: customer) for awards given through the API, or null for automatic awards.
achievementThe achievement — embedded on /users/{id}/achievements and in the webhook.
userThe holder — embedded on /achievements/{id}/awards and in the webhook.