Skip to main content
Workflows Library MCP Directory Realtime AI News Sponsor Tier Subscribe

Build a Small-Business Agentic Operations Workflow with HoneyBook & Claude

HoneyBook's new Claude connector turns proposals, contracts, invoices, and client messaging into an API an agent can drive; this LangGraph workflow drafts documents in the owner's voice, tracks money status, auto-follows-up on a cadence, and escalates unpaid invoices to a human before the relationship sours.

Deepak Bagada

Deepak Bagada

CEO, SaaSNext

Aug 20, 2026 Published
|
Aug 20, 2026 Updated
|
9 Minutes Reading Time
Core Takeaways for Founders & Builders
  • The HoneyBook Claude connector collapses proposal, contract, invoice, and messaging surfaces into one connected client record an agent can drive.
  • Chasing discipline is engineered, not hoped for: FOLLOWUP_MAX and ESCALATION_THRESHOLD_DAYS hard-cap automated follow-ups and force a sticky human handoff.
  • The workflow's boundary rule is simple: reversible low-touch actions are automatic; anything that changes money terms or gets pushy stays human.
  • Approvals are concentrated at exactly two points — the drafted-proposal send gate and the overdue-account escalation gate — so autonomy never outruns judgment.

By Deepak Bagada, CEO at SaaSNext & Principal AI Architect.

The average small business does not fail because of bad craft — it fails because of follow-through. A proposal that takes four days to write, a contract that sits unsigned for a week, an invoice that goes unpaid for forty days because nobody chased it. For photographers, agencies, consultants, and freelancers running their studio on HoneyBook, the money is in the pipeline, and the pipeline is made of paper. On August 20, 2026, HoneyBook shipped a Claude connector that turns its proposal, contract, invoice, and client messaging surfaces into a programmatic API an agent can drive. This dispatch builds the agentic operations workflow on top of it: a LangGraph state machine that reads client projects, drafts proposals in the studio owner's voice, tracks invoice status, follows up with clients automatically, and escalates unpaid invoices to a human before the relationship sours. No new dashboard, no new inbox — the owner approves, the agent executes.

Why Small-Business Ops Is the Perfect Agent Problem

Enterprise agentic workflows are impressive but orchestrated. A small business has no orchestrator, no platform team, and no tolerance for a 200-line YAML config that needs a consultant to maintain. What it does have is a repeating loop: win a lead, scope the work, send a proposal, close the contract, deliver, invoice, collect. Every iteration of that loop is the same shape, and every deviation — a price change, a scope change, an overdue invoice — is a state transition. That is exactly what a state machine encodes well, and it is exactly what Claude, as the reasoning layer, can keep humane.

The HoneyBook connector matters because it collapses the integration surface. Instead of maintaining separate webhooks for proposals, contracts, invoices, and messages, the agent gets one connected view of the client record. Projects become typed state, and every document becomes a draft the agent can generate and a status it can track. The workflow below assumes HoneyBook is the system of record for money documents, and Claude is the drafting and decision engine.

Architecture at a Glance

A scheduler wakes the workflow on a cadence, reads HoneyBook projects and invoices, and drives each project through draft, send, follow-up, and escalation states. Human approvals are concentrated at exactly two points: approving a drafted proposal before it goes out, and authorizing an escalation before a client is pinged a fourth time.

+---------------------+     +----------------------+
|  HoneyBook Scheduler|     |  Claude Connector    |
|  (daily cron / hook)| --> |  (drafts + decides)  |
+----------+----------+     +----------+-----------+
           |                          |
           v                          v
+---------------------+     +----------------------+
|  Project Reader     |     |  Proposal Writer     |
|  (clients/invoices) |     |  (voice + pricing)   |
+----------+----------+     +----------+-----------+
           |                          |
           v                          v
+---------------------+     +----------------------+
|  Invoice Tracker    |     |  Follow-Up Agent     |
|  (due/overdue/paid) | --> |  (cadence + tone)    |
+----------+----------+     +----------+-----------+
           |                          |
           |     +--------+           |
           +---->| Human  |<----------+  approval gates
                 | Approve|
                 +---+----+
                     |
                     v
        +--------------------------+
        |  Escalation + Bookkeeping|
        |  (owner notified / paid) |
        +--------------------------+

The two clocks in the system are the scheduler, which drives the daily cadence, and the escalation gate, which enforces the "no chasing more than N times" rule. Everything else is reactive: the graph only acts when a document status changes or a deadline crosses.

Environment Configuration

The connector is configured through HoneyBook's public API plus the Claude connection. Everything is scoped per workspace so the same workflow can serve multiple studios without cross-client leakage.

HONEYBOOK_API_URL=https://honeybook-api.example.com/v1
HONEYBOOK_CLIENT_ID=hb_live_xxxxxxxxxxxxxxxx
HONEYBOOK_CLIENT_SECRET=hb_secret_xxxxxxxxxxxxxxxx
HONEYBOOK_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxx

CLAUDE_API_KEY=sk-ant-xxxxxxxxxxxxxxxx
CLAUDE_MODEL=claude-sonnet-4-5

STUDIO_TZ=Asia/Kolkata
PROPOSAL_AUTO_SEND=false
FOLLOWUP_MAX=3
FOLLOWUP_INTERVAL_DAYS=5
ESCALATION_THRESHOLD_DAYS=7

Two flags define how autonomous the workflow is. PROPOSAL_AUTO_SEND=false means every proposal comes back to the owner for a one-tap approval before it reaches a client — the default for a business where the owner's name is the brand. FOLLOWUP_MAX=3 hard-caps automated follow-ups per invoice, after which the escalation gate takes over.

Domain Schemas

The state model is deliberately small. A small business does not need seventeen entity types; it needs a client, a project, a money document, and a contact log.

from dataclasses import dataclass, field
from datetime import date
from enum import Enum
from typing import Optional


class DocumentKind(str, Enum):
    PROPOSAL = "proposal"
    CONTRACT = "contract"
    INVOICE = "invoice"


class DocumentStatus(str, Enum):
    DRAFT = "draft"
    SENT = "sent"
    VIEWED = "viewed"
    SIGNED = "signed"
    PARTIAL = "partial"
    PAID = "paid"
    OVERDUE = "overdue"


@dataclass
class Client:
    client_id: str
    name: str
    email: str
    phone: Optional[str] = None


@dataclass
class Project:
    project_id: str
    client: Client
    title: str
    value_usd: float
    created_on: date
    documents: list["Document"] = field(default_factory=list)


@dataclass
class Document:
    document_id: str
    kind: DocumentKind
    status: DocumentStatus
    issued_on: Optional[date] = None
    due_on: Optional[date] = None
    amount_usd: Optional[float] = None
    follow_up_count: int = 0


@dataclass
class FollowUp:
    project_id: str
    document_id: str
    message: str
    sent_on: date


@dataclass
class OpsState:
    project: Project
    draft: Optional[str] = None
    proposal_approved: bool = False
    follow_ups: list[FollowUp] = field(default_factory=list)
    escalated: bool = False

The follow_up_count lives on the document, not on the project, so a proposal and an invoice on the same project chase independently. That matters: a signed proposal should not reset the clock on an overdue invoice.

HoneyBook API and Claude Tools

The tool layer has two halves. The first is a typed client for HoneyBook's API and the Claude connector endpoints that HoneyBook exposes. The second is the drafting engine, which converts a project record into a proposal, contract, or follow-up message in the owner's voice.

import os
import requests
from schemas import Client, Document, DocumentStatus, OpsState, Project


class HoneyBookClient:
    def __init__(self):
        self.base = os.environ["HONEYBOOK_API_URL"]
        self.session = requests.Session()
        self.session.headers.update(
            {"Authorization": f"Bearer {_token()}"}
        )

    def list_projects(self, status: str = "active") -> list[Project]:
        resp = self.session.get(f"{self.base}/projects", params={"status": status})
        resp.raise_for_status()
        return [Project(**item) for item in resp.json()["projects"]]

    def get_document(self, project_id: str, doc_id: str) -> Document:
        resp = self.session.get(f"{self.base}/projects/{project_id}/documents/{doc_id}")
        resp.raise_for_status()
        return Document(**resp.json())

    def create_document(self, project_id: str, kind: str, title: str, body: str) -> str:
        resp = self.session.post(
            f"{self.base}/projects/{project_id}/documents",
            json={"kind": kind, "title": title, "body": body},
        )
        resp.raise_for_status()
        return resp.json()["document_id"]

    def send_message(self, project_id: str, message: str) -> None:
        self.session.post(
            f"{self.base}/projects/{project_id}/messages",
            json={"body": message},
        )


class ClaudeConnector:
    def __init__(self):
        self.api_key = os.environ["CLAUDE_API_KEY"]
        self.model = os.environ["CLAUDE_MODEL"]

    def draft_proposal(self, project: Project, owner_notes: str) -> str:
        prompt = _build_proposal_prompt(project, owner_notes)
        return _claude_complete(self.api_key, self.model, prompt)

    def draft_followup(self, project: Project, doc: Document, attempt: int) -> str:
        tone = "polite nudge" if attempt <= 2 else "gentle final reminder"
        prompt = _build_followup_prompt(project, doc, tone)
        return _claude_complete(self.api_key, self.model, prompt)

The Claude connector from HoneyBook matters here because it keeps the drafting inside the business context: the proposal knows the client's past projects, the owner's service catalog, and the pricing history, so the draft reads like the owner wrote it — not like a template with a name slot.

The LangGraph State Machine

graph.py encodes the loop as a compact state machine. Drafting, tracking, following up, and escalating are separate nodes so each can be tested and rate-limited independently.

import os
from datetime import date, timedelta
from langgraph.graph import END, START, StateGraph
from schemas import DocumentKind, DocumentStatus, OpsState, Project
from tools import ClaudeConnector, HoneyBookClient


def refresh_state(state: OpsState) -> OpsState:
    hb = HoneyBookClient()
    state["project"] = hb.get_document(state["project"].project_id, "")
    return state


def draft_proposal(state: OpsState) -> OpsState:
    claude = ClaudeConnector()
    state["draft"] = claude.draft_proposal(state["project"], owner_notes="")
    return state


def proposal_gate(state: OpsState) -> OpsState:
    if os.environ["PROPOSAL_AUTO_SEND"] == "true":
        state["proposal_approved"] = True
    return state  # otherwise interrupted for human approval


def send_proposal(state: OpsState) -> OpsState:
    hb = HoneyBookClient()
    hb.create_document(
        state["project"].project_id,
        DocumentKind.PROPOSAL.value,
        f"Proposal - {state['project'].title}",
        state["draft"],
    )
    return state


def track_invoices(state: OpsState) -> OpsState:
    hb = HoneyBookClient()
    invoices = [
        d
        for d in state["project"].documents
        if d.kind == DocumentKind.INVOICE and d.status == DocumentStatus.SENT
    ]
    for inv in invoices:
        inv.status = hb.get_document(state["project"].project_id, inv.document_id).status
    state["pending"] = [d for d in invoices if d.status in (DocumentStatus.SENT, DocumentStatus.PARTIAL)]
    return state


def follow_up(state: OpsState) -> OpsState:
    claude = ClaudeConnector()
    hb = HoneyBookClient()
    for inv in state["pending"]:
        if inv.follow_up_count >= int(os.environ["FOLLOWUP_MAX"]):
            continue
        if inv.last_nudge and inv.last_nudge >= date.today() - timedelta(days=int(os.environ["FOLLOWUP_INTERVAL_DAYS"])):
            continue
        msg = claude.draft_followup(state["project"], inv, inv.follow_up_count + 1)
        hb.send_message(state["project"].project_id, msg)
        inv.follow_up_count += 1
        inv.last_nudge = date.today()
    return state


def escalation_gate(state: OpsState) -> OpsState:
    overdue = [
        inv
        for inv in state["pending"]
        if inv.follow_up_count >= int(os.environ["FOLLOWUP_MAX"])
        and (date.today() - inv.due_on).days >= int(os.environ["ESCALATION_THRESHOLD_DAYS"])
    ]
    if overdue:
        state["escalated"] = True
        # Interrupt: owner reviews the account before any further action.
    return state


builder = StateGraph(OpsState)
builder.add_node("refresh_state", refresh_state)
builder.add_node("draft_proposal", draft_proposal)
builder.add_node("proposal_gate", proposal_gate)
builder.add_node("send_proposal", send_proposal)
builder.add_node("track_invoices", track_invoices)
builder.add_node("follow_up", follow_up)
builder.add_node("escalation_gate", escalation_gate)

builder.add_edge(START, "refresh_state")
builder.add_edge("refresh_state", "draft_proposal")
builder.add_edge("draft_proposal", "proposal_gate")
builder.add_edge("proposal_gate", "send_proposal")
builder.add_edge("send_proposal", "track_invoices")
builder.add_edge("track_invoices", "follow_up")
builder.add_edge("follow_up", "escalation_gate")
builder.add_edge("escalation_gate", END)

graph = builder.compile(interrupt_before=["proposal_gate"])

The escalation gate is intentionally blunt: after FOLLOWUP_MAX automated messages and ESCALATION_THRESHOLD_DAYS past due, the workflow stops being polite and hands the account to the owner with the full message log. An agent that chases a client eight times in a week destroys the relationship it is trying to preserve; the gate exists to make that impossible.

Running the Workflow

main.py wires the graph to the daily scheduler and to the approval channels. In production this runs as a cron-style job inside the same process as the webhook handler, so document status changes trigger immediate state refreshes.

import os
from datetime import time
from apscheduler.schedulers.blocking import BlockingScheduler
from graph import graph, OpsState


def daily_ops_run():
    hb = HoneyBookClient()
    for project in hb.list_projects(status="active"):
        state = OpsState(project=project)
        config = {"configurable": {"thread_id": project.project_id}}
        result = graph.invoke(state, config)
        if "draft" in result and not result["proposal_approved"]:
            # Owner approves from the CMS: one tap, nothing else to configure.
            graph.invoke(
                config,
                input={"proposal_approved": await_owner_approval(project.project_id)},
            )
        if result.get("escalated"):
            notify_owner_urgent(project.project_id)


scheduler = BlockingScheduler(timezone=os.environ["STUDIO_TZ"])
scheduler.add_job(
    daily_ops_run,
    trigger="cron",
    hour=9,
    minute=0,
    timezone=os.environ["STUDIO_TZ"],
)
scheduler.start()

Retry Rules

Money documents fail differently from logs, so the retry policy is asymmetric.

  • Bounded retries. HoneyBook API and Claude connector calls retry at most 3 times on transient errors. Drafting calls are retried once with a relaxed temperature because a mid-draft network error can produce a truncated proposal; document creation is never blindly retried without a GET to confirm whether the previous call actually created the document.
  • Exponential backoff. Retries wait base * (2 ** attempt) seconds with jitter, capped at 60 seconds. This keeps the workflow polite to HoneyBook's rate limits while still recovering from a hiccup in the scheduler.
  • Re-queue, never re-send. If a follow-up message is sent but the acknowledgement fails, the graph re-queues the message for a later batch instead of re-sending immediately. Duplicate follow-ups to a client are the fastest way to look like a spammer; the queue is the backstop against that.
  • Escalation is not retried. Once an account escalates, the workflow refuses to auto-follow-up again. The escalation state is sticky — only the owner can release it, and releasing it resets follow_up_count so the client is not chased twice in the same cycle.

What the Agent Handles vs. What Stays Human

The whole value proposition of small-business ops agents is deciding where the boundary sits. This matrix is the decision rule the workflow encodes.

Operation Agent handles Human approves
Drafting a proposal in owner voice Yes Yes, before send
Sending a proposal Yes Config flag
Contract status tracking Yes No
Invoice status tracking Yes No
Follow-up messages (cadence + tone) Yes No
Late-fee or discount offers No Yes
Escalating an overdue account Stops and hands off Yes, fully
Refund or credit decisions No Yes

The rule of thumb: anything reversible and low-touch is automatic; anything that changes money terms or touches the client relationship beyond a polite nudge stays human. That single rule is why this workflow scales from a two-person studio to a fifty-person agency without a single angry client email. If you are deciding which agent workflows to standardize next, the workflows library has the adjacent blueprints.

Frequently Asked Questions

Do I need to build HoneyBook integrations myself?

No. The Claude connector is provided by HoneyBook, and the workflow sits on top of it. Your code only defines the state machine and the drafting prompts; HoneyBook handles document rendering, e-signature collection, and client messaging delivery.

How does the agent match the owner's voice in proposals?

The drafting prompt is seeded with the project record plus a short style block — the owner's pricing history, service phrasing, and a few example sentences they provide once during setup. Claude reuses that voice across drafts, and the owner's one-tap approval before send is the quality gate.

What stops the agent from chasing a client too aggressively?

Two hard caps: FOLLOWUP_MAX limits automated follow-ups per document, and ESCALATION_THRESHOLD_DAYS converts an overdue account into a human handoff. The escalation gate node is a hard stop in the graph — after it fires, only the owner can resume contact.

Can the same workflow manage multiple HoneyBook workspaces?

Yes. Every client and document carries a workspace_id, and the scheduler runs one graph instance per workspace with separate credentials. The state model never mixes projects across workspaces because each thread is scoped to a single project id.

What happens if the Claude connector is down at draft time?

The draft node retries with bounded backoff, and if it still fails, the graph leaves the document in draft and flags the project for the next daily run. No partial proposal is ever sent, and no invoice is ever chased on stale data — the tracker re-reads HoneyBook status before every follow-up.

New platform connectors ship weekly; keep up with the AI news desk and explore the MCP directory when you want to extend these agents with more tools.

Executive Briefing

Enjoyed this breakdown? Get our morning dispatch in your inbox.

Curated breakdowns of frontier model architectures and compute markets delivered every weekday. Zero fluff.

Frequently Asked Questions
No. The Claude connector is provided by HoneyBook; your code only defines the LangGraph state machine and the drafting prompts. HoneyBook handles document rendering, e-signature collection, and client messaging delivery.
The drafting prompt is seeded with the project record plus a short style block: the owner's pricing history, service phrasing, and a few example sentences captured once during setup. The one-tap approval before send is the quality gate.
Two hard caps: FOLLOWUP_MAX limits automated follow-ups per document, and ESCALATION_THRESHOLD_DAYS converts an overdue account into a human handoff. The escalation node is a hard stop in the graph — only the owner can release it.
Yes. Every client and document carries a workspace id, the scheduler runs one graph instance per workspace with separate credentials, and each thread is scoped to a single project id so state never leaks across workspaces.
The draft node retries with bounded backoff; if it still fails, the document stays in draft and the project is flagged for the next daily run. No partial proposal is ever sent, and the tracker re-reads HoneyBook status before every follow-up.
Deepak Bagada
Author Profile

Deepak Bagada

CEO, SaaSNext

Deepak Bagada is the CEO of SaaSNext and founder of Daily AI World. He covers AI workflows, agentic automation, LLM architectures, and founder growth strategies.

Related Intelligence Analysis

Research Breakdown AI Workflows

The Step-by-Step Guide to Automating Meeting Tasks with Whisper

You're spending 45 minutes after every client meeting typing up notes and manually assigning tasks in Jira. This guide shows you how to wire OpenAI Whisper and Claude to automatically convert meeting recordings into assi...

Deepak Bagada Deepak Bagada
9m read
Research Breakdown AI Workflows

Lovable AI UI-to-Code Pipeline: 2026 Tutorial

Lovable AI UI-to-code automation pipeline uses Lovable AI on Lovable Cloud to convert visual UI designs and natural language specs into production-grade web applications. UI/UX designers and frontend developers bridging...

Deepak Bagada Deepak Bagada
8m read
Breaking AI Workflows

Claude Code's New Browser: 5 Workflows That Save Hours Daily

Claude Code's built-in browser is a sandboxed tabbed browser inside the Claude Code desktop app (Week 28, July 2026) accessible via Cmd+Shift+B (macOS) or Ctrl+Shift+B (Windows). It lets Claude open websites, read docume...

Deepak Bagada Deepak Bagada
12m read
Audio Briefing
Accessibility Preferences
High Contrast Mode
Accessible Reading Font

Keyboard Shortcuts

Open Search Dialog ⌘K or /
Toggle Theme (Dark/Light) t
Toggle Audio Player a
Open Shortcuts Menu ?
Close Active Dialog Esc