Navigate directly to When I Work scheduler weeks and return structured schedule-grid data, including the schedule publish state. The recipe supports inspecting one week or stitching together consecutive weeks from a caller-supplied start date, including a 12-week collection.
Use Cases
Use when the caller needs to inspect schedule rows, positions, cells, shifts, or whether each schedule week is published. The caller must have an existing authenticated session or provide credentials through the site's normal login flow; this skill does not embed credentials.
Automation Flow
- For one requested week, construct
https://app.wheniwork.com/scheduler?date={date}where{date}isYYYY-MM-DD. Theappx.wheniwork.comhost may canonicalize or redirect to theapp.wheniwork.comhost. - For a rolling collection, step seven days at a time from
{start-date}:{start-date},{start-date + 7 days}, through{start-date + 7 * ({weeks} - 1) days}. Navigate directly to each scheduler URL; do not visit the homepage or use the visible next-week control. The default collection size is one week; callers may request 12 weeks or any other count. - Batch the navigations and page evaluations in the fewest available browser-agent calls. For each URL, use
goto(..., {waitUntil: "domcontentloaded"}), then wait until.schedule-row-containeror[id^="concern-target-"]exists, allowing several additional seconds for client rendering. - If navigation redirects to
https://login.wheniwork.com/?redirect=..., complete the normal authenticated login using the caller's approved session or credentials, then return to the encoded scheduler URL. Do not guess or inject credentials. - On each loaded scheduler page, run this extractor exactly once:
(() => {
const clean = value => (value || '').replace(/\s+/g, ' ').trim();
const text = el => clean(el && el.innerText);
const shiftData = el => ({
id: el.id || null,
text: text(el),
className: typeof el.className === 'string' ? el.className : ''
});
const cellData = cell => ({
id: cell.id || null,
text: text(cell),
shifts: [...cell.querySelectorAll('[id^="shift_"]')].map(shiftData)
});
const cells = [...document.querySelectorAll('[id^="concern-target-"]')].map(cellData);
const rows = [...document.querySelectorAll('.schedule-row-container')].map(row => ({
text: text(row),
position: text(row.querySelector('.position-site-details')) || null,
cells: [...row.querySelectorAll('[id^="concern-target-"]')].map(cellData)
}));
const body = document.body.innerText || '';
const primary = document.querySelector('button[data-testid="schedule-publish-button"]');
const publishContainer = document.querySelector('div.schedule-publish-button');
const publishButton = primary ? {
text: text(primary),
ariaLabel: primary.getAttribute('aria-label'),
className: String(primary.className),
backgroundColor: getComputedStyle(primary).backgroundColor,
disabled: primary.disabled === true || primary.getAttribute('aria-disabled') === 'true'
} : null;
const publishText = text(publishContainer || primary);
const everythingPublished = /Everything Published/i.test(body) || /Everything Published/i.test(publishText);
const draftVisible = /Publish\s*&\s*Notify/i.test(body) || /Publish\s*&\s*Notify/i.test(publishText);
const publishState = everythingPublished ? 'published' : (draftVisible ? 'draft' : 'unknown');
const weekMatch = body.match(/[A-Z][a-z]{2}\s+\d{1,2}(?:,\s*\d{4})?\s*[–-]\s*(?:[A-Z][a-z]{2}\s+)?\d{1,2},?\s*\d{4}/);
const loginPage = /login\.wheniwork\.com$/i.test(location.host) ||
!!document.querySelector('input#email, input[type="email"]');
return {
url: location.href,
requestedDate: new URL(location.href).searchParams.get('date'),
weekLabel: weekMatch ? weekMatch[0] : null,
authenticated: !loginPage,
publishState,
everythingPublished,
noChanges: /No changes/i.test(body),
publishButton,
publishControlsPresent: !!publishContainer,
publishControlMenuPresent: !!document.querySelector('button[data-testid="schedule-publish-button-controls"]'),
rowCount: rows.length,
cellCount: cells.length,
shiftCount: document.querySelectorAll('[id^="shift_"]').length,
rows,
cells
};
})()- For a collection, return one extractor object per requested date in chronological order. Validate that each returned
requestedDateandweekLabelcorrespond to the intended week; do not treat a missing grid or an unauthenticated result as a valid publish state.
Possible Friction Points
- The non-obvious date filter is the
date={YYYY-MM-DD}query parameter; it selects the schedule week containing that date, so{start-date}does not need to fall on the account's week-start day. When I Work lets each account choose its week-start day; the observed run used Saturday, and pinning collection dates to Saturday would land every request mid-week for a Monday-start account. Consecutive weeks can be fetched by adding seven days to the date rather than clickingbutton.navigate-forward. - Direct date URLs on
app.wheniwork.comare the shortest reliable path even when the initial application host isappx.wheniwork.com; the app may canonicalize between these hosts. - The scheduler is client-rendered.
domcontentloadedcan precede the grid and publish controls by several seconds, so wait for.schedule-row-containeror[id^="concern-target-"]and allow a bounded settling period. - Publish controls use
div.schedule-publish-button,button[data-testid="schedule-publish-button"], andbutton[data-testid="schedule-publish-button-controls"]. VisibleEverything Publishedindicates published;Publish & Notifyindicates a draft; otherwise reportunknownand retain the button metadata. - Schedule positions are represented by
.position-site-details; day/assignment cells use IDs beginning withconcern-target-, and shift elements use IDs beginning withshift_. - Unauthenticated navigation can redirect to
login.wheniwork.comwith the original scheduler URL URL-encoded inredirect. Preserve the intended URL and return to it after approved authentication. - The application may switch between
appx.wheniwork.comandapp.wheniwork.com; treat either authenticated application host as valid. - Do not infer entity IDs, company identity, week boundaries, or login state from nearby text. Verify the loaded URL, requested date, week label, and authenticated result.
- A row containing text such as
Need Driveris still a normal schedule row; include it in the returned row data rather than filtering it out. - Clicking
button.navigate-forwardchanges the displayed week but is unnecessary for collection runs and may not provide a stable URL for later validation; prefer independently navigated date URLs.