Import and webhooks
Most of what your agents need to know comes from them: they log what they do. The rest arrives on its own: the CRM you're leaving, calls your voice agents make, meetings people book, forms they fill in, payments. Both are set up by a workspace admin in the app, under Settings → Import and Settings → Webhooks.
Everything that comes in goes through the same rules as an agent's writes: people are matched by email, phone or handle (never duplicated), opt-outs follow their identities, records count toward your plan, and each change lands in the audit log.
Import
- CSV file: any spreadsheet export. You choose what each column is (email, phone, LinkedIn, name, company, job title, lifecycle stage); other columns become properties agents can read.
- HubSpot, Pipedrive, Attio: paste a read-only token, and UndoCRM pulls companies, then people (linked to their company), then pipelines and deals (linked to their person and company). The token is used for that import only and isn't stored.
How imports treat what's already there:
- A person already in the workspace (same email, phone or handle) is filled in, never overwritten: a blank job title gets the imported one; a name or property they already have stays.
- Running an import again is safe. People are matched by identity, and companies and deals by their id in the other CRM.
- Rows with no email, phone or handle are skipped and listed, so you can fix them.
- At your plan's record limit the import stops and says so. Upgrade, run it again, and it continues where it stopped.
- Undo archives the contacts and deals an import created, except anyone with activity since (a conversation, a task, an opt-out), and retracts the properties it added.
Webhooks
Each source gets its own URL, https://undocrm.com/in/<token>, to paste into the tool. The URL is the credential: it's shown once, and an admin can replace it at any time (the old one stops working). Each source keeps a log of its last deliveries and what became of them.
| Source | What's logged |
|---|---|
| Vapi | Each call, from its end-of-call report: the summary, transcript and recording, and structured data as properties. |
| Retell | Each call, from call_analyzed: the call summary, transcript and recording, and custom analysis data as properties. |
| Bland | Each completed call, with its summary and transcript. |
| Cal.com, Calendly | Bookings, reschedules and cancellations, on the attendee, with an open task for the meeting that moves or closes with the booking. |
| Typeform, Tally, Webflow forms | Each submission, on the person who sent it. Answers to short questions become properties. |
| Stripe | Customers, payments, refunds, failed payments (with a follow-up task) and subscriptions. A payment sets the lifecycle stage to customer. |
| Custom | Anything that can POST JSON: Zapier, Make, n8n, or your own code. |
A source can count as an agent's work. Attribute a Vapi source to your voice agent, and its calls show as that agent's, in timelines, briefs and the audit log; an outbound call to someone another agent has claimed is flagged, as it would be for any agent.
Deliveries are idempotent: when a provider sends the same event twice, it's logged once. Events a source doesn't log (a call still in progress, an unrelated Stripe event) are answered with 200 and skipped, with the reason in the source's log. A paused organization or a full record limit is answered with 402, so providers that retry deliver the event again after it's resolved.
Custom JSON
POST one object per event. person is any identity (email, phone, or linkedin:handle); leave out summary to save the contact without a conversation.
curl -X POST "https://undocrm.com/in/$TOKEN" -H "content-type: application/json" -d '{
"person": "jane@acme.com",
"name": "Jane Doe",
"company": "Acme",
"channel": "sms",
"direction": "inbound",
"summary": "Replied yes to the webinar invite.",
"facts": { "webinar": "Nov 3" },
"follow_ups": [{ "due": "2d", "action": "Send the webinar link" }],
"external_id": "zap-123"
}'
| Field | |
|---|---|
person, email, phone, identities |
Who it's about. At least one. |
name, title, company, lifecyclestage |
Filled in when the person has none yet. |
channel, direction, summary, content, occurred_at |
The conversation. direction is inbound, outbound or internal. |
facts |
Properties, as an object or as [{ "key", "value" }]. |
follow_ups |
Tasks: [{ "due": "3d" or an ISO date, "action": "..." }]. |
external_id |
Your id for the event; the same id is never logged twice. |