CRM & sales
AI Agents and Pipedrive
Pipedrive takes a plain API token and a POST. That makes it the shortest path from a finished call to a deal someone will actually work.
Where this stands today
Same starting point as every other CRM here: no native connector, one outbound webhook, your code in between. What makes Pipedrive pleasant is that its API needs no OAuth dance for a server-to-server integration - an API token as a query parameter is enough - and its object model is small enough to hold in your head.
The interesting design question is not how to call the API. It is what to create. A person for every inbound call floods the database within a month. Creating a deal only when the conversation summary says follow-up is required keeps the pipeline meaningful.
What the agent can do
Person search and create
Pipedrive's search endpoint matches on phone and email directly, which removes most of the duplicate handling you would otherwise write yourself.
Deal only when it is a deal
Gate creation on the follow-up flag in the summary. Everything else stays an activity on the person - visible, searchable, not in the pipeline.
Activity with the transcript
A completed call activity with the transcript in its note gives the rep the context before they dial back.
Negative sentiment routing
The summary carries a sentiment. Routing NEGATIVE to a named owner with a due date today is a two-line change and the most valuable one on this page.
How it is wired
- Conversation ends
- bitpull webhookPOST to your URL
- Your functionsearch person
- Pipedriveperson · activity · deal
Code
const BASE = `https://${process.env.PIPEDRIVE_DOMAIN}.pipedrive.com/api/v1`
const TOKEN = process.env.PIPEDRIVE_TOKEN
const pd = (path, init) =>
fetch(`${BASE}${path}${path.includes('?') ? '&' : '?'}api_token=${TOKEN}`, {
headers: { 'Content-Type': 'application/json' },
...init
}).then((r) => r.json())
export default async function handler(req, res) {
res.status(200).end() // acknowledge first, work afterwards
const body = req.body ?? {}
const summary = body.summary ?? body.session?.summary ?? {}
const messages = body.messages ?? body.transcript ?? []
const phone = String(body.sipFrom ?? body.from ?? '').replace(/[^\d+]/g, '')
if (!phone) return
// 1. Person - search first, create only on a miss.
const found = await pd(`/persons/search?term=${encodeURIComponent(phone)}&fields=phone&limit=1`)
const personId = found.data?.items?.[0]?.item?.id ?? (
await pd('/persons', {
method: 'POST',
body: JSON.stringify({ name: summary.title ?? phone, phone: [phone] })
})
).data?.id
// 2. The call itself, as a completed activity carrying the transcript.
await pd('/activities', {
method: 'POST',
body: JSON.stringify({
subject: summary.title ?? 'AI agent call',
type: 'call',
done: true,
person_id: personId,
note: messages
.map((m) => `${m.role === 'USER' ? 'Caller' : 'Agent'}: ${m.content}`)
.join('\n')
})
})
// 3. A deal only when the conversation asked for one.
if (summary.followUp?.required) {
await pd('/deals', {
method: 'POST',
body: JSON.stringify({
title: summary.title ?? 'Inbound AI agent call',
person_id: personId,
...(summary.sentiment === 'NEGATIVE' && { label: 'hot' })
})
})
}
}Limits worth knowing before you start
- No Pipedrive marketplace app. Everything here is your own function against Pipedrive's public API.
- The API token in a query string is convenient and unforgiving - it ends up in access logs. Keep it server-side and rotate it like a password.
- Pipedrive rate limits per token, not per endpoint. A burst of concurrent calls at nine in the morning is the shape that hits it.
- Custom fields are addressed by hash, not by name. Anything beyond the standard fields means resolving those hashes once and pinning them in config.
Questions
Can the agent read the pipeline during a call?
Not through the webhook, which fires afterwards. A live CRM read is a custom tool, and its execution endpoint is set up with the bitpull team.
Should every call create a person?
In most shops, no. Callers who got their answer do not belong in a sales database. Gate on the follow-up flag and let the rest stay in the bitpull conversation history.
Agents are created and operated on bitpull.ai - this site documents how to connect one. If the outbound webhook is not visible in your dashboard yet, that is the account capability flag, and support can enable it.