# SDK and CLI changelog

> What changed in each version of @shuffl/sdk, @shuffl/cli and @shuffl/api-contracts, and how versions and breaking changes work.

Audience: Developers who use the SDK, CLI or API contracts

Kind: Reference

Canonical: https://shuffl.ai/docs/sdk-changelog

<a id="versioning"></a>
## Versions and breaking changes

The SDK, the CLI and the API contracts share one version number and are released together. Upgrade the SDK and CLI at the same time, and pin an exact version in your project.

While the major version is 0, a minor release such as 0.2.0 to 0.3.0 can rename or remove operations and fields. A patch release such as 0.2.0 to 0.2.1 never does. Each version below lists breaking changes first, under Breaking. Read them before you upgrade.

The hosted API always runs the newest contract. An older SDK or CLI keeps working for every operation that hasn’t changed, but a call to an operation or field that was renamed or removed fails until you upgrade.

Release candidates, such as 0.4.0-rc.1, are published under the next tag. Install one with @shuffl/sdk@next. shuffl --version prints the version you have.

```sh
npm install @shuffl/sdk@0.7.6 @shuffl/cli@0.7.6
shuffl --version
```

<a id="v0-7-6"></a>
## 0.7.6 (2026-10-18)

### Added

- Mentees choose their mentor: `mentorship.programs.save` takes `menteesChoose`. In such a program `mentorship.mine` returns each waiting mentee's `choices` (up to three mentors with `topics` and `reasons`) and the mentor they `requested`; `mentorship.mentors.choose` (`mentorship.chooseMentor`, `programId`, `mentorId`) asks that mentor. Mentors see `requests` in `mentorship.mine` and answer with `mentorship.requests.answer` (`mentorship.answerRequest`, `decision` accept or pass). Program summaries return `menteesChoose` and `requested`; pairs can be `requested`.
- Session notes: `mentorship.sessions.save` (`mentorship.saveSession`, `pairId`, `heldOn`, `notes`, `nextStep`, optional `sessionId`) and `mentorship.sessions.remove` (`mentorship.removeSession`). Each pair in `mentorship.mine` returns its `sessions`, a `prep` (the newest `nextStep`, `openGoals`, `questions` and `stage`) and its `nextMeeting`. Only the two people in the pair read notes. Keys with `self:write`.
- Calendar targets take `mentorship_pair` (a pair id) for Pick a time between a mentor and mentee.

<a id="v0-7-5"></a>
## 0.7.5 (2026-10-18)

### Changed

- `reports.playbooks` and `praise.insights` return `previous`: the same counts for the window before the one asked for (`null` when there is nothing to compare), so a client can show the change since then without a second call.

<a id="v0-7-4"></a>
## 0.7.4 (2026-10-18)

### Added

- Requests Mello filed outside Shuffl: `actions.mine` (`self:read`) lists what Mello filed for the asking person in a connected HR system and where each one stands (`status` `submitted`, `approved`, `declined`, `cancelled`, `failed` or `unknown`, with `statusLabel`, `provider`, `providerName`, `details` such as the time-off policy and dates, `vendorUrl`, `submittedAt`, `settledAt` and `cancellable`). Only the person's own; a key with `self:read`.
- `sync.setWrites` (`integrations:write`) turns a People Sync connection's write switch on or off: off, Mello files nothing there and sends the person's request to HR instead; reads carry on. `sync.current` connections now return `writes` (`offered`, `enabled`).

<a id="v0-7-3"></a>
## 0.7.3 (2026-10-18)

### Changed

- Mello's read as figures: `pulse.latestRead` also returns `delta`, the change since the question was last asked (`change`, `direction` `up`/`down`/`flat`/`new`, `tone` `good`/`bad`/`neutral`/`watch`, `text` such as `↓ 6 pts`; moves under 5 points read neutral), and `gap`, the lowest and highest published group with the `points` between them, or null when the gap is under 15 points. Both are null when the result is withheld.

<a id="v0-7-2"></a>
## 0.7.2 (2026-10-18)

### Added

- What Mello can do: `capabilities.sheet` returns the rows Mello can answer (`kind: 'ask'`) and do (`kind: 'do'`) for the key's person in this workspace, each with `say` (what to ask), `result`, `system` (Shuffl or the connected system's name) and `status` (`available`, `needs_connection`, `needs_setup` or `coming`), plus `need` when it doesn't work yet. `capabilities.unlocks` lists what an HR system or document tool adds once connected. `capabilities.requestConnection` (`self:write`) asks HR to connect what a `needs_connection` row needs; administrator keys read the open asks per capability with `capabilities.requests` (`integrations:read`) and clear them with `capabilities.dismissRequests` (`integrations:write`). Asks carry counts, never names.

<a id="v0-7-1"></a>
## 0.7.1 (2026-10-18)

### Added

- Celebrations insights: `reports.celebrations` (`reporting.celebrationInsights`) returns, for the last 30, 90 or 180 days (`days`, default 90) and the same period before, celebrations sent (`sent.current.total`, with `people`, `birthdays`, `anniversaries` and `shared` with a channel or team), team cards delivered and the notes on them (`cards`), share answers (`consent.share`, `consent.private`, `consent.waiting`), messages that failed or were skipped (`missed`) and a `weekly` series of birthdays and anniversaries. Counts only. Administrator keys with `reports:read`.

<a id="v0-7-0"></a>
## 0.7.0 (2026-10-18)

### Changed

- The CLI reads a workspace key from `SHUFFL_API_KEY` instead of `SHUFFL_TOKEN` (breaking), matching the SDK's `apiKey` option. `SHUFFL_COMPANY_KEY` and `SHUFFL_BASE_URL` are unchanged. The README, guides, cookbook and API reference use the new name.

<a id="v0-6-5"></a>
## 0.6.5 (2026-10-18)

### Changed

- Playbook steps before day 1 go by email. `playbooks.enrollmentPreview` steps return `byEmail`: true when the step goes to a hire whose Slack isn't ready yet, so it goes by email to their work email until it is. Meet steps and check-ins never go by email.

<a id="v0-6-4"></a>
## 0.6.4 (2026-10-18)

### Added

- Mentorship insights: `reports.mentorship` (`reporting.mentorshipInsights`, `days` 30, 90 or 180, optional `programId`) returns `current` and `previous` periods with `pairs` (`started`, `endedEarly`, `completed`) and `checkIns` (`answered`, `met`, `asked`, `finals`, `helped`), `now` (`meeting`, `paused`, `suggested`, `waiting`) and `weekly` `pairs` and `checkIns`. Counts only, never goals. Administrator keys with `reports:read`.
- Mentorship settings: `mentorship.settings.get` returns the `settings` a new program starts with (`pairingWeeks`, `checkInWeeks`, `mentorCapacity`), Shuffl's `defaults` and `updatedAt`; `mentorship.settings.save` changes them (`mentorship:write`) and returns `changed` and `custom`.

### Changed

- `mentorship.programs.save` takes `pairingWeeks`, `checkInWeeks` and `mentorCapacity` as optional: a new program left without them starts with the workspace's mentorship settings, and an existing one keeps what it has.

<a id="v0-6-3"></a>
## 0.6.3 (2026-10-18)

### Added

- Automation insights: `groups.automationInsights` counts group automation runs finished in the last `days` (30, 90 or 180) against the period before: `current` and `previous` with `done`, `skipped`, `failed` and `people` reached, `weekly`, `byKind`, the top five `reasons` a run was skipped or failed (`code`, `why`, `outcome`, `n`), runs still `scheduled` and `automations` on of total. `groupId` narrows it. Administrator keys with `users:read`.

<a id="v0-6-2"></a>
## 0.6.2 (2026-10-18)

### Changed

- The READMEs list what the SDK client covers, resource by resource, and say how the packages are licensed. Operation descriptions and release notes no longer cite internal tracker numbers.

<a id="v0-6-1"></a>
## 0.6.1 (2026-10-18)

### Added

- The SDK and CLI name their release on every request in an `x-shuffl-client` header (`shuffl-sdk/0.6.1`, and for the CLI `shuffl-cli/0.6.1 shuffl-sdk/0.6.1`), so Shuffl can count requests by client and release. `new Shuffl()` takes an optional `client`, such as `billing-sync/1.2.0`, which goes in front; Shuffl never stores that part. No operation changed.

<a id="v0-6-0"></a>
## 0.6.0 (2026-10-18)

### Changed

- One client for every key (breaking): `new Shuffl({ apiKey })` replaces `createShuffl` and `createShufflCompany`, which are removed. A workspace key (`shuffl_api_`) calls the workspace methods at the top level (`shuffl.users.list()`); a company key (`shuffl_company_`) calls the company methods under `shuffl.company` (`shuffl.company.members.list()`). The client reads the key's prefix and refuses the other kind's methods with `UNAUTHORIZED` before sending a request. `token` is now `apiKey` (a string or a function that returns one), and `baseUrl` is optional, defaulting to production. The CLI and HTTP API are unchanged.

<a id="v0-5-12"></a>
## 0.5.12 (2026-10-18)

### Added

- Automation runs, a page at a time: `groups.automationRuns` lists every group automation run in the workspace newest first, 50 at a time, each with `personId`, `name`, `groupId`, `groupName`, `kind`, `trigger`, `outcome`, `why`, `at` and `dueAt`. `next` is the cursor for the next page (null on the last); `counts` gives every run by outcome (`scheduled`, `done`, `skipped`, `failed`). `outcome` and `groupId` narrow it. Administrator keys with `users:read`.

<a id="v0-5-11"></a>
## 0.5.11 (2026-10-18)

### Changed

- Leaving playbooks start on their own: `groups.setPlaybookAutoStart` now takes a playbook counted from each person's last day (`dayZero: 'last_day'`) as well as one counted from each start date, and `playbooks.savePlaybookProgram` accepts `autoStart` on one. When People Sync or People gives someone a last day ahead, they start it within ten minutes, with steps waiting for approval; someone removed is not added back for that last day. `groups.allAutomations` `playbookStarts` return `dayZero` (`start_date` or `last_day`).

<a id="v0-5-10"></a>
## 0.5.10 (2026-10-18)

### Added

- Pulse group results over time: `pulse.groupTrends` (`pulse:read`, `days` 30, 90 or 180) returns each group's published, frozen totals period by period (`unit` `week`, `fortnight` or `month`, with `periods`), for `everyone` and every `dimensions` row (department, role, tenure, group). Each row's `series` has one entry per `metric` (`favourable`, `enps` or a driver) with `points` (`value`, null when withheld; `seen`), `latest` and `change` since the first published point. eNPS points follow `enpsPeriods`, the last eight eNPS questions.

<a id="v0-5-9"></a>
## 0.5.9 (2026-10-18)

### Added

- Playbook form steps: a written step in `playbooks.saveProgram` can be `kind: 'form'`, going to the hire (`recipient: 'hire'`, no to-dos) with `formFields`, any of `preferredName`, `pronouns`, `bio`, `birthday` and `photo`. The hire answers with `playbooks.answerForm` (`stepId`, plus `preferredName`, `pronouns`, `bio` and `birthday` `{ month, day }` or `null`); answers write to their profile and Celebrations birthday under their own privacy settings, and the step records only when it was answered. `playbooks.mine` steps return `form` (`fields`, `answeredAt`) and the run returns `formValues`. Profiles take and return `preferredName`.

<a id="v0-5-8"></a>
## 0.5.8 (2026-10-18)

### Added

- Slack channel automations: `groups.setAutomation` takes `kind` `slack_invite` (add each person to a channel) and `slack_post` (post that they joined, with an optional `config.note`; joins only). `config.channelId` must be a channel Shuffl is in: the API reads it back from Slack and saves `config.channelName` itself. Automations return the channel as `target` (`#name`). The Slack call runs from a queue after the run is written; a refusal turns the run `failed` with Slack's reason as `code`, such as `not_in_channel`, and someone with no linked Slack account is `skipped` with `no_slack`.

<a id="v0-5-7"></a>
## 0.5.7 (2026-10-18)

### Added

- Mentorship programs. Administrators (`mentorship:read`, `mentorship:write`): `mentorship.programs.list` and `mentorship.programs.get` (`programId`: the program with `members`, each with `role` `mentor` or `mentee`, `topics`, `status` and open `pairs`, and `pairs` with `mentor`, `mentee`, `reasons`, `status`, dates, `checkIns` counts and `final` answers; never goals). `mentorship.programs.save` (`name`, `description`, `groupId` or null for everyone, `pairingWeeks` 4 to 52, `checkInWeeks` 1 to 8, `mentorCapacity` 1 to 5) and `mentorship.programs.setStatus` (`active`, `paused` or `archived`, which ends open pairs). `mentorship.pairs.suggest` suggests one mentor for every waiting mentee, never in their reporting line; `mentorship.pairs.decide` (`pairId`, `decision` `approve` or `skip`) starts a pair and introduces both people; `mentorship.pairs.end` ends one early; `mentorship.members.setStatus` takes someone off a program (`removed`) or puts them back. Everyone (`self:read`, `self:write`): `mentorship.mine` lists the programs open to the key's person and their pairings with `goals` and `checkIns`; `mentorship.join` (`programId`, `role`, `topics`) and `mentorship.leave`; `mentorship.pairs.setMine` (`pairId`, `status` `active`, `paused` or `ended`); `mentorship.goals.save` and `mentorship.goals.remove` for the mentee's goals (up to 5); `mentorship.checkIns.answer` (`checkInId`, `answer` `helped`, `met` or `not_yet`). Notification kind `mentorship`. `badges.list` includes the automatic `Mentor` badge for finishing a pairing as the mentor.

### Changed

- `home.places` can return `mentorship` when a mentorship program is running or a check-in waits for the person.

<a id="v0-5-6"></a>
## 0.5.6 (2026-10-18)

### Added

- More group automations: `groups.setAutomation` takes `kind` `ask_pulse` (`config.questionId`), `give_badge` (`config.badgeId`), `add_to_group` and `remove_from_group` (`config.targetGroupId`, a Shuffl list) and `tell_person` (`config.personId`, optional `config.note`), and `delayDays` (0 to 365) to run that many days after the join or leave. Automations return `delayDays`, `when` and `target` for the new kinds; runs can be `scheduled` with a `dueAt`, and `groups.allAutomations` totals add `scheduled`. A run that comes due after the person moved back is `skipped` with code `moved_back`. `groups.automationChoices` lists the Pulse `questions` (`id`, `text`, `retired`), `badges` and `groups` an automation can use. Pulse questions an automation asks gather in a send of its own that stays open four weeks and closes a week later, under the reporting floor.

<a id="v0-5-5"></a>
## 0.5.5 (2026-10-18)

### Added

- Playbook meet steps: a written step in `playbooks.saveProgram` can be `kind: 'meet'`, going to the hire (`recipient: 'hire'`, no to-dos) with `meet` set to `manager`, `skip_level` (their manager's manager), `person` (with `personId`) or `teammates` (with `meetCount`, 1 to 5: people with the same manager, longest here first). Who they meet is resolved from People when the steps are reviewed and approved: `playbooks.enrollmentPreview` and `playbooks.previewAs` steps return `meets` (names; empty when People has nobody, which skips the step at approval). The step sends one Slack group message with a card for each person and Pick a time. Calendar targets take `kind: 'playbook_meet'` with `id` `<enrollment id>.<step id>`, and bookings return `from` (`person`, `group`, `playbook` or `playbook_meet`). `playbooks.mine` returns `met`, the people meet steps introduced the hire to.

<a id="v0-5-4"></a>
## 0.5.4 (2026-10-17)

### Added

- Playbook tracks: written steps in `playbooks.saveProgram` take `onlyFor` and `notFor` (up to 5 group ids each, from `groups.list`; empty is everyone). A step only for groups goes to people in any of them, and a step not for a group never goes to its people. Groups are checked against People when the steps are reviewed and at approval: `playbooks.enrollmentPreview` steps return `track` (why this person doesn't get it, such as `Only for Engineering`, or null), a People change before approval fails `playbooks.approve` until the steps are reviewed again, and approved steps a track leaves out are skipped with no to-dos. `playbooks.program` returns `tracks` (each group the steps name, with `id`, `name`, `kind`, `department`, `rules` in words or null, `only` and `not` step counts and `hires` on the playbook now in it). `playbooks.previewAs` (`programId`, `personId`) returns the steps one person would get, without enrolling them: `dayZero`, `person`, `steps` (each with `ref`, `title`, `kind`, `offsetDays`, `dueAt`, `recipient`, `track` and `skipped`, why they wouldn't get it), `skipped` and `tracked`.

<a id="v0-5-3"></a>
## 0.5.3 (2026-10-17)

### Added

- Connections for employees: `network.introductions` returns the key's own `programs` (each Connections program that introduces them, with `id`, `name`, `nextAt`, null while paused, and `breakUntil`), `past` (their latest introductions, each with `id`, `at`, `program` and `with`: the other people, `id`, `name`, `title`, `photoUrl` and `slackUserId`) and `total`. Nothing about other people's introductions or matches not yet sent. `home.places` can include `connections`.

<a id="v0-5-2"></a>
## 0.5.2 (2026-10-17)

### Added

- One list of join and leave rules: `groups.allAutomations` also returns `playbookStarts` (each playbook that starts on each new hire's start date: `playbook` with `id`, `name` and `status`, `group` or null for everyone, `state` `active` or `paused`, `runs`, `runs30` and `lastRunAt`) and `programRosters` (each Connections program whose people are a group: `program`, `group` and `state`). `groups.setPlaybookAutoStart` (`programId`, `state` `active`, `paused` or `off`, optional `groupId`, null for everyone) turns a playbook's start-date rule on, pauses it keeping its group, or removes it. Playbooks return `playbook.autoStartPaused`; a paused playbook starts nobody until it is turned back on.

<a id="v0-5-1"></a>
## 0.5.1 (2026-10-17)

### Added

- Playbook Hires view: `playbooks.program` returns `hires` (`active`, `onTime` percent of to-dos done by a day after their day or null, `overdue`, `askedForHelp`) and each active row's `progress` (`day` from day 0, `done`, `total`, `next` step `title` and `at`, `overdue` to-dos grouped by owner with `ownerId`, `ownerName`, `role` `hire`, `manager`, `buddy` or `other`, `count`, `since` and `nudgedAt`, `unconfirmed` read-and-confirm pages and `help`). `playbooks.nudge` (`enrollmentId`, `ownerPersonId`) sends that person one Slack reminder with their overdue to-dos for the hire and returns `status` `sent`, `recent` (already nudged today), `nothing_overdue`, `off`, `no_slack` or `failed`.

<a id="v0-5-0"></a>
## 0.5.0 (2026-10-17)

- `playbooks`: `answerBuddyAsk`, `answerBuddyFollowUp`, `answerCheckIn`, `answerFirstDaysCheckIn`, `approve`, `askBuddy`, `assignBuddy`, `buddyCandidates`, `buddyFollowUps`, `buddyProgram`, `buddySuggestions`, `candidates`, `completeTodo`, `draftPlan`, `enroll`, `enrollmentPreview`, `firstDaysCheckIns`, `firstDaysResults`, `mine`, `person`, `plans`, `previewEnrollment`, `program`, `remove`, `respondBuddy`, `retry`, `run`, `savePlan`, `saveProgram`, `setBuddyVolunteer`, `setPlanGoal`, `sharePlan`, `update`, `volunteerAsBuddy` - `onboarding`: `answerBuddyAsk`, `answerCheckIn`, `approve`, `askBuddy`, `assignBuddy`, `buddySuggestions`, `candidates`, `completeTodo`, `mine`, `person`, `remove`, `respondBuddy`, `retry`, `update` (removed)

### Breaking

- Playbooks everywhere. The `onboarding.*` operations are now `playbooks.*`, with the same inputs and outputs. `onboarding.journey`, a person's run through a playbook, is `playbooks.run`, and `reports.onboarding` is `reports.playbooks`. The `onboarding:read` and `onboarding:write` scopes are `playbooks:read` and `playbooks:write`; existing keys and connections keep their access under the new names. Webhook event types `onboarding.*` are `playbooks.*` (such as `playbooks.enrolled` and `playbooks.step.sent`), and subscribed endpoints were moved over. Values that said onboarding or journey say playbook: program and draft `kind` `playbook`, the `playbook` config on `playbooks.saveProgram`, `playbooks.program` and `programs.*`, group automation and Culture nudge kind `start_playbook`, reminder kind `playbook_todo`, notification kind `playbook`, citation kind `playbook`, person activity kinds `playbook_started` and `playbook_completed`, `playbookMessagesSent`, and the `playbooks` list on `playbooks.run`.
- Departments are groups. Every department is now a group (same id) marked `department` in `groups.*`, and a person is in one department at most. `units.save` and `units.archive` are gone: create a department with `groups.save` (`kind` `list`, `department` true, people in `personIds`), rename it with `groups.save` and remove it with `groups.remove`; departments are no longer archived. `units.list` takes no `archived` and returns `id`, `name` and `members` for each department. `groups.list`, `groups.get` and the other group reads return `department`. A department group is never `onProfile` or the HR team and can't be a rules group. `users.save` still sets a person's department with `departmentId` (a department group's id), and `users.page`'s `departmentId` filter reads department groups. Program `unitIds`, knowledge audience `units` and group rules on `department` keep working with the same ids. `exports.team` no longer returns `organizationUnits` or `unitMemberships`: departments are in `peopleGroups` and `peopleGroupMembers` with `department` true, and an older export that carries them still imports. The `directory.unit.created`, `directory.unit.updated` and `directory.unit.archived` webhook events are gone, since nothing sends them; read departments with `units.list` or `groups.list`. An endpoint subscribed to them keeps its other events, and saving it drops them.
- Teams are groups. Every team is now a group (same id) in `groups.*`, and `units.*` covers departments only: `units.list` and `units.save` no longer return `kind`, `hrTeam` or `slackUserGroupId`, and `units.save` takes no `kind`. Groups return `source` (`shuffl`, `slack` or `directory`: synced groups have read-only members), `onProfile` (shown on profiles and the org chart, as teams were) and `hrTeam` (the group that answers HR requests); `groups.save` takes `onProfile`, and refuses member or rule changes on a synced group. `users.details` returns `department` and `groups` instead of `units`, and `users.save` takes `groupIds` instead of `teamIds`. `users.page` filters by `groupId` instead of `directoryTeamId`; `users.filterOptions` and `programs.options` return `units` of kind `department` or `group`. `profiles.get` and `network.orgChart` return `groups` instead of `teams`. Pulse results no longer split by team: `pulse.insights` and `pulse.questionDetail` drop the `team` dimension, and former team results are under `group`. `digest.culture` returns `focus.kind` `department` or `group`. A group rule `team is X` becomes `segment` (In group). `exports.team` returns `peopleGroups` and `peopleGroupMembers`.
- Pulse questions go to a group, not an audience. `pulse.addQuestion` takes `groupId` (one of the workspace's groups, or null for everyone) instead of `audience`, and `pulse.setQuestionGroup` (`questionId`, `groupId`) replaces `pulse.setQuestionAudience`. `pulse.audienceOptions` is gone: pick from `groups.list`. `pulse.questions`, `pulse.questionDetail`, `pulse.actions`, `pulse.action` and `pulse.insights` return `groupId` and `groupLabel` where they returned `audience` and `audienceLabel`. eNPS still goes to everyone.

### Added

- `users.page` rows return `directory`: `jobTitle` from the directory for every key, and `startDate` (a calendar date) for administrator keys only, null for everyone else.
- New hire 30/60/90 plans: `playbooks.plans` lists the plans for the new hires the key's person manages (`asManager`: each new hire playbook episode with `hire`, `startDate`, `programName` and `plan` or null) and their own plan once shared (`asHire`). `playbooks.draftPlan` has Mello draft three goals per stage (`draftedBy` `ai`, or `rule` without a model); `playbooks.savePlan` (`enrollmentId`, `expectedRevision`, `goals` with `stage` 30, 60 or 90 and `text`, at most 5 per stage) edits them; `playbooks.sharePlan` shows the plan to the hire; `playbooks.setPlanGoal` (`goalId`, `done`) ticks a goal off, for the manager or the hire. Only the hire and their manager in People change a plan. `playbooks.program` rows return `plan` (`state` `none`, `draft` or `shared`, `done`, `total`, and `goals` with `stage`, `text` and `done` for HR to read) on new hire playbooks. Mello now drafts each new hire's plan by itself when they join a playbook counted from their start date, and plans return `autoDrafted`.
- Buddy program health: `playbooks.buddyCandidates` (`personId`) suggests up to five buddies for a new hire, never anyone in their reporting line, volunteers first, each with `reasons`, `volunteer` and `load` (`active`, `total`). `playbooks.volunteerAsBuddy` (`volunteer`) puts the key's person on the buddy volunteer list or takes them off; `playbooks.setBuddyVolunteer` (`personId`, `status` `volunteer`, `removed` or `none`) is HR's version, and only HR undoes `removed`. `playbooks.buddyProgram` lists volunteers and recent buddies with `status`, `load`, `hiresAnswered`, `hiresMet`, `hiresHelped` and `neverMet`. `playbooks.buddyFollowUps` and `playbooks.answerBuddyFollowUp` (`feedbackId`, `answer` `helped`, `met` or `never`) are the "did you meet?" question sent to the hire and buddy on the playbook's `buddyFollowUpOffsetDays` (default 30). Onboarding playbooks also take `buddyTopics`, the list sent to the buddy with the invitation.
- Add people by department or community: `playbooks.previewEnrollment` and `playbooks.enroll` take `departmentId` (everyone in a department) or `communityId` (everyone who shows a community on their profile) beside `groupId`; one at a time, and people already on the playbook are left out.
- Playbooks that start automatically: `playbooks.saveProgram` takes, and `playbooks.program` and `programs.*` return, `playbook.autoStart` (default `false`) and `playbook.autoStartGroupId` (a group, or null for everyone). Only a playbook whose `dayZero` is `start_date` can start automatically. Once it is on, everyone People gives a start date ahead (People Sync or an edit) starts it within ten minutes, with steps waiting for approval as `playbooks.enroll` leaves them. Each person starts it once per start date: someone removed is not added back, and a new start date is a new episode.
- Every group automation in one list: `groups.allAutomations` returns each automation of the workspace with its `group` (`id`, `name`, `kind`, `members`) beside the fields `groups.automations` returns, `totals` (`active`, `paused`, `groups`, and `done30`, `skipped30`, `failed30` runs in the last 30 days) and the latest 20 `runs` across groups with `groupName`. `targetId` keeps only the automations that start that playbook or program or invite to that community. `groups.list` and `groups.get` return `automations`, how many of the group's automations are on.
- Read and confirm in playbooks: a playbook step can be `kind` `acknowledge` in `playbooks.saveProgram`, naming one published Knowledge page in `resources` and going to the `hire`. Once it goes out, the person confirms the page in the app as for a policy confirmation; `playbooks.program` returns each such step's `answeredAt` and `acknowledgedVersionId`, and `playbook.journey` returns `page` and `confirmedAt` on its steps.
- Praise in the network and badges: `network.mine` suggestions carry `praised` (true when the two people thanked each other, or one thanked the other, in public praise in the last year) and list the reason first (`Praised you`, `You praised them` or `You praised each other`); it stays a suggestion until both say they know each other. `badges.list` includes three automatic badges for being thanked in public praise: `First thanks`, `Thanked across teams` (from three departments) and `Lives our values` (five thanks for one value).
- Connections from communities: `programs.create` and `programs.update` take `communityIds` (1 to 20 of the workspace's communities) so a program's people come from those communities instead of everyone in the Slack channel or a group; a program uses one of the three, and choosing a group or communities in an update replaces the other. Only members who show the community on their profile take part, read again before each round; introductions are still posted in the program's channel. `match.unitKind` takes `community`, so `cross-unit` mixes people from different communities and `within-unit` keeps each group inside one (within a program's own communities when it has them). `programs.get` and `programs.list` return `communityNames`, `programs.options` returns `communities` (`id`, `name`, `memberCount` of members who show it) and `programs.communities` lists the same.
- How people like to be thanked: `praise.setRecognition` (`recognition`: `public` or `private`) sets the key's own choice; choosing private hides praise they already received from their profile and the feed, and returns how many were `hidden`. `praise.mine` returns `recognition` and `notes` (`received` and `given` private notes, each with `id`, `giver`, `recipient`, `text`, `values` and `sentAt`). `praise.send` takes `privately` (send each person a private note instead of posting) and sends a private note to anyone who prefers one; it returns `posted`, `privateNotes`, `notSent` (names Slack refused a note to) and a `praiseId` that is null when nothing was posted. `praise.insights` returns `teams` and `locations` beside `departments`, each row with `low` when under `coverageLow` percent were recognized, and counts private notes as recognition. `praise.settings` and `praise.saveSettings` carry `managerNudges`.
- SCIM groups: a group's `source` can be `scim`, a synced group whose members follow the identity provider. `sync.groups` and `sync.setGroup` take a SCIM connection's id too (from `sync.current`, `provider` `scim`, `connection.source` `scim`) when called by a signed-in company administrator: `imported` brings the group into every workspace of the company, `onProfile` applies to this workspace, and `syncing` is always false because members arrive at once; like other synced groups, its members are read-only in `groups.save` and `users.save`.
- Outgoing webhooks: `webhooks.list`, `webhooks.get`, `webhooks.catalog`, `webhooks.create`, `webhooks.update`, `webhooks.delete`, `webhooks.rotateSecret`, `webhooks.sendTest`, `webhooks.deliveries.list`, `webhooks.deliveries.get`, `webhooks.deliveries.replay` and `webhooks.deliveries.replayFailed`, under the new `webhooks:read` and `webhooks:write` scopes. `create` takes a public `https://` `url`, an optional `description` and `events` (catalog types; each group needs its read scope, such as `knowledge:read` for `knowledge.*` and `hr:read` for `hr.*`), and returns the `endpoint`, its signing `secret` (`whsec_…`, shown only here and from `rotateSecret`) and the `testDeliveryId` of the `webhook.test` it queues. `rotateSecret` takes `keepOldFor` (`now`, `1h`, `24h` or `7d`); both secrets sign until then. Deliveries follow Standard Webhooks (`webhook-id`, `webhook-timestamp`, `webhook-signature`) with retries for a little over a day. The SDK exports `verifyWebhook({ body, headers, secret })`, `signWebhook`, `webhookEventCatalog`, `webhookEventTypes` and the `WebhookEvent` type from `@shuffl/sdk`, and the catalog from `@shuffl/api-contracts/webhooks`.
- Warm introductions: `network.requestIntro` (`personId`, `viaId`) asks someone you and that person both know to introduce you. Shuffl sends them a Slack DM with Introduce and Not this time; Introduce starts a group DM with the three of you. It returns `id`, `created` (false when the same request was already waiting) and `state` (`asked`). The person asked must have a tie to both of you that you can see, and you can have 3 requests waiting at once. `network.mine` returns, on each suggestion, `introVia` (the first person in common Shuffl can ask, or null) and `intro` (your latest request about them: `state` `asked`, `introduced` or `not_this_time`, with `viaId`). A decline only ever reads as `not_this_time`. `reminders.settings`, `reminders.saveSetting`, `reminders.mine` and `reminders.saveMine` cover a new kind, `network` ("Do you know this person?", choices `once_a_week` and `once_a_month`, off by default).
- Choosing synced groups: `sync.groups` (`directory.providerGroups`, `connectionId`) lists every group a People sync connection or the Slack workspace (its connection id is the Slack team id) offers, each with `id`, `name`, `memberCount` (people in it at the source), `imported`, `groupId` (the group in Shuffl, when brought in), `onProfile`, `listed` (seen by the latest sync) and `seenAt`, plus the `connection` (`id`, `label`, `provider`, `source`). `sync.setGroup` (`directory.setProviderGroup`: `connectionId`, `id`, `imported` and/or `onProfile`) brings a group in, takes it out or shows it on profiles, and returns `groupId`, `imported` and `syncing` (a sync started so its members arrive). New groups from a source no longer become groups by themselves: a sync lists them and only the ones brought in follow the source. Taken out, a group stays a Shuffl list with its members. `sync.current` connections return `groups` (`listed`, `imported`).
- Group stats: `groups.glance` (`id`) returns a group at a glance in the same shape as `users.glance`, counted over the group's current members: `headcount`, `enough` (5 people or more; below that the splits are empty), `hiredThisYear`, `averageTenureYears`, `tenure`, `departments`, `timezones`, `me` (where the key's person sits by start date, when they are in it) and `topics` (what its people are asked about). `leftThisYear` is always null. Any key reads groups shown on profiles; an owner or administrator key reads any group. A group the key can't see is `NOT_FOUND`.
- `digest.culture` returns the morning Culture page's figures: `week` (people `thanked`, `introductions` and playbooks `finished` in the last seven days), `playbooks` (the four busiest active playbooks, each with `id`, `name`, `leaving`, `active` and up to four `people` with `enrollmentId`, `name`, `photoUrl`, `day`, `progressPct`, `flag`: `help`, `no_buddy` or `late`, and `late`), `help` (new hires who asked for help at a check-in in the last 14 days), `wins` (`finished` playbooks this week and shared work `anniversaries` in the next seven days), `focus.company.previous` (the company's `thankedPct`, `metPct` and `pulse` over the 90 days before) and each outcome's short `step`.
- Groups for everyone: `groups.directory` lists the groups shown on profiles to any key in the workspace, the key's own groups first, each with `id`, `name`, `description`, `source`, `hrTeam`, `memberCount`, `mine` and up to five `faces` (`id`, `name`, `title`, `photoUrl`). `groups.directoryGroup` (`id`) reads one with every `member` the same way; a group not shown on profiles is `NOT_FOUND`. People whose employment ended are left out of both.
- `users.glance` returns `undated`, how many people in the headcount have no start date, and `leftThisYear`, how many were marked as left in the last year: a number for an owner or administrator key, null for anyone else.
- `celebrations`: `thisWeek` lists who was celebrated in a channel in the last seven days (`days`, and `people`, each with `personId`, `name`, `eventType`: `birthday` or `anniversary`, `years` and `sentAt`). Celebrations sent privately or to a person's own team are left out.
- Automations on a group: `groups.setAutomation` adds or changes what happens when someone joins or leaves a group (`groupId`, `trigger`: `entered` or `left`, `kind`: `send_message`, `start_onboarding`, `invite_community`, `add_to_program`, `tell_hr` or `tell_manager`, and a `config` for the kind: the message's `subject`, `body`, `action` and `channel`; a `programId`; a `communityId`; or a `note`). A new automation starts paused; `groups.setAutomationState` (`id`, `state`: `paused` or `active`) turns it on or off, `groups.removeAutomation` (`id`) drops it with its runs, and `groups.automations` (`groupId`) lists a group's automations with each one's `summary`, `state`, `runs` and `lastRunAt`, and its last 50 runs (`personId`, `name`, `trigger`, `outcome`: `done`, `skipped` or `failed`, and `why` a run did nothing). Runs happen from the groups refresh, once per person and entry; `personActivity.personActivity` returns them as `group_automation` events.
- Message a group: `groups.prepareMessage` writes a draft to everyone in a group (`groupId`, `subject`, `body` as paragraphs, optional `action` with `label` and a `path` starting with `/app`, and `channel`: `preferred`, `slack` or `email`) and returns it with `preview` (how it reads to its first recipient, with `{{first_name}}`, `{{manager}}` and `{{company}}` filled in) and `reach` (`recipients`, `reachable`, `alreadyToday`, `unreachable`). `groups.sendMessage` (`id`, `requestKey`) queues it once per request key on each person's chosen channel, leaving out anyone HR already messaged that day, and returns `queued` and `skipped`; the same key replays the result. `groups.messages` (`groupId`) lists a group's drafts and sends, `groups.message` (`id`) reads one with its preview and reach, `groups.discardMessage` (`id`) drops a draft, and `groups.export` (`id`) returns the members as `csv` with a `filename`. People get the message under the new `hr_message` notification kind in `notifications.preferences`, which offers Slack and email and cannot be fully turned off; a Slack thread reply to one becomes an HR request.
- A Connections program's people can come from a group: `programs.create` and `programs.update` take `groupId` (null or omitted keeps the Slack channel roster), `programs.get` and `programs.list` return `groupId` and `groupName`, `programs.options` returns `groups` to pick from, and `programs.syncRoster` reads the group's members when one is set. Joining or leaving the channel no longer changes who takes part in such a program; the channel stays where introductions are posted.
- `playbooks.previewEnrollment` and `playbooks.enroll` take `groupId`: everyone in that group who is not on the journey yet is added after `rows`, which may now be empty. The preview returns the expanded rows and its fingerprint covers them.
- `groups`: `list`, `get`, `members`, `options`, `searchPeople`, `suggestions`, `preview`, `save`, `refresh`, `remove`. A group is a live set of people in the workspace, kept by rules on People attributes (re-evaluated every ten minutes and on save) or a list picked by name; `suggestions` lists the starter groups not made yet and `preview` counts who a set of rules would hold today.
- `knowledge.templates`: `draft` has Mello research a policy template and write the whole page. Send the template id, `title`, `body` (the template text, prefilled or not), its `checks`, and optional `notes` and `website`. It returns `title`, `body`, `choices` (each value Mello chose, with its `topic`, `value`, `basis`: `company`, `law` or `typical`, and the `source` link the web search returned, if any), `sources`, `placeholders` (how many are left) and `researched` (whether the web search ran). Nothing is saved.
- `knowledge.templates`: `facts` returns what Shuffl knows that policy templates ask for: `companyName`, `hrContact` (who answers HR requests), `hrRequests` (whether people can send one), `countries` (from the directory's time zones, most people first) and `holidaysTitle` (the published holidays page). The app fills a new template's placeholders with them.
- `knowledge.handbook`: `export` returns the pages published to everyone as one handbook: `teamName`, `generatedAt`, `pages` (each with `title`, `effectiveAt`, `version` and its Markdown `body`), `leftOut` (how many published pages are for particular groups or admins only) and the whole handbook as `markdown`. Each call is recorded as a download.
- `knowledge.acknowledgements`: `status`, `request`, `remind`, `close`, `mine`. HR asks the people who can read a published page to confirm they read it: `request` asks the page's audience to confirm its published version (and messages them unless `notify` is false), `remind` messages the people who haven't confirmed, `close` stops asking, and `status` lists who is asked and who confirmed which version. `mine` lists the pages the key's person is asked to confirm. Confirming itself happens only in the app.
- Notification preferences cover a new kind, `policy_acknowledgement`: the message asking someone to read and confirm a Knowledge page.
- `digest`: `culture` lists the Culture page's recommendations in Mello's order, each with its `area` (`praise`, `connections`, `onboarding` or `pulse`) and `rank`, and `outcomes`: what happened after each recommendation acted on in the last 60 days. It also returns `focus`: each department (or team, in a smaller company) of 7 or more people with its share thanked, share introduced to another team, new hires without a buddy and Pulse score beside the company's, the areas where it is behind, and introductions between each pair of groups. Each group also carries its weakest Pulse driver (`weakDriver`), `responsePct`, `joined`, `left`, `needHelp`, `missedCheckIns` and `previous` figures for the 90 days before, and `focus.praise` counts praise between groups.

### Changed

- Badge offers when a project or event channel goes quiet: `badges.settings` returns and `badges.saveSettings` takes `channelOffers` (default `true`). Once a day Shuffl looks at the channels it is in whose names mark a project, launch or event and offers the channel's maker a badge for everyone who posted, once per channel; such offers have `source` `channel`.
- Celebration send days: `celebrations.save` and `celebrations.preview` take `sendDays` (ISO weekdays, 1 = Monday; Monday to Friday when left out) and `offDayShift` (`before` or `after`: where a birthday or anniversary on another day goes), and `celebrations.program` returns both for each celebration. `celebrations.preview` returns the sample's `sendDate` and `shifted`, and `celebrations.review` rows carry `sendDate`, the local day each one goes out.
- Journeys are now called playbooks. Operation descriptions, error messages and the app's links say playbook and point at `/app/playbooks` and `/app/playbooks/all`; the old `/journeys` addresses redirect there. Operation names and fields followed in a later release.
- Slack user groups become synced groups with `source` `slack`, kept in step with Slack's members. The Slack import also fills `jobTitle`, `timezone`, `startDate` and `managerId` in `directory.directoryPerson` from Slack profiles for people no HR system manages.
- Pulse results HR can share: `pulse.program` returns and `pulse.saveProgram` takes `shareResults` (default `false`). When it is on, `pulse.asked` returns `result` on each closed question asked to everyone whose reporting floor was met (`unit`: `favourable` or `eNPS`, `value`, `respondents`); otherwise `result` is null.
- Journeys: onboarding is the first kind of journey. `playbooks.saveProgram` takes, and `playbooks.program` and `programs.*` return, `playbook.dayZero` (`start_date`, `joined` or `date`: what each person's days count from), `playbook.dayZeroDate` (the one date when `dayZero` is `date`), `playbook.buddy` and `playbook.helpCheckIn` (whether the buddy messages and the help check-in are part of the journey; both default to true). A `joined` journey has no steps before day 0. `playbooks.enroll` and `playbooks.previewEnrollment` fill a missing `startDate` from the journey's day 0. `playbook.journey` takes an optional `enrollmentId` and returns `enrollmentId`, `dayZero`, `buddyOn` and `journeys` (each journey the person is on, with `current`). `playbook.personOnboarding` returns `buddyOn` per enrollment.
- Catch-up for people who join a journey after day 0: `playbooks.saveProgram` takes, and `playbooks.program` and `programs.*` return, `playbook.catchUp` (`skip`, the default, `bundle` or `shift`). `playbooks.enroll` takes an optional `catchUp` for that batch and returns `catchUp` and `late`, how many were added after day 0. `playbooks.previewEnrollment` returns `missed` per row and `catchUp` (`default`, `late` and `sample`, one person's steps under each choice). `playbooks.enrollmentPreview` returns `joinedOn` and `catchUp` per step (`planned`, `today`, `skipped` or `bundled`). Steps can have the status `skipped` and the kind `catch_up`, the one What you missed message.
- Check-ins on any day with a journey's own questions: a journey step of kind `checkpoint` can have `checkpoint: 'custom'` with `questions` (1 to 6, each with an `id`, `text` and `kind`: `scale` with a `scale` of `agree_5`, `rating_5` or `yes_no`, or `open`), on any day and as often as the journey likes, in `playbooks.saveProgram`, `playbooks.program` and `programs.*`. `playbooks.firstDaysCheckIns` returns them with `checkpoint` set to `step:<journey id>:<step id>`, plus `intro`, `note` and `journey` on every check-in; `playbooks.answerFirstDaysCheckIn` takes that `checkpoint` and answers the step's questions. `playbooks.firstDaysResults` returns `journeys`: each journey's own check-ins (`programId`, `journey`, `stepKey`, `title`, `offsetDays`, `dayZero`, `sent`, `answered`, `published` and `questions`), combined only within that step and shown above the reporting floor like the built-in ones. `playbook.journey` returns `kind` per checkpoint (`day_30`, `day_60`, `day_90`, `day_365` or `custom`) and `checkpoint` as the check-in's subject.
- Leaving: `directory.saveDirectoryPerson` takes, and `directory.directoryPerson` returns, `lastDay` (the person's last day; null clears it, left out keeps it) and `lastDayAnnounced` (default false). Shuffl never takes or stores a reason. Until a last day is announced, `directory.directoryPerson` returns `lastDay` as null to anyone but owners, admins and the person's manager. A journey's `playbook.dayZero` can be `last_day`: each person's days count from their last day in People, with steps up to 60 days before and after, and no buddy. Until the leaving is announced only steps to the manager and HR go; the others wait with status `waiting` and `errorCode` `until_announced`. A changed last day sends the run's unsent steps back for approval at the new dates, and a cleared one ends the run with `removalReason` `leaving_cancelled`. `playbooks.previewEnrollment` reports `missing_last_day` for someone without one. Ending someone's employment or access no longer ends their leaving journey; only the steps to them stop.
- `praise.feed` returns `values`: on the first page, the five values praised most in the last 30 days (`value`, `count`), counted from the praise the key's person can read. Later pages return an empty list.
- Pulse results by people group: `pulse.insights` and `pulse.questionDetail` return a fifth split, `groups.dimensions.group` (`questionDetail`: `groups.group`), one row per group with the same `label`, `respondents`, `favourablePct`, `enps` and `drivers` as departments and teams. `pulse.insights` also lists every current group with `seen: false` until a question closes after it was made. A group's figures are withheld under the reporting floor, when the people outside it are under the floor, and when it and any other published total would give fewer than the floor away by subtraction.
- Praise in private Slack channels: `praise.feed` and `praise.ofPerson` include praise from private praise channels the key's person is in now, and `praise.mine` leaves out private channels they have left. `praise.composer` returns `private` per channel and offers private channels only to their members; `praise.send` refuses a private channel for someone who isn't in it. `praise.settings` returns `membersFresh` and `membersError` per channel.
- Charts on the employee People page: `users.glance` returns `monthly`, the last twelve months of headcount and people who joined (empty below five people).
- Onboarding to-do reminders: `reminders.settings`, `reminders.saveSetting`, `reminders.mine` and `reminders.saveMine` cover a third kind, `onboarding_todo`, with the choices `one_day_late` and `three_days_late`. It is on (`one_day_late`) unless a team turns it off.
- Mello answers from the asker's onboarding journey: a citation's `kind` can be `onboarding` in `assistant.history`, linking to Your onboarding. `knowledge.search`, `knowledge.context` and `manager.current` share the citation shape but never return it.
- `reports.onboarding` returns `hires.withBuddy`, the hires in onboarding now who have a buddy. `digest.actOnNudge` and `digest.dismissNudge` accept `from: 'culture'` for nudges acted on from the Culture page. Culture nudges have six more kinds: `praise_low`, `pulse_low`, `start_praise`, `start_pulse`, `start_onboarding` and `start_cross_team`.

<a id="v0-4-0"></a>
## 0.4.0 (2026-10-12)

The company API joins the SDK, the CLI and OpenAPI. It takes a company key (`shuffl_company_`) from Company settings → API keys, never a workspace key, so it has its own client and commands.

### Breaking

- `GET /api/v1/company/members.list` is paginated: it takes `limit` (1 to 100, 25 by default) and `cursor`, and returns `{ items, nextCursor }` instead of an array of every member. Scripts that call it over HTTP read `items` and follow `nextCursor`.
- The first 90 days' check-ins are onboarding journey steps (`kind: 'checkpoint'` with `checkpoint` of `day_30`, `day_60`, `day_90` or `day_365`). `reminders.settings`, `reminders.saveSetting`, `reminders.mine` and `reminders.saveMine` now cover only `praise` and `pulse`: the `manager_check_in` and `new_hire_check_in` kinds and their choices are gone. `onboarding.firstDaysResults` no longer returns `manager`, and `onboarding.journey` no longer returns `managerCheckIns`.

### Added

9 operations, by resource:

- `company`: `current`, `details`, `rename`
- `company.emailDomains`: `list`
- `company.members`: `list`, `remove`
- `company.slack`: `list`
- `company.workspaces`: `list`, `setDefault`
- `onboarding`: `completeTodo` checks off (or reopens) one of your own onboarding to-dos.

### SDK, CLI and OpenAPI

- SDK: `createShufflCompany({ baseUrl, token })` returns a typed client for these operations, such as `company.members.list({ limit: 100 })`. It calls the REST paths under `/api/v1/company` and throws `ORPCError` like `createShuffl`.
- CLI: `shuffl company` commands, with `shuffl login --company --token-stdin` and `shuffl logout --company`, or `SHUFFL_COMPANY_KEY` in the environment. The company key is saved in `company.json`, apart from the workspace key.
- OpenAPI: the operations are under `/company`, with the `companyKey` security scheme and `x-shuffl-credential: company`.

### Changed

- Company API responses return exactly the published fields: `company.details` no longer includes `internal`, and `company.slack.list` sends `revokedAt` as an ISO time.
- Company API errors include `status` and `defined: false`, like workspace API errors.
- Onboarding journeys: `onboarding.saveProgram`, `onboarding.program` and `programs.*` carry `onboarding.steps`, the steps HR writes (message, to-do or check-in, day offset, recipient, Knowledge, link and community resources, to-dos). `onboarding.enrollmentPreview` returns each step's `ref`, `title`, recipient and to-dos; `onboarding.program` returns `stepKey` and `title` per step; `onboarding.journey` returns `todos`, `gettingReady` and `resources`.
- `reports.people` returns `history`: headcount, joins and people marked as left for each of the last 12 months, plus today's departments and tenure.
- `exports.team` and `reports.overview` return each program's `onboarding.steps` too; `credentials.api.create`, `credentials.api.list` and `permissions.list` include the `onboarding.completeTodo` permission.
- `reports.onboarding` returns `messages.weekly` (sent and on time per week); `praise.insights` returns `givers` and `recognized` per week.

<a id="v0-3-0"></a>
## 0.3.0 (2026-10-12)

Breaking: two fields were removed and one object was reshaped, so this is a minor release. Upgrade the SDK and CLI together.

### Breaking

- `teams.setup`: `slack.connected` and `slack.status` are replaced by `slack.state`, `slack.live` and `slack.reason`.
- `celebrations.preview` no longer returns `fingerprint`, and `celebrations.save` no longer takes `previewFingerprint`.

### Added

7 operations, by resource:

- `celebrations`: `saveAnniversaryDate`, `startDates`
- `home`: `places`
- `hr`: `mine`
- `onboarding`: `journey`
- `teams`: `slackConnection`
- `users`: `glance`

### Changed

- Schedules take `days` (1 to 7, Monday first) to pick days of the week: program schedules in `programs.*` and `reports.overview`, `reminders.saveSetting` and `reminders.settings`. `pulse.program` and `pulse.saveProgram` add `sendEvery`, `sendUnit`, `sendStart` and `sendMonthWeek`.
- Notification kinds add `check_in` and `getting_started` (`notifications.preferences`, `notifications.save`).
- `users.save` takes `originalStartDate`; `exports.team` returns it, and `celebrations.preferences` returns `startDate`.
- `communities.members` returns `joinedAt`; `praise.settings` returns `slack`; `sync.current` returns `slack` per connection; `digest.preview` returns `trend` and a `ratio` per section; `operations.activity` always returns `summary`.
- `profiles.whoKnows` takes `from`; `support.create` takes `guides`; `praise.send` accepts more `from` values.
- `credentials.api.create`, `credentials.api.list` and `permissions.list` include the new operations' permissions.

<a id="v0-2-1"></a>
## 0.2.1 (2026-10-03)

No operation changes. `shuffl --help` and the OpenAPI summaries now describe all 303 operations in words. In 0.2.0, 133 of them showed only their operation id, such as `access.get`.

<a id="v0-2-0"></a>
## 0.2.0 (2026-10-03)

Breaking: while the major version is 0, a minor release may rename or remove operations. Upgrade the SDK and CLI together.

### Renamed

- `workspaces.*` is now `teams.*`: `current`, `setup`, `status`, `initialize`, `saveAgentSetup`, `resendSlackWelcome`, `slackJoinAccess`, `saveSlackJoinAccess`, `slackProfileLink`, `saveSlackProfileLink`, `syncSlackProfileLinks`. `GET /api/v1/workspace` is now `GET /api/v1/team`, and the `/manage/workspaces/...` paths are now `/manage/teams/...`.
- `exports.workspace` is now `exports.team`.

### Removed

- `billing.*`: company billing moved to Company settings, and a team key never gains company scope.
- `provisioning.current` and `provisioning.configure`: SCIM connections are created in the browser, because creating one issues the identity provider's secret.

### Added

97 operations, by resource:

- `badges`: `generatePicture`, `holders`, `saveSettings`, `settings`, `uploadPicture`
- `celebrations`: `card`, `cards`, `removeCardNote`, `signCard`
- `communities`: `channelSuggestions`, `createCapabilities`, `createSlackChannel`, `fromChannels`, `generateCover`, `invite`, `revokeInvitation`, `uploadCover`
- `digest`: `actOnNudge`, `dismissNudge`, `nudge`, `nudges`, `preview`, `saveSettings`, `settings`
- `exports`: `team`
- `getStarted`: `celebrate`, `celebration`, `dismiss`, `draftIntro`, `get`, `intro`, `wizard`
- `home`: `preferences`, `react`, `story`
- `hr`: `categories`, `rebuildCategories`, `saveCategories`
- `invitations`: `candidates`, `createMany`, `inviteDirectory`
- `knowledge`: `sources.write`
- `onboarding`: `answerBuddyAsk`, `answerFirstDaysCheckIn`, `firstDaysCheckIns`, `firstDaysResults`
- `praise`: `composer`, `send`
- `profiles`: `whoKnows`
- `pulse`: `actions`, `answer`, `audienceOptions`, `createAction`, `decideProposal`, `editProposal`, `latestRead`, `mine`, `proposals`, `proposeQuestions`, `questionDetail`, `requestProposals`, `setQuestionAudience`, `skip`, `updateAction`
- `reminders`: `mine`, `saveMine`, `saveSetting`, `settings`
- `reports`: `melloTrial`, `melloValue`
- `runs`: `history.get`, `history.list`, `stopKnownFailures`
- `team`: `answerMinutes`, `dismissLaunchPost`, `launchPost`
- `teams`: `connectionPermissions`, `current`, `discardPairHistory`, `initialize`, `moveSlackChannels`, `rename`, `resendSlackWelcome`, `saveAgentSetup`, `saveSlackJoinAccess`, `saveSlackProfileLink`, `setup`, `slackChannels`, `slackJoinAccess`, `slackProfileLink`, `status`, `syncSlackProfileLinks`, `uploadPairHistory`
- `users`: `assignManagers`, `proposeReportingLines`, `reportingLineProposal`, `reviewReportingLines`

## Related guides

- [Use the SDK and HTTP API](https://shuffl.ai/docs/sdk)

- [Use the Shuffl CLI](https://shuffl.ai/docs/cli)

- [REST API reference](https://shuffl.ai/docs/api-reference)

Agent documentation index: https://shuffl.ai/llms.txt

