AgentLinkOps / Walkthrough
How it works, with the real outputs.
Five steps take a backlink list from a supplier export to a dated observation your agent can read. Each step below shows the output the tool produced during a sanitized run on September 12, 2026. Inputs on reserved example hostnames are labeled fixtures. Steps that are switched off in this build are labeled disabled, and nothing here was staged to look better than it ran.
1. Import a file
Start with an export you already have. Eight presets read Ahrefs, Semrush, Majestic, Moz, DataForSEO, Linkody, Search Console and generic CSV layouts, and any other column layout works with an explicit column map. A preset saves typing; it is never a requirement.
The fixture below is a seven-row CSV with non-standard column names. It carries one repeated placement, one row pointing at a different brand, one empty target cell and one date the tool cannot read, on purpose.
From page,Links to,Anchor,First seen,Last seen,Domain rating
https://notes.example.org/posts/getting-started,https://demo-store.example.org/,editorial walkthrough,2026-08-02,2026-09-10,23
https://news.example.org/roundups/september,https://demo-store.example.org/pricing,pricing notes,2026-09-01,2026-09-11,41
https://hub.example.org/tools,https://demo-store.example.org/,tools we like,2026-08-15,2026-09-09,17
https://notes.example.org/posts/getting-started,https://demo-store.example.org/,editorial walkthrough,2026-08-02,2026-09-10,23
https://news.example.org/roundups/september,https://other-brand.example.net/,roundup mention,2026-09-01,2026-09-11,41
https://broken.example.org/article,,missing target,2026-08-20,2026-09-01,9
https://hub.example.org/guide,https://demo-store.example.org/guide,guide link,not-a-date,2026-09-02,12Fixture. The hostnames are IANA-reserved example domains and resolve nowhere. No customer data appears on this page.
The import preview, as it ran:
$ agentlinkops import demo-import.csv --target demo-store.example.org --from csv \
--map "source=From page,target=Links to,anchor=Anchor,first_seen=First seen,last_seen=Last seen"
mapped source="From page", target="Links to", anchor="Anchor", first_seen="First seen", last_seen="Last seen"
kept as supplier metrics: Domain rating
{"read":7,"accepted":3,"rejected":3,"duplicates":1}
1 target_url: outside the scope of this import (first at row 6)
1 target_url: empty (first at row 7)
1 first_seen: not a date we can read (…) (first at row 8)
preview only — nothing was writtenRead the numbers. Seven rows were read, three accepted, three rejected and one folded into a duplicate. Each rejected row names its file line and its reason. The unmapped Domain rating column rides along as the supplier’s own metric and is never scored by the tool. A preview writes nothing, and no import ever claims to be the whole picture: coverage of an import is always partial, because nothing in an export says whether it is page one of forty.
One layout is refused rather than imported. A Search Console export that counts links per site would turn into invented placements if it were read as rows, so the tool declines it and says why. The Search Console import reference shows the accepted layouts.
The import formats page lists each preset with its columns, and importing backlinks from CSV walks through the column map in more detail.
2. Review a candidate
Running the same import with --json returns the candidate rows themselves. Here is the first one, from the same fixture:
id: "dc_938f2ca3247de28586386cb6b493ef704780964f7ea20869bd35342d5935d7dc"
discovery_run_id: "dr_preview_0"
source_url: "https://notes.example.org/posts/getting-started"
target_url: "https://demo-store.example.org/"
provider: "imported" data_mode: "imported"
verification_status: "not_checked" verified_at: null observation_id: null
provider_metrics.imported.supplier: "csv"Registration is not verification. Every imported candidate starts as not_checked, with no observation behind it. Verifying one is an explicit choice: selecting a candidate creates a paused watch and one metered check, and nothing enrolls it into recurring monitoring on your behalf. The state machine behind that choice is asserted by a synthetic-HTML test suite that passed 13 of 13 cases on September 12 and again on September 15.
What your agent does with the candidate before that click is its own work: reading the page, judging relevance, deciding whether the placement is worth a check. The candidate evaluation guide covers that judgment, and the discovery hub covers where candidates come from. Once the shortlist is chosen, the recall, verify and monitor pipeline walks the rest of the path: a bounded batch of up to 50 candidates under one budget reservation, an outcome for every item, and an explicit conversion of a present result into a watch that keeps its candidate lineage.
3. Compare against an approved inventory
A gap report compares the inventory you approved for yourself with the inventories you hold for named competitors. The report below was produced in-process from the repository’s own synthetic competitor fixtures, with no network and no hosted service involved.
comparison mode complete_query_datasets
absence claim not_supported
whole-web coverage false (selected_inventory_datasets)
counts {"groups":4,"rows":7,"observed_overlap_groups":1,"dataset_gap_groups":3,
"comparison_unknown_groups":0,"excluded_groups":0,"excluded_rows":0}
unselected members []
not_found_in_customer_dataset https://publisher.example.org/Alpha
rows 1 (customer 0, competitor 1), contributors cm_alpha=1
not_found_in_customer_dataset https://publisher.example.org/Beta
rows 1 (customer 0, competitor 1), contributors cm_beta=1
observed_overlap https://publisher.example.org/Shared?lang=en
rows 4 (customer 1, competitor 3), contributors cm_alpha=2 cm_beta=1 cm_customer=1
not_found_in_customer_dataset https://www.publisher.example.org/Alpha
rows 1 (customer 0, competitor 1), contributors cm_alpha=1Fixture. Synthetic inventories on reserved hostnames; the driver script is recorded with the demonstration.
Read the vocabulary before the counts. A gap is not_found_in_customer_dataset: a statement about two selected datasets, never a statement that your competitor holds a link you lack somewhere on the web. The report says so in its own metadata, with absence_claim: not_supported and whole_web_coverage: false. When an inventory is partial or failed, the affected groups are reported as comparison_unknown instead of being guessed.
Comparing inventories you already hold works today. Requesting a new paid inventory for a competitor does not: that lane is disabled in this build, and the request stops before anything is saved with PROVIDER_NOT_CONFIGURED. The competitor backlinks hub and the gap analysis guide explain what a frozen-inventory comparison can and cannot tell you.
4. Verify a placement
This step ran against a real public page. Two placements were registered on IANA’s reserved demonstration page, one wanted and one expected, and both were checked. The desktop CLI runs the same verifier module the hosted service runs, imported from the same file, and the tests assert that both paths return the same shape for the same cases.
$ agentlinkops add --source https://example.com/ \
--target "https://www.iana.org/domains/example" --scope exact --intent wanted → lk_24b91ee32c
$ agentlinkops add --source https://example.com/ \
--target "https://www.iana.org/" --scope exact --intent expected → lk_4e9fb90ef9
$ agentlinkops check --all
checking 2 entries…
LOST lk_4e9fb90ef9 https://example.com/ [no_matching_link_in_complete_html]
{"present":0,"absent":2,"unknown":0,"source_unavailable":0,"unchecked":0}
2 observations recorded, 0 repeated an answer already on fileBoth came back absent, and both answers were right. The page’s only anchor points at https://iana.org/domains/example, without www, which an independent fetch confirmed. Exact scope means exact. A third entry, registered with the href as the page spells it, was checked next:
$ agentlinkops add --source https://example.com/ \
--target "https://iana.org/domains/example" --scope exact --intent wanted → lk_d74d540cfd
$ agentlinkops check
APPEARED lk_d74d540cfd https://example.com/ (1 occurrence(s))
{"present":1,"absent":0,"unknown":0,"source_unavailable":0,"unchecked":0}
1 observation recorded, 0 repeated an answer already on file exit code: 0
$ agentlinkops check --all --filter dp0023-demo-real-2
LOST lk_4e9fb90ef9 https://example.com/ [no_matching_link_in_complete_html]
0 observations recorded, 1 repeated an answer already on file exit code: 1Two exit-code rules show up live. An expected link observed absent in a complete page is the only result that exits 1. A repeated answer appends nothing, so re-running a check does not pad the record. A check that cannot conclude exits with unknown and a named reason; unknown never fails a run and never means the link was removed.
This walkthrough used one real host, so it exercised exact-scope matching, anchor extraction, a missing robots file and the exit codes. It did not exercise redirects, render-required pages, login walls or subdomain scoping. Those are covered by the fixture suites and by the dated benchmark summarized on what your agent can prove, with its denominators attached. Those cases were exercised by the September 11, 2026 benchmark over 1,922 real pages, which records how often each one occurred.
5. Read an event
Every check leaves an observation. The present one from step 4, as written to the local observations file:
{"id":"lk_d74d540cfd","checked_at":"2026-09-12T14:51:39.359Z","state":"present","reason":"link_found",
"occurrences":1,"complete":true,"checker_version":"1","source":"local","evidence_key":null,
"result":{"state":"present","reason":"link_found","sourceUrl":"https://example.com/","finalUrl":"https://example.com/",
"targetUrl":"https://iana.org/domains/example","targetScope":"exact","httpStatus":200,
"occurrences":[{"href":"https://iana.org/domains/example","anchor":"Learn more","rel":[],
"context":"Learn more","locator":{"line":1,"column":478,"offset":477},"visibility":"not_rendered"}],
"evidence":{"method":"http_html","complete":true,"bytes":559,"contentType":"text/html",
"etag":null,"lastModified":"Fri, 11 Sep 2026 17:42:00 GMT","rendered":false,
"sha256":"ff67a9d764d6a2367a187734e697f6a53217db9a21c101d410a113ca871a299d"},
"robots":{"allowed":true,"reason":"robots_allowed","fetched":"robots_not_found"},
"warnings":["Static HTML observation; JavaScript execution and visual visibility were not checked."]}}The honesty fields travel with the data: the SHA-256 of the 559 bytes that were read, the robots posture, the HTTP status, whether a browser was used (rendered: false) and a standing warning that visibility was not checked. The page HTML itself never enters your repository; that is a contract constant in the CLI, not a setting.
An event is what the service emits when a watch changes state between two checks. The local run recorded none, and the record says so: agentlinkops diff reported no state changes recorded, because no entry had yet changed state and no change was simulated. In the hosted service an event carries a type such as placement_lost or source_unavailable, a before summary of the last successful observation and an after summary of the latest attempt. Your agent reads the feed with list_link_events, which returns events, next_cursor and has_more under an opaque cursor, and signed webhooks deliver the same changes on their own ten-minute schedule.
The event cursor guide shows how to apply a page before saving its cursor. The monitoring hub covers what a single check can and cannot conclude.
Current, last known and unknown
Three words carry most of the meaning in this product, and they are kept apart on every hosted watch.
| Word | What it means on a watch |
|---|---|
| Current | The latest attempt, whatever it concluded. It has its own field and its own timestamp. |
| Last known | The last observation that concluded, with its evidence summary: the hash, the byte count, the fetch time and whether the read was complete. A blocked attempt does not overwrite it. |
| Unknown | An attempt that could not conclude, with a named reason. It never advances the loss clock and never means the link was removed. |
Confirmed loss needs two complete absent observations at least 30 minutes apart. A first absence starts that window with one extra check; an unknown in between does not count. The methodology page walks through the state transitions, and backlink missing but the page is live shows the reading in a worked case.
Destination health is checked separately
A placement has two ends. The source page can carry the link while the destination it points at returns an error, or the reverse. So the destination is its own watch: monitor_target registers an exact public URL for recurring health checks, and its observations are recorded apart from any source watch. Healthy means a complete, accessible static HTML response. It does not mean the page is indexed, correct, or rendered in a browser, and it says nothing about whether any backlink points at it.
Each completed destination check uses one unit of the same monthly check allowance as a source check, and both kinds appear in the usage totals described on the access and data page.
What stayed disabled during this walkthrough
- Live supplier discovery. Disabled in this build. A discovery request stops before persistence or dispatch with
PROVIDER_NOT_CONFIGURED. Imports, shown in step 1, are the way candidates arrive today. - New competitor inventories and domain overviews. Disabled for the same reason. Comparison of inventories you already hold, shown in step 3, works.
- Cloud sync of the local ledger. Needs a token and a configured cloud origin, which the sanitized run did not have; the record shows
cursors: null. The local CLI works with neither.
These labels are the evidence. A disabled lane is described here as closed, not as coming soon, and the tool catalog shows the same state per tool.
Where to go next
- Methodology: how the verifier, the gap reports, the webhooks and retention decide what they decide.
- What your agent can prove to you: the buyer’s reading of the same evidence, with the dated benchmark numbers.
- Verifier benchmark, September 11, 2026: one dated run over 1,922 real pages, with every denominator, the unknowns by reason and the defect it found.
- Access limits and data handling: who can use the service today, what is stored and for how long.
- Connect your agent: tested setup for Claude Code, Codex and Cursor.
- Integrations: file imports, agent clients, the API and CLI, your own CRM through the portable handoff, and what is only planned, kept as five separate things.
- Keeping a backlink ledger in your repository: the file the local steps above wrote to.
- Recall, verify and monitor as one pipeline: the batch verification and candidate-to-watch commands in order, with per-item outcomes and provenance.
- Discovery use cases: one guide each for guest posts, link insertions, listicle and review inclusions, niche directories and AI-citation sources, with the evidence each request needs.
- Opportunity library: candidate publisher surfaces counted by niche, with the verification state, the fresh, aging and stale split and the coverage statement beside every number, and no record listed until a person publishes it.
- Reading search trends beside placement dates: what Search Console rows and a placement chronology can say together, the confounders, and why no link is credited with a ranking move.
How this page was checked
Every command output above is pasted from the DP-0023 demonstration record, run for real on September 12, 2026 at source commit 2c0af8c, trimmed only where the record marks it. The real-network budget of that run was six fetches of https://example.com/ at 559 bytes each plus one robots request per verifier run; the fixture steps made no network requests. The product statements quote rows W-01 to W-07, W-11, W-12, M-02, P-02, P-05, A-01, A-02 and A-04 of the claims ledger, and the cited test suites were re-run on September 15, 2026 against commit cc2c74f: candidate verification 13 of 13, competitor reports 12 of 12, CLI check 13 of 13, verifier 55 of 55, destination verifier 20 of 20.
A changed source file voids a claim until it is rechecked. If an output here no longer matches the tool, the ledger is where the correction starts.