Description
n8n-nodes-fondaro
This is an n8n community node package for Fondaro, the real estate CRM. It lets you create, assign and update leads, deals, tasks, notes and tags, read the ad, property references and form answers behind Fondaro-generated leads, list leads by assignee, unassigned state, tag or CRM status, read a lead’s activity and call log (with call outcomes) from your n8n workflows, and start workflows the moment things happen in your Fondaro CRM — new leads, status changes, logged calls, and deal and task events.
The package ships three nodes:
| Node | Type | What it does |
|—|—|—|
| Fondaro | Action | Create, find, assign, list and update leads, deals, tasks, notes and tags |
| Fondaro Trigger | Webhook trigger | Starts a workflow the moment a CRM event happens, delivered as a signed webhook |
| Fondaro Polling Trigger | Polling trigger | Starts a workflow by polling for new leads, for instances that cannot receive webhooks |
n8n is a fair-code licensed workflow automation platform.
Installation
Self-hosted n8n (GUI)
1. Open Settings > Community Nodes.
2. Select Install.
3. Enter n8n-nodes-fondaro and confirm.
See the n8n community nodes installation guide for details.
Self-hosted n8n (CLI)
Install the package into your n8n user folder (usually ~/.n8n/nodes):
cd ~/.n8n/nodes
npm i n8n-nodes-fondaro
Restart n8n afterwards.
Environment bootstrap
For containerized or declarative setups you can have n8n install the package on boot:
N8NCOMMUNITYPACKAGESMANAGEDBY_ENV=true
N8NCOMMUNITYPACKAGES='[{"name":"n8n-nodes-fondaro","version":"1.8.1"}]'
Credentials
Creating a Fondaro API key
1. In the Fondaro dashboard, go to Organization > Integrations > n8n.
2. Select Generate key and give the key a name.
3. Copy the key immediately. It is shown exactly once and cannot be retrieved later. Keys look like fdrn8n followed by 32 hex characters.
Setting up the credential in n8n
1. In n8n, create a new Fondaro API credential.
2. Paste the API key.
3. Leave the Base URL at https://api.fondaro.com unless Fondaro support has told you otherwise.
4. Save. n8n tests the credential against the Fondaro whoami endpoint and shows your organization name on success.
The action node uses Fondaro API integration keys. An OAuth2 credential class is included in the package, but the action node does not select it. Public MCP OAuth is a separate authentication plane.
Key rotation
To rotate a key: generate a new key in the Fondaro dashboard, swap it into the n8n credential, then revoke the old key. There is no in-place rotation; generate, swap, revoke.
The Fondaro node (actions)
| Resource | Operation | Description |
|—|—|—|
| Lead | Add Assignees | Add one or more assignees without removing the lead’s existing assignees |
| Lead | Create | Create a new lead |
| Lead | Find | Find one lead by email, phone or external ID |
| Lead | Get | Get a lead by its numeric ID |
| Lead | Get Activities | Read a lead’s activity feed — notes, tasks, emails, status changes and call attempts with their outcomes |
| Lead | Get Calls | Read a paginated call summary collection with recorded owner IDs |
| Lead | Semantic Search | Discover ranked leads with indexed activity evidence and reranking status |
| Lead | Match to Listing | Match indexed CRM evidence to an own-listing UUID or source-qualified listing reference |
| Viewing | Get | Read one registered own-listing viewing with full authorized notes |
| Viewing | Get Many | Read a filtered viewing page with bounded notes previews |
| Lead | Get Many | List leads, filterable by assignee, unassigned state, tags (names or IDs, OR semantics) and CRM status, with limit and offset |
| Lead | Search | Free text search across leads, with limit and offset |
| Lead | Update Contact | Update name, email, phone, lead type or language |
| Lead | Update Status | Set the CRM status |
| Deal | Create | Create a deal for a lead |
| Deal | Change Stage | Move a deal to a different stage |
| Deal | Close Won | Mark a deal as won |
| Deal | Close Lost | Mark a deal as lost, with an optional free-text reason |
| Deal | Get | Get a deal by its ID |
| Deal | Get Many | List all deals on a lead |
| Task | Create | Create a task on a lead |
| Note | Create | Add a note to a lead |
| Tag | Add | Add tags to a lead by name or ID (additive; missing names are created, IDs must exist) |
| Tag | Get | Read a lead’s current set of tags |
| User | List | List your team members with their email, name and role |
| User | Get | Resolve one team member (email, name, role) by their user_… ID |
Lead IDs are numbers. Deal, task, note and subscription IDs are UUID strings. Team-member IDs are Clerk user_… strings — the same values you see in a lead’s assigneeIds and on the lead.assigned event.
The node can also be used as a tool by n8n AI agents.
Find returns purchased leads only
The Lead > Find operation returns only leads that have been purchased into your CRM. A 404 response means no purchased lead matched the identifier, not that your key is broken or the API is down.
Adding lead assignees
Lead > Add Assignees takes a numeric Lead ID and one or more team members. It is additive: existing assignees stay on the lead, and selecting someone who is already assigned is a successful no-op. The response contains the updated lead plus added and removed arrays; removed is always empty for this operation.
Listing leads by assignee, tag or status (Get Many)
Lead > Get Many lists your CRM working set, newest first by purchase date, and answers “which leads belong to this rep, are unassigned, or carry tag X today” in one call. All filters live under Additional Fields:
- Assignee — return leads assigned to one selected team member.
- Unassigned — return only leads whose assignee set is empty. Assignee and Unassigned are mutually exclusive.
- Tags — comma-separated tag names or IDs, OR semantics: a lead matches if it carries any of the tags. Names match case-insensitively; an entry that does not resolve to an existing tag fails the whole call with a 404 naming the entry, so a typo never silently returns an empty set. Archived tags still resolve. A tag name that itself contains a comma can only be filtered by its ID.
- CRM Status — return only leads with that status.
- Limit (1–100, default 50) and Offset for paging. The response is
{ leads, total, hasMore }.
Every lead payload now carries tags[]. Server-side as of the same release, every lead returned by Create, Find, Get, Get Many, Search and the polling trigger includes its tags as [{ id, name, color, archivedAt }] — always present, [] when untagged. Tag > Get returns the same shape. For real-time tag automations prefer the Lead Tagged / Lead Untagged triggers; use Get Many as a periodic reconcile so a missed webhook delivery never strands a lead.
Reading the ad, property and form answers behind a lead
Every lead returned by Create, Find, Get, Get Many, Search, Update Contact, Update Status, Add Assignees, and the polling trigger carries an origin field. For a lead Fondaro generated, this is the same frozen acquisition receipt shown in the lead’s dashboard panel:
{
"origin": {
"version": 1,
"channel": "metaleadform",
"platform": "ig",
"capturedAt": "2026-09-03T15:10:00.000Z",
"site": null,
"ad": {
"platform": "meta",
"platformAdId": "120210000000000001",
"campaignName": "Marbella buyers"
},
"properties": [
{
"reference": "R5410204",
"source": "resales_online",
"role": "advertised"
}
],
"form": {
"provider": "meta",
"formId": "120210000000000002",
"answers": [
{
"name": "search_area",
"label": "Search area",
"values": ["Marbella"]
},
{
"name": "budget",
"label": "Budget",
"values": ["€1m–€1.5m"]
},
{
"name": "timing",
"label": "Timing",
"values": ["Within 6 months"]
}
]
},
"utm": null
}
}
Use origin.properties[].reference for the property or ad reference and origin.form.answers[] for the person’s non-contact answers. A property role of viewed means the person opened that listing on the Fondaro brand site; advertised means the listing appeared in the ad. The receipt can also include the brand site visit and campaign details when they were captured.
origin is null for leads created manually, imported, or received from another integration. Fondaro’s raw source metadata and internal lead purchase cost are never returned.
Dropdown values
Tag and assignee dropdowns load live from your organization. Since 1.4.0 the tag dropdowns bind stable tag IDs (shown by name), so a picked tag keeps working after it is renamed or merged in the dashboard; workflows saved with older versions hold names, which the API still accepts. In the tags field, a plain name that does not exist is created automatically, while a UUID-shaped entry must match an existing tag and is never created. The status and stage dropdowns are fixed lists, kept in sync with Fondaro:
lead, potential, bad_timing, client, unqualifiedbuyer, sellerqualified, viewing, offer, reserved, under_contractDeal currency is controlled by your organization settings in Fondaro; the node never sends a currency. A 400 on deal creation means the organization has no billing currency set yet.
lead is the entry status; the dashboard labels it “New”. They are the same value — the dropdown shows New, the API value is lead.
There is no unassigned status. A lead is unassigned when its assigneeIds is empty — it still has a status (usually lead/New). Use the Unassigned Get Many filter to list that queue, and read assignment from assigneeIds, not from status. The lead.created, lead.statusChanged and lead.assigned events all carry assigneeIds, so you can compute “unassigned” directly with data.assigneeIds.length === 0, no extra lookup.
Reading activity and call outcomes
Lead > Get Activities returns a lead’s unified, paginated activity feed: notes, tasks, emails, status changes, deal-stage changes, assignee changes and call attempts with their outcomes. Each entry is { type, occurredAt, …type-specific fields }; the response is { entries, total, totalIsExact, hasMore, nextOffset }. Continue with the returned nextOffset while hasMore is true. limit is an upper bound. General timeline totals may be a lower bound; never use them for KPI counts.
call to fetch only the call log, or call,note,status-change. Valid values: call, note, task-created, task-completed, email, status-change, deal-stage-change, deal-won, deal-lost, assignee-change, lead-created, document-attached, viewing.A call entry looks like:
{
"type": "call",
"occurredAt": "2026-06-15T10:00:00.000Z",
"call": {
"id": "…",
"ownerId": "userrecordedrep",
"direction": "outbound",
"outcome": "no_answer",
"status": "no-answer",
"durationSeconds": 0,
"startedAt": "…",
"endedAt": "…",
"hasRecording": false
}
}
outcome is the call result in Fondaro’s own vocabulary:
| outcome | Meaning |
|—|—|
| success | Connected. Someone picked up. |
| no_answer | Rang out. Nobody picked up. |
| busy | The line was engaged, the call was declined, or the carrier screened it. A normal telephony result, not a failure. |
| failed | The carrier could not complete the call as dialled. Fondaro records the carrier’s own hedge and does not assert why. |
| invalid_number | Stamped only when the carrier’s error code proved the destination was unusable. A plain failure is failed, never this. |
| canceled | The call ended before it resolved either way. |
Transcripts and recording URLs are never included, only hasRecording.
> Old call rows can still carry the retired value not_interested. Treat any outcome you do not recognise as “no useful result” rather than matching it exactly, so a future value cannot silently break a branch.
Call summary on the lead record. Every Lead > Get / Find / Search result now also carries the latest call summary, so you can branch a flow without a separate Get Activities call:
| Field | Meaning |
|—|—|
| totalCalls | Number of calls placed/received. 0 ⇒ assigned but never called. |
| lastCallStatus | Carrier status of the most recent call, in the carrier’s own spelling: completed, no-answer, busy, canceled or failed. Note the hyphen in no-answer. This is not the outcome vocabulary above. |
| lastCallAt | When the most recent call happened. |
| lastCallDirection | inbound or outbound. |
This answers “assigned-but-not-called yet” (totalCalls === 0) vs “called, no answer” (lastCallStatus === 'no-answer'), the distinction the CRM status enum does not encode.
> Two vocabularies, on purpose. lastCallStatus carries the raw carrier status, while the activity feed’s outcome carries Fondaro’s interpretation of it. So the same call reads as no-answer in one field and no_answer in the other, and as completed in one and success in the other. Compare each field against its own spelling.
Resolving a team member (User resource)
Events and a lead’s assigneeIds give you the assignee as a Clerk user_… ID, not an email. The User resource turns that ID into a real person:
{ id, firstName, lastName, email, role, imageUrl }.User ID) → that one member, or a 404 if the ID is not a member of your organization.This needs the users:read scope on your API key. Keys minted with the default “everything” scope already have it; a key with a hand-picked scope set must have users:read granted.
> This is distinct from the assignee dropdown used when creating a lead/deal/task (that’s a design-time picker). User > Get is for resolving an ID to an email at run time inside a flow.
#### Speed-to-lead recipe
To alert the rep a lead was just assigned to:
1. Fondaro Trigger on Lead Assigned.
2. Fondaro → User → Get with User ID set to {{$json["data"]["added"][0]}} (or {{$json["data"]["assigneeIds"][0]}}).
3. Send the notification to the resolved email.
No hardcoded name→ID map — new hires resolve automatically.
The Fondaro Trigger node (webhooks)
The webhook trigger registers a subscription with Fondaro when the workflow is activated and removes it on deactivation. Fondaro then POSTs signed deliveries to your n8n webhook URL as events happen.
Event catalog
| Event | Value |
|—|—|
| New Lead | lead.created |
| Lead Status Changed | lead.statusChanged |
| Lead Assigned | lead.assigned |
| Deal Created | deal.created |
| Deal Stage Changed | deal.stageChanged |
| Deal Won | deal.won |
| Deal Lost | deal.lost |
| Task Created | task.created |
| Task Completed | task.completed |
| Note Created | note.created |
| Lead Tagged | lead.tagged |
| Lead Untagged | lead.untagged |
| Call Logged | call.logged |
| Call Analyzed | call.analyzed |
Most webhook payloads contain IDs and CRM state only, never contact details. The one exception is call.analyzed (see below), which is opt-in and content-bearing. Fetch full records back through the Fondaro action node.
#### Enriched routing fields
So you can route without a follow-up lookup, the lead and deal events carry the relevant non-PII state inline:
| Event | Extra data fields |
|—|—|
| lead.created | status, assigneeIds |
| lead.statusChanged | assigneeIds (current owner set) |
| lead.assigned | assigneeIds (resulting full set), status |
| deal.created | leadId, stage, value |
| deal.stageChanged | leadId |
| deal.won | leadId, value |
| deal.lost | leadId |
| lead.tagged | tagId, changedBy (one delivery per tag added) |
| lead.untagged | tagId, changedBy (one delivery per tag removed) |
Resolving a tagId to its name: call GET /integrations/v1/tags/{tagId} (scope tags:read) with an HTTP Request node — it returns { id, name, color, archivedAt } and works for archived tags. For lead.tagged events the Fondaro node’s Tag > Get on the lead also works; for lead.untagged the id lookup is the only way, since the tag is no longer in the lead’s set.
Filtering tag events: the trigger node has an optional Only for Tags field (shown when Lead Tagged/Untagged is selected). Non-matching deliveries are acknowledged and dropped inside the node, by stable tag ID, so the filter survives renames. Note that merging tags in the dashboard deliberately fires no lead.tagged events for the re-pointed leads — a merge is a catalogue correction, not thousands of lead events.
value is the deal’s own amount (the sale figure you entered) in your organization’s currency — not Fondaro’s lead pricing, which is never emitted. status is the CRM status (lead/New, potential, …); assigneeIds is the set of user_… IDs the lead is assigned to (empty ⇒ unassigned). A lead.assigned delivery, for example:
{
"organizationId": "…",
"leadId": 123,
"added": ["user_abc"],
"removed": [],
"assigneeIds": ["user_abc"],
"status": "lead",
"changedBy": "user_admin"
}
> Ordering note: for leads created through this node, auto-routing assigns the owner after the create commits, so lead.created may carry an empty assigneeIds for those leads.
call.logged fires the first time a call reaches a terminal state, for both outbound and inbound calls attached to a lead. The data carries the outcome and recorded internal owner ID:
{
"organizationId": "…",
"leadId": 123,
"callId": "…",
"ownerId": "userrecordedrep",
"direction": "outbound",
"outcome": "no_answer",
"status": "no-answer",
"durationSeconds": 0,
"missed": true,
"occurredAt": "2026-06-15T10:00:00.000Z"
}
outcome uses the Fondaro vocabulary documented under Reading activity and call outcomes; status is the raw carrier status beside it, so the same call reads no_answer in one field and no-answer in the other. missed is simply outcome !== 'success', so it stays correct for every outcome including ones added later. Branch on missed, or on outcome === 'success', rather than listing every failure value so a new outcome cannot silently fall through your workflow.
It does not fire for calls from unknown numbers that aren’t matched to a lead (there is nothing for a lead-centric automation to act on). Use Lead > Get Activities to read the full call log back.
call.analyzed fires separately, when a call’s AI analysis is ready (summary, sentiment, lead interest, objections, next steps). It’s distinct from call.logged, which still carries the carrier-level outcome unchanged. Analysis runs asynchronously and only after a transcript exists, so this arrives after call.logged (and only for calls that get analyzed). The data:
{
"organizationId": "…",
"leadId": 123,
"callId": "…",
"ownerId": "userrecordedrep",
"direction": "outbound",
"occurredAt": "2026-06-15T10:05:00.000Z",
"analysis": {
"summary": "…",
"sentiment": "positive",
"leadInterest": "high",
"objections": ["price"],
"nextSteps": ["send brochure"],
"coachingTips": ["…"],
"keyTopics": ["financing"]
}
}
> Content-bearing event — opt in deliberately. Unlike every other event, call.analyzed carries an AI-generated summary (free text) that may mention names, numbers or other details spoken on the call. It carries no transcript or recording URL. It only arrives if you subscribe to Call Analyzed; it never rides the other events.
Caller attribution, viewing reads, and semantic workflows
call.ownerId on activities and ownerId on direct calls, call.logged, and call.analyzed identify the recorded internal owner. For outbound calls this is the recorded calling rep. For inbound calls it is the recorded internal owner when present, not the external caller. Missing, blank, or system ownership is null. The current lead assignee and a shared phone number never replace the recorded owner. A departed user’s historical ID remains attached even if User > Get cannot resolve a label.
For per-person reporting, run Lead > Get Activities with Types call, or Lead > Get Calls, then join call.ownerId or ownerId against User > Get Many, or call User > Get. Calls require leads:read; labels require users:read. Get Calls returns { calls, total, hasMore, nextOffset } (default 20, maximum 100). Keep the envelope and loop using nextOffset; changing data can shift offsets, so multiple requests are not a snapshot.
Viewing > Get / Get Many require the additive viewings:read permission. Existing keys do not gain this permission automatically; explicitly grant it on a new key. These read registered own-listing viewings only. All collection filters intersect: property listing UUID, lead ID, collaborator lead ID, hosting agent Clerk ID, deal UUID, status, kind, and viewing time. From includes its viewingAt timestamp; To excludes it. Sort is viewingAt DESC, id DESC. Collections return { viewings, total, hasMore, nextOffset }, default 20 and maximum 100, with notes previews and notesTruncated. Get reads full authorized notes. agentUserId is the host and createdBy is the registering user; use users:read to resolve labels. Relationship IDs do not grant lead contact access. No viewing write operations or viewing webhooks are provided.
Lead > Search keeps its existing name/email/contact search behavior and search operation value. Semantic Search and Match to Listing use existing indexed CRM history and require leads:read. They return the complete { matches, tookMs, reranked } envelope, with ranked evidence snippets and source IDs. Inspect reranked before splitting matches; failed reranking returns fused candidates with reranked: false, while embedding failure is an error. There is no Return All or pagination: maximum results is 1–30, default 10. A rate limit can return 429 with Retry-After.
The semantic corpus includes eligible notes, call transcripts/summaries, human-written emails and lead-scoped Assistant memory over an asynchronous 24-month indexing window. It does not index every lead field or every viewing. Match/source counts describe matching indexed evidence, not total calls or viewings. An empty result means no relevant indexed history was found; it does not prove no such lead exists. Occurred From / To filter the source activity occurrence time (inclusive bounds), not a future trip date mentioned in text. Supply valid ISO timestamps and an ordered range.
Example: Semantic Search for “buyers who mentioned a garden near the beach” → inspect evidence and reranked → Lead > Get each leadId → Viewing > Get Many filtered to that lead → branch on recorded scheduled/completed viewings. For a listing-first workflow, Match to Listing accepts exactly one identity: an own-listing UUID or a connected source identifier plus its source-native ID. It never accepts a URL, and third-party listing text is a query rather than newly indexed corpus.
Version 1.8.0 adds these operation definitions. Deploy the matching API routes before publishing/installing this version; local build and routing tests do not verify a deployed API or package publication.
Signature verification
Every delivery is signed. The node verifies each request before your workflow runs:
X-Fondaro-Signature: sha256=, X-Fondaro-Event and X-Fondaro-Timestamp headers.timestamp.rawBody using the per-subscription secret issued at registration.The Fondaro Polling Trigger node
Use the polling trigger when your n8n instance cannot receive inbound webhooks, which is typical for self-hosted instances behind a firewall or NAT without a public URL. It polls Fondaro for new leads on the schedule you configure, dedupes on lead ID, and tracks its own cursor so each lead is emitted once. When you test the workflow manually it returns the latest leads as a sample.
If your instance is publicly reachable, prefer the webhook trigger: it is instant and cheaper on your rate limit.
Rate limits
Every API key has its own request ceiling. The ceiling is generous for normal workflow volume, but a runaway loop will be throttled with 429 responses. Back off and retry, or slow the offending workflow down.
Compatibility
Requires n8n 1.x and Node.js 20.15 or newer. The package has zero runtime dependencies.