Reference
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.
For Developers who use the SDK, CLI or API contracts
On this page
App links open your signed-in workspace. Check the selected workspace before making changes.
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.
npm install @shuffl/sdk@0.11.8 @shuffl/cli@0.11.8
shuffl --version0.11.8 (2026-11-07)
Changed
teams.setDeliveryModeanswers as soon as the workspace is live. The held messages from the last seven days go out right after the response, soreleasedcounts the messages being sent andfailedis 0;teams.rehearsalshows each one asreleased, ordroppedwhen it could not be sent.
0.11.7 (2026-11-02)
Added
availability.timeOffAwayreads your own switches for approved time off:slackStatus,calendar,slackReady(whether the workspace allows Slack profile updates) andcalendarProvider(google,microsoftor null).availability.saveTimeOffAway(slackStatus,calendar, both optional) turns them on or off. WithslackStatuson, your Slack status says when you're back from your first day out and clears itself the day you return. Withcalendaron, an out-of-office event is added to your connected calendar once the time off is approved.
0.11.6 (2026-11-02)
Changed
requests.typesactions carryslackConnected: whether Slack is connected at all. WithmissingScopesit tells a workspace that never connected Slack (connect it) from one whose connection lacks a permission (reconnect it).
0.11.5 (2026-11-02)
Added
- Requests can be
kind: "time_off": time off Mello filed in the HR system, waiting on the approver the HR system names. They carrytimeOff(providerName,vendorUrl,decidesIn:shufflorvendor) and theirfieldslist the policy, dates, amount and HR system. requests.decideon a time-off request is sent to the HR system first and recorded there as the approver's decision; only that approver may make it, never an administrator or owner. Where the HR system has no approve API (decidesIn: "vendor"),canDecideis false and the request links there. A time-off request can't be cancelled throughrequests.cancel; cancel the filed request instead.
0.11.4 (2026-11-02)
Changed
praise.sendreturnsnotified: how many of the people praised in Shuffl (with no praise channel yet) were told right away by Slack DM or email, as each chose in their notification settings. Praise posted in a channel answers0; the channel post and the daily digest tell them.home.feeditems can bekind: "thanked": praise the signed-in person received in the last two weeks, withperson(who gave it),praiseIdandtext. Only the person thanked sees it.
0.11.3 (2026-11-02)
Added
requests.letterTemplatereads the workspace's letter template for proof-of-employment letters:companyName,address,signatory(id,name, or null for the People team),clause,salaryWithApproval,revisionandsaved. Administrators only.requests.saveLetterTemplate(companyName,address,signatoryPersonId,clause,salaryWithApproval,expectedRevision) saves it.- Requests carry
letter(salary,downloadUrl) for letter requests andforYou. A letter without a salary needs no approval when its type says so and People has the facts it states; it settlesdonestraight away with the letter ready to download. A salary letter always waits for an administrator, who adds the figure in Shuffl.
0.11.2 (2026-10-30)
Changed
accessRequests.grantcan answerstatus: "checking"(witherrorCode: "no_answer"): the identity provider took the group write but has not confirmed it yet. The request staysin_progress, reads Checking, and settlesdoneorfailedonce a readback confirms it. Shuffl never writes the membership a second time while it checks.
0.11.1 (2026-10-27)
Added
betaFeatures.listreturns the workspace's beta features:feature(whos_outorapps),label,description,onanddecidedBy(workspace,companyordefault). Administrators only.betaFeatures.set(feature,on) turns a beta feature on or off for the workspace. Everyone in it sees the change within seconds, in the app, Slack and Mello.
Changed
- Operations of a product area that is off for the workspace (Who's out, Apps, and features still in testing) are no longer offered to Mello. The API, SDK, CLI and MCP still reach them.
0.11.0 (2026-10-26)
Added
- Request types can have Shuffl carry them out in Slack once approved.
requests.saveTypetakesaction(slack_channel_create,slack_channel_invite,slack_usergroup_addorslack_guest_invite),actionTarget(id,name: the channel or user group) andapprovalNeeded. A public channel type may skip approval; private channels and guests always wait for a yes.requests.typesreturns each type'sactionwithmissingScopes, the Slack permissions still needed; until Slack grants them, people don't see the type. - Requests carry
action:kind,plan(what Shuffl will do, in words),attempts,error,reversibleandreceipt. An approved request runs once, is read back from Slack, and settlesdonewith the channel in its receipt orfailedwith the reason. - Trying a failed action again, taking a done one back (archiving the channel or removing the person) and listing the channels and user groups a type can name stay with the request's owners and administrators in Shuffl: no key or agent runs them. Agents follow the action through
requests.get. - The New Slack channel and Slack guest starter types.
0.10.0 (2026-10-26)
Added
requests.typeslists the workspace's request catalog: each type'sname,description,kind,fields(id,label,type,required,options),approvers,owners,onApproval,responseDays,sensitiveandkeywords. A workspace starts with ten types, fromnew_equipmenttosomething_else.includeArchivedadds archived types.requests.saveTypeadds or edits a type andrequests.archiveType(typeId,archived) archives or restores one, for administrators. Something else can't be archived. Asensitivetype names its approvers, and only they and its owners see its requests.requests.filetakestypeIdandfields, the answers to that type's questions. The type sets the kind, approvers and owners, and the request getsrespondBy. Results addtype,fields,respondByandsensitive.
Changed
requests.filetitleis optional with atypeId; it defaults to the type's name. Filing for someone else tells that person.
0.9.0 (2026-10-26)
Added
requests.file(title,note,kind,approverPersonIds,ownerPersonIds,forPersonId,requestKey) files a request for anything that needs a yes:equipment,letter,finance,slack,hrorcustom. Without named approvers the person's manager decides, then an administrator. Owners carry it out once approved; without them the approval is the outcome. A manager or an administrator can file for someone else. The samerequestKeyreturns the request already filed.requests.minelists the caller's own requests and the ones filed for them.requests.get(requestId) returns one request withapprovals(each step, its approvers,quorumand decision),waitingOn,question,ownersand itseventsledger.requests.waitingreturnstoDecideandtoComplete, every request waiting on the caller.requests.answer(requestId,answer) answers an approver's question;requests.cancel(requestId,note) withdraws a request that is waiting or approved;requests.complete(requestId,note) marks an approved request done, for its owners and administrators.- Deciding and asking for more (
requests.decide,requests.ask) stay with people in Shuffl, Slack and email: no key, MCP client or agent approves a request.
Changed
- Access requests are requests of
kindaccesson the same ledger. Their statuses are nowpending_approval,approved,in_progress,done,declined,expired,failed,revokedandcancelled:requestedbecamepending_approval,grantingbecamein_progressandgrantedbecamedone.accessRequests.decidewithdecisiondeclinerequires anote, the reason the person sees.
0.8.1 (2026-10-26)
Changed
accessRequests.inboxreturnsapprover: whether the caller decides access requests here (an owner, an administrator, someone an app or its group names, a manager while an app asks managers, or anyone a waiting request was sent to). The app shows its Approvals view only to these people.
0.8.0 (2026-10-26)
Changed
praise.sendtakeschannelIdas optional. While the workspace has no praise channel, praise sent without one stays in Shuffl: it shows on the feed and the recipients' profiles, anyone in People can get it, and nothing is posted in Slack. The result addsdelivery(slackorapp), and itschannelIdis null for praise kept in Shuffl.praise.composeraddsdelivery, and only reportsblockedoffwhen an administrator turned Praise off.no_channelnow means every praise channel is private and the person is in none of them.- Praise items in
praise.feed,praise.mineandpraise.ofPersoncan have a nullchannelIdandchannelName: praise given in Shuffl before there was a praise channel.
Added
hr.commandtakesactionremind: the person who asked tells HR they are still waiting, once the request has waited three days for HR since they asked, HR last replied or they last reminded. The request's owner, or the people a new request in its category reaches, is told by Slack or email, without what was asked.hr.getaddsfollowUpfor the person who asked: how HR's reply reaches them (hearBack.slack,hearBack.email), HR's response target in working days, whether they can remind HR now (remind:ready,laterornot_waiting), from when, and when they last did.hr.listrows addremindedAt.
0.7.35 (2026-10-24)
Added
sync.setAccess(integrations:write) controls automatic employee access separately for each pull-based People Sync connection.sync.currentincludesautomaticAccessfor every source.sync.connectaccepts optionalautomaticAccessbefore the first sync; reconnecting preserves the saved choice. Slack and company SCIM controls remain in their existing permission boundaries.
0.7.34 (2026-10-24)
Added
- Playbook steps can be of kind
cardwithcardoperationissueorlock. On the step's day Shuffl issues the hire's company card in Ramp or Brex with the limit their groups map to and tells them (the step goes to the hire), or locks every card a leaver holds, reads each one back and tells HR (the step goes to HR or a named person). Saving the playbook approves both. Without a spend platform with card access the step is canceled witherrorCodeno_card_access; a card that could not be issued fails the step withcard_not_issuedand asks owners and administrators to issue it by hand.
0.7.33 (2026-10-24)
Added
- Company cards in Ramp or Brex follow the person.
spend.setCardLimits(connectionId,limits,integrations:write) saves which limit new cards get:defaultCardLimitand each group'scardLimitin minor units, theintervalthe limit resets (monthly,quarterly,yearlyortotal) andcurrency; a person gets the highest limit of their groups, or the default.spend.currentconnections returncardLimits, andcapabilitiescan includecards. spend.cards.issue(personId,cardLimit,requestKey,integrations:write) invites the person to the platform when it has no user for them, issues a virtual card once perrequestKeyand makes their manager its approver.spend.cards.updateLimit(personId,cardId,cardLimitoractionId,requestKey) changes a card's limit or runs a prepared change.spend.cards.lock(personId,requestKey) locks every card the person holds and reads each one back. Each answerssummary,status,statusLabeland the ledgeraction.spend.cards.forPerson(personId,integrations:read) returns the person's cards, the limit their groups map to and the card changes made for them.spend.cards.changes(integrations:read) lists the changes Shuffl prepared after someone moved group or manager in People, andspend.cards.dismissChange(actionId,integrations:write) dismisses one.- Brex connections take an
accessfield (readorcards); withcardsthe token can issue, change and lock cards. - Ledger
action.kindcan becard_issue,card_limitorcard_lock, withdetailsreason(joining,leaving,movedorasked),cardName,lastFour,amount,previousAmount,currency,interval,groupName,approverNameand, for a lock,cards. capabilities.sheet,capabilities.unlocksandcapabilities.requestsadd the capability idspend_cards.
0.7.32 (2026-10-24)
Added
- Laptops and devices:
devices.list(devices:read) returns the fleet device management reports (Kandji, Jamf Pro or Microsoft Intune), each withperson,model,platform,serialLast4,status,issues(os_update,encryption_off,not_checked_in,passcode,firewall_off),canLockandcanWipe, pluscountsand the shipments.devices.forPersonreads one person's devices, laptop orders and return kits;devices.connectionsanddevices.cataloglist the connections and the laptops the shipping partner (Firstbase) can send.devices.mine(self:read) returns your own devices and what is on its way. devices.orderanddevices.return(devices:write,requestKey) order a laptop or send a return kit through the shipping partner, once per key; a hire's shipping address is deleted once the laptop arrives.devices.lockreturns an unlock PIN once and never stores it;devices.wipeneedsconfirmSerial, the serial's last four.devices.sync,devices.matchanddevices.disconnectround out the set. Connecting stays in Apps → Devices, since it takes the vendor's own secret. Administrator keys only, apart fromdevices.mine.- Playbooks add a
devicestep (device.operationorderorreturn, optionalskuIdandmodel) that prepares the shipment for HR to send, and theshippingAddressform field. Playbook hires carryprogress.device, their laptop status.
0.7.31 (2026-10-24)
Added
- Gifts through the workspace's own Tremendous account.
gifts.current(celebrations:read) returns thebudgetsper occasion (anniversary,birthday,welcome,praise), thecurrency, themessagesent with every gift, theconnection(provider,label,status,ordering,fundingSource,campaign), thismonth's spend against the cap, thedraftswaiting for approval and thehistoryof gifts approved or skipped, each withstate(drafted,sending,waiting,sent,not_sent,cancelled,checkingorskipped).gifts.optionslists the Tremendous funding sources and campaigns. gifts.save(celebrations:write) sets budgets, the monthly cap (monthlyCap, null removes it), the currency, the note, the funding source and campaign, andordering, the switch that lets approved gifts be ordered.gifts.draftdrafts a gift for a person and occasion (a praise reward names itspraiseId), within the occasion's budget;gifts.draftUpcomingdrafts the next two weeks of occasions now.gifts.approveandgifts.approveManyorder approved gifts with LINK delivery, keyed on the gift's own external id so a repeat never orders twice; the recipient receives the link privately and it is never returned. An approver cannot approve their own gift, and a gift over the monthly cap is refused with what is left.gifts.skipskips a gift;gifts.disconnectdisconnects Tremendous and forgets its key. Connecting stays in Celebrations → Gifts.
0.7.30 (2026-10-24)
Added
- Reimbursements in Ramp for the signed-in person.
expenses.stageReceipt(fileName,receiptas base64,self:write) hands over a receipt photo (JPEG, PNG, WebP or HEIC) or PDF of 10 MB or less and returnsreceiptId,fileNameandexpiresAt; Shuffl keeps it for a day.expenses.submitReceipt(receiptId,memo,requestKey,self:write) files it once perrequestKey; Ramp drafts the reimbursement from the receipt and the file is deleted.expenses.submitMileage(tripDate,miles,from,to,memo,requestKey,self:write) files a trip at the company's rate. Both answersummary,status,statusLabel,stage(Ramp's own word, such asDraftorPaid) and the ledgeraction.expenses.mine(self:read) lists them with where each stands. spend.setWrites(connectionId,enabled,integrations:write) lets Mello file reimbursements on the connection, or stops it.spend.currentconnections returnwritesEnabled, andcapabilitiescan includereimbursements.- Ledger
action.kindcan beexpense, withdetailstype(receiptormileage),memo,fileName,tripDate,miles,from,to,merchant,amount(minor units),currencyandstage.
0.7.29 (2026-10-24)
Added
- Who's out and free time.
availability.whosOut(users:read,fromanddaysup to 21) returns everyone away in those days, once per person:name,photoUrl,jobTitle,startsOn,endsOnandsources(calendarfor a person's own shared out-of-office,hr_systemfor approved time off from People Sync), withenabled,asOfandcoverage.availability.freeTime(users:read,personId,daysup to 10) returns a coworker's freewindowsinside their working hours and the days they areaway, orshared: falsewith areason(not_connected,not_sharedorreconnect). Neither returns an event title, a reason or a kind of leave.availability.settings(team:read) andavailability.saveSettings(enabled,team:write) read and change whether Who's out is on;availability.share(share,self:write) turns the caller's own calendar sharing on or off. - People Sync catalog entries name
whosOutamong theircapabilitiesfor HR systems that report approved time off (BambooHR, HiBob). capabilities.sheet,capabilities.unlocksandcapabilities.requestsadd the capability idswhos_out,free_timeandcalendar_sharing.
0.7.28 (2026-10-24)
Added
- Webhook endpoints name the automation app that added them.
webhooks.list,webhooks.get,webhooks.createandwebhooks.updatereturncreatedVia(zapier,make,n8nor null) andwebhooks.deletereturns it too. An endpoint is marked when the API request comes from that app (thex-shuffl-clientheadershuffl-zapier/…,shuffl-make/…orshuffl-n8n/…, or the app's user agent) or when its URL is the app's own hook address. Endpoints from these apps count apart from a workspace's own, up to 50.
0.7.27 (2026-10-24)
Added
- Expenses and cards: a workspace connects one spend platform, Ramp or Brex, in Settings, and Mello answers expense policy and card limit questions from it.
spend.current(integrations:read) returns the connection (provider,label,status,capabilitiesprogramsandlimits,policySourceId,policyDraftedAt,sandbox,lastUsedAt) and the platforms that can connect.spend.programs(connectionId,integrations:read) reads the spend programs live (name,description,limit,interval,limitLabel,transactionLimit,allowedCategories,blockedCategories,reimbursements).spend.draftPolicy(connectionId,integrations:write) writes the expense policy page in Knowledge from the programs as a draft for review (sourceId,title,programs), updating the same page when run again.spend.disconnect(connectionId,integrations:write) forgets the credential.spend.mine(self:read) returns the signed-in person's own cards and limits (cardswithname,lastFour,state,limitLabel,availableLabel,programName) or why there are none (reasonnot_connected,unavailable,no_userorno_person). Nothing read from the platform is stored.
0.7.26 (2026-10-24)
Added
- Hiring systems through People Sync: Ashby and Greenhouse connect as People Sync connections in the new
hiringcategory. A candidate who accepted an offer becomes a pre-start person with the job title and start date, and the joining playbook starts on its own. A withdrawn offer stops that playbook (removal reasonoffer_withdrawn).sync.connectionsrows returnhiring(open,total,notes) for a hiring connection. hiring.hires(integrations:read) lists each hiring connection (id,provider,providerName,label,notes,canUpdateStartDates) and itshires:status(accepted,hiredorwithdrawn),offerStartDateand People'sstartDate,startDateDiffers, the joiningplaybookandbuddyName,met(people on the hiring team who are in Shuffl), the noteswrittenback andheldByHrSystem.hiring.setNotes(connectionId,enabled,integrations:write) turns on recruiter notes and the "Onboarding in Shuffl" tag on the candidate record when the playbook starts, a buddy is set and on day one.hiring.updateStartDate(hireId,startDate,requestKey,integrations:write) writes People's start date to the accepted offer in the hiring system, once perrequestKey, and answers the ledgeractionwith itsstatus.
0.7.25 (2026-10-24)
Added
- Pay dates and benefits from the connected HR system.
pay.schedule(people.paySchedule,self:read) returns the company's pay schedules from the HR system People Sync reads, the signed-in person's own first: each withname,frequency(weekly,biweekly,semimonthly,monthly,quarterlyorannually),yours,nextPayDate, thepayDatesof about the next three months and thenextPeriodit covers, plusportalUrl, the HR system's employee portal for pay stubs and tax forms.benefits.mine(people.myBenefits,self:read) returns the person's ownenrollments(plan,kind,carrier,statusenrolled,waivedorpending,coveragetier,startDate,endDate), the company'splansand theopenEnrollmentwindow (start,end,effective,stateopenorupcoming). Both answerconnected: falsewith areason(no_person,not_linked,unavailableorunauthorized) instead of failing. Neither returns a pay amount, deduction, premium or tax document, and neither takes a person: only ever the caller's own. - People Sync catalog entries name
payCalendarandbenefitsamong theircapabilities, andemployeePortalUrlwhere the provider has one.
0.7.24 (2026-10-24)
Added
- Pilot group while rehearsing:
teams.setPilotGroup(groupIdor null,team:write) names one People group that hears from Shuffl for real while the rest of the workspace is held, or stops piloting; it answers thepilot(groupId,name,memberCount).teams.rehearsalreturnspilotand the held counts gainsentandsentWeek;recentrows can carrystatussent, a message the pilot received for real.
0.7.23 (2026-10-24)
Added
- Reminder kind
connections_met("Did you meet?"), with one choice,a_week_after, inreminders.settings,reminders.saveSetting,reminders.mineandreminders.saveMine. It is on by default; a team or a person can set it tooff. runs.detailsreturnsoutcomes.met(asked,answered,met,planned,notMet,on) and, per group,outcomes.groups[].met(the same counts, or null before anyone in it was asked). Counts only, never who answered what.
0.7.22 (2026-10-23)
Added
- Rehearsal before going live: a new workspace rehearses until an owner or administrator takes it live. Programs run on the real people and settings, but every message Shuffl starts on a schedule to anyone but an owner or administrator is held instead of sent.
teams.rehearsal(team:read) returns themode(rehearsalorlive),rehearsalStartedAt,wentLiveAt,heldcounts (open,releasable,week,total), therecentheld messages (each withkind,transport,audience,statusheld,releasedordropped,summary,personName,releasable) andupcomingsends for the next four weeks from Pulse, Connections, celebrations, playbooks and automations (at,kind,title,people,why).teams.heldDelivery(id) returns one held message with its Slack blocks or email.teams.setDeliveryMode(mode,team:write) takes the workspace live, sending the held messages from the last seven days and dropping older ones (released,dropped,failed), or puts it back in rehearsal.
0.7.21 (2026-10-23)
Added
playbooks.askManagerForBuddy(note,self:write) asks the signed-in hire's manager in People to help find an onboarding buddy, by Slack or email. It answersstatusaskedoralready_asked(once a week) withmanagerName, and refuses when no reachable manager is recorded or someone already said yes.playbooks.buddySuggestionsreturnswhoCanHelp:manager(name, or null when People has no manager Shuffl can reach) andmanagerAskedAt;playbooks.minereturns the same asbuddyHelp.
0.7.20 (2026-10-23)
Changed
celebrations.preview(milestones.previewCelebration) returns the Slackblocksof each message, as the delivery posts them (null for an in-app notice), andupcoming: the next three sends with these settings, each withsendDate,shiftedand the person'stimezone.
0.7.19 (2026-10-23)
Changed
pulse.questionsrows returnqueue(an active question's place in the rotation, 1 for the next one asked, null when retired) andnextAskAt(the send it is expected on from the schedule, skipping eNPS sends, or null while Pulse is paused or for a retired question). An action's re-ask can still take a slot first.
0.7.18 (2026-10-23)
Changed
runs.detailsreturnsoutcomes: per group (groupId) itspairs,firstTimePairs(pairs who never shared an earlier group),acrossTeamsand thebookingmade with Pick a time (start,statusbookedorpending, or null); for the round,acrossTeams(metofpeopleon a team),firstIntroductionsandlatest(whether its left-out people go first in the next round).
0.7.17 (2026-10-23)
Added
- Access grants through identity-provider groups: an app level saved with
apps.savecan carrygroups(eachconnectionId,externalIdandname, all from one connection). An approved request for such a level is granted by Shuffl: the person is added to those groups in JumpCloud, Google Workspace or Microsoft Entra ID and the membership read back before the request readsgranted;accessRequests.revokeand a leaver's last day remove it again. A request'sgrantthen haskindgroupwithconnectionId,providerand thegroupswritten; a write that did not land leaves the requestfailedwith anerrorCode(not_linked,read_only,disconnected,no_adapter,unauthorized,forbidden,rate_limited,unavailable,invalid_response,no_answer,not_foundorrefused). apps.grantSources(access:read) lists the connections that can add people to groups, withwritessaying whether the connection's write switch is on;apps.grantGroups(connectionId) lists one connection's groups live, withexternalId,nameandmemberCount.accessRequests.grant(self:write) tries a failed grant again, as an owner or administrator; it answersstatusgrantedorfailedwith theerrorCode.actions.minerows can bekindaccess:detailsthen carriesrequestId,operation(addorremove),appName,levelNameand thegroupswritten, each withconfirmed.
0.7.16 (2026-10-23)
Added
- Mentorship formats:
mentorship.programs.savetakesformat(one_to_one,ask_once,circleorreverse). Ask-once pairings last two weeks with no goals; circles take three to five mentees per mentor; reverse mentoring prefers the newer person as mentor. Programs and pairs inmentorship.minereturnformat; circle pairs returncircle(programId,mentorId,members), and calendar targets takementorship_circle(<program>.<mentor>). - Cohorts:
mentorship.programs.savetakescohort(signupClosesOn,startsOn, or null for a rolling program). Program summaries returncohort(withwrapsUpOn,matched,started) andapproved; pairs returnapprovedAt.mentorship.pairs.approveAll(mentorship.approvePairs,mentorship:write) approves every suggestion: before the start day they wait for it.mentorship.decidePaircan returnstatusapproved. Cohort pairs inmentorship.minereturntimeline, and programs returncohortwithsignupOpenandapproved. - Thank-yous:
mentorship.thanks.save(mentorship.thank,pairId,message,onProfile,tellManager,praiseChannelId,self:write) once a pairing ends; pairs returnthanks.mentorship.profiles.get(mentorship.profile,personId,users:read) returns a person's Mentoring section (mentored,topics, sharedthanks,hidden), andmentorship.profiles.setHidden(mentorship.setProfileHidden,profile:write) hides your own. - HR on a program:
mentorship.programs.getreturnshealth(needs: matches to approve, pairs not met in three weeks, topics wanted more than offered and mentees waiting for a new mentor;topicswithwant,offerandshort;vitalsmeeting,metRate,waiting,daysToMatch) and the latestinvitations.mentorship.pairs.nudge(mentorship.nudgePairs,programId,mentorship:write) nudges pairs that haven't met in three weeks, once a week at most.reports.mentorshipreturnsoutcomes: people who were in a pairing against peers at similar tenure (stayed,moved, finishedgoals),hiddenunder five people. - Inviting mentors:
mentorship.mentors.candidates(mentorship.mentorCandidates,programId,topic,mentorship:read) returns people who could mentor a topic withreasons, any earlier answer and adraftinvite;mentorship.invitations.send(mentorship.inviteMentors,programId,topic,personIds,message,tellManagers,mentorship:write) sends it. Invitees answer withmentorship.invitations.answer(mentorship.answerInvitation,invitationId,answeryes,not_noworlater,self:write);mentorship.minereturns openinvitations. Saying yes enrolls nobody: the person still signs up as a mentor. - Leaders:
mentorship.leaders.get(mentorship.leaderView,self:read) returns totals for the people someone leads (seven or more) or, for HR, the company: participation against the company, teams with fewer than seven grouped, topics short of mentors, managers who could mentor, shared thank-yous and cohorts to sponsor.mentorship.cohorts.sponsor(mentorship.sponsorCohort,programId,noteor null,self:write) puts a note on an upcoming cohort's invite and kickoff; program summaries returnsponsor.
0.7.15 (2026-10-23)
Added
- Compliance evidence:
compliance.current(integrations:read) lists the compliance platforms a workspace sends evidence to (Vanta or Drata), each withproviderName,status(connected,needs_attention),enabled, theevidencekinds it receives (people,policy_acknowledgements),offered,lastPushAt,lastSuccessAt,lastErrorand its recentpushes(statusrunning,succeededorfailedwithstatusLabel,trigger,kinds,countsper kind anderrorCode), plus thecatalogof platforms andevidencekinds.compliance.setEvidence(integrations:write) chooses which kinds a connection receives or pauses it (enabled);compliance.pushNowsends a fresh set now, idempotent onrequestKey;compliance.disconnectstops sending and forgets the credential. Connecting takes the platform's own secret, so it stays in Settings → Compliance. Counts and statuses only, never a record or a credential.
0.7.14 (2026-10-23)
Added
- Helpdesk routing for HR requests:
hr.helpdesks.connect(hr:write) connects Jira Service Management, Linear, Zendesk or Freshservice with its token and returns the connection and, on a first connection, its webhook secret, shown once (nullon a reconnect, which keeps the secret, and for Linear, whose own signing secret goes infields.webhookSecret);hr.helpdesks.disconnectsends its categories back to the Shuffl inbox.hr.helpdesks.list(hr:read) returns each connection (status,routes,webhookaddress and header,publicRule), the helpdesks on offer and where every request category goes;hr.helpdesks.queueslists a connection's queues.hr.route(hr:write) sends a category to a queue or back to the inbox; a sensitive category needssensitiveOptIn. Administrator keys only. hr.getreturnscase.ticketfor HR responders on a routed request:provider,providerName,destination,ticketKey,url,state(opening,open,resolvedornot_opened) andreconnect. Messages carrysource(shufflorhelpdesk); a helpdesk reply has noauthorPersonIdand names its agent inauthorName.- Outgoing webhooks add
action.resolved, sent when a helpdesk resolves the ticket a request became.
0.7.13 (2026-10-23)
Added
runs.resolveMonthlyReview(runs:write,runId,decision,reason) settles a preserved monthly round held for review with the administrator's recorded answer:notice-not-requiredorsend-noticewhen the imported channel-notice setting was unknown,skip-occurrencewhen the new app could form no groups. A repeat of the same decision changes nothing; a different one is refused.runs.history.people(exports:read) lists the people recorded in preserved rounds with theirlinkedperson or Shuffl'ssuggestion, andlinked,suggestedandunmatchedcounts.runs.history.linkPeople(runs:write,sourceUserIds) confirms those suggestions.runs.history.getmemberships returnpersonBindinglinkedwithperson(id,name) once linked.
0.7.12 (2026-10-23)
Added
- App catalog:
apps.list(self:read) lists the apps people can ask for, with each app'srequestPolicy(open,approvalorclosed),levels, owners and the person's own current request;apps.getreturns one. Administrators save an app and its approvers, owners, levels and expiry withapps.saveand retire it withapps.archive(access:write);apps.importSourcesnames the connected identity providers that list applications andapps.importpulls their catalog in (access:write);apps.members(access:read) lists who has access. - Access requests:
accessRequests.request(self:write) asks for an app (and a level) with a note and arequestKey;accessRequests.minelists the asking person's requests andaccessRequests.getone. Approvers decide withaccessRequests.decide(decisionapproveordecline, with a note); owners confirm a hand-set-up grant withaccessRequests.markGrantedand remove access withaccessRequests.revoke;accessRequests.inbox(self:read) lists what waits on the key's person andaccessRequests.list(access:read) everything by app or status. Statuses:requested,pending_approval,approved,declined,expired,granting,granted,failed,revoked. - Webhooks:
access.request.requested,access.request.approved,access.request.declined,access.request.expired,access.request.granted,access.request.failedandaccess.request.revokedcarryrequest_id,app_id,person_idandrevision.
0.7.11 (2026-10-23)
Added
- Time off filed in the HR system:
timeOff.request(people.requestTimeOff,self:write) files the asking person's own request as pending in the Deel, HiBob or BambooHR connection that manages their record, withpolicyastimeOff.balancesnames it,startDate,endDateand optionalstartHalfDayandendHalfDay, never a reason. Itsresultcarriessummary,status,statusLabel,providerName,vendorUrland the ledger row asaction; the vendor's own approval flow decides. A workspace with the connection's write switch off, or a person nobody manages from an HR system, getsFORBIDDENorBAD_REQUESTand sends the request to HR instead. timeOff.cancel(outbound.cancelAction,self:write,idandrequestKey) takes a filed request back while the vendor still holds it pending.actions.minerows returncancellabletrue when that works.timeOff.balancesreturnsrequests: whether Mello can file a request in that connection.capabilities.sheetrows can name thetime_off_requestsconnection: an HR system Mello can file time off in, with the write switch on.
0.7.10 (2026-10-23)
Changed
reports.overviewprograms returndraft: a playbook saved as a draft that has had no hire yet (paused, never started).- A
start_playbookrecommendation fromdigest.culturecarriesdraft(id,name) when an onboarding playbook is saved as a draft; its action then opens that playbook.
0.7.9 (2026-10-23)
Added
- Mentor load:
mentorship.jointakescapacity(mentees, up to the program'smentorCapacity) andhoursPerMonth(1 to 20) for a mentor; matching never gives a mentor more than theircapacity.mentorship.minereturns them on each program'smentor, andmentorship.programs.getreturns them on mentormembers. - What each pair needs: every pair in
mentorship.minereturnslastMetOnanddue(kindcheck_in,no_meeting,no_goalsorgoals_stalled, with alabel), andmentorship.minereturnssuggestedTopicsfrom the person's profile. - Mentee feedback:
mentorship.feedback.save(mentorship.saveFeedback,pairId,helped,change) once a pairing has ended, mentee only, keys withself:write. Pairs returnfeedbackandcanGiveFeedback.mentorship.minereturnsmentoringfor a mentor:mentored,meetings,goalsDone,answered,feedback(unnamedhelpedandchangelists, null until three mentees answered),badgeandcircles. - Mentor circles:
mentorship.circles.save(mentorship.saveCircle,programId,startsAt,minutes,topic) andmentorship.circles.cancel(mentorship.cancelCircle,circleId) withmentorship:write; mentors answer withmentorship.circles.reply(mentorship.replyCircle,circleId,going) withself:write.mentorship.programs.getreturns the nextcirclewithgoingandnotGoing. - Mello citations can have kind
mentorship(the asker's own mentorship).
0.7.8 (2026-10-23)
Changed
- One “Across teams”:
reporting.connectionsInsightsreturnsacrossTeams(currentandprevious, eachpeople,metandintroduced), the share of people on a team who were introduced to someone on none of their teams. Culture'sfocus.company.metPctnow reads the same figure.digest.cultureweekaddsprevious(the seven days before), andpulse.insightsaddspreviousFavourablePct. For eNPS questions,pulse.latestReaddeltacounts eNPS points (unit: 'count') rather than percentage points.
0.7.7 (2026-10-18)
Changed
- The SDK's default production origin is
https://shuffl.ai. Package homepage links, guides and CLI login examples use the same domain. Update any explicit old-hostbaseUrl,SHUFFL_BASE_URLor saved CLI login; credential-bearing requests still refuse redirects. API operations and permissions are unchanged.
0.7.6 (2026-10-18)
Added
- Mentees choose their mentor:
mentorship.programs.savetakesmenteesChoose. In such a programmentorship.minereturns each waiting mentee'schoices(up to three mentors withtopicsandreasons) and the mentor theyrequested;mentorship.mentors.choose(mentorship.chooseMentor,programId,mentorId) asks that mentor. Mentors seerequestsinmentorship.mineand answer withmentorship.requests.answer(mentorship.answerRequest,decisionaccept or pass). Program summaries returnmenteesChooseandrequested; pairs can berequested. - Session notes:
mentorship.sessions.save(mentorship.saveSession,pairId,heldOn,notes,nextStep, optionalsessionId) andmentorship.sessions.remove(mentorship.removeSession). Each pair inmentorship.minereturns itssessions, aprep(the newestnextStep,openGoals,questionsandstage) and itsnextMeeting. Only the two people in the pair read notes. Keys withself:write. - Calendar targets take
mentorship_pair(a pair id) for Pick a time between a mentor and mentee.
0.7.5 (2026-10-18)
Changed
reports.playbooksandpraise.insightsreturnprevious: the same counts for the window before the one asked for (nullwhen there is nothing to compare), so a client can show the change since then without a second call.
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 (statussubmitted,approved,declined,cancelled,failedorunknown, withstatusLabel,provider,providerName,detailssuch as the time-off policy and dates,vendorUrl,submittedAt,settledAtandcancellable). Only the person's own; a key withself: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.currentconnections now returnwrites(offered,enabled).
0.7.3 (2026-10-18)
Changed
- Mello's read as figures:
pulse.latestReadalso returnsdelta, the change since the question was last asked (change,directionup/down/flat/new,tonegood/bad/neutral/watch,textsuch as↓ 6 pts; moves under 5 points read neutral), andgap, the lowest and highest published group with thepointsbetween them, or null when the gap is under 15 points. Both are null when the result is withheld.
0.7.2 (2026-10-18)
Added
- What Mello can do:
capabilities.sheetreturns the rows Mello can answer (kind: 'ask') and do (kind: 'do') for the key's person in this workspace, each withsay(what to ask),result,system(Shuffl or the connected system's name) andstatus(available,needs_connection,needs_setuporcoming), plusneedwhen it doesn't work yet.capabilities.unlockslists what an HR system or document tool adds once connected.capabilities.requestConnection(self:write) asks HR to connect what aneeds_connectionrow needs; administrator keys read the open asks per capability withcapabilities.requests(integrations:read) and clear them withcapabilities.dismissRequests(integrations:write). Asks carry counts, never names.
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, withpeople,birthdays,anniversariesandsharedwith 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 aweeklyseries of birthdays and anniversaries. Counts only. Administrator keys withreports:read.
0.7.0 (2026-10-18)
Changed
- The CLI reads a workspace key from
SHUFFL_API_KEYinstead ofSHUFFL_TOKEN(breaking), matching the SDK'sapiKeyoption.SHUFFL_COMPANY_KEYandSHUFFL_BASE_URLare unchanged. The README, guides, cookbook and API reference use the new name.
0.6.5 (2026-10-18)
Changed
- Playbook steps before day 1 go by email.
playbooks.enrollmentPreviewsteps returnbyEmail: 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.
0.6.4 (2026-10-18)
Added
- Mentorship insights:
reports.mentorship(reporting.mentorshipInsights,days30, 90 or 180, optionalprogramId) returnscurrentandpreviousperiods withpairs(started,endedEarly,completed) andcheckIns(answered,met,asked,finals,helped),now(meeting,paused,suggested,waiting) andweeklypairsandcheckIns. Counts only, never goals. Administrator keys withreports:read. - Mentorship settings:
mentorship.settings.getreturns thesettingsa new program starts with (pairingWeeks,checkInWeeks,mentorCapacity), Shuffl'sdefaultsandupdatedAt;mentorship.settings.savechanges them (mentorship:write) and returnschangedandcustom.
Changed
mentorship.programs.savetakespairingWeeks,checkInWeeksandmentorCapacityas optional: a new program left without them starts with the workspace's mentorship settings, and an existing one keeps what it has.
0.6.3 (2026-10-18)
Added
- Automation insights:
groups.automationInsightscounts group automation runs finished in the lastdays(30, 90 or 180) against the period before:currentandpreviouswithdone,skipped,failedandpeoplereached,weekly,byKind, the top fivereasonsa run was skipped or failed (code,why,outcome,n), runs stillscheduledandautomationson of total.groupIdnarrows it. Administrator keys withusers:read.
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.
0.6.1 (2026-10-18)
Added
- The SDK and CLI name their release on every request in an
x-shuffl-clientheader (shuffl-sdk/0.6.1, and for the CLIshuffl-cli/0.6.1 shuffl-sdk/0.6.1), so Shuffl can count requests by client and release.new Shuffl()takes an optionalclient, such asbilling-sync/1.2.0, which goes in front; Shuffl never stores that part. No operation changed.
0.6.0 (2026-10-18)
Changed
- One client for every key (breaking):
new Shuffl({ apiKey })replacescreateShufflandcreateShufflCompany, 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 undershuffl.company(shuffl.company.members.list()). The client reads the key's prefix and refuses the other kind's methods withUNAUTHORIZEDbefore sending a request.tokenis nowapiKey(a string or a function that returns one), andbaseUrlis optional, defaulting to production. The CLI and HTTP API are unchanged.
0.5.12 (2026-10-18)
Added
- Automation runs, a page at a time:
groups.automationRunslists every group automation run in the workspace newest first, 50 at a time, each withpersonId,name,groupId,groupName,kind,trigger,outcome,why,atanddueAt.nextis the cursor for the next page (null on the last);countsgives every run by outcome (scheduled,done,skipped,failed).outcomeandgroupIdnarrow it. Administrator keys withusers:read.
0.5.11 (2026-10-18)
Changed
- Leaving playbooks start on their own:
groups.setPlaybookAutoStartnow takes a playbook counted from each person's last day (dayZero: 'last_day') as well as one counted from each start date, andplaybooks.savePlaybookProgramacceptsautoStarton 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.allAutomationsplaybookStartsreturndayZero(start_dateorlast_day).
0.5.10 (2026-10-18)
Added
- Pulse group results over time:
pulse.groupTrends(pulse:read,days30, 90 or 180) returns each group's published, frozen totals period by period (unitweek,fortnightormonth, withperiods), foreveryoneand everydimensionsrow (department, role, tenure, group). Each row'sserieshas one entry permetric(favourable,enpsor a driver) withpoints(value, null when withheld;seen),latestandchangesince the first published point. eNPS points followenpsPeriods, the last eight eNPS questions.
0.5.9 (2026-10-18)
Added
- Playbook form steps: a written step in
playbooks.saveProgramcan bekind: 'form', going to the hire (recipient: 'hire', no to-dos) withformFields, any ofpreferredName,pronouns,bio,birthdayandphoto. The hire answers withplaybooks.answerForm(stepId, pluspreferredName,pronouns,bioandbirthday{ month, day }ornull); answers write to their profile and Celebrations birthday under their own privacy settings, and the step records only when it was answered.playbooks.minesteps returnform(fields,answeredAt) and the run returnsformValues. Profiles take and returnpreferredName.
0.5.8 (2026-10-18)
Added
- Slack channel automations:
groups.setAutomationtakeskindslack_invite(add each person to a channel) andslack_post(post that they joined, with an optionalconfig.note; joins only).config.channelIdmust be a channel Shuffl is in: the API reads it back from Slack and savesconfig.channelNameitself. Automations return the channel astarget(#name). The Slack call runs from a queue after the run is written; a refusal turns the runfailedwith Slack's reason ascode, such asnot_in_channel, and someone with no linked Slack account isskippedwithno_slack.
0.5.7 (2026-10-18)
Added
- Mentorship programs. Administrators (
mentorship:read,mentorship:write):mentorship.programs.listandmentorship.programs.get(programId: the program withmembers, each withrolementorormentee,topics,statusand openpairs, andpairswithmentor,mentee,reasons,status, dates,checkInscounts andfinalanswers; never goals).mentorship.programs.save(name,description,groupIdor null for everyone,pairingWeeks4 to 52,checkInWeeks1 to 8,mentorCapacity1 to 5) andmentorship.programs.setStatus(active,pausedorarchived, which ends open pairs).mentorship.pairs.suggestsuggests one mentor for every waiting mentee, never in their reporting line;mentorship.pairs.decide(pairId,decisionapproveorskip) starts a pair and introduces both people;mentorship.pairs.endends one early;mentorship.members.setStatustakes someone off a program (removed) or puts them back. Everyone (self:read,self:write):mentorship.minelists the programs open to the key's person and their pairings withgoalsandcheckIns;mentorship.join(programId,role,topics) andmentorship.leave;mentorship.pairs.setMine(pairId,statusactive,pausedorended);mentorship.goals.saveandmentorship.goals.removefor the mentee's goals (up to 5);mentorship.checkIns.answer(checkInId,answerhelped,metornot_yet). Notification kindmentorship.badges.listincludes the automaticMentorbadge for finishing a pairing as the mentor.
Changed
home.placescan returnmentorshipwhen a mentorship program is running or a check-in waits for the person.
0.5.6 (2026-10-18)
Added
- More group automations:
groups.setAutomationtakeskindask_pulse(config.questionId),give_badge(config.badgeId),add_to_groupandremove_from_group(config.targetGroupId, a Shuffl list) andtell_person(config.personId, optionalconfig.note), anddelayDays(0 to 365) to run that many days after the join or leave. Automations returndelayDays,whenandtargetfor the new kinds; runs can bescheduledwith adueAt, andgroups.allAutomationstotals addscheduled. A run that comes due after the person moved back isskippedwith codemoved_back.groups.automationChoiceslists the Pulsequestions(id,text,retired),badgesandgroupsan 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.
0.5.5 (2026-10-18)
Added
- Playbook meet steps: a written step in
playbooks.saveProgramcan bekind: 'meet', going to the hire (recipient: 'hire', no to-dos) withmeetset tomanager,skip_level(their manager's manager),person(withpersonId) orteammates(withmeetCount, 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.enrollmentPreviewandplaybooks.previewAssteps returnmeets(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 takekind: 'playbook_meet'withid<enrollment id>.<step id>, and bookings returnfrom(person,group,playbookorplaybook_meet).playbooks.minereturnsmet, the people meet steps introduced the hire to.
0.5.4 (2026-10-17)
Added
- Playbook tracks: written steps in
playbooks.saveProgramtakeonlyForandnotFor(up to 5 group ids each, fromgroups.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.enrollmentPreviewsteps returntrack(why this person doesn't get it, such asOnly for Engineering, or null), a People change before approval failsplaybooks.approveuntil the steps are reviewed again, and approved steps a track leaves out are skipped with no to-dos.playbooks.programreturnstracks(each group the steps name, withid,name,kind,department,rulesin words or null,onlyandnotstep counts andhireson the playbook now in it).playbooks.previewAs(programId,personId) returns the steps one person would get, without enrolling them:dayZero,person,steps(each withref,title,kind,offsetDays,dueAt,recipient,trackandskipped, why they wouldn't get it),skippedandtracked.
0.5.3 (2026-10-17)
Added
- Connections for employees:
network.introductionsreturns the key's ownprograms(each Connections program that introduces them, withid,name,nextAt, null while paused, andbreakUntil),past(their latest introductions, each withid,at,programandwith: the other people,id,name,title,photoUrlandslackUserId) andtotal. Nothing about other people's introductions or matches not yet sent.home.placescan includeconnections.
0.5.2 (2026-10-17)
Added
- One list of join and leave rules:
groups.allAutomationsalso returnsplaybookStarts(each playbook that starts on each new hire's start date:playbookwithid,nameandstatus,groupor null for everyone,stateactiveorpaused,runs,runs30andlastRunAt) andprogramRosters(each Connections program whose people are a group:program,groupandstate).groups.setPlaybookAutoStart(programId,stateactive,pausedoroff, optionalgroupId, null for everyone) turns a playbook's start-date rule on, pauses it keeping its group, or removes it. Playbooks returnplaybook.autoStartPaused; a paused playbook starts nobody until it is turned back on.
0.5.1 (2026-10-17)
Added
- Playbook Hires view:
playbooks.programreturnshires(active,onTimepercent of to-dos done by a day after their day or null,overdue,askedForHelp) and each active row'sprogress(dayfrom day 0,done,total,nextsteptitleandat,overdueto-dos grouped by owner withownerId,ownerName,rolehire,manager,buddyorother,count,sinceandnudgedAt,unconfirmedread-and-confirm pages andhelp).playbooks.nudge(enrollmentId,ownerPersonId) sends that person one Slack reminder with their overdue to-dos for the hire and returnsstatussent,recent(already nudged today),nothing_overdue,off,no_slackorfailed.
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 nowplaybooks.*, with the same inputs and outputs.onboarding.journey, a person's run through a playbook, isplaybooks.run, andreports.onboardingisreports.playbooks. Theonboarding:readandonboarding:writescopes areplaybooks:readandplaybooks:write; existing keys and connections keep their access under the new names. Webhook event typesonboarding.*areplaybooks.*(such asplaybooks.enrolledandplaybooks.step.sent), and subscribed endpoints were moved over. Values that said onboarding or journey say playbook: program and draftkindplaybook, theplaybookconfig onplaybooks.saveProgram,playbooks.programandprograms.*, group automation and Culture nudge kindstart_playbook, reminder kindplaybook_todo, notification kindplaybook, citation kindplaybook, person activity kindsplaybook_startedandplaybook_completed,playbookMessagesSent, and theplaybookslist onplaybooks.run. - Departments are groups. Every department is now a group (same id) marked
departmentingroups.*, and a person is in one department at most.units.saveandunits.archiveare gone: create a department withgroups.save(kindlist,departmenttrue, people inpersonIds), rename it withgroups.saveand remove it withgroups.remove; departments are no longer archived.units.listtakes noarchivedand returnsid,nameandmembersfor each department.groups.list,groups.getand the other group reads returndepartment. A department group is neveronProfileor the HR team and can't be a rules group.users.savestill sets a person's department withdepartmentId(a department group's id), andusers.page'sdepartmentIdfilter reads department groups. ProgramunitIds, knowledge audienceunitsand group rules ondepartmentkeep working with the same ids.exports.teamno longer returnsorganizationUnitsorunitMemberships: departments are inpeopleGroupsandpeopleGroupMemberswithdepartmenttrue, and an older export that carries them still imports. Thedirectory.unit.created,directory.unit.updatedanddirectory.unit.archivedwebhook events are gone, since nothing sends them; read departments withunits.listorgroups.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.*, andunits.*covers departments only:units.listandunits.saveno longer returnkind,hrTeamorslackUserGroupId, andunits.savetakes nokind. Groups returnsource(shuffl,slackordirectory: synced groups have read-only members),onProfile(shown on profiles and the org chart, as teams were) andhrTeam(the group that answers HR requests);groups.savetakesonProfile, and refuses member or rule changes on a synced group.users.detailsreturnsdepartmentandgroupsinstead ofunits, andusers.savetakesgroupIdsinstead ofteamIds.users.pagefilters bygroupIdinstead ofdirectoryTeamId;users.filterOptionsandprograms.optionsreturnunitsof kinddepartmentorgroup.profiles.getandnetwork.orgChartreturngroupsinstead ofteams. Pulse results no longer split by team:pulse.insightsandpulse.questionDetaildrop theteamdimension, and former team results are undergroup.digest.culturereturnsfocus.kinddepartmentorgroup. A group ruleteam is Xbecomessegment(In group).exports.teamreturnspeopleGroupsandpeopleGroupMembers. - Pulse questions go to a group, not an audience.
pulse.addQuestiontakesgroupId(one of the workspace's groups, or null for everyone) instead ofaudience, andpulse.setQuestionGroup(questionId,groupId) replacespulse.setQuestionAudience.pulse.audienceOptionsis gone: pick fromgroups.list.pulse.questions,pulse.questionDetail,pulse.actions,pulse.actionandpulse.insightsreturngroupIdandgroupLabelwhere they returnedaudienceandaudienceLabel. eNPS still goes to everyone.
Added
users.pagerows returndirectory:jobTitlefrom the directory for every key, andstartDate(a calendar date) for administrator keys only, null for everyone else.- New hire 30/60/90 plans:
playbooks.planslists the plans for the new hires the key's person manages (asManager: each new hire playbook episode withhire,startDate,programNameandplanor null) and their own plan once shared (asHire).playbooks.draftPlanhas Mello draft three goals per stage (draftedByai, orrulewithout a model);playbooks.savePlan(enrollmentId,expectedRevision,goalswithstage30, 60 or 90 andtext, at most 5 per stage) edits them;playbooks.sharePlanshows 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.programrows returnplan(statenone,draftorshared,done,total, andgoalswithstage,textanddonefor 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 returnautoDrafted. - Buddy program health:
playbooks.buddyCandidates(personId) suggests up to five buddies for a new hire, never anyone in their reporting line, volunteers first, each withreasons,volunteerandload(active,total).playbooks.volunteerAsBuddy(volunteer) puts the key's person on the buddy volunteer list or takes them off;playbooks.setBuddyVolunteer(personId,statusvolunteer,removedornone) is HR's version, and only HR undoesremoved.playbooks.buddyProgramlists volunteers and recent buddies withstatus,load,hiresAnswered,hiresMet,hiresHelpedandneverMet.playbooks.buddyFollowUpsandplaybooks.answerBuddyFollowUp(feedbackId,answerhelped,metornever) are the "did you meet?" question sent to the hire and buddy on the playbook'sbuddyFollowUpOffsetDays(default 30). Onboarding playbooks also takebuddyTopics, the list sent to the buddy with the invitation. - Add people by department or community:
playbooks.previewEnrollmentandplaybooks.enrolltakedepartmentId(everyone in a department) orcommunityId(everyone who shows a community on their profile) besidegroupId; one at a time, and people already on the playbook are left out. - Playbooks that start automatically:
playbooks.saveProgramtakes, andplaybooks.programandprograms.*return,playbook.autoStart(defaultfalse) andplaybook.autoStartGroupId(a group, or null for everyone). Only a playbook whosedayZeroisstart_datecan 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 asplaybooks.enrollleaves 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.allAutomationsreturns each automation of the workspace with itsgroup(id,name,kind,members) beside the fieldsgroups.automationsreturns,totals(active,paused,groups, anddone30,skipped30,failed30runs in the last 30 days) and the latest 20runsacross groups withgroupName.targetIdkeeps only the automations that start that playbook or program or invite to that community.groups.listandgroups.getreturnautomations, how many of the group's automations are on. - Read and confirm in playbooks: a playbook step can be
kindacknowledgeinplaybooks.saveProgram, naming one published Knowledge page inresourcesand going to thehire. Once it goes out, the person confirms the page in the app as for a policy confirmation;playbooks.programreturns each such step'sansweredAtandacknowledgedVersionId, andplaybook.journeyreturnspageandconfirmedAton its steps. - Praise in the network and badges:
network.minesuggestions carrypraised(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 themorYou praised each other); it stays a suggestion until both say they know each other.badges.listincludes three automatic badges for being thanked in public praise:First thanks,Thanked across teams(from three departments) andLives our values(five thanks for one value). - Connections from communities:
programs.createandprograms.updatetakecommunityIds(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.unitKindtakescommunity, socross-unitmixes people from different communities andwithin-unitkeeps each group inside one (within a program's own communities when it has them).programs.getandprograms.listreturncommunityNames,programs.optionsreturnscommunities(id,name,memberCountof members who show it) andprograms.communitieslists the same. - How people like to be thanked:
praise.setRecognition(recognition:publicorprivate) sets the key's own choice; choosing private hides praise they already received from their profile and the feed, and returns how many werehidden.praise.minereturnsrecognitionandnotes(receivedandgivenprivate notes, each withid,giver,recipient,text,valuesandsentAt).praise.sendtakesprivately(send each person a private note instead of posting) and sends a private note to anyone who prefers one; it returnsposted,privateNotes,notSent(names Slack refused a note to) and apraiseIdthat is null when nothing was posted.praise.insightsreturnsteamsandlocationsbesidedepartments, each row withlowwhen undercoverageLowpercent were recognized, and counts private notes as recognition.praise.settingsandpraise.saveSettingscarrymanagerNudges. - SCIM groups: a group's
sourcecan bescim, a synced group whose members follow the identity provider.sync.groupsandsync.setGrouptake a SCIM connection's id too (fromsync.current,providerscim,connection.sourcescim) when called by a signed-in company administrator:importedbrings the group into every workspace of the company,onProfileapplies to this workspace, andsyncingis always false because members arrive at once; like other synced groups, its members are read-only ingroups.saveandusers.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.replayandwebhooks.deliveries.replayFailed, under the newwebhooks:readandwebhooks:writescopes.createtakes a publichttps://url, an optionaldescriptionandevents(catalog types; each group needs its read scope, such asknowledge:readforknowledge.*andhr:readforhr.*), and returns theendpoint, its signingsecret(whsec_…, shown only here and fromrotateSecret) and thetestDeliveryIdof thewebhook.testit queues.rotateSecrettakeskeepOldFor(now,1h,24hor7d); 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 exportsverifyWebhook({ body, headers, secret }),signWebhook,webhookEventCatalog,webhookEventTypesand theWebhookEventtype 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 returnsid,created(false when the same request was already waiting) andstate(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.minereturns, on each suggestion,introVia(the first person in common Shuffl can ask, or null) andintro(your latest request about them:stateasked,introducedornot_this_time, withviaId). A decline only ever reads asnot_this_time.reminders.settings,reminders.saveSetting,reminders.mineandreminders.saveMinecover a new kind,network("Do you know this person?", choicesonce_a_weekandonce_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 withid,name,memberCount(people in it at the source),imported,groupId(the group in Shuffl, when brought in),onProfile,listed(seen by the latest sync) andseenAt, plus theconnection(id,label,provider,source).sync.setGroup(directory.setProviderGroup:connectionId,id,importedand/oronProfile) brings a group in, takes it out or shows it on profiles, and returnsgroupId,importedandsyncing(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.currentconnections returngroups(listed,imported). - Group stats:
groups.glance(id) returns a group at a glance in the same shape asusers.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) andtopics(what its people are asked about).leftThisYearis always null. Any key reads groups shown on profiles; an owner or administrator key reads any group. A group the key can't see isNOT_FOUND. digest.culturereturns the morning Culture page's figures:week(peoplethanked,introductionsand playbooksfinishedin the last seven days),playbooks(the four busiest active playbooks, each withid,name,leaving,activeand up to fourpeoplewithenrollmentId,name,photoUrl,day,progressPct,flag:help,no_buddyorlate, andlate),help(new hires who asked for help at a check-in in the last 14 days),wins(finishedplaybooks this week and shared workanniversariesin the next seven days),focus.company.previous(the company'sthankedPct,metPctandpulseover the 90 days before) and each outcome's shortstep.- Groups for everyone:
groups.directorylists the groups shown on profiles to any key in the workspace, the key's own groups first, each withid,name,description,source,hrTeam,memberCount,mineand up to fivefaces(id,name,title,photoUrl).groups.directoryGroup(id) reads one with everymemberthe same way; a group not shown on profiles isNOT_FOUND. People whose employment ended are left out of both. users.glancereturnsundated, how many people in the headcount have no start date, andleftThisYear, how many were marked as left in the last year: a number for an owner or administrator key, null for anyone else.celebrations:thisWeeklists who was celebrated in a channel in the last seven days (days, andpeople, each withpersonId,name,eventType:birthdayoranniversary,yearsandsentAt). Celebrations sent privately or to a person's own team are left out.- Automations on a group:
groups.setAutomationadds or changes what happens when someone joins or leaves a group (groupId,trigger:enteredorleft,kind:send_message,start_onboarding,invite_community,add_to_program,tell_hrortell_manager, and aconfigfor the kind: the message'ssubject,body,actionandchannel; aprogramId; acommunityId; or anote). A new automation starts paused;groups.setAutomationState(id,state:pausedoractive) turns it on or off,groups.removeAutomation(id) drops it with its runs, andgroups.automations(groupId) lists a group's automations with each one'ssummary,state,runsandlastRunAt, and its last 50 runs (personId,name,trigger,outcome:done,skippedorfailed, andwhya run did nothing). Runs happen from the groups refresh, once per person and entry;personActivity.personActivityreturns them asgroup_automationevents. - Message a group:
groups.prepareMessagewrites a draft to everyone in a group (groupId,subject,bodyas paragraphs, optionalactionwithlabeland apathstarting with/app, andchannel:preferred,slackoremail) and returns it withpreview(how it reads to its first recipient, with{{first_name}},{{manager}}and{{company}}filled in) andreach(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 returnsqueuedandskipped; 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, andgroups.export(id) returns the members ascsvwith afilename. People get the message under the newhr_messagenotification kind innotifications.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.createandprograms.updatetakegroupId(null or omitted keeps the Slack channel roster),programs.getandprograms.listreturngroupIdandgroupName,programs.optionsreturnsgroupsto pick from, andprograms.syncRosterreads 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.previewEnrollmentandplaybooks.enrolltakegroupId: everyone in that group who is not on the journey yet is added afterrows, 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;suggestionslists the starter groups not made yet andpreviewcounts who a set of rules would hold today.knowledge.templates:drafthas Mello research a policy template and write the whole page. Send the template id,title,body(the template text, prefilled or not), itschecks, and optionalnotesandwebsite. It returnstitle,body,choices(each value Mello chose, with itstopic,value,basis:company,lawortypical, and thesourcelink the web search returned, if any),sources,placeholders(how many are left) andresearched(whether the web search ran). Nothing is saved.knowledge.templates:factsreturns 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) andholidaysTitle(the published holidays page). The app fills a new template's placeholders with them.knowledge.handbook:exportreturns the pages published to everyone as one handbook:teamName,generatedAt,pages(each withtitle,effectiveAt,versionand its Markdownbody),leftOut(how many published pages are for particular groups or admins only) and the whole handbook asmarkdown. 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:requestasks the page's audience to confirm its published version (and messages them unlessnotifyis false),remindmessages the people who haven't confirmed,closestops asking, andstatuslists who is asked and who confirmed which version.minelists 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:culturelists the Culture page's recommendations in Mello's order, each with itsarea(praise,connections,onboardingorpulse) andrank, andoutcomes: what happened after each recommendation acted on in the last 60 days. It also returnsfocus: 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,missedCheckInsandpreviousfigures for the 90 days before, andfocus.praisecounts praise between groups.
Changed
- Badge offers when a project or event channel goes quiet:
badges.settingsreturns andbadges.saveSettingstakeschannelOffers(defaulttrue). 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 havesourcechannel. - Celebration send days:
celebrations.saveandcelebrations.previewtakesendDays(ISO weekdays, 1 = Monday; Monday to Friday when left out) andoffDayShift(beforeorafter: where a birthday or anniversary on another day goes), andcelebrations.programreturns both for each celebration.celebrations.previewreturns the sample'ssendDateandshifted, andcelebrations.reviewrows carrysendDate, 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/playbooksand/app/playbooks/all; the old/journeysaddresses redirect there. Operation names and fields followed in a later release. - Slack user groups become synced groups with
sourceslack, kept in step with Slack's members. The Slack import also fillsjobTitle,timezone,startDateandmanagerIdindirectory.directoryPersonfrom Slack profiles for people no HR system manages. - Pulse results HR can share:
pulse.programreturns andpulse.saveProgramtakesshareResults(defaultfalse). When it is on,pulse.askedreturnsresulton each closed question asked to everyone whose reporting floor was met (unit:favourableoreNPS,value,respondents); otherwiseresultis null. - Journeys: onboarding is the first kind of journey.
playbooks.saveProgramtakes, andplaybooks.programandprograms.*return,playbook.dayZero(start_date,joinedordate: what each person's days count from),playbook.dayZeroDate(the one date whendayZeroisdate),playbook.buddyandplaybook.helpCheckIn(whether the buddy messages and the help check-in are part of the journey; both default to true). Ajoinedjourney has no steps before day 0.playbooks.enrollandplaybooks.previewEnrollmentfill a missingstartDatefrom the journey's day 0.playbook.journeytakes an optionalenrollmentIdand returnsenrollmentId,dayZero,buddyOnandjourneys(each journey the person is on, withcurrent).playbook.personOnboardingreturnsbuddyOnper enrollment. - Catch-up for people who join a journey after day 0:
playbooks.saveProgramtakes, andplaybooks.programandprograms.*return,playbook.catchUp(skip, the default,bundleorshift).playbooks.enrolltakes an optionalcatchUpfor that batch and returnscatchUpandlate, how many were added after day 0.playbooks.previewEnrollmentreturnsmissedper row andcatchUp(default,lateandsample, one person's steps under each choice).playbooks.enrollmentPreviewreturnsjoinedOnandcatchUpper step (planned,today,skippedorbundled). Steps can have the statusskippedand the kindcatch_up, the one What you missed message. - Check-ins on any day with a journey's own questions: a journey step of kind
checkpointcan havecheckpoint: 'custom'withquestions(1 to 6, each with anid,textandkind:scalewith ascaleofagree_5,rating_5oryes_no, oropen), on any day and as often as the journey likes, inplaybooks.saveProgram,playbooks.programandprograms.*.playbooks.firstDaysCheckInsreturns them withcheckpointset tostep:<journey id>:<step id>, plusintro,noteandjourneyon every check-in;playbooks.answerFirstDaysCheckIntakes thatcheckpointand answers the step's questions.playbooks.firstDaysResultsreturnsjourneys: each journey's own check-ins (programId,journey,stepKey,title,offsetDays,dayZero,sent,answered,publishedandquestions), combined only within that step and shown above the reporting floor like the built-in ones.playbook.journeyreturnskindper checkpoint (day_30,day_60,day_90,day_365orcustom) andcheckpointas the check-in's subject. - Leaving:
directory.saveDirectoryPersontakes, anddirectory.directoryPersonreturns,lastDay(the person's last day; null clears it, left out keeps it) andlastDayAnnounced(default false). Shuffl never takes or stores a reason. Until a last day is announced,directory.directoryPersonreturnslastDayas null to anyone but owners, admins and the person's manager. A journey'splaybook.dayZerocan belast_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 statuswaitinganderrorCodeuntil_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 withremovalReasonleaving_cancelled.playbooks.previewEnrollmentreportsmissing_last_dayfor someone without one. Ending someone's employment or access no longer ends their leaving journey; only the steps to them stop. praise.feedreturnsvalues: 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.insightsandpulse.questionDetailreturn a fifth split,groups.dimensions.group(questionDetail:groups.group), one row per group with the samelabel,respondents,favourablePct,enpsanddriversas departments and teams.pulse.insightsalso lists every current group withseen: falseuntil 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.feedandpraise.ofPersoninclude praise from private praise channels the key's person is in now, andpraise.mineleaves out private channels they have left.praise.composerreturnsprivateper channel and offers private channels only to their members;praise.sendrefuses a private channel for someone who isn't in it.praise.settingsreturnsmembersFreshandmembersErrorper channel. - Charts on the employee People page:
users.glancereturnsmonthly, the last twelve months of headcount and people who joined (empty below five people). - Onboarding to-do reminders:
reminders.settings,reminders.saveSetting,reminders.mineandreminders.saveMinecover a third kind,onboarding_todo, with the choicesone_day_lateandthree_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
kindcan beonboardinginassistant.history, linking to Your onboarding.knowledge.search,knowledge.contextandmanager.currentshare the citation shape but never return it. reports.onboardingreturnshires.withBuddy, the hires in onboarding now who have a buddy.digest.actOnNudgeanddigest.dismissNudgeacceptfrom: 'culture'for nudges acted on from the Culture page. Culture nudges have six more kinds:praise_low,pulse_low,start_praise,start_pulse,start_onboardingandstart_cross_team.
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.listis paginated: it takeslimit(1 to 100, 25 by default) andcursor, and returns{ items, nextCursor }instead of an array of every member. Scripts that call it over HTTP readitemsand follownextCursor.- The first 90 days' check-ins are onboarding journey steps (
kind: 'checkpoint'withcheckpointofday_30,day_60,day_90orday_365).reminders.settings,reminders.saveSetting,reminders.mineandreminders.saveMinenow cover onlypraiseandpulse: themanager_check_inandnew_hire_check_inkinds and their choices are gone.onboarding.firstDaysResultsno longer returnsmanager, andonboarding.journeyno longer returnsmanagerCheckIns.
Added
9 operations, by resource:
company:current,details,renamecompany.emailDomains:listcompany.members:list,removecompany.slack:listcompany.workspaces:list,setDefaultonboarding:completeTodochecks 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 ascompany.members.list({ limit: 100 }). It calls the REST paths under/api/v1/companyand throwsORPCErrorlikecreateShuffl. - CLI:
shuffl companycommands, withshuffl login --company --token-stdinandshuffl logout --company, orSHUFFL_COMPANY_KEYin the environment. The company key is saved incompany.json, apart from the workspace key. - OpenAPI: the operations are under
/company, with thecompanyKeysecurity scheme andx-shuffl-credential: company.
Changed
- Company API responses return exactly the published fields:
company.detailsno longer includesinternal, andcompany.slack.listsendsrevokedAtas an ISO time. - Company API errors include
statusanddefined: false, like workspace API errors. - Onboarding journeys:
onboarding.saveProgram,onboarding.programandprograms.*carryonboarding.steps, the steps HR writes (message, to-do or check-in, day offset, recipient, Knowledge, link and community resources, to-dos).onboarding.enrollmentPreviewreturns each step'sref,title, recipient and to-dos;onboarding.programreturnsstepKeyandtitleper step;onboarding.journeyreturnstodos,gettingReadyandresources. reports.peoplereturnshistory: headcount, joins and people marked as left for each of the last 12 months, plus today's departments and tenure.exports.teamandreports.overviewreturn each program'sonboarding.stepstoo;credentials.api.create,credentials.api.listandpermissions.listinclude theonboarding.completeTodopermission.reports.onboardingreturnsmessages.weekly(sent and on time per week);praise.insightsreturnsgiversandrecognizedper week.
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.connectedandslack.statusare replaced byslack.state,slack.liveandslack.reason.celebrations.previewno longer returnsfingerprint, andcelebrations.saveno longer takespreviewFingerprint.
Added
7 operations, by resource:
celebrations:saveAnniversaryDate,startDateshome:placeshr:mineonboarding:journeyteams:slackConnectionusers:glance
Changed
- Schedules take
days(1 to 7, Monday first) to pick days of the week: program schedules inprograms.*andreports.overview,reminders.saveSettingandreminders.settings.pulse.programandpulse.saveProgramaddsendEvery,sendUnit,sendStartandsendMonthWeek. - Notification kinds add
check_inandgetting_started(notifications.preferences,notifications.save). users.savetakesoriginalStartDate;exports.teamreturns it, andcelebrations.preferencesreturnsstartDate.communities.membersreturnsjoinedAt;praise.settingsreturnsslack;sync.currentreturnsslackper connection;digest.previewreturnstrendand aratioper section;operations.activityalways returnssummary.profiles.whoKnowstakesfrom;support.createtakesguides;praise.sendaccepts morefromvalues.credentials.api.create,credentials.api.listandpermissions.listinclude the new operations' permissions.
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.
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 nowteams.*:current,setup,status,initialize,saveAgentSetup,resendSlackWelcome,slackJoinAccess,saveSlackJoinAccess,slackProfileLink,saveSlackProfileLink,syncSlackProfileLinks.GET /api/v1/workspaceis nowGET /api/v1/team, and the/manage/workspaces/...paths are now/manage/teams/....exports.workspaceis nowexports.team.
Removed
billing.*: company billing moved to Company settings, and a team key never gains company scope.provisioning.currentandprovisioning.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,uploadPicturecelebrations:card,cards,removeCardNote,signCardcommunities:channelSuggestions,createCapabilities,createSlackChannel,fromChannels,generateCover,invite,revokeInvitation,uploadCoverdigest:actOnNudge,dismissNudge,nudge,nudges,preview,saveSettings,settingsexports:teamgetStarted:celebrate,celebration,dismiss,draftIntro,get,intro,wizardhome:preferences,react,storyhr:categories,rebuildCategories,saveCategoriesinvitations:candidates,createMany,inviteDirectoryknowledge:sources.writeonboarding:answerBuddyAsk,answerFirstDaysCheckIn,firstDaysCheckIns,firstDaysResultspraise:composer,sendprofiles:whoKnowspulse:actions,answer,audienceOptions,createAction,decideProposal,editProposal,latestRead,mine,proposals,proposeQuestions,questionDetail,requestProposals,setQuestionAudience,skip,updateActionreminders:mine,saveMine,saveSetting,settingsreports:melloTrial,melloValueruns:history.get,history.list,stopKnownFailuresteam:answerMinutes,dismissLaunchPost,launchPostteams:connectionPermissions,current,discardPairHistory,initialize,moveSlackChannels,rename,resendSlackWelcome,saveAgentSetup,saveSlackJoinAccess,saveSlackProfileLink,setup,slackChannels,slackJoinAccess,slackProfileLink,status,syncSlackProfileLinks,uploadPairHistoryusers:assignManagers,proposeReportingLines,reportingLineProposal,reviewReportingLines