If you're relatively new to programming, there's some helpful, more detailed explainer stuff at the end of the README, so if anything's confusing you may find answers there before you have to search around or talk to an LLM.
One thing I enjoy in life is getting guys to do stuff for me. Hang this TV, build these shelves. It makes me feel like Galadriel when she bugs out in Fellowship.
And I thought, why am I here manually marking this task overdue? Why am I manually doing anything? Manual is for mans. So I'm building Notion Boyfriend to automate this kind of stuff for me. A handsome little set of Deno scripts in a nice jacket to whom I can say, "Baby can you email a list of my 3 highest priority tasks organized by [xyz] criteria" while I kick back and sip an ice cold Diet Coke.
The best part about Notion Boyfriend is you can make your own, then make him your own, the sky's the limit. Really there's no limit. Unlimited boyfriend, that's the power of Deno and Val Town.
Each util lives in its own top-level file with a default export that wires up its trigger (cron, HTTP, or webhook). In Val Town, point the configured trigger type at the file.
Walks every configured Tasks data source, finds pages whose Due date is before
today (in America/Chicago) and whose Status is "not started" or "in
progress" flavored, and flips them to the Overdue status. Set conditional
formatting on the Overdue value in Notion to make rows pop red.
Trigger: cron (or HTTP for manual runs). Set around 2:30am
America/Chicago ā Val Town crons run UTC, so use 30 7 * * * during CDT or
30 8 * * * during CST.
Required env vars:
NOTION_TOKENā internal integration token from developers.notion.comNOTION_TASK_DATA_SOURCE_IDSā comma-separated bare data source IDs (no quotes)
See docs/label_overdue.md for status-variation
extension points and internals.
Notion guests get database-wide access by default ā there's no built-in way to
gate visibility down a relation chain. This util fakes row-level inheritance by
cascading a Team people-property from Organization ā Project ā Task.
Combined with Notion's data-source-level access rules that key off the Team
property, each client guest only sees their own Org and everything underneath
it.
Trigger: HTTP webhook from a Notion automation on the Organization data
source. Organization is the source of truth; the cascade only flows downward.
Manual additions to a Project's or Task's Team will be overwritten the next
time its parent Org fires the webhook.
Flow on each webhook:
- Authenticate the request via the
X-Notion-Webhook-Secretheader (constant- time compare; rejection paths return a bare404). - Read the triggering Org page; walk its
Projectsrelation to find affected projects, and each project'sTasksrelation to find affected tasks. - For each affected project, set
Teamto the union of its parent Orgs' teams. Skip writes when already correct. - For each affected task, set
Teamto the union of its parent Projects' teams (post-update). Same skip/warn behavior.
Required env vars:
NOTION_TOKENNOTION_WEBHOOK_SECRETā shared secret sent by Notion asX-Notion-Webhook-SecretNOTION_ORGANIZATION_DATASOURCE_IDNOTION_PROJECT_DATASOURCE_IDNOTION_TASK_DATASOURCE_ID
Required Notion setup:
- Every Org, Project, and Task data source has a property named
Teamof typePeople. - Org has a relation property named
Projectspointing at the Project DS. - Project has a relation property named
Organizationpointing at the Org DS (the bi-directional reflection ofProjects). - Project has a relation property named
Taskspointing at the Task DS. - Task has a relation property named
Projectpointing at the Project DS (the bi-directional reflection ofTasks). - The integration is shared into all three data sources.
- A Notion automation on the Org DS triggers on
Teamchanges (and any other field whose change should propagate) and sends a webhook to this util's URL withX-Notion-Webhook-Secretset toNOTION_WEBHOOK_SECRET.
See docs/cascade_access.md for auth, dispatch, and
cascade internals.
notion-boyfriend/ āāā label_overdue.ts # overdue-labeling util (cron + http) āāā cascade_access.ts # team-cascade util (webhook) āāā shared/ ā āāā notion.ts # Notion client, env helpers, sleep, trigger wrapper āāā docs/ # internal reference docs (see AGENTS.md index) āāā deno.json āāā README.md āāā AGENTS.md
All configuration is via Val Town environment variables ā never bake IDs or tokens into the source. Utils fail fast at startup if any required env var is missing or empty.
(coming soon)