> For the complete documentation index, see [llms.txt](https://help.tahua.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://help.tahua.io/product-news/2026/readme/fundraiserone-contact-sync-integration.md).

# FundraiserOne Contact Sync Integration

## Overview

A per-organisation integration that syncs registration entities from Tahua to FundraiserOne (F1) as contacts. When a registration is submitted or an approved registration is updated, the corresponding F1 contact record is created or updated automatically.

## Architecture

The integration follows the same pattern as the existing Xero, Accredo, QuickBooks, and Slack integrations:

* **Integration concern** (`FundraiserOneIntegration`) on Organisation — stores API key (encrypted via SimpleEncryption), base URL, and enabled flag in the `settings` hash column
* **API client** (`FundraiserOneClient`) — Faraday-based HTTP client authenticating via `SecretKey` header against `PUT /api/v1/Customer/`
* **Integration log** (`FundraiserOneLog`) — STI subclass of `IntegrationLog`, automatically visible in the admin integration logs UI
* **Sync job** (`FundraiserOne::SyncContactJob`) — builds the payload, calls upsert, stores the returned `ContactID` on the registration entity, and logs success/failure

## Payload Mapping

Each registration entity is synced as a single F1 contact. The payload is built by `FundraiserOnePayloadBuilder`:

**Fixed mappings** from User (account owner) and RegistrationEntity:

* `User.first_name` / `last_name` / `title` / `email` to `FirstName` / `LastName` / `Title` / `Email`
* `RegistrationEntity.name` to `Organisation`
* `RegistrationEntity.id` to `ReferenceId` (used for deduplication)

**Configurable mappings** from registration form fields — each form field can be assigned an F1 target field (e.g. `BusinessPhone`, `Email`, `Region`, `PostalAddressCity`) via the form builder UI. The builder reads all fields with a `fundraiser_one_field` value and maps their input values into the payload.

**Contact type** is set per registration type via the `fundraiser_one_contact_type` column (e.g. "Individual", "Organisation"), allowing orgs with multiple applicant types to send the correct value.

## Sync Triggers

| Event                                      | Behaviour                                                                                                                 |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------- |
| Registration submitted                     | `SyncContactJob` enqueued immediately                                                                                     |
| Approved registration updated (field edit) | `SyncContactJob` enqueued with a 30-second debounce (Redis `SET NX`) to coalesce rapid field edits into a single API call |

The debounce uses an atomic Redis key (`f1_sync_pending[org_id][entity_id]`) with a TTL. The first change sets the key and schedules a delayed job; subsequent changes within the window are no-ops. The job clears the key on execution so future edits can trigger a new sync.

## Per-Field Form Builder Mapping

Rather than configuring field mappings at the organisation level, each form field can be assigned an F1 target field directly in the form builder. A dropdown in the field context bar lets admins select which F1 field (if any) a form field maps to. This is stored as `fundraiser_one_field` on the field record.

## Organisation Setup

Configuration is done via Rails console:

```ruby
org = Organisation.find_by(internal_sub_domain: 'your-org-subdomain')
org.switch!

org.fundraiser_one_integration_enabled = true
org.fundraiser_one_api_key = 'your-f1-secret-key'
org.save!

# Set contact type per registration type
reg_type = org.registration_types.find_by(name: 'Your Type')
reg_type.update!(fundraiser_one_contact_type: 'Organisation')

# Verify connection
org.fundraiser_one_client.test_connection!
```

## Admin UI

* **Registration type edit modal**: shows a "FundraiserOne Contact Type" text field when the integration is enabled
* **Applicant show page** and **registered provider show page**: display the F1 Contact ID when present
* **Integration logs**: all sync attempts (success and failure) appear in admin settings > integration logs

## Error Handling

* API errors raise `FundraiserOneApiError`, logged to `FundraiserOneLog` and reported to Rollbar, then re-raised for Sidekiq retry
* Unexpected errors are logged and reported but not re-raised (to avoid infinite retry loops)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://help.tahua.io/product-news/2026/readme/fundraiserone-contact-sync-integration.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
