Skip to content
Docs

Ship social posts from Slack with eve and Typefully

A Slack-based social media agent built on eve. It drafts posts and threads for X, LinkedIn, Threads, Bluesky, and Mastodon through Typefully, manages the publishing queue, reads analytics, and drafts long-form pieces into Notion.

Ben SabicContent Engineer

Run your team's social presence without leaving Slack. You @mention the bot, and it drafts posts and threads for X, LinkedIn, Threads, Bluesky, and Mastodon through Typefully, schedules and manages the publishing queue, uploads media, reads post and follower analytics, and pulls briefs from Notion. Drafting flows freely, but anything with consequences pauses for your approval before it runs, rendered as buttons in the thread: scheduling or publishing a post, and deleting drafts, comments, or threads.

The bot is built on eve, a filesystem-first framework for durable backend agents from Vercel. Slack and Notion authenticate through Vercel Connect, and Vercel Blob and AI Gateway authenticate with your project's OpenID Connect (OIDC) token. The one credential you supply is your Typefully API key, set as the TYPEFULLY_API_KEY environment variable.

Deploy the template now, or read on for a deeper look at how it all works.

Typefully Social Media Agent eve Template

A Slack bot that runs your social posts through Typefully: drafts, threads, scheduling, analytics, and Notion drafting, with approvals for publishing and deletes.

Deploy Template

Copy link to headingQuick start with an AI coding agent

If you're working with an AI coding agent like Claude Code or Cursor, you can use this prompt to have it help you with building your agent:

Agent Prompt

I want to build a Slack agent with the eve framework, using the Typefully social media agent template. Read the setup instructions at https://agent-resources.dev/typefully-eve-template.md and follow them. They will cover deploying the template, building with eve, how everything works overall, and more.

Copy link to headingVercel Plugin

The Vercel Plugin turns your AI coding agent (e.g., OpenAI Codex, Claude Code, or Cursor) into a Vercel expert. It adds skills, slash commands, and current knowledge of the tools this template uses, including Vercel Connect, Vercel Blob, and AI Gateway. The plugin is optional; it isn't required to use eve or to follow this guide.

Terminal
npx plugins add vercel/vercel-plugin

Copy link to headingSetup and deployment

Copy link to headingWhat you need before deploying

You need four accounts to deploy and run the agent:

  • A Vercel account.
  • A Slack workspace where you can install an app.
  • A Typefully account with an API key (find it under Settings > API).
  • A Notion workspace for briefs, source material, and long-form drafts.

For local development, you also need Node.js 24 or newer, pnpm, and the Vercel CLI.

Copy link to headingDeploy to Vercel

Deploying with the one-click flow provisions everything the agent needs:

  • A Slack connector, with its event trigger pointed at the route eve serves (/eve/v1/slack) and the connector UID stored in SLACK_CONNECTOR.
  • A prompt for your Typefully API key, stored in TYPEFULLY_API_KEY.
  • A Notion connector, with its UID stored in NOTION_CONNECTOR.
  • A public Vercel Blob store for the asset and user-preference tools.

When the deployment finishes, your agent is live and wired up. All that's left is to invite it to a Slack channel and have team members sign in to Notion on first use.

Copy link to headingConnect Slack

The one-click flow installs the Slack app and points its event trigger at the route the agent serves (/eve/v1/slack), so there's nothing to wire up. After the deploy finishes, invite the bot to a channel in your workspace and @mention it to start a thread.

You can't test the Slack surface locally, because Slack forwards events to Vercel Connect, which verifies them and delivers them to your deployed project rather than to a local URL. Test the inbound Slack path against a preview or production deployment; everything else runs in the local dev terminal UI.

Copy link to headingAuthorize Typefully and Notion

The two connections authenticate differently, and the difference matters.

Typefully authenticates with the static API key in TYPEFULLY_API_KEY. The connection is app-scoped: one shared Typefully workspace credential across all Slack users. The key is resolved on each connection attempt and never exposed to the model.

Notion is user-scoped. The first time a team member asks the agent to touch Notion, Vercel Connect prompts them to sign in in the browser. That sign-in is per user, so the agent reads and writes Notion as the signed-in person with their own permissions, and there's no shared integration token to manage.

On top of the auth model, consequential operations are gated on approval: the call pauses and renders approve/deny buttons in Slack before it runs.

ConnectionOperations that require approval
TypefullyDeleting drafts, comments, or threads (always); creating or editing a draft only when it schedules or publishes (publish_at is set)
NotionUpdating pages, moving pages, updating data sources, updating views
Vercel BlobDeleting an asset, clearing a user's preferences

Plain drafting, edits to copy, reads, queue listing, and analytics flow freely. Creating a new Notion page is also not gated, because drafting into Notion is the normal flow.

Copy link to headingLocal development

You can run the agent locally in a terminal UI (TUI).

First, clone the repository that the one-click flow created under your GitHub account:

Terminal
git clone https://github.com/your-username/typefully-eve-template.git
cd typefully-eve-template

Then link it to the Vercel project you deployed and pull the environment variables:

Terminal
vercel link
vercel env pull

vercel env pull writes a short-lived OIDC token into .env.local, which authenticates Vercel Blob and the model locally, along with TYPEFULLY_API_KEY for the Typefully connection. Re-run it when the token expires. Then start the dev server and link a model provider with /model in the TUI:

Terminal
pnpm dev

In the TUI you can exercise the Typefully, Notion, Blob, and preference flows directly.

The template ships a dev-only auth shim (agent/channels/eve.ts) that presents your local session as a signed-in user, so the user-scoped Notion connection works from the TUI without affecting production.

Push changes to production with eve deploy:

Terminal
eve deploy

The eve deploy command is the same thing as running vercel deploy --prod.

The template also uses Ultracite (a Biome preset) for linting and formatting.

pnpm check checks formatting and lint rules, pnpm fix auto-fixes what it can, and pnpm validate runs lint, typecheck, and eve's discovery diagnostics together.

Copy link to headingHow the social media agent works

The agent runs one loop: load the user's preferences and the right skills, ground the work in the real accounts, work in drafts, and run anything consequential past you first.

  1. @mention in Slack. A team member @mentions the bot or DMs it with a social task, like a thread for X, a LinkedIn version of a launch post, or a queue change. The Slack channel turns the message into a session and threads every reply.
  2. Load preferences and the matching skills. The agent calls get_user_preferences to apply that user's standing notes (a default social set, tone, or workflow), then loads the skills that match the task: writing-quality for any prose meant for humans, plus the style skill for each target platform (x-style, linkedin-style, threads-style, bluesky-style, or mastodon-style). Skills load on demand, so a skill enters the agent's context only when the task calls for it.
  3. Ground everything in the real accounts. The agent lists the user's social sets and their connected accounts and reads existing drafts before creating or editing anything, so it never invents draft IDs, account names, or content. It pulls briefs and source material from Notion when you point it there. When a piece needs an outside fact, it delegates to the researcher subagent, which runs web research in a fresh session and returns cited findings.
  4. Work in drafts, schedule only on approval. Typefully drafts are created and edited freely; that's the agent's normal mode. It never sets publish_at (which schedules or publishes a post) unless you explicitly ask, and the scheduling call itself still pauses for an approval button.
  5. Draft long pieces into Notion. When the destination is Notion, or the piece is long-form, the agent creates it as a new Notion page and replies with the link. A page is easier to read than a long in-thread message.
  6. Check the draft before proposing it. Before proposing any social draft, the agent runs lint_against_style, a deterministic check against the target platform's banned-words list, and fixes what it flags. On the final draft, it delegates to the reviewer subagent, which judges the draft against a writing-quality rubric in a fresh session and returns a verdict with concrete issues. The agent addresses them before proposing the draft in the thread.

Along the way, the Vercel Blob tools store any durable files (e.g., exported threads or images) and return URLs that the agent can reuse, and the preference tools persist each user's standing preferences across sessions. Once a week, a scheduled run pulls Typefully analytics and posts a digest to a Slack channel you choose.

Every credential in this loop is handled outside your code:

  • Slack and Notion through Vercel Connect
  • Vercel Blob and AI Gateway through the OIDC token
  • Typefully through the TYPEFULLY_API_KEY environment variable

No static keys live in the code.

Copy link to headingCode walkthrough

The whole agent is defined under agent/, and eve discovers each capability from the filesystem. A tool's, connection's, or channel's filename is its name, so there's no central registry to wire up. These are the pieces that matter.

Copy link to headingThe Slack surface

The Slack surface is one file:

import { connectSlackCredentials } from "@vercel/connect/eve";
import { slackChannel } from "eve/channels/slack";
export default slackChannel({
credentials: connectSlackCredentials(
process.env.SLACK_CONNECTOR ?? "slack/social-media-agent"
),
});

connectSlackCredentials reads the connector UID from SLACK_CONNECTOR and lets Vercel Connect handle token rotation, multi-workspace tenancy, and webhook verification, so none of that lives in your code. Because the file is slack.ts, eve registers it as the slack channel and serves its events at /eve/v1/slack. Each human Slack user becomes a per-user principal, allowing the agent to act in Notion as that specific person and to link their preferences to them.

Copy link to headingDrafting and scheduling through Typefully

Typefully is a Model Context Protocol (MCP) connection. This connection is the agent's core: social sets, drafts, the publishing queue, media uploads, tags, comment threads, and post and follower analytics.

import { defineMcpClientConnection } from "eve/connections";
const DELETE_TOOLS = [
"typefully_delete_draft",
"typefully_delete_comment",
"typefully_delete_thread",
];
const PUBLISH_TOOLS = ["typefully_create_draft", "typefully_edit_draft"];
export default defineMcpClientConnection({
approval: ({ toolName, toolInput }) => {
if (DELETE_TOOLS.some((tool) => toolName.includes(tool))) {
return "user-approval";
}
if (PUBLISH_TOOLS.some((tool) => toolName.includes(tool))) {
const publishAt = readPublishAt(toolInput);
return typeof publishAt === "string" && publishAt.length > 0
? "user-approval"
: "not-applicable";
}
return "not-applicable";
},
auth: {
getToken: () => {
const token = process.env.TYPEFULLY_API_KEY;
if (!token) {
throw new Error("TYPEFULLY_API_KEY is not set.");
}
return Promise.resolve({ token });
},
},
description:
"Typefully: draft, schedule, and manage social posts across X, LinkedIn, Threads, " +
"Bluesky, and Mastodon. List social sets (accounts) and their connected platforms; " +
"create, edit, read, and list drafts; schedule posts and manage the publishing queue; " +
"upload media; organize with tags; manage comment threads; and read post and follower analytics.",
url: "https://mcp.typefully.com/mcp",
});

Two details carry the weight here:

  • getToken makes the connection app-scoped: it reads your API key from TYPEFULLY_API_KEY on each connection attempt, eve sends it as a Bearer token, and the model never sees the URL or the key. One shared workspace credential covers every Slack user.
  • The approval policy gates by consequence, not by tool. Deletes always pause for an approve/deny decision, rendered as a Slack button. Creating or editing a draft pauses only when the call actually schedules or publishes, that is, when publish_at is set. The policy reads publish_at defensively through a readPublishAt helper that never trusts the shape of the model-supplied input. Plain drafting and copy edits stay friction-free; scheduling, publishing, and deleting wait for a human.

The model discovers the available Typefully tools at runtime through the built-in connection__search and calls them by their qualified name, such as typefully__typefully_create_draft. The description field is the hint the model uses to decide when to reach for Typefully.

Copy link to headingReading and writing Notion

Notion follows the connection pattern, but with user-scoped auth:

import { connect } from "@vercel/connect/eve";
import { defineMcpClientConnection } from "eve/connections";
const notionConnector =
process.env.NOTION_CONNECTOR ?? "notion/social-media-agent";
const APPROVAL_REQUIRED_TOOLS = [
"notion-update-pages",
"notion-move-pages",
"notion-update-data-source",
"notion-update-view",
];
export default defineMcpClientConnection({
approval: ({ toolName }) =>
APPROVAL_REQUIRED_TOOLS.some((tool) => toolName.includes(tool))
? "user-approval"
: "not-applicable",
auth: connect(notionConnector),
description: "Notion workspace: search, read, and edit pages and databases.",
url: "https://mcp.notion.com/mcp",
});

connect() makes it user-scoped: each person signs in once, the per-user token is resolved before every call, and the model never sees it. Page updates, moves, and data-source changes are gated; page creation (notion-create-pages) is left ungated on purpose, because drafting a new page into Notion is the agent's normal flow. To gate creation too, add it to the list.

Copy link to headingPlatform craft as skills

Six skills sit under agent/skills/, each a folder with a SKILL.md and reference files:

  • writing-quality: generic prose rules for anything written for humans, from AI-tells to avoid through plain-English alternatives. The platform skills layer on top of it.
  • x-style, linkedin-style, threads-style, bluesky-style, and mastodon-style: one per platform, each carrying that platform's voice, hooks, thread structure, length specs, and a banned-words.json reference.

The description in each skill's frontmatter is a routing hint, not a label. eve exposes it to the model alongside a built-in load_skill tool, and the model loads a skill only when a request matches the description. Loading a skill only adds instructions to the turn, it never adds a new action the agent can take. The instructions tell the agent to load writing-quality plus the matching platform skill before drafting, and to load each target's skill in turn when adapting a piece for several platforms.

Copy link to headingA deterministic style lint

The style skills aren't only guidance the model reads. The lint_against_style tool turns each platform's banned-words list into a deterministic check the agent runs on every draft before proposing it:

export default defineTool({
description:
"Check a draft against the surface's banned-words list and return any violations. " +
"Run before proposing a draft to the writer.",
async execute({ surface, text }, ctx) {
let banned: string[] = [];
try {
const raw = await ctx
.getSkill(`${surface}-style`)
.file("references/banned-words.json")
.text();
const parsed = BANNED_WORDS_SCHEMA.safeParse(JSON.parse(raw));
if (parsed.success) {
banned = [...new Set(parsed.data.map((w) => w.trim()).filter(Boolean))];
}
} catch {
banned = [];
}
const hits = banned.filter((w) => toMatcher(w).test(text));
return {
ok: hits.length === 0,
violations: hits.map(
(w) => `Avoid "${w}" per the ${surface} style guide.`
),
};
},
inputSchema: z.object({
surface: z.enum(["x", "linkedin", "threads", "bluesky", "mastodon"]),
text: z.string().min(1).max(MAX_TEXT_LENGTH),
}),
});

The surface input is constrained to a fixed enum, so the resolved skill and file path can never be influenced by the caller. Each banned entry is regex-escaped and matched as a literal whole word, which closes off both regex syntax errors and catastrophic backtracking against caller-supplied text. Editing a platform's banned-words.json changes both what the model reads and what this tool enforces.

Copy link to headingThe weekly analytics digest

A schedule under agent/schedules/ turns the agent into a Monday reporter:

import { defineSchedule } from "eve/schedules";
export default defineSchedule({
cron: "0 14 * * 1",
markdown: [
"Post this week's Typefully analytics digest to the team.",
"",
"1. Call typefully_list_social_sets and pick the first social set to report on. ...",
// ...pull follower and post analytics, build two tables,
// then call post_analytics_report with a short summary...
].join("\n"),
});

Every Monday at 14:00 UTC (Vercel evaluates cron in UTC, so adjust the hour for your timezone), the schedule runs the agent on that prompt. It pulls follower and post analytics for the first social set, builds two tables from only the fields the tools actually return, and delivers them through the post_analytics_report tool as Slack data_table blocks, with a fixed-width text fallback when a workspace rejects the block.

The delivery tool is hardened the same way as the rest of the template: the destination channel comes from the TYPEFULLY_ANALYTICS_CHANNEL environment variable, never from model input, so a prompt can't redirect the digest. The read-only analytics tools aren't approval-gated, so the scheduled session never stalls waiting for a human.

In development, schedules never fire on their cadence; trigger a run out of band with:

Terminal
curl -X POST http://localhost:3000/eve/v1/dev/schedules/weekly-analytics

Copy link to headingFresh-context subagents

Two subagents live under agent/subagents/, and each runs in its own fresh child session: it inherits none of the root's skills, connections, or tools, so the root packs everything the subagent needs into the call message, and the result comes back as a structured tool result.

The researcher handles outside facts. When a post needs a statistic, a competitor detail, or a claim verified, the root delegates rather than reaching from memory. The researcher uses the framework's web_search and web_fetch tools and returns an outputSchema-typed result: findings where every claim carries at least one real source URL and a confidence level, plus gaps for anything it couldn't verify. The root weaves in only cited findings and surfaces the gaps to you instead of papering over them.

The reviewer handles the final pass. On a finished draft, the root sends the full text plus the target platform and any voice or audience context, and the reviewer judges it against the writing-quality rubric with fresh eyes, returning a ready or revise verdict with one entry per concrete issue: the severity, the rule broken, the offending quote, and a suggested fix. A reviewer that never saw the sources or the drafting reasoning catches the AI-tells and bloated wording that self-review reads right past.

Two implementation details make the reviewer self-sufficient: it carries its own copy of the writing-quality skill under agent/subagents/reviewer/skills/, and it declares its own sandbox.ts, because a subagent's sandbox doesn't inherit from the root and it needs one to read the skill's reference files.

Copy link to headingRemembering each user, agent/tools/*_user_preferences.ts

Three tools give the agent durable, per-user memory backed by Vercel Blob: get_user_preferences, save_user_preferences, and clear_user_preferences.

When a user states a standing preference ("always draft for the X and LinkedIn set", "keep threads under 8 posts"), the agent loads the current document, merges the note in, and saves the full result, so the file stays curated rather than append-only.

The isolation model is the part worth reading:

export const userPreferencesKey = (principal: UserPrincipal): string | null => {
if (principal?.principalType !== "user" || !principal.principalId) {
return null;
}
const id = createHash("sha256").update(principal.principalId).digest("hex");
return `${USER_PREFERENCES_PREFIX}${id}.md`;
};

The Blob key is derived entirely from the framework-resolved principal (ctx.session.auth.current), never from model input, so a session can only ever read or write its own user's file, and the principal id is hashed so the stored path carries no raw identifier. The general asset tools refuse the reserved user-preferences/ prefix, so they can't be used as a side channel to another user's preferences. clear_user_preferences is irreversible and gated on approval.

Copy link to headingStoring assets in Vercel Blob

Five tools store and manage files in Vercel Blob: upload_asset, list_assets, get_asset_info, download_asset, and delete_asset. They're the agent's file layer: export a finished thread as Markdown, save an image before uploading it to a draft, or keep anything that should be reachable by URL.

Two of the tools are hardened for an agent that runs on model output. download_asset fetches only URLs whose host ends in .blob.vercel-storage.com, which stops the model from using it to reach arbitrary internal addresses. delete_asset is irreversible, so it's gated on human approval:

import { del } from "@vercel/blob";
import { defineTool } from "eve/tools";
import { always } from "eve/tools/approval";
import { z } from "zod";
export default defineTool({
approval: always(),
description:
"Permanently delete an asset from Vercel Blob storage by its URL. Use only when the user " +
"explicitly asks to remove a stored file. This is irreversible.",
async execute({ url }) {
await del(url);
return { deleted: true, success: true, url };
},
inputSchema: z.object({
url: z.url().describe("The full Vercel Blob URL of the asset to delete."),
}),
});

always() from eve/tools/approval displays a card with approve and deny buttons in Slack before the deletion runs, so the model can never delete a file on its own.

Copy link to headingBehavior and model

instructions.md is the agent's always-on system prompt.

eve prepends it to every model call, and it encodes the loop from the section above: load preferences and the right skills, ground work in the real social sets and drafts, draft freely but schedule and delete only on approval, draft long pieces into Notion, and lint plus review before proposing a draft.

The rule worth calling out is the scheduling contract: the agent never sets publish_at unless you explicitly ask to schedule or publish, and the approval gate in the Typefully connection backs that promise up at the tool layer.

The runtime configuration adds session management on top of the model:

import { defineAgent } from "eve";
export default defineAgent({
compaction: { thresholdPercent: 0.75 },
limits: {
maxInputTokensPerSession: 500_000,
maxOutputTokensPerSession: 20_000,
},
model: "anthropic/claude-sonnet-5",
});

Conversation history is compacted once it reaches 75% of the context window, and the per-session token limits cap runaway sessions; raise them if long content work hits the caps. The model id routes through the Vercel AI Gateway, which authenticates through the linked project, so there's no provider API key to set.

To use a different model, change this string (any Gateway model ID works) or run /model in the dev TUI. Each subagent sets its own model in its agent.ts.

Copy link to headingCustomize the agent

The agent picks up changes to these files as you save:

  1. Change the approval gates. Edit the DELETE_TOOLS and PUBLISH_TOOLS lists and the publish_at check in agent/connections/typefully.ts, and the APPROVAL_REQUIRED_TOOLS list in agent/connections/notion.ts, to change which MCP tools pause for a human decision. For example, add notion-create-pages to gate Notion page creation too.
  2. Change the behavior. Edit agent/instructions.md; it describes the whole workflow the agent follows.
  3. Edit or add skills. Each folder under agent/skills/ holds a SKILL.md (give it a description that names when to use it) plus reference files, and eve discovers new skills automatically. The five *-style skills each carry a banned-words.json that the lint_against_style tool checks drafts against, so editing a list changes both the guidance and the enforcement. The reviewer keeps its own copy of writing-quality under agent/subagents/reviewer/skills/, so keep the two in step when you edit either.
  4. Add or change tools. Files under agent/tools/ are tools, and the filename is the tool name.
  5. Tune the analytics digest. Set TYPEFULLY_ANALYTICS_CHANNEL to the Slack channel id it should post to, change the day or time in agent/schedules/weekly-analytics.ts (the cron is evaluated in UTC), and edit agent/tools/post_analytics_report.ts to change the report format.

To add another chat platform, add a channel file under agent/channels/ and a matching Vercel Connect connector. eve discovers the channel from the file the same way it discovers slack.ts, and Vercel Connect brokers the new platform's credentials, so the workflow, skills, tools, and connections stay unchanged.

Copy link to headingPair it with the content agent template

The eve content agent template is a full content assistant: a house voice and style skills for long-form surfaces this agent doesn't cover, like blog posts, release notes, and newsletters. Instead of merging all of that into this agent, you can deploy it as its own agent and let this one delegate to it through eve's remote agents feature.

Deploy the content agent template as its own Vercel project, then add a remote subagent file to this repo. The filename is the tool name, and vercelOidc() handles deployment-to-deployment auth with no shared secret:

import { defineRemoteAgent } from "eve";
import { vercelOidc } from "eve/agents/auth";
export default defineRemoteAgent({
url: () => process.env.CONTENT_AGENT_URL ?? "https://your-content-agent.vercel.app",
description:
"Drafts blog posts, LinkedIn and X posts, release notes, and newsletters in the house voice. " +
"Pass the surface, the source material, and any constraints in the message.",
auth: vercelOidc(),
});

Set CONTENT_AGENT_URL in this project's environment variables and mention the new subagent in agent/instructions.md so the agent knows when to hand off.

The remote agent runs in its own deployment with its own skills and connections, and it never sees this agent's conversation history, so this agent packs everything the writer needs into the call message. The result comes back as a normal tool result, the same shape as the local researcher and reviewer subagents.

Copy link to headingTroubleshooting

Each item below lists a symptom, its cause, and the fix.

Copy link to heading@mentions don't get a response

Symptom: You @mention the bot in Slack, but it doesn't reply.

Cause: The bot isn't in the channel yet, or the deployment that registered the Slack trigger hasn't finished.

Fix: Invite the bot to the channel and confirm the deployment succeeded, then @mention it again. The Deploy button points Slack's events at the route the agent serves (/eve/v1/slack), so the path is already correct.

Copy link to headingTypefully calls fail with a missing key error

Symptom: Any Typefully operation errors with TYPEFULLY_API_KEY is not set.

Cause: The environment variable isn't present where the agent is running. .env.local is local-only; the deployed app reads the Vercel project's environment.

Fix: Set TYPEFULLY_API_KEY in the project's production environment (the Deploy button prompts for it), and run vercel env pull for local development. Find your key in Typefully under Settings > API.

Copy link to headingConnectors work locally but fail in production

Symptom: The agent works in the dev TUI, but the deployed bot can't reach Slack or Notion.

Cause: .env.local is local-only; the deployed app reads the Vercel project's environment.

Fix: Set SLACK_CONNECTOR and NOTION_CONNECTOR in the project's production environment. The Deploy button sets them for you.

Copy link to headingA schedule or delete pauses and nothing happens

Symptom: Scheduling a post, or deleting a draft, comment, or thread, appears to stall.

Cause: The call is gated on approval and is waiting for a decision. In Slack it renders as approve/deny buttons; in the dev TUI it renders as an approval prompt.

Fix: Approve or deny the pending call. To change which tools are gated, edit the approval policy in agent/connections/typefully.ts and the APPROVAL_REQUIRED_TOOLS list in agent/connections/notion.ts.

Copy link to headingThe weekly digest never posts

Symptom: Monday comes and goes with no analytics digest in Slack.

Cause: TYPEFULLY_ANALYTICS_CHANNEL isn't set to a channel id, or you're expecting the schedule to fire in local development, where schedules never run on their cadence.

Fix: Set TYPEFULLY_ANALYTICS_CHANNEL in the project's environment and invite the bot to that channel. In dev, trigger a run manually with curl -X POST http://localhost:3000/eve/v1/dev/schedules/weekly-analytics. Note the cron is evaluated in UTC, so 14:00 UTC may not be the local time you expect.

Copy link to headingRelated resources

eve Software Factory Template

Four coding stations behind one orchestrator: triage, planning against a live checkout of your repo, implementation verified with your own checks, and independent review.

Learn more

Related documentation

More eve guides