Guides

The Pet Care board

Two people, one dog, one cat, and the daily question: has anyone actually done this yet? This guide builds a shared board you update by talking to Siri and read from your home screen without unlocking your phone. It fits the free plan, needs no server, and takes about twenty minutes.

What you end up with

Three walk slots and two feeding timers. You say "Hey Siri, walked the dog" on the way back in; your partner glances at the widget and sees it already happened. Nobody double-feeds the cat, nobody texts "did you walk her?" from the office.

walk_morning

Walk — morning

Toggle

Not yet / Done

walk_afternoon

Walk — afternoon

Toggle

Not yet / Done

walk_evening

Walk — evening

Toggle

Not yet / Done

dog_fed

Dog — last fed

Elapsed

an ISO 8601 timestamp

cat_fed

Cat — last fed

Elapsed

an ISO 8601 timestamp

Why walks and meals are built differently

The failure mode of any "did we do this today?" board is a stale yes. If the widget still says walked from yesterday, someone trusts it and the dog doesn't go out. A confident wrong answer is worse than no board at all.

So walks are toggles — they match the morning/afternoon/evening model and read at a glance — but they only mean "today" because something clears them overnight. Meals are elapsed tiles, which store the moment of the last feed and count up on their own. "Fed 14h ago" cannot go stale; there is nothing to reset and nothing to forget. If the nightly reset ever fails, the walk slots go stale but the feeding tiles still tell the truth. The board degrades instead of lying.

What you'll need

  • An EasyBoard account — the free plan allows exactly five tiles, which is what this uses
  • An iPhone with the built-in Shortcuts app
  • Scriptable — free on the App Store, for the home-screen widget
  • Your dashboard ID and write token (Dashboards → ⋯ → Write token)

Step 1 — Build the board

Create a dashboard called Pet Care and add the five tiles from the table above. For each one, open the tile's edit modal and set the Alternate ID to the value in the first column. That handle is what every shortcut below targets, so it has to match exactly — the tile titles can say whatever you like.

Give each walk toggle two options, Not yet and Done, and colour the second one green. For the two elapsed tiles, set the start time to now — you'll overwrite it the first time you feed anyone.

Step 2 — "Hey Siri, walked the dog"

A shortcut's name is its Siri phrase, so name them the way you'd say them out loud. Build one shortcut with a single Get Contents of URL action:

URL

https://easyboard.live/api/d/YOUR_DASHBOARD_ID/tiles

Method

PATCH

Headers → Authorization

Bearer YOUR_WRITE_TOKEN

Headers → Content-Type

application/json

Request Body

JSON → Raw

Walked the dog — body
{
  "updates": [
    {
      "target": { "alternateId": "walk_morning" },
      "patch": { "value": "Done" }
    }
  ]
}

Duplicate it twice, changing walk_morning to walk_afternoon and walk_evening. Name them "Walked the dog this morning", "…this afternoon", "…this evening". Sending the value directly rather than cycling the toggle matters: saying it twice by accident still leaves the slot on Done, instead of flipping it back to Not yet.

Step 3 — "Hey Siri, fed the dog"

Same shape, except the value is a timestamp rather than a word. Add two actions before the URL action:

  1. Date — leave it on Current Date
  2. Format Date — set Date Format to ISO 8601, with time included

Then in the raw JSON body, tap the timestamp and replace it with the Formatted Date variable:

Fed the dog — body
{
  "updates": [
    {
      "target": { "alternateId": "dog_fed" },
      "patch": { "value": "2026-08-22T07:14:00Z" }
    }
  ]
}

Duplicate for cat_fed. The tile immediately starts counting up from that moment, in every browser and widget, for both of you.

Step 4 — Clear the walks at midnight

Build a fourth shortcut called Reset pet board — one request, all three walk slots in a single batch:

Reset pet board — body
{
  "updates": [
    { "target": { "alternateId": "walk_morning" },   "patch": { "value": "Not yet" } },
    { "target": { "alternateId": "walk_afternoon" }, "patch": { "value": "Not yet" } },
    { "target": { "alternateId": "walk_evening" },   "patch": { "value": "Not yet" } }
  ]
}

Then open the Automation tab → new Personal Automation → Time of Day → 12:00 AM, Daily → Run Shortcut → Reset pet board. Turn Ask Before Running off so it fires while you sleep.

Why a phone automation and not a server cron

A scheduled job on our side would work for exactly one household — ours — and would need you to tell it your timezone. A personal automation runs on your own phone, in your own local midnight, with no configuration and no account beyond the ones you already have. It also means EasyBoard never writes to this board: everything on it was put there by someone in your house.

Step 5 — The home-screen widget

Open Scriptable, tap +, name the script Pet Care, and paste this in. Replace PASTE_YOUR_DASHBOARD_ID_HERE with your dashboard ID — there is no write token in the widget, because reading needs none.

Pet Care.js
// -- Configuration -------------------------------------------
const DASHBOARD_ID = "PASTE_YOUR_DASHBOARD_ID_HERE";
// -- End Configuration ----------------------------------------

const BASE = "https://easyboard.live";
const WALKS = [["walk_morning", "M"], ["walk_afternoon", "A"], ["walk_evening", "E"]];
const MEALS = [["dog_fed", "Dog"], ["cat_fed", "Cat"]];

const DONE = new Color("#22c55e");   // green — this slot is handled
const TODO = new Color("#334155");   // slate — still outstanding
const MUTED = new Color("#94a3b8");

async function fetchTiles() {
  const req = new Request(BASE + "/api/d/" + DASHBOARD_ID + "/tiles");
  const json = await req.loadJSON();
  if (json && json.error) throw new Error("API error: " + json.error);
  return Array.isArray(json) ? json : [];
}

function byId(tiles, id) {
  return tiles.find(function (t) { return t.alternateId === id; });
}

// Elapsed tiles store a MOMENT, so the widget does the counting. That is why
// a stale widget still reads correctly: "7h ago" grows on its own.
function ago(tile) {
  if (!tile) return "—";
  const t = Date.parse(tile.value);
  if (isNaN(t)) return "—";
  const mins = Math.max(0, Math.round((Date.now() - t) / 60000));
  if (mins < 60) return mins + "m ago";
  const hours = Math.floor(mins / 60);
  if (hours < 24) return hours + "h " + (mins % 60) + "m ago";
  return Math.floor(hours / 24) + "d " + (hours % 24) + "h ago";
}

function addChip(row, label, done) {
  const chip = row.addStack();
  chip.backgroundColor = done ? DONE : TODO;
  chip.cornerRadius = 7;
  chip.setPadding(4, 10, 4, 10);
  const el = chip.addText(label);
  el.font = Font.boldSystemFont(13);
  el.textColor = done ? new Color("#052e16") : MUTED;
  row.addSpacer(6);
}

async function buildWidget() {
  const w = new ListWidget();
  w.backgroundColor = new Color("#0f172a");
  w.url = BASE + "/d/" + DASHBOARD_ID;
  w.refreshAfterDate = new Date(Date.now() + 15 * 60 * 1000);

  let tiles = [];
  try {
    tiles = await fetchTiles();
  } catch (e) {
    const err = w.addText(e.message || String(e));
    err.textColor = Color.red();
    err.font = Font.systemFont(11);
    err.minimumScaleFactor = 0.4;
    return w;
  }

  const heading = w.addText("Walks");
  heading.font = Font.mediumSystemFont(11);
  heading.textColor = MUTED;
  w.addSpacer(6);

  const row = w.addStack();
  row.layoutHorizontally();
  for (const [id, label] of WALKS) {
    const tile = byId(tiles, id);
    addChip(row, label, !!tile && tile.value === "Done");
  }
  row.addSpacer();

  w.addSpacer(10);
  for (const [id, label] of MEALS) {
    const line = w.addStack();
    line.layoutHorizontally();
    const nameEl = line.addText(label);
    nameEl.font = Font.mediumSystemFont(12);
    nameEl.textColor = MUTED;
    line.addSpacer();
    const valueEl = line.addText(ago(byId(tiles, id)));
    valueEl.font = Font.boldSystemFont(12);
    valueEl.textColor = Color.white();
    w.addSpacer(4);
  }

  return w;
}

const widget = await buildWidget();
if (config.runsInWidget) {
  Script.setWidget(widget);
} else {
  await widget.presentSmall();
}
Script.complete();

Tap Run to preview it, then long-press your home screen → + → Scriptable → small widget → Edit Widget → Script: Pet Care. Three chips show the walk slots, green once done; underneath, how long since each animal was fed. Tapping the widget opens the live board.

Widgets refresh on iOS's schedule

iOS decides when a widget reloads — usually every 15–30 minutes. The feeding lines survive that fine, because the widget computes "how long ago" from a stored moment, so a stale render still counts up correctly. The walk chips are the part that can lag behind by a few minutes. Tap the widget to open the live board, which updates instantly.

Sharing it with the household

The board is the shared state; everyone just needs their own way in. Send your partner the shortcuts (share sheet → Copy iCloud Link) and the widget script, and they run on their phone against the same board. Both phones can say "fed the cat"; whoever gets there first wins, and the other one sees it.

What you're handing over

A shared shortcut carries your write token inside it, and that token can change anything on this dashboard. That is the right trade for a partner or a housemate, and the wrong one for a group chat — the token is scoped to this one board, but it is not revocable per person. If it ever leaks, regenerate it (Dashboards → ⋯ → Write token) and re-share the shortcuts. Reading is separate: the dashboard URL and the widget need no token at all, so anyone who knows the URL can see when the dog was fed. Don't put anything on this board you wouldn't say out loud.

Troubleshooting

Shortcut runs but nothing changes

Almost always the alternateId. Run GET https://easyboard.live/api/d/YOUR_DASHBOARD_ID/tiles in a browser and compare the alternateId values against the ones in your JSON — they are case-sensitive, and a tile with no Alternate ID set has none to match.

Shortcut reports 401

The Authorization header is wrong. It must read "Bearer " followed by the token, with a single space and no quotes or trailing whitespace.

A feeding tile says "Set start time"

The value is not a date the browser can parse. Check the Format Date action is set to ISO 8601 with time included — a friendly format like "22 Aug 2026" will not parse.

Walks never clear

Open Shortcuts → Automation and confirm the midnight automation exists, is enabled, and has "Ask Before Running" off. Run the reset shortcut by hand to prove the request itself works.

Widget shows a red error

The script prints the real error. "API error: not found" means the dashboard ID is wrong; a network error means the phone had no connection at the last refresh.

Make it yours

Nothing here is about pets. Three toggles and two timers is the shape of most household coordination: bins out, plants watered, medication taken, the boiler serviced. Swap the titles, keep the pattern — anything with a deadline becomes a toggle you reset, and anything you want to know the age of becomes an elapsed tile you never reset at all.