Backlinks — AI Agents
Live Agents
backlink-researcher
File: packages/agents/src/workers/backlink-researcher.worker.ts
Queue: agent__backlink_researcher
Credit type: backlink_research
Timeout: 180 s
Discovers link-building prospects for a tenant. Takes client context + niche brief as input, uses the LLM to generate a prioritised list of domains, then seeds the Backlink and Campaign tables and enqueues one website-crawler job per prospect.
Output (JSON):
{
"prospects": [
{
"sourceDomain": "example.com",
"sourceUrl": "https://example.com/resources",
"prospectType": "resource-page",
"relevanceScore": 8,
"pitchRationale": "Curated list of SEO tools; client's tool fits naturally in the 'Analytics' section"
}
]
}Utils: backlink-researcher.utils.ts — prospect parsing, domain normalisation, deduplication.
website-crawler
File: packages/agents/src/workers/website-crawler.worker.ts
Queue: agent__website_crawler
Timeout: 300 s (5 min)
Crawls a prospect domain to find contact information. Checks CrawlCache first (14-day TTL). On a cache miss, spawns a Claude sub-process to extract: contact page URL, recipient email, recipient name, and a raw extract used as email-writing context.
Skips prospect when: site is unreachable, has CAPTCHA, or blocks the crawler — decrements Campaign.totalEmails.
Side effects:
- Upserts
CrawlCache - Updates
Backlink.sourceUrl - Auto-creates
Contactrecord if email is found
backlink-outreach-writer
File: packages/agents/src/workers/backlink-outreach-writer.worker.ts
Queue: agent__backlink_outreach_writer
Credit type: backlink_outreach
Timeout: 120 s
Concurrency: 3
Lock duration: 180 s
Generates a personalised cold outreach email for each prospect. Combines: agent system prompt, client name + context, prospect domain, prospectType, pitchRationale, and the raw crawl extract. Parses JSON { subject, body } from the LLM response.
Checks campaign completion after every email — when all prospects have been processed (drafted or skipped), promotes Campaign.status to dm_review.
Planned Agents
opportunity-matcher (To Build)
File: packages/agents/src/workers/opportunity-matcher.worker.ts
Queue: agent__opportunity_matcher
Credit type: backlink_opportunity_match
Trigger: On-demand (DM or client) or enqueued as part of the post-onboarding setup chain
Matches a tenant to relevant entries in the BacklinkDirectory table and discovers competitor-gap opportunities. Creates Backlink rows with sourceType: "directory_opportunity" or "competitor_gap" — no emails, no campaigns.
Process:
- Load tenant region, business category, and competitor domains from
Tenant+ClientContext. - Query
BacklinkDirectorywhereisActive = trueandregionsincludes the tenant’s region or"GLOBAL". - Filter by subcategory match against the tenant’s business type.
- Deduplicate: skip any directory where a
Backlinkrow with thatdirectoryIdalready exists for this tenant. - LLM scores each remaining directory 1–10 for relevance. Drop entries scoring < 4.
- Competitor gap: crawl competitor domains, find listing sites they appear on that are not in the
BacklinkDirectoryDB, propose assourceType: "competitor_gap"rows. - Insert
Backlinkrows — copyingstepsfrom the directory entry at creation time.
Output (per Backlink row created):
sourceType,directoryId,sourceDomain,domainRating,linkType,relevanceScore,outreachStatus: "opportunity",steps(copied)
mention-monitor (To Build)
Monitors the web (via Google Custom Search API or BrightData) for brand/client name mentions that do not contain a hyperlink back to the client’s domain. Outputs a list of unlinked-mention prospects which feed into the existing backlink-outreach-writer with prospectType: "mention".
Suggested trigger: Weekly scheduled job per tenant.
Output: Prospects inserted as Backlink rows directly (no researcher LLM step needed).
guest-post-content-writer (To Build)
Triggered when a Backlink with prospectType: "guest-post" transitions to outreachStatus: "agreed". Reuses the existing blog-writer agent with an adapted prompt that targets the prospect’s site audience, tone, and topic guidelines (pulled from the crawl extract).
Trigger: Backlink status change hook → enqueue with contentType: "guest_post" and targetSite context.
Output: Activity row (type: blog_post) linked back to the Backlink via activityId.
haro-monitor (To Build)
Polls Connectively (HARO), Terkel, or SourceBottle APIs for journalist queries matching the client’s industry keywords. For each matching query, enqueues a pr-response-writer job.
Suggested trigger: Twice-daily scheduled job per tenant.
New model needed: HaroQuery { id, tenantId, source, queryText, deadline, respondedAt, backlinkId? }
pr-response-writer (To Build)
Drafts an expert response to a journalist query (HARO/Connectively). Uses client context, expertise keywords, and the query text as prompt input. Output goes to DM for review before submission to the journalist.
Queue: agent__pr_response_writer
Credit type: TBD (pr_response)
Output: { pitchSubject, pitchBody, quoteSuggestion }
Side effect: Creates a Backlink row (status: prospecting) so the link can be tracked if the journalist publishes.
broken-backlink-detector (To Build)
Sweeps existing Backlink rows where isLive = false and outreachStatus = "published" — links that went live but have since broken. Generates a short fix-request email using the existing outreach writer with a specialised “link reclamation” prompt.
Data source: isLive + lastCheckedAt fields already exist on Backlink (populated by DataForSEO health checks).
No new model needed — operates on existing Backlink rows.
competitor-gap-analyst (To Build)
Extends backlink-researcher with a competitor gap mode. Takes a list of competitor domains, queries an Ahrefs/SEMrush API for their backlink profiles, and returns domains that link to 2+ competitors but not the client — the highest-conversion cold outreach targets.
Trigger: Separate queue action from the standard researcher, or an optional flag on the researcher job.
New field needed on Backlink: gapCompetitors String[] — which competitors are already linked from this domain.