Skip to Content
BacklinksBacklinks — AI Agents

Backlinks — AI Agents

Live Agents

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 Contact record if email is found

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:

  1. Load tenant region, business category, and competitor domains from Tenant + ClientContext.
  2. Query BacklinkDirectory where isActive = true and regions includes the tenant’s region or "GLOBAL".
  3. Filter by subcategory match against the tenant’s business type.
  4. Deduplicate: skip any directory where a Backlink row with that directoryId already exists for this tenant.
  5. LLM scores each remaining directory 1–10 for relevance. Drop entries scoring < 4.
  6. Competitor gap: crawl competitor domains, find listing sites they appear on that are not in the BacklinkDirectory DB, propose as sourceType: "competitor_gap" rows.
  7. Insert Backlink rows — copying steps from 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.


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.

© 2026 Leadmetrics — Internal use only