Build a GTM Agent with ABM.dev
A complete, copy-paste-ready guide to building an AI agent that does account-based marketing — enriching contacts, researching LinkedIn profiles, and sending personalised outreach — all powered by ABM.dev APIs.
1. Introduction
ABM.dev provides a suite of APIs that let AI agents do account-based marketing autonomously. Instead of manually researching prospects, toggling between tabs, and copy-pasting data, your agent can call ABM.dev to enrich contacts with data from 10+ sources, research people on LinkedIn, and send personalised outreach messages — all in a single agentic loop.
This tutorial walks you through building a GTM (go-to-market) agent from scratch. By the end you will have a working agent that takes a target company, finds key contacts, enriches them, researches their LinkedIn profiles, and drafts personalised outreach.
2. What You'll Build
Your GTM agent will:
- Take a target company name as input
- Enrich key contacts at that company with verified emails, phone numbers, and company data
- Research their LinkedIn profiles for experience, skills, and mutual connections
- Generate personalised outreach messages based on enriched data
- Send messages via LinkedIn through your team's closest connection
The agent uses a tool-calling loop: you provide tool definitions, the LLM decides which tools to call and in what order, and your handler code executes the actual API requests against ABM.dev.
3. Prerequisites
- ✓ABM.dev API key — create one in Settings → API Keys
- ✓LinkedIn account connected — link it in Settings → Integrations
- ✓Python 3.10+ or Node.js 18+
# Install the SDKs you'll need pip install anthropic requests # or, for OpenAI: pip install openai requests
4. Tool Definitions
AI agents need tool definitions that describe the available actions, their parameters, and what they return. Below are the three core tools your GTM agent needs, formatted for three popular frameworks.
Claude (Anthropic SDK) — Python
tool_definitions.py
import anthropic tools = [ { "name": "enrich_contact", "description": "Enrich a contact with data from 10+ sources. Returns verified emails, phone numbers, company data, and AI-generated insights.", "input_schema": { "type": "object", "properties": { "email": {"type": "string", "description": "Contact's email address"}, "linkedin_url": {"type": "string", "description": "Contact's LinkedIn profile URL"}, }, "required": ["email"] } }, { "name": "linkedin_research", "description": "Research a person's LinkedIn profile. Returns experience, education, skills, and contact info.", "input_schema": { "type": "object", "properties": { "public_id": {"type": "string", "description": "LinkedIn public ID (from profile URL)"}, }, "required": ["public_id"] } }, { "name": "linkedin_message", "description": "Send a personalised LinkedIn message to a prospect via your team's closest connection.", "input_schema": { "type": "object", "properties": { "recipient": {"type": "string", "description": "LinkedIn public ID of the recipient"}, "text": {"type": "string", "description": "Message text (max 8000 chars)"}, }, "required": ["recipient", "text"] } }, ]
OpenAI Function Calling — Python
openai_tools.py
import openai tools = [ { "type": "function", "function": { "name": "enrich_contact", "description": "Enrich a contact with data from 10+ sources. Returns verified emails, phone numbers, company data, and AI-generated insights.", "parameters": { "type": "object", "properties": { "email": {"type": "string", "description": "Contact's email address"}, "linkedin_url": {"type": "string", "description": "Contact's LinkedIn profile URL"}, }, "required": ["email"] } } }, { "type": "function", "function": { "name": "linkedin_research", "description": "Research a person's LinkedIn profile. Returns experience, education, skills, and contact info.", "parameters": { "type": "object", "properties": { "public_id": {"type": "string", "description": "LinkedIn public ID (from profile URL)"}, }, "required": ["public_id"] } } }, { "type": "function", "function": { "name": "linkedin_message", "description": "Send a personalised LinkedIn message to a prospect via your team's closest connection.", "parameters": { "type": "object", "properties": { "recipient": {"type": "string", "description": "LinkedIn public ID of the recipient"}, "text": {"type": "string", "description": "Message text (max 8000 chars)"}, }, "required": ["recipient", "text"] } } }, ]
LangChain — Python
langchain_tools.py
from langchain.tools import tool import requests ABM_API_KEY = "your_api_key" ABM_BASE_URL = "https://api.abm.dev" HEADERS = { "Authorization": f"Bearer {ABM_API_KEY}", "Content-Type": "application/json", } @tool def enrich_contact(email: str, linkedin_url: str = None): """Enrich a contact with data from 10+ sources. Returns verified emails, phone numbers, company data, and AI-generated insights.""" response = requests.post( f"{ABM_BASE_URL}/v1/enrichments", headers=HEADERS, json={"email": email, "linkedinUrl": linkedin_url}, ) return response.json() @tool def linkedin_research(public_id: str): """Research a person's LinkedIn profile. Returns experience, education, skills, and contact info.""" response = requests.get( f"{ABM_BASE_URL}/v1/linkedin/profile/{public_id}", headers=HEADERS, ) return response.json() @tool def linkedin_message(recipient: str, text: str): """Send a personalised LinkedIn message to a prospect via your team's closest connection.""" response = requests.post( f"{ABM_BASE_URL}/v1/linkedin/messages", headers=HEADERS, json={"recipient": recipient, "text": text}, ) return response.json() # Bind tools to your LLM: # from langchain_anthropic import ChatAnthropic # llm = ChatAnthropic(model="claude-sonnet-4-20250514") # llm_with_tools = llm.bind_tools([enrich_contact, linkedin_research, linkedin_message])
5. Implementing the Tool Handlers
Each tool definition needs a handler that makes the actual HTTP request to ABM.dev. Here are the implementations:
handlers.py
import requests ABM_API_KEY = "your_api_key" ABM_BASE_URL = "https://api.abm.dev" HEADERS = { "Authorization": f"Bearer {ABM_API_KEY}", "Content-Type": "application/json", } def enrich_contact(email: str, linkedin_url: str = None): """Call the ABM.dev enrichment endpoint.""" response = requests.post( f"{ABM_BASE_URL}/v1/enrichments", headers=HEADERS, json={"email": email, "linkedinUrl": linkedin_url}, ) return response.json() def linkedin_research(public_id: str): """Fetch a LinkedIn profile via ABM.dev.""" response = requests.get( f"{ABM_BASE_URL}/v1/linkedin/profile/{public_id}", headers=HEADERS, ) return response.json() def linkedin_message(recipient: str, text: str): """Send a LinkedIn message via ABM.dev.""" response = requests.post( f"{ABM_BASE_URL}/v1/linkedin/messages", headers=HEADERS, json={"recipient": recipient, "text": text}, ) return response.json() # Dispatcher — maps tool names to handler functions TOOL_HANDLERS = { "enrich_contact": enrich_contact, "linkedin_research": linkedin_research, "linkedin_message": linkedin_message, } def handle_tool_call(name: str, inputs: dict): """Route a tool call to the correct handler.""" handler = TOOL_HANDLERS.get(name) if handler is None: raise ValueError(f"Unknown tool: {name}") return handler(**inputs)
6. The Agent Loop
Now wire everything together into a complete agentic loop. The agent sends a prompt to Claude, Claude decides which tools to call, your code executes the calls, and the results are fed back until the agent has a final answer.
agent.py
import json import anthropic from handlers import handle_tool_call from tool_definitions import tools client = anthropic.Anthropic() def run_gtm_agent(company: str): """Run the GTM agent against a target company.""" messages = [ { "role": "user", "content": ( f"Find decision-makers at {company}, enrich their profiles, " f"research them on LinkedIn, and draft personalised " f"outreach messages." ) } ] while True: response = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=4096, tools=tools, messages=messages, ) # If the model wants to use tools, execute them if response.stop_reason == "tool_use": tool_results = [] for block in response.content: if block.type == "tool_use": result = handle_tool_call(block.name, block.input) tool_results.append({ "type": "tool_result", "tool_use_id": block.id, "content": json.dumps(result), }) # Feed tool results back to the model messages.append({"role": "assistant", "content": response.content}) messages.append({"role": "user", "content": tool_results}) else: # Model is done — return its final text response return response.content[0].text # Run the agent if __name__ == "__main__": result = run_gtm_agent("Acme Corp") print(result)
That's it. Run python agent.py and the agent will orchestrate calls to ABM.dev, enriching contacts and drafting outreach autonomously. You can watch the tool calls in real-time by adding print(block.name, block.input) inside the loop.
7. Next Steps
Now that you have a working GTM agent, here are some ways to extend it:
- →People Finder — use the People Finder API to discover contacts at a company by role, seniority, or department (coming soon)
- →Webhook notifications — set up webhooks so your agent gets notified when async enrichments complete, instead of polling
- →Batch enrichment — upload a CSV of contacts and enrich them all at once, then iterate over results
- →Full API reference — explore every endpoint in the API Reference
Questions or feedback? Reach us at support@abm.dev