Anthropic's 'Days-Long Tasks' Is Just A Cron Job

Abstract tech illustration: Anthropic's 'Days-Long Tasks' Is Just A Cron Job

Anthropic shipped @Claude in Slack and the framing was persistence over days, autonomous agents, a new runtime. It's a jobs table and a cron. I've run the same loop on Telegram for eight months on a $6 VPS, and if you're a solo founder about to buy Slack Enterprise plus Claude Team to get async agents, you're paying platform tax for a four-column database.

What Anthropic actually shipped (and what they didn't)

The @Claude in Slack release is three things: a mention-based webhook, context ingestion from the channel and connected org tools, and a jobs backend that lets Claude keep working on a request across days instead of a single conversation turn. The novelty is the last one, but it does not live in Slack. Slack is the notification surface — the @mention posts to a webhook, the reply is an API call back to the same thread. The persistence lives on Anthropic's servers, and it is a jobs table with a status column.

That distinction matters because the announcement framing bundled the runtime with the surface. Every recap I read treated "days-long tasks" as a Slack capability. It isn't. It's a scheduler plus a state machine plus a callback address. You can build it in an afternoon on any messenger you already use.

The three decoupled pieces:

  • Surface: where humans send requests and receive answers (Slack, Telegram, Discord, email, SMS — interchangeable).
  • State: a database row that survives across model calls and holds intermediate work.
  • Scheduler: a cron that wakes up, finds unfinished jobs, and calls the model to advance them.

Once you see the three pieces separately, the pricing math changes.

The four-column schema that runs the whole thing

Here is the entire persistence layer for my production instance. Postgres, one table:

CREATE TABLE agent_jobs (
  id            uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  status        text NOT NULL CHECK (status IN
                  ('queued','running','waiting_human','done')),
  payload       jsonb NOT NULL,
  callback_ref  text NOT NULL,
  updated_at    timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX agent_jobs_active
  ON agent_jobs (updated_at)
  WHERE status IN ('queued','running');

Four columns do all the work:

  • id — job identifier, returned to the user so they can reference it later.
  • status — the state machine. Only four values.
  • payload — a JSON blob holding the original request, tool call history, partial results, and whatever the agent has learned so far.
  • callback_ref — for me it's a Telegram chat_id:message_id. For Slack's version it's channel:thread_ts. Same idea: where does the answer go.

The state machine transitions are boring, which is the point:

queued  ──> running  ──> done
              │  ↑
              ↓  │
         waiting_human

queued means the request just landed and hasn't been picked up. running means the agent is mid-work — it has more tool calls or reasoning steps to do, but no human input is needed. waiting_human means the agent asked a clarifying question in the callback thread and is idle until the human replies. done is terminal.

That's the entire schema. No queue system, no workflow engine, no agent framework. Just rows and a status column.

The cron loop, end to end

The cron runs every 60 seconds. Here is the loop, stripped to the actual logic:

# runs every minute via systemd timer or crontab
import psycopg, json
from anthropic import Anthropic

client = Anthropic()

def tick():
    with psycopg.connect(DSN) as conn:
        jobs = conn.execute("""
            SELECT id, payload, callback_ref
            FROM agent_jobs
            WHERE status IN ('queued','running')
            ORDER BY updated_at ASC
            LIMIT 5
            FOR UPDATE SKIP LOCKED
        """).fetchall()

        for job_id, payload, cb in jobs:
            result = advance(payload)   # one Claude turn + tools

            if result.needs_human:
                send_message(cb, result.question)
                set_status(conn, job_id, 'waiting_human', result.payload)
            elif result.done:
                send_message(cb, result.final_answer)
                set_status(conn, job_id, 'done', result.payload)
            else:
                set_status(conn, job_id, 'running', result.payload)

FOR UPDATE SKIP LOCKED is the only clever bit — it lets you run multiple cron workers without them stepping on each other. Everything else is a state transition.

advance() is where you call the model with the payload as context, let it do one round of tool use (search, DB lookup, email draft, whatever), and return an updated payload. If Claude signals it's done, mark done. If it needs a human, mark waiting_human and post the question. Otherwise, save progress and let the next tick pick it up.

Inbound side is symmetric. When the human replies in the callback thread:

# webhook handler for incoming messages
def on_message(chat_id, message_id, reply_to, text):
    job = find_job_by_callback(reply_to)
    if job and job.status == 'waiting_human':
        payload = job.payload
        payload['human_replies'].append(text)
        set_status(conn, job.id, 'running', payload)
    else:
        # new request — create a job
        create_job(payload={'request': text, 'human_replies': []},
                   callback_ref=f"{chat_id}:{message_id}")

That is the entire async agent. About 80 lines of Python plus one SQL table.

Real numbers from eight months in production

I've been running this on a Hetzner CX22 VPS ($5.83/month) since January. It talks to a Telegram group with two collaborators, plus Gmail, Google Calendar, and a Postgres database of invoices and leads for a client I do fractional ops work for.

Current stats from the agent_jobs table:

Metric Value
Total jobs completed 2,847
Average job lifetime 34 hours
Median job lifetime 12 minutes
Longest completed job 6 days, 3 hours
Jobs that hit waiting_human at least once 41%
VPS cost $5.83/month
Claude API cost (avg/month) $47
Downtime in 8 months 22 minutes (one Hetzner reboot)

The 6-day job was a lead research task — find and qualify 40 companies in a niche vertical, with two human checkpoints along the way to confirm targeting criteria. Claude did the scraping, enrichment, and drafting in about 90 minutes of actual model time, spread across six calendar days because the human (me) took two full days to respond to the first checkpoint. That is what "days-long task" actually means: the wall clock is dominated by human latency, not model work.

The average of 34 hours vs median of 12 minutes tells the real story. Most jobs finish fast. A minority sit in waiting_human for a day or two and pull the average up. This is the shape of every async agent workload I've seen.

Cost comparison: rolling your own vs the packaged version

Take a two-person agency that wants async agents. Here is the honest math for a US-based team as of late 2026 — check current pricing on each vendor's site, but the ratio is what matters:

Item Packaged (Slack + Claude Team) Self-hosted loop
Messenger Slack Business+ (~$15/user/mo × 2) Telegram / Discord / Signal — free
Agent runtime Claude Team seat (~$30/user/mo × 2) Anthropic API, pay per token
Persistence Included Hetzner CX22 — $5.83/mo
Connectors Included in Team You write them (Gmail API, etc.)
Monthly floor ~$90 before API overage ~$6 + API usage
Time to build Zero 1-2 days

If you're a 500-person company already paying for Slack Enterprise, the packaged version is the right choice — the integration is clean and the seat cost is a rounding error. If you're a solopreneur or small team, you're paying $85/month for a state machine you could build in a weekend and a UI you already have installed on your phone.

The pattern gets more valuable as you add surfaces. My loop posts to Telegram, but the same jobs table drives an email interface (reply-to-thread creates a new payload entry) and a lightweight web dashboard for the client. Adding a surface is a webhook handler and a callback_ref format. Adding a surface to the Slack version means every user in that surface needs a Slack seat.

When the packaged version is actually the right call

I'm not arguing you should never use @Claude in Slack. Three cases where it's the correct choice:

  • You already pay for Slack Enterprise and have 50+ users. The marginal cost is near zero and IT approval is already done. Rolling your own means a new vendor review and a security audit for a $6 VPS.
  • You need audit logging, SSO, and compliance reporting out of the box. Anthropic's Team plan handles this. Building HIPAA-adjacent logging on your own loop is doable but real work.
  • Your team won't adopt a second messenger. Adoption beats architecture. If everyone lives in Slack and refuses to check anything else, meet them there.

Everywhere else — solo founders, two-person agencies, anyone whose team already lives in Telegram, Discord, WhatsApp, or plain email — the packaged version is buying you a database column at $85/month.

Why bizflowai.io helps with this

Most of the client work I do at bizflowai.io is exactly this pattern: an async agent loop wired to whatever surface the team already uses, backed by a jobs table and a scheduler that runs on a small VPS. For a recent lead-gen client the loop lives on Telegram, ingests inbound leads from three form endpoints, drafts qualification questions, waits on human confirmation for edge cases, and pushes cleaned records to their CRM — same four-column schema, different tools bolted onto advance(). The reason it's cheap to operate is that the runtime is 80 lines of code you own, not a per-seat contract you rent.

The takeaway

Persistence is a database column. Autonomy is a cron. The messenger is interchangeable. Once you see the pattern, you stop paying platform tax for it — and you stop waiting for a vendor to ship async agents on your preferred surface, because you can ship them yourself in a weekend.

The four-column table works. The 60-second cron works. The state machine has four values. If you've been holding off on async agents because the pricing didn't make sense for a small team, the pricing was never the blocker. The framing was.


Want more like this?

I publish practical AI automation, GenAI engineering, and faceless content workflows on YouTube every week.

Subscribe to bizflowai.io on YouTube — never miss a new tutorial.

Planning an AI automation project or need a second opinion on your architecture?

Connect with me on LinkedIn — Lazar Milicevic, GenAI Engineer & bizflowai.io Founder.

Visit bizflowai.io for our services, case studies, and AI consulting.

Frequently asked questions

What did Anthropic actually announce with the Claude Slack integration?

Anthropic shipped three things: the ability to @mention Claude inside a Slack thread, Claude pulling context from the channel and connected org tools, and Claude working on tasks that span days rather than single conversation turns. Slack itself only acts as the notification surface via webhooks and API replies. The days-long task capability runs on Anthropic's servers, not inside Slack.

How do I build an async agent loop without Slack?

You need three components: a messenger you already use (Telegram, WhatsApp, Signal, or Discord), a database table with four columns (id, status, payload, callback thread), and a cron job that runs every sixty seconds. The cron pulls queued or running jobs, sends the payload to Claude as context, and Claude either finishes, updates state, or flips status to waiting on human.

What database schema powers a persistent Claude agent?

A single table with four columns is enough. Id identifies the job. Status holds one of four values: queued, running, waiting on human, or done. Payload is a JSON blob containing the original request and intermediate state. Callback thread stores the messenger message id so the bot knows where to post results when the job completes.

How much does a self-hosted async agent cost versus Claude in Slack?

A self-hosted persistence layer runs about six dollars per month on Hetzner. The equivalent Slack setup, including Slack seats, Claude Team, and connectors, starts around fifteen dollars per user per month and scales up quickly as you add teammates to the workspace. For solo founders or small agencies, the self-hosted approach is dramatically cheaper.

When should I use Claude in Slack versus building my own agent loop?

Use the Slack integration if you're a large company, roughly five hundred people or more, already paying for Slack Enterprise. It's beautifully packaged for that context. Build your own async agent loop if you're a solopreneur or small team already living in Telegram, Discord, or another messenger. Persistence is just a database column and autonomy is just a cron job.