# The DevTool Outreach Queue: From GitHub Activity to Ranked Sends

*You will be able to stand up a weekly pipeline that captures GitHub activity, resolves actors to companies and reachable contacts, dedupes, gates for lawful send, and outputs a ranked queue.*

- Canonical URL: https://www.refolk.ai/guides/github-devtool-outreach-queue
- Pillar: Sales and go-to-market
- Format: Playbook
- Published: 2026-08-12
- Last reviewed: 2026-08-12
- Reading time: 19 min

This guide is for founders selling a developer tool, account executives, SDR leads, and partnerships teams who want to turn a week of GitHub activity into pipeline. The job is narrow and repeatable: take the stars, forks, issues, pull requests, and dependency adoption across your repo and your competitors' repos, resolve each actor to a company and a reachable contact, dedupe across events, gate every record on lawful send, and hand reps a ranked queue. What follows is the full pipeline, in order, with what to do at each stage, how long it takes, and what a good result looks like.

Two facts shape every decision here. First, since July 2026 GitHub restricts stargazer-listing endpoints to repo admins and collaborators, so a stars-first design is now the most fragile thing you can build. Second, the events feed excludes anything older than 30 days, which turns cadence from a nice-to-have into the load-bearing part of the system.

## What this pipeline captures and where the data comes from

The pipeline captures typed GitHub events plus dependency adoption, resolves each actor to a person and a company, and outputs a deduped, lawful-send, ranked queue. The durable source is the Events API, not stargazer lists.

GitHub's public activity is exposed through the Events API, which returns typed events including `WatchEvent` (a star, with action `started`), `ForkEvent`, `IssuesEvent`, and `PullRequestEvent`. You can read them per repo, per org, per user, or across the public network for a repo. Each event carries a stable numeric `actor.id`, a mutable `login`, the repo, the type, and a `created_at` timestamp. That numeric ID is the primitive the whole pipeline hangs on, because logins change and IDs do not.

Stars used to be the obvious entry point. They are not anymore. Since July 2026, stargazer-listing endpoints are limited to admins and collaborators, and the subscriber and watched-repos endpoints are similarly restricted or being deprecated. For your own repo you can still list stargazers; for a competitor's repo you cannot. The path that works uniformly across every repo is `WatchEvent` from the Events API, which reports a star as it happens without touching the restricted listing.

> **Watch out:** Do not build on stargazer lists
>
> Since July 2026, stargazer-listing calls on repos you do not own return limited or blocked data. An empty diff reads as "no new stars" when the endpoint is simply refusing you. Capture stars as WatchEvents from the Events API and reconcile counts to catch silent breakage.

Dependency adoption is the other high-value signal, and it is messier. For public repositories the dependency graph lists dependents, meaning other public repos that depend on your package. It is not reported for private repos, the counts are approximate, and the numbers may not match the listed dependents. Treat it as a candidate list of adopting organizations, not a ledger.

## Why events beat stars, and what each event fails to prove

Context-rich events beat raw stars because a fork or a feature-request issue exposes a company and a use-case, while a star exposes only a mutable username. That is the whole argument for how you rank intent.

No regulator or neutral body publishes a canonical intent ranking. This is practitioner opinion, and I will label it as such. Vendors converge on the same mechanism: knowing who opens issues about enterprise features, forks with company-specific changes, or authors pull requests at well-funded companies beats knowing who clicked a star. A star indicates popularity and awareness. It does not prove evaluation, and it certainly does not prove authority to buy.

A defensible heuristic ordering, highest intent first:

| Signal | What it proves | What it looks like when it lies |
|---|---|---|
| Dependency adoption | An org ships your package in production | Approximate count includes phantom or abandoned dependents |
| Pull request | Hands-on use, often at a named company | A one-line typo fix from an unaffiliated hobbyist |
| Feature-request issue | An evaluation with a specific need | A support question from a student or tire-kicker |
| Fork with context | Company-specific changes and a visible actor | A fork made to read code, never touched again |
| Star | Awareness only | A vanity star from someone who never returns |

Read this as a heuristic, not a law. A canonical, evidence-backed ordering is not established publicly. The mechanism is what makes it defensible: the further down the list you go, the less the event tells you about the person behind it.

> A star exposes a mutable username. A fork exposes a company and a use-case.

## Sizing the audience: who could this repo actually reach

Before you work a queue, size the pool it draws from, because pool size sets the ceiling on your weekly throughput and reveals where the lawful-send gate will bite hardest.

In Refolk's index of professional profiles, the addressable engineer pool varies sharply by skill and market. These are the numbers I use to set expectations for a devtool aimed at a given ecosystem.

| Segment | Count | Derived vs US Go |
|---|---|---|
| Go, Software Engineer, US | 2,766 | 1.00x |
| Go, Software Engineer, Germany | 304 | 0.11x (US is 9.1x) |
| Rust, Software Engineer, US | 606 | 0.22x (US Go is 4.6x) |

Two things fall out of this table. The US Go pool is about 9.1 times the German Go pool and about 4.6 times the US Rust pool. And raw pool size is misleading the moment you factor in the send gate. A German pool of 304 looks small but is workable by hand in an afternoon; the same effort against a 2,766-strong US pool hits the rate limit, not the enrichment budget.

**9.1x - How much larger the US Go engineer pool is than Germany's**

2,766 US versus 304 German Software Engineers listing Go in Refolk's index. Pool size overstates the sendable German queue because the lawful-send gate is stricter there.

Germany inverts the usual "biggest market first" instinct. Germany effectively requires prior consent and bars personal-address legitimate-interest sends, so the sendable German queue is far smaller than its pool suggests. When you sequence markets, let the gate, not TAM, decide volume.

## The weekly pipeline, start to finish

Run this end to end once a week. Each step lists the owner, rough duration, and what "done" looks like. The whole run is designed to be executed by hand for a small monitor set or scripted for a large one.

#### The weekly run

1. **Configure the monitor set** - Version a list of your repo plus 3 to 10 competitor or adjacent repos and the event types you will capture. Rely on the Events API for competitor repos, since stargazer listing is admin-only. Done when the list and scope are in source control.
2. **Capture the week's events** - Poll the Events API with ETags to conserve quota, respecting the five-minute delay and the 30-day, 300-event window. Done when you have raw rows with actor.id, login, repo, type, and created_at.
3. **Add dependency-adoption signals** - Pull dependent orgs and repos from the dependents view for each package. This is UI-derived and approximate. Done when you have a candidate list of adopting organizations.
4. **Resolve actors to people** - Spend 2 to 5 API calls per actor for name, company text, location, website, and a candidate email from recent commits. Flag noreply-only commits as unresolved-email. Done when every actor has a person record or a flag.
5. **Resolve people to accounts and contacts** - Normalize free-text company to a domain and attach a verified business email and role. Done when each resolvable person maps to a company account with a reachable contact.
6. **Dedupe** - Collapse to one record per numeric actor.id, bundle event types into a single record keyed to the highest-intent signal, and merge accounts by domain. Done when no actor.id or domain repeats.
7. **Apply the lawful-send gate** - Tag each record with jurisdiction, basis, email type, published status, and suppression status. Drop personal-address EU and German records and unpublished-address Canadian records. Done when every record is send-eligible.
8. **Score and rank** - Order by intent tier times account fit times recency, weighting dependency, PR, and feature-issue above stars, decaying records past 3 to 6 months. Done when the queue is sorted send-first.
9. **Route** - Hand each rep a record with owner, signal citation, and opt-out wired, sending VIP maintainers to founders. Done when every top record has an owner and a suppression check.

The single most important discipline here is that steps 2 through 9 must run every week without a gap. Events older than 30 days are excluded even when the timeline holds fewer than 300 events. Skip a week and the actors from that gap are gone permanently, with no backfill. The pipeline's value decays to zero without a standing run.

#### What survives each stage of a weekly run

| Stage | Figure | Note |
| --- | --- | --- |
| Captured events | 2,766 | US Go pool as an illustrative ceiling |
| Actors resolved to a person | 606 | identity resolves readily; email is the drop-off |
| With a reachable business email | 304 | noreply and personal addresses removed |
| Passing the lawful-send gate | 200 | jurisdiction and email-type gate applied |

*Volume narrows sharply at resolution and again at the lawful-send gate, so raw event counts overstate the sendable queue.*

The funnel figures are illustrative counts drawn from the Refolk pool sizes to show the shape of the narrowing, not a measured conversion rate for any one repo.

## Rate limits set your real throughput

Rate limits, not enrichment cost, cap how many leads you can resolve. At 4 to 5 API calls per lead against a 5,000-per-hour authenticated ceiling, one token tops out near 1,000 resolved leads per hour.

| Method | Limit/hour | Approx leads/hr at 5 calls each |
|---|---|---|
| Unauthenticated | 60 | ~12 (derived) |
| PAT / OAuth | 5,000 | ~1,000 (derived) |
| GitHub App on GHEC | 15,000 | ~3,000 (derived) |

Manual prospecting works for your first 50 or so leads, then the cost is prohibitive: paginating events, calling the users endpoint per handle, deduping against CRM, and checking existing customers runs 4 to 5 API calls per lead against that hourly ceiling. This is exactly why the German Go pool of 304 is a manual afternoon and the US Go pool of 2,766 is not.

There are two practical levers. Authenticate with a personal access token or OAuth to get 5,000 requests per hour instead of 60. If you operate a GitHub App owned by a GitHub Enterprise Cloud org, you get 15,000. And poll with ETags so unchanged responses do not count against your quota. Respect the five-minute delay on the public feed and the up-to-six-hour event latency when you reason about freshness.

> **Tip:** Poll with ETags, run at least weekly
>
> An ETag lets an unchanged response return without spending quota. Combined with a weekly cadence inside the 30-day window, it keeps a mid-size monitor set inside one authenticated token's budget.

## Resolving an actor to a company and a reachable contact

The documented procedure is to take the actor's login, retrieve the GitHub profile for real name, company, location, and website, then enrich against public professional records and work-email lookups to produce a lead record. Identity resolves readily. Email discoverability is the bottleneck.

Email typically comes from searching the user's recent commits, the last 30 days or commits in repos they own, then adding company and role via enrichment, and flagging power users such as maintainers of repos with more than 50 stars. Two blockers recur. First, many users commit under a noreply address: accounts created after July 18, 2017 use the form `ID+USERNAME@users.noreply.github.com`, which is a valid string and an undeliverable mailbox. Second, the profile "company" field is free text, not a resolved account, so "freelance", "@ex-employer", or a joke will map to the wrong domain if you trust it verbatim.

Plan for meaningful drop-off. A GitHub-specific email discovery rate is not established publicly. The only adjacent published figure comes from a different pipeline claiming 65 to 80 percent discovery at $6.49 per run, which I cite only to set expectations, not as a benchmark for this job.

Ask me this: `People who forked or opened issues on kubernetes/kubernetes in the last 30 days and list a company domain email.` - [run the search](https://www.refolk.ai/start?q=People%20who%20forked%20or%20opened%20issues%20on%20kubernetes%2Fkubernetes%20in%20the%20last%2030%20days%20and%20list%20a%20company%20domain%20email.).

*Returns actors resolved to a company and a reachable work contact, skipping the noreply and free-text-company problems that stall a hand-run pipeline.*

When the resolution step is the part eating your week, this is where [Refolk](/) removes the friction: you describe the actors you want in plain English and get people already resolved to a company and a reachable contact, so you spend your rep hours on ranking and sending rather than on chasing noreply addresses.

## Dedupe on the ID, never the login

Collapse to one record per numeric `actor.id`, bundle a person's event types into a single record keyed to the highest-intent signal, and merge accounts by domain. No published dedup standard exists for this job, so the rules below are derived from GitHub's own data model.

The load-bearing fact is that events carry a stable numeric `actor.id` while the `login` is mutable. If a user renames their account, login-based dedup creates a duplicate record and a second cold email to the same person. Worse, a user who commits under a noreply address and then changes their username de-links those commits from the account entirely, so identity threads that look separate may be one person.

Three dedup rules, in order:

- **Person level:** one record per numeric `actor.id`. Never key on login.
- **Signal level:** one signal-bundle per person per window, keyed to the highest-intent event. Someone who starred, forked, and opened a feature issue is one record ranked at the feature-issue tier.
- **Account level:** collapse people whose resolved emails share a domain into one account, so five engineers at the same company do not read as five accounts.

> **Rule:** Numeric ID is the dedup key
>
> Every dedup and merge decision keys on the immutable numeric actor.id. Logins are mutable and noreply commits de-link on rename, so any login-based key will eventually split one person into two records and send them two cold emails.

## The lawful-send gate every record must pass

Before a record reaches a rep, tag it with jurisdiction, legal basis, business-versus-personal email, published-versus-not, and suppression status. A real buying signal does not make an email lawful to send.

Three frameworks govern most of your queue. Map every record to the right one before scoring, because a record that fails the gate should never earn rank.

| Jurisdiction | Consent model | Non-personal-email gate |
|---|---|---|
| US (CAN-SPAM) | Opt-out; no prior consent | Not required; needs sender ID, physical address, and opt-out |
| EU/UK (GDPR) | Legitimate interest plus a documented assessment | Business email and role-relevant; personal domains not defensible |
| Canada (CASL) | Consent before the first send | Address must be conspicuously published |

For B2B in the EU, the legal basis is legitimate interest under Article 6(1)(f). If you pass the three-part test, meaning a real commercial reason, processing that is necessary, and interests that do not override the contact's, you can email EU business contacts without prior consent. Two hard gates cut into that. You cannot email personal addresses such as gmail.com under legitimate interest for cold B2B in most member states, and Germany applies national rules that effectively require prior consent. For Canada, an address must be conspicuously published to qualify for CASL implied consent; if an individual did not publish their email for professional contact, cold outreach is difficult to justify.

The operational duties are the same everywhere: a visible one-click unsubscribe with opt-outs suppressed immediately, a permanent suppression list checked against every send, and a documented data source with legitimate-interest reasoning for the EU. The stakes are not theoretical. CAN-SPAM penalties have been cited up to $53,088 per email, GDPR fines reach €20M or 4 percent of global revenue, and CASL runs up to CAD $10M per violation.

**Lawful-send record tags**

```
jurisdiction: US | EU/UK | Canada | other
legal_basis: CAN-SPAM opt-out | GDPR legitimate interest (LIA on file) | CASL published-consent
email_type: business_domain | personal_domain | noreply (reject)
published_for_contact: yes | no
suppression_checked: yes (date) | no
```

*Attach these five tags to every record; drop any that fail before scoring.*

## Score, rank, and route

Order the queue by intent tier times account fit times recency, weighting dependency, PR, and feature-issue signals above stars, and decaying records older than 3 to 6 months. Route the top of the queue to reps and the VIP maintainers to founders.

Recency is not optional. Stars older than 3 to 6 months rarely convert because developers move on, so a record's rank should decay with age and drop out past that window. Working a six-month-old star wastes rep time on someone who has already made a decision.

#### Where a resolved record lands

Horizontal axis runs from Low account fit to High account fit. Vertical axis runs from Low intent signal to High intent signal.

| Quadrant | What it means |
| --- | --- |
| Nurture list | Low fit, high intent; add to a watch list, do not spend rep time |
| Founder or top-rep queue | High fit, high intent; a fork or feature issue at a target account, work first |
| Drop or decay out | Low fit, low intent; a stale star from an unaffiliated user, let recency decay remove it |
| Account-based nudge | High fit, low intent; a star at a great-fit account, route to the account owner as context |

*Cross intent tier with account fit to decide who a rep works first and who a founder takes.*

Route with a signal citation attached so the rep opens with the actual event, not a generic pitch. A message that references the specific pull request or feature issue reads as informed; one that references "your interest in our project" reads as scraped.

**Signal-cited first touch**

```
Subject: your [feature-request issue] on [repo]

Hi [first name], I saw you [opened an issue about X / forked repo with company-specific changes] last week. That is usually a sign a team is evaluating [use-case]. If that is where [company] is, I can share how others solved it. If not, tell me to close the loop and I will. Unsubscribe: [link].
```

*Swap the bracketed context for the record's real signal citation; never send with the brackets in place.*

## How this pipeline goes wrong

The failure modes below are the highest-value part of this guide, because each one produces a queue that looks complete and correct while quietly leaking or misleading. For each, I give the false positive and the check that catches it.

- **Star capture silently breaks.** After the July 2026 restriction, stargazer-listing calls on repos you do not own return limited data, and an empty diff reads as "no new stars." Check: reconcile against `WatchEvent` counts from the Events API.
- **A stale queue looks fresh.** The 30-day, 300-event cap plus up-to-six-hour latency means a weekly run misses high-volume repos, and a "complete" week may have dropped events beyond 300. Check: if the event count hits the cap, shorten the polling interval.
- **Noreply email treated as reachable.** Commits under `users.noreply.github.com` yield a valid-looking, undeliverable address. Check: reject any address on the noreply domain before routing.
- **Company field trusted verbatim.** Free-text "company" resolves to the wrong account. Check: require a normalized domain plus independent confirmation before assigning an account.
- **Dedupe on login, not ID.** A renamed user creates a duplicate record and a second cold email. Check: key on numeric `actor.id`.
- **Legitimate interest assumed globally.** Sending to a personal-domain EU contact, or any German contact without consent, fails the gate even when the star is real. Check: apply the jurisdiction and email-type gate before scoring.
- **Dependent counts over-trusted.** Approximate dependents inflate adoption tiers. Check: verify the dependent org exists and ships the package before scoring it top-tier.
- **Stale stars ranked high.** Working six-month-old stars wastes rep time. Check: apply recency decay and drop beyond 3 to 6 months.

> **Note:** The two failures that cost the most
>
> Login-based dedup and a skipped weekly run are the expensive ones. The first sends duplicate cold emails and damages sender reputation; the second permanently loses a window of actors the 30-day cap will never return.

## Verify before you call it done

Run this checklist against the finished queue each week. If any item fails, the queue is not send-ready, no matter how good the signals look.

#### Weekly queue sign-off

- [ ] Every star was captured as a WatchEvent, not from a stargazer-listing call, and counts reconcile.
- [ ] The run happened inside the 30-day window with no skipped week, and no repo hit the 300-event cap unnoticed.
- [ ] Every record is keyed to a numeric actor.id, with event types bundled and accounts merged by domain.
- [ ] No record carries a noreply-domain address or an unverified free-text company.
- [ ] Each record is tagged with jurisdiction, legal basis, email type, published status, and a same-week suppression check.
- [ ] Personal-address EU and German records and unpublished-address Canadian records are dropped.
- [ ] Ranking weights dependency, PR, and feature-issue above stars, and records past 3 to 6 months are decayed or dropped.
- [ ] Every top record has an owner, a signal citation, and a wired opt-out, with VIP maintainers routed to founders.

## Keeping the pipeline current

Treat the pipeline as a standing system, not a one-off pull. The two things most likely to change out from under you are GitHub's access rules and the send-compliance landscape, so build a habit of re-checking both.

Re-verify the endpoint access model on a schedule. The July 2026 restriction on stargazer and subscriber listings is the kind of change that breaks a pipeline silently, and GitHub has adjusted the events window before, cutting it from 90 to 30 days in January 2025. If your capture counts drop without an obvious cause, check whether an endpoint moved behind an auth wall before you assume the repo went quiet. Re-check the lawful-send gate whenever you enter a new market, because the jurisdiction rules, not your pool size, decide how much of a market you can actually work. Do those two reviews and the rest of the pipeline runs itself, one week at a time.

## Frequently asked questions

### Can I still pull a competitor's stargazer list for prospecting?

Not reliably. Since July 2026, GitHub restricts stargazer-listing endpoints to repo admins and collaborators, so calls against repos you do not own return limited or blocked data. Build capture on the Events API instead, where a star arrives as a WatchEvent with action started. You get the actor and timestamp without needing the stargazer listing, and it works the same across your repo and competitor repos.

### How many leads can one GitHub token resolve per hour?

Roughly 1,000, derived from the 5,000-requests-per-hour authenticated limit and 4 to 5 API calls per lead. Unauthenticated access is capped at 60 requests per hour, or about 12 leads. A GitHub App on a GitHub Enterprise Cloud org gets 15,000 requests per hour, around 3,000 leads. Manual prospecting breaks down past roughly 50 leads regardless, once CRM deduping and customer checks are included.

### Why does the events window matter so much for cadence?

Because events older than 30 days are excluded even when the timeline holds fewer than 300 events. A team that skips a week permanently loses the actors from that gap, since there is no way to backfill beyond the window. High-volume repos can also hit the 300-event cap inside a week. If you see the cap, shorten the polling interval so you do not silently drop events.

### Is it legal to cold email a developer I found from a GitHub star?

It depends on jurisdiction and email type. Under GDPR you need legitimate interest plus a documented three-part test, and personal-domain addresses are not defensible in most member states. Germany effectively requires prior consent. Canada's CASL requires the address be conspicuously published. US CAN-SPAM allows opt-out sending with honest sender identification. Gate every record on jurisdiction and email type before it reaches a rep.

### Should I trust the dependent counts shown on GitHub?

Treat them as approximate. Dependents are only computed for public repositories, and GitHub states the counts may not match the listed dependents. Use the list to find candidate adopting organizations, but verify each org actually exists and ships the package before scoring it top-tier. Over-trusting raw dependent counts inflates your adoption tiers and wastes rep time on phantom accounts.

### What is the biggest reason a resolved contact turns out to be unreachable?

The noreply email. Accounts created after July 18, 2017 commit under an ID+USERNAME@users.noreply.github.com address, which is syntactically valid but undeliverable. Reject any address on the noreply domain before routing. Identity resolution is rarely the bottleneck; email discoverability is. Plan for meaningful drop-off at the email step, and do not let a valid-looking noreply address pass into a send queue.

---

*From the Refolk guide library. I revise these guides rather than replacing them, so the current version is always at https://www.refolk.ai/guides/github-devtool-outreach-queue*
