Back to Knowledge Base Maintenance

Developer Handover Pack

Everything needed to build the automation from scratch: current state, full agent specs, data flow, and the build stack.

4 pagesPDF · Technical
FS-DOC-04Technical

Developer Handover Pack

Knowledge Base Maintenance

[YourCompany.com] · Customer Support Department · Prepared by FullSpec · [Today's Date]

This document is the primary technical reference for the FullSpec team building the Knowledge Base Maintenance automation. It covers the current-state process map, all three agent specifications with IO contracts, the end-to-end data flow, and the recommended build stack. The owner's team supplies API credentials and approval on draft quality; FullSpec handles all build, wiring, and testing. Nothing in this document requires action from the business owner unless a credential or access step is explicitly flagged.

01Process snapshot

Step
Name
Description
1 BOTTLENECK
Pull Ticket Data for Common Topics
Support Manager exports recent tickets from Zendesk and manually reads through them to spot recurring themes. No automation flags clusters. (40 min/cycle)
2
Cross-Reference Tickets Against Existing Articles
Support Agent searches the knowledge base to check whether an article exists for each recurring topic and notes gaps or outdated content. (30 min/cycle)
3
Create Article Review List in Notion
Notion tracker is updated manually with flagged articles, last-edited dates, and ticket volume behind each flag. (20 min/cycle)
4
Assign Articles to Reviewers
Manager manually assigns each flagged article to an agent or writer via Slack, with no structured handoff or deadline enforcement. (15 min/cycle)
5
Draft Article Update in Google Docs
Assigned reviewer reads the existing article, checks current product behaviour, and writes a revised draft in a shared Google Doc. (45 min/cycle)
6
Notify Manager That Draft Is Ready
Reviewer sends an ad-hoc Slack message to the manager to flag the draft is ready, with no automated routing or tracking. (5 min/cycle)
7 BOTTLENECK
Manager Reviews and Approves Draft
Manager reads the draft, leaves comments in Google Docs, and either approves or returns for a second revision. Drafts can queue here indefinitely. (20 min/cycle)
8
Publish Updated Article to Help Centre
Once approved, the reviewer manually copies or edits article content inside Intercom and publishes the update. (15 min/cycle)
9
Update Notion Tracker as Complete
Reviewer manually marks the article as updated in the Notion tracker and records the publish date. (5 min/cycle)
10
Monitor Post-Publish Ticket Volume
One to two weeks later, the manager manually checks whether tickets on the same topic have reduced. No automated feedback loop exists. (20 min/cycle)
Time cost summary: Total manual time per article cycle is 215 minutes (approximately 3.6 hours). At a weekly volume that produces 5 hours of KB maintenance labour, this process consumes roughly $130/week in staff cost at the assumed $26/hour rate. Steps 1, 2, 3, 4, 5, 6, 8, and 9 are replaced by automation. Steps 7 (manager approval and optional edit) and 10 (post-publish monitoring) remain with the human. Step 10 is out of scope for this build but is flagged as a future feedback-loop enhancement.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Ticket Trend Analyst

This agent runs on a daily scheduled poll against the Zendesk API. It retrieves tickets created or updated in the past seven days, applies a topic-clustering model to group tickets sharing common keywords or phrases, and scores each cluster against the existing Intercom knowledge base article inventory. A staleness and accuracy score is calculated per article based on cluster size, article age, and last-edited date. When a cluster meets or exceeds the configured threshold (default: five tickets on the same topic within seven days), the agent outputs a ranked list of articles requiring review. This agent replaces the most time-consuming bottleneck in the current process: the manual weekly ticket export and cross-reference task.

Trigger
Daily scheduled poll (configurable cron, default 07:00 UTC). Fires downstream workflow only when at least one cluster meets the threshold.
Tools
Zendesk (ticket read), Intercom (article list read)
Replaces steps
Steps 1 and 2
Estimated build
8 hours / Moderate
// Input
zendesk.tickets[]  ->  { id, subject, description, tags[], created_at, updated_at }
intercom.articles[]  ->  { article_id, title, body, updated_at, url }

// Processing
cluster_model(tickets)  ->  topic_clusters[]  { topic_label, ticket_ids[], ticket_count }
score_articles(topic_clusters, articles)  ->  flagged_articles[]  { article_id, title, url, staleness_score, cluster_size, trigger_ticket_ids[] }

// Output
flagged_articles[]  ->  {
  article_id:         string,
  article_title:      string,
  article_url:        string,
  staleness_score:    float (0.0 to 1.0),
  cluster_size:       integer,
  cluster_topic:      string,
  trigger_ticket_ids: string[],
  ticket_summaries:   string[]
}
  • Zendesk plan must be Suite Team or above to expose the Search API and Ticket List endpoint with date filtering. Confirm plan tier before build.
  • Intercom requires a workspace-level API key with Articles:Read scope. The agent must not have write access at this stage.
  • Topic clustering: use a keyword-frequency approach as the baseline. If the team's ticket text is too terse for keyword clustering, fall back to embedding-based similarity grouping. Confirm average ticket description length with the support manager before committing to approach.
  • Threshold (five tickets, seven-day window) is a configurable parameter stored in the workflow environment variables, not hardcoded. Label it CLUSTER_THRESHOLD and CLUSTER_WINDOW_DAYS.
  • Dedupe: if the same article was flagged and processed in the past 14 days, suppress the output for that article unless staleness_score exceeds 0.85. Store last-flagged timestamps in the orchestration platform's key-value store or a lightweight Notion lookup table.
  • On zero clusters meeting the threshold, the workflow terminates silently. No downstream agents are triggered. Log the run result to the audit table.
  • Confirm with the owner whether Zendesk is the sole ticket source or whether Intercom Conversations are also in scope. The agent spec currently covers Zendesk only. Intercom Conversations require a separate API call and merge logic.
Article Draft Writer

This agent receives the flagged article list from the Ticket Trend Analyst and processes each article in sequence. For each article, it retrieves the full current body from Intercom, reads the associated ticket summaries, and instructs the language model to produce a structured revision that preserves the existing article format while correcting or extending sections that are contradicted or not covered by the ticket evidence. The draft is written directly into a new Google Doc in the configured shared drive folder. Tracked changes are simulated by wrapping new or amended passages in a consistent highlight or comment block so reviewers can see exactly what changed. Once the Doc is created, the agent creates a corresponding row in the Notion review tracker and the workflow signals the next step.

Trigger
Upstream output from Ticket Trend Analyst: one execution per flagged article in the flagged_articles[] array.
Tools
Intercom (article body read), Google Docs (Doc create, content write), Notion (tracker row create)
Replaces steps
Steps 3, 4, 5, and 6
Estimated build
8 hours / Moderate
// Input
flagged_article  ->  {
  article_id:         string,
  article_title:      string,
  article_url:        string,
  staleness_score:    float,
  cluster_size:       integer,
  cluster_topic:      string,
  trigger_ticket_ids: string[],
  ticket_summaries:   string[]
}

intercom.article.body  ->  string (HTML or markdown, depending on Intercom plan)

// Processing
llm_prompt(article_body, ticket_summaries)  ->  revised_draft_markdown
google_docs.create(title, revised_draft_markdown)  ->  { doc_id, doc_url }
notion.pages.create(tracker_db_id, row_payload)  ->  { notion_page_id }

// Output
draft_record  ->  {
  article_id:       string,
  article_title:    string,
  doc_id:           string,
  doc_url:          string,
  notion_page_id:   string,
  assigned_reviewer: string (email, from reviewer_map config),
  created_at:       ISO8601 timestamp
}
  • Google Docs API requires OAuth2 with scopes: https://www.googleapis.com/auth/drive.file and https://www.googleapis.com/auth/documents. Use a service account tied to the team's shared drive folder. Confirm the shared drive folder ID before build and store it as GDOCS_FOLDER_ID in the credential store.
  • Intercom article body may be returned as HTML (older workspaces) or as a JSON block structure (newer workspaces). The agent must detect the format and normalise to plain markdown before passing to the language model. Test this against a live article before building the prompt.
  • Notion database ID for the review tracker must be confirmed and stored as NOTION_KB_TRACKER_DB_ID. Required properties on the tracker row: Article Title (title), Google Doc URL (url), Ticket Count (number), Staleness Score (number), Assigned Reviewer (person or email), Status (select: Pending Review), Created Date (date), Article ID (rich text).
  • Reviewer assignment: build a static reviewer_map in environment config mapping cluster topics or article categories to reviewer email addresses. Default to the support manager email (stored as DEFAULT_REVIEWER_EMAIL) if no mapping matches.
  • Draft quality is directly dependent on ticket description richness. Run five sample articles through the agent before go-live and have the support manager score output quality. If average scores are below acceptable, adjust the prompt to be more conservative and flag for human rewrite rather than attempting a full AI draft.
  • The Notion Slack alert (replacing step 4 and 6) is triggered by the completion of this agent, not by a separate agent. Post a formatted Slack message to the channel stored as SLACK_REVIEW_CHANNEL_ID with: article title, doc link, notion link, cluster size, and assigned reviewer mention.
Publish and Audit Agent

This agent listens for an approval signal on each Google Doc draft. Approval is detected by polling the Google Docs comments API for a comment containing a configured approval keyword (default: 'APPROVED') left by an authorised reviewer, or by a status change on the linked Notion tracker row to 'Approved'. When approval is confirmed, the agent retrieves the final Doc body, transforms it to the format required by the Intercom Articles API, and PATCHes the existing article with the updated content. It does not create new articles and does not delete articles. After a successful publish, it updates the Notion tracker row to 'Published', records the published_at timestamp, and posts a Slack confirmation to the support channel. All publish events are written to an audit log.

Trigger
Polling check on Google Docs comments API and/or Notion status field. Poll interval: every 30 minutes during business hours (08:00 to 18:00 local time, configurable). Configurable via POLL_INTERVAL_MINUTES and BUSINESS_HOURS_ONLY flag.
Tools
Google Docs (comments read, body read), Intercom (article PATCH), Notion (row update), Slack (message post)
Replaces steps
Steps 8 and 9
Estimated build
6 hours / Moderate
// Input
draft_record  ->  {
  article_id:      string,
  doc_id:          string,
  notion_page_id:  string,
  assigned_reviewer: string
}

google_docs.comments(doc_id)  ->  comments[]  { author, content, created_at }
notion.page.status(notion_page_id)  ->  string

// On approval
google_docs.body(doc_id)  ->  final_draft_markdown
transform(final_draft_markdown)  ->  intercom_article_body (HTML)
intercom.articles.patch(article_id, { body: intercom_article_body, updated_at })  ->  { status: 200 }

// Output
audit_record  ->  {
  article_id:      string,
  article_title:   string,
  published_at:    ISO8601 timestamp,
  approved_by:     string (reviewer email),
  doc_url:         string,
  notion_page_id:  string,
  publish_status:  string (success | failed)
}

notion.page.update(notion_page_id, { status: 'Published', published_at })  ->  acknowledged
slack.message.post(SLACK_REVIEW_CHANNEL_ID, confirmation_payload)  ->  acknowledged
  • Intercom Articles PATCH requires the articles:write scope on the API key. Confirm this is scoped to update existing articles only. The agent must never call the POST /articles endpoint (create) or DELETE /articles endpoint. Validate this restriction by testing with a non-production article in the Intercom sandbox workspace before live build.
  • Approval keyword is configurable via APPROVAL_KEYWORD env variable (default: 'APPROVED'). It must appear as the full content of a comment, case-insensitive. Partial matches must be rejected to avoid accidental triggers.
  • Authorised reviewers: build an AUTHORISED_REVIEWERS list (email array) in config. Comments from addresses not in this list must not trigger publish, even if they contain the approval keyword.
  • If the Notion status-based approval path is used in parallel, implement a mutex: whichever signal arrives first sets a processing_lock flag on the draft_record to prevent double publish.
  • Markdown to Intercom HTML transform: Intercom accepts a subset of HTML. Test the transform against articles containing tables, ordered lists, code blocks, and images before go-live. Intercom does not support all markdown extensions.
  • On publish failure (non-200 response from Intercom), the agent must: log the failure to the audit table, update the Notion row status to 'Publish Failed', and post an error alert to SLACK_ERROR_CHANNEL_ID. Do not retry automatically more than twice.
  • Audit log table (Supabase or equivalent) must record: article_id, article_title, approved_by, published_at, publish_status, doc_url, notion_page_id. This table is append-only.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full trigger-to-output data flow with field names at each agent handoff
// ═══════════════════════════════════════════════════════════════════
// TRIGGER: Daily scheduled poll
// ═══════════════════════════════════════════════════════════════════
cron(CRON_SCHEDULE)  ->  workflow.start()

// ── AGENT 1: Ticket Trend Analyst ─────────────────���─────────────────
// Step 1: Fetch tickets from Zendesk (rolling 7-day window)
zendesk.GET /api/v2/search.json
  params: { query: 'created>{{date_7d_ago}} type:ticket', per_page: 100 }
  ->  tickets[]  { id, subject, description, tags[], created_at, updated_at }

// Step 2: Fetch article inventory from Intercom
intercom.GET /articles
  params: { per_page: 50 }
  ->  articles[]  { id, title, body, updated_at, url }

// Step 3: Cluster tickets by topic
cluster_model(tickets[].description, tickets[].subject)
  ->  topic_clusters[]  {
        topic_label:  string,
        ticket_ids[]: string[],
        ticket_count: integer
      }

// Step 4: Filter clusters below threshold
topic_clusters.filter(c => c.ticket_count >= CLUSTER_THRESHOLD)
  ->  qualifying_clusters[]

// Step 5: Score articles against qualifying clusters
score_articles(qualifying_clusters, articles[])
  ->  flagged_articles[]  {
        article_id:         string,
        article_title:      string,
        article_url:        string,
        staleness_score:    float,
        cluster_size:       integer,
        cluster_topic:      string,
        trigger_ticket_ids: string[],
        ticket_summaries:   string[]
      }

// Step 6: Dedupe suppression check
flagged_articles.filter(a => last_flagged(a.article_id) > 14d
                           || a.staleness_score > 0.85)
  ->  articles_to_process[]

// If articles_to_process is empty: log run, terminate workflow.

// ── HANDOFF 1 → AGENT 2: Article Draft Writer ────────────────────
// articles_to_process[] passed as input; one execution per article

// Step 7: Retrieve full article body from Intercom
intercom.GET /articles/{{article_id}}
  ->  { id, title, body: string (HTML or markdown) }

// Step 8: Normalise article body to markdown
normalise(body)  ->  article_markdown: string

// Step 9: Generate draft via language model
llm.complete(
  system: KB_DRAFT_SYSTEM_PROMPT,
  user: { article_markdown, ticket_summaries[] }
)
  ->  revised_draft_markdown: string

// Step 10: Create Google Doc in shared drive folder
google_docs.POST /documents
  body: { title: 'DRAFT: {{article_title}} - {{date}}' }
  ->  { documentId: doc_id, document_url: doc_url }

google_docs.POST /documents/{{doc_id}}/batchUpdate
  body: { insertText: revised_draft_markdown }
  ->  acknowledged

// Step 11: Create Notion tracker row
notion.POST /pages
  body: {
    parent: { database_id: NOTION_KB_TRACKER_DB_ID },
    properties: {
      'Article Title':    { title: article_title },
      'Google Doc URL':   { url: doc_url },
      'Ticket Count':     { number: cluster_size },
      'Staleness Score':  { number: staleness_score },
      'Assigned Reviewer':{ email: reviewer_email },
      'Status':           { select: 'Pending Review' },
      'Created Date':     { date: now() },
      'Article ID':       { rich_text: article_id }
    }
  }
  ->  { id: notion_page_id }

// Step 12: Post Slack review alert
slack.POST /chat.postMessage
  body: {
    channel: SLACK_REVIEW_CHANNEL_ID,
    text: 'New KB draft ready for review: {{article_title}}',
    blocks: [ article_title, doc_url, notion_url, cluster_size, reviewer_mention ]
  }
  ->  acknowledged

// draft_record emitted:  { article_id, doc_id, doc_url, notion_page_id, assigned_reviewer }

// ── HANDOFF 2 → AGENT 3: Publish and Audit Agent ─────────────────
// Polling loop begins on draft_record; interval = POLL_INTERVAL_MINUTES

// Step 13: Poll Google Docs comments for approval keyword
google_docs.GET /documents/{{doc_id}}/comments
  ->  comments[]  { author_email, content, created_at }

comments.find(c =>
  AUTHORISED_REVIEWERS.includes(c.author_email) &&
  c.content.toLowerCase() === APPROVAL_KEYWORD.toLowerCase()
)
  ->  approval_comment | null

// Parallel: poll Notion status as secondary approval signal
notion.GET /pages/{{notion_page_id}}
  ->  { properties.Status.select.name: status_value }

// If neither signal found: wait POLL_INTERVAL_MINUTES, repeat.
// If approval found: set processing_lock = true, proceed.

// Step 14: Retrieve final approved Doc body
google_docs.GET /documents/{{doc_id}}
  ->  final_draft_markdown: string

// Step 15: Transform to Intercom HTML
transform_md_to_html(final_draft_markdown)
  ->  intercom_article_body: string (HTML subset)

// Step 16: Patch Intercom article
intercom.PUT /articles/{{article_id}}
  body: { body: intercom_article_body, updated_at: now() }
  ->  { status: 200 } | { status: 4xx/5xx }

// Step 17: Update Notion tracker
notion.PATCH /pages/{{notion_page_id}}
  body: {
    properties: {
      'Status':       { select: 'Published' },
      'Published At': { date: now() }
    }
  }
  ->  acknowledged

// Step 18: Post Slack confirmation
slack.POST /chat.postMessage
  body: {
    channel: SLACK_REVIEW_CHANNEL_ID,
    text: 'Article published: {{article_title}}',
    blocks: [ article_title, article_url, published_at, approved_by ]
  }
  ->  acknowledged

// Step 19: Write audit log record
audit_table.insert({
  article_id, article_title, approved_by,
  published_at, publish_status: 'success',
  doc_url, notion_page_id
})

// ── ON PUBLISH FAILURE ────────────────────────────────────────────
// intercom.PUT returns non-200:
audit_table.insert({ ..., publish_status: 'failed', error_detail })
notion.PATCH(notion_page_id, { status: 'Publish Failed' })
slack.POST(SLACK_ERROR_CHANNEL_ID, { text: 'Publish failed: {{article_title}}', error_detail })
// Retry up to 2 times with 5-minute back-off. Do not retry beyond attempt 2.

// ═══════════════════════════════════════════════════════════════════
// END OF FLOW
// ═══════════════════════════════════════════════════════════════════
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation platform with one workflow per agent (three workflows total): 'KB-01-TicketTrendAnalyst', 'KB-02-ArticleDraftWriter', 'KB-03-PublishAuditAgent'. All credentials stored in a shared credential store within the platform, referenced by name rather than hardcoded. Workflows are version-controlled and documented with inline comments at each node.
Webhook configuration
No inbound webhooks are required for the Ticket Trend Analyst (scheduled trigger only). The Publish and Audit Agent uses polling rather than webhooks due to Google Docs lacking a native push event for comments. If Notion webhook support is available on the owner's plan, an outbound webhook from Notion on status change can replace the Notion polling leg. Confirm Notion plan tier: webhooks require the Notion API beta or a third-party bridge.
Templating approach
All LLM prompts stored as versioned templates in the orchestration platform's environment or a dedicated prompt config file. System prompt (KB_DRAFT_SYSTEM_PROMPT) and user prompt structure separated. Prompt variables injected at runtime: {{article_markdown}}, {{ticket_summaries}}, {{article_title}}, {{cluster_topic}}. Do not embed prompt text directly in workflow nodes.
Error logging
All agent run results (success and failure) written to an append-only audit table (Supabase recommended, or equivalent Postgres-compatible store). Table schema: run_id (uuid), agent_name (text), article_id (text), article_title (text), status (text), error_detail (text), created_at (timestamptz). On any failure, an alert is posted to SLACK_ERROR_CHANNEL_ID within the same workflow execution. No silent failures.
Testing approach
All agents built and tested against sandbox environments first: Intercom sandbox workspace, a test Google Drive folder (GDOCS_TEST_FOLDER_ID), a test Notion database (NOTION_TEST_DB_ID), and a staging Slack channel (SLACK_TEST_CHANNEL_ID). Historical Zendesk ticket data (minimum three months) used to calibrate clustering. Promote to production only after the full end-to-end flow completes successfully three times in staging with zero manual intervention required.
Credential store entries required
ZENDESK_SUBDOMAIN, ZENDESK_API_TOKEN, ZENDESK_EMAIL; INTERCOM_API_KEY; GOOGLE_SERVICE_ACCOUNT_JSON, GDOCS_FOLDER_ID; NOTION_API_KEY, NOTION_KB_TRACKER_DB_ID; SLACK_BOT_TOKEN, SLACK_REVIEW_CHANNEL_ID, SLACK_ERROR_CHANNEL_ID; CLUSTER_THRESHOLD (default: 5), CLUSTER_WINDOW_DAYS (default: 7), APPROVAL_KEYWORD (default: APPROVED), AUTHORISED_REVIEWERS (JSON array of emails), DEFAULT_REVIEWER_EMAIL, POLL_INTERVAL_MINUTES (default: 30), BUSINESS_HOURS_ONLY (default: true)
Estimated total build time
Ticket Trend Analyst: 8 hours. Article Draft Writer: 8 hours. Publish and Audit Agent: 6 hours. End-to-end integration testing and staging validation: 6 hours (included in the 22-hour build effort confirmed in the process template). Total: 28 hours across four delivery weeks, inclusive of discovery, credential setup, QA, and handoff. Note: the template records 22 hours of build effort; the additional 6 hours cover end-to-end testing and go-live validation as a separate phase.
Before any build work begins, the FullSpec team requires: (1) Zendesk API token with ticket read access confirmed, (2) Intercom API key with Articles:Read and Articles:Write scopes scoped and tested against the sandbox workspace, (3) Google service account created and granted editor access to the shared KB draft folder, (4) Notion integration token with read and write access to the KB tracker database, and (5) Slack bot token with chat:write scope installed to the workspace. Contact support@gofullspec.com if any access cannot be granted within the expected scope.
PII note: ticket descriptions passed to the language model may contain customer names, email addresses, or account details. Before build, confirm with the owner whether ticket text must be anonymised before LLM submission. If anonymisation is required, a pre-processing step must be added to Agent 1 before the cluster_model call. This is a build decision that must be resolved before the Ticket Trend Analyst goes into production.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Knowledge Base Maintenance.

Launch Plan
Operations · Owner
View
ROI and Business Case
Finance · Owner
View
Process Runbook / SOP
Operations · Owner
View
Integration and API Spec
Technical · Developer
View
Test and QA Plan
Quality · Developer
View