Link building CRM API — accounts, contacts, relationships, and event log
CRUD endpoints for the CRM beneath every link-building activity. Shared accounts (partner domains), contacts, per-workspace relationships, and an event log — all queryable and writable via REST and MCP.
The CRM layer beneath every link-building activity. Accounts (the partner domains), contacts (the people), relationships (per-workspace state), events (the log). All queryable, all read/write.
For the conceptual model — shared accounts, per-workspace relationships, event-driven status — see Link Building.
Endpoints
Accounts (shared, system-wide)
| Method | Path | Purpose |
|---|---|---|
| GET | /v1/link-building/crm/account | List accounts (in your workspace's view) |
| GET | /v1/link-building/crm/account/{id} | Get one |
| PATCH | /v1/link-building/crm/account/{id} | Update fields where allowed |
Contacts (tied to accounts)
| Method | Path | Purpose |
|---|---|---|
| GET | /v1/link-building/crm/contact | List contacts |
| GET | /v1/link-building/crm/contact/{id} | Get one |
| POST | /v1/link-building/crm/contact | Create manually |
| PATCH | /v1/link-building/crm/contact/{id} | Update |
| DELETE | /v1/link-building/crm/contact/{id} | Delete |
| POST | /v1/link-building/crm/contact/discover | Discover contacts via Apollo |
Relationships (per-workspace pipeline)
| Method | Path | Purpose |
|---|---|---|
| GET | /v1/link-building/crm/relationship | List your workspace's relationships |
| GET | /v1/link-building/crm/relationship/{id} | Get one |
| POST | /v1/link-building/crm/relationship | Create manually |
| PATCH | /v1/link-building/crm/relationship/{id} | Update status / priority / notes |
| DELETE | /v1/link-building/crm/relationship/{id} | Archive |
Events (the log)
| Method | Path | Purpose |
|---|---|---|
| GET | /v1/link-building/crm/event | List events |
| GET | /v1/link-building/crm/event/{id} | Get one |
| POST | /v1/link-building/crm/event | Add a note / status-change event manually |
GET /v1/link-building/crm/account
curl -G .../v1/link-building/crm/account \
--data-urlencode "minDR=40" \
--data-urlencode "status=NEVER_CONTACTED,IN_DISCUSSION" \
--data-urlencode "limit=50"{
"data": [
{
"id": "acct_***",
"domain": "engineering-blog.com",
"name": "Engineering Blog Weekly",
"source": "SEARCH",
"systemContactStatus": "RATE_ESTABLISHED",
"priceLinkInsertion": 350,
"priceGuestPost": 900,
"domainRating": 62,
"organicTraffic": 48500,
"relationshipInThisWorkspace": {
"id": "rel_***",
"status": "ACTIVELY_ENGAGED",
"lastContactedByUsAt": "2026-05-22T..."
}
}
],
"total": 124
}| Param | Notes |
|---|---|
domain / domainContains | — |
minDR / maxDR | — |
status | LinkBuildingAccount.systemContactStatus values |
hasRelationship | boolean — filter to accounts with/without a relationship in this workspace |
GET /v1/link-building/crm/account/{id}
Returns full account record + the workspace's relationship + contacts + the most recent 50 events.
PATCH /v1/link-building/crm/account/{id}
You can update some fields (your perspective): rawNotes, priceLinkInsertion, priceGuestPost, priceContext. The systemContactStatus can only be updated by the system or via an explicit DO_NOT_CONTACT action.
curl -X PATCH .../v1/link-building/crm/account/acct_*** -d '{
"priceLinkInsertion": 450,
"priceContext": "Asking $450 for editorial insertions in 2026"
}'POST /v1/link-building/crm/contact/discover
The Apollo contact-finder. Returns contacts found at the target domains.
curl -X POST .../v1/link-building/crm/contact/discover -d '{
"accountIds": ["acct_***A", "acct_***B"],
"roles": ["Editor", "Head of Content", "Marketing Manager"],
"seniority": ["manager", "director", "vp"]
}'Response
{
"invocationId": "inv_***",
"agentId": "agt_***",
"skill": "link_building.crm.contact.discover",
"status": "RUNNING",
"estimatedCost": { "credits": 0.8, "durationSeconds": 30 }
}Final result
{
"invocationId": "inv_***",
"status": "SUCCEEDED",
"creditsUsed": 0.8,
"result": {
"perAccount": [
{
"accountId": "acct_***A",
"contactsFound": [
{
"id": "ct_***",
"firstName": "Marina",
"lastName": "Olafsson",
"position": "Senior Editor",
"email": "marina@engineering-blog.com"
}
]
},
{
"accountId": "acct_***B",
"contactsFound": [],
"reason": "Apollo returned no contacts matching the role filter"
}
]
}
}GET /v1/link-building/crm/relationship
curl -G .../v1/link-building/crm/relationship \
--data-urlencode "status=ACTIVELY_ENGAGED,LINK_PLACED" \
--data-urlencode "since=2026-05-01T00:00:00Z"{
"data": [
{
"id": "rel_***",
"accountId": "acct_***",
"accountDomain": "engineering-blog.com",
"status": "ACTIVELY_ENGAGED",
"priority": "HIGH",
"targetBlogPost": "https://engineering-blog.com/best-pm-tools",
"ourContent": "https://acme.com/blog/engineering-team-capacity-tracking",
"lastEventAt": "2026-05-22T..."
}
]
}PATCH /v1/link-building/crm/relationship/{id}
curl -X PATCH .../v1/link-building/crm/relationship/rel_*** -d '{
"status": "LINK_PLACED",
"priority": "MEDIUM",
"rawNotes": "Live on /best-pm-tools — confirmed 2026-05-22"
}'Status transitions are logged as STATUS_CHANGED events automatically.
GET /v1/link-building/crm/event
curl -G .../v1/link-building/crm/event \
--data-urlencode "relationshipId=rel_***" \
--data-urlencode "type=EMAIL_SENT,EMAIL_RECEIVED" \
--data-urlencode "limit=50"{
"data": [
{
"id": "evt_***",
"relationshipId": "rel_***",
"contactId": "ct_***",
"type": "EMAIL_SENT",
"subject": "Section 7 of your PM tools roundup",
"content": "Hi Marina, ...",
"metadata": { "instantlyMessageId": "im_***" },
"occurredAt": "2026-05-08T09:14:23Z"
},
{
"id": "evt_***",
"relationshipId": "rel_***",
"type": "EMAIL_RECEIVED",
"subject": "RE: Section 7 of your PM tools roundup",
"content": "Sure, send the methodology ...",
"occurredAt": "2026-05-09T14:22:00Z"
}
]
}POST /v1/link-building/crm/event
Add a manual note or out-of-band event.
curl -X POST .../v1/link-building/crm/event -d '{
"relationshipId": "rel_***",
"type": "NOTE_ADDED",
"subject": "Off-platform reply",
"content": "Marina replied on LinkedIn DM — asked to schedule a call"
}'MCP
> Who are my actively-engaged link-building partners?Claude calls link_building.crm.relationship.list with status=ACTIVELY_ENGAGED.
> Find contacts at engineering-blog.com and devleadweekly.com.Claude calls link_building.crm.contact.discover.
> Add a note to my relationship with engineering-blog.com that Marina prefers Mondays.Claude calls link_building.crm.relationship.list to find the relationship, then link_building.crm.event.create.
Errors
| Status | Code | Cause |
|---|---|---|
| 404 | account_not_found / relationship_not_found / contact_not_found / event_not_found | — |
| 403 | system_field_protected | Tried to PATCH a systemContactStatus field directly |
| 422 | apollo_quota_exceeded | Workspace's Apollo quota hit; try again next cycle |
Cost
| Action | Credits |
|---|---|
| All CRUD reads / writes | free |
Manual send-email | 2 (covered by campaign.send_email) |
contact.discover (per account) | 0.4 |
Use cases (string things together)
A. Daily engaged-partners digest
curl -G .../v1/link-building/crm/relationship \
--data-urlencode "status=ACTIVELY_ENGAGED" \
| jq '.data[] | { domain: .accountDomain, lastEventAt }'Pipe to Slack as a morning standup post.
B. Track placement rate per source
curl -G .../v1/link-building/crm/relationship \
--data-urlencode "status=LINK_PLACED" \
--data-urlencode "since=2026-05-01" \
| jq 'group_by(.source) | map({ source: .[0].source, count: length })'C. Manual override of agent decisions
The agent moved a relationship to NOT_INTERESTED based on a polite-decline reply. You disagree. PATCH it back to PROSPECTING.
curl -X PATCH .../v1/link-building/crm/relationship/rel_*** -d '{
"status": "PROSPECTING",
"rawNotes": "Re-evaluated; their decline was about timing, not content"
}'D. Cross-portfolio account intelligence
Because accounts are shared system-wide, agencies see other agencies' negotiated rates and DO_NOT_CONTACT flags. Powerful for portfolio benchmarking.
Related
Campaign management
Unified campaign endpoints across SERP, competitor, and custom outreach. List campaigns, read metrics, pause and resume, send one-off emails, and respond to inbound replies from one endpoint family.
Workspaces
REST API for agencies to create, list, audit, and run bulk operations across many client workspaces. Includes per-workspace usage, integration health checks, and bulk-action endpoints for cross-portfolio automation.