> ## Documentation Index
> Fetch the complete documentation index at: https://docs.allgoodhq.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Data Sharing

> How data gets into allGood from Salesforce, your warehouse and your website, how it gets back out to your warehouse, and how allGood writes back to Salesforce.

allGood runs on Snowflake. Your account has its own isolated Snowflake database, and everything the platform knows about your people and companies lives there. This page is about the edges of that database: how your Salesforce data gets in, how allGood's data gets back to your warehouse, and how allGood writes what it decides back to Salesforce. For what happens inside, see [Data Architecture](https://docs.allgoodhq.com/mk/data/architecture.md). For the setup steps, see [Snowflake](https://docs.allgoodhq.com/ops/snowflake).

## The big picture

Three outside systems feed allGood, and allGood feeds two of them back.

```mermaid theme={null}
flowchart LR
    SFDC["Your Salesforce"]
    WH["Your Snowflake"]
    WEB["Your website and tools"]
    AG["allGood<br/>records · activities · decision log"]

    SFDC -- "your own replication" --> WH
    WH -- "inbound share<br/>+ warehouse sync" --> AG
    SFDC -- "direct integration" --> AG
    WEB -- "edge capture" --> AG
    AG -- "outbound share<br/>(live views)" --> WH
    AG -- "Salesforce sync" --> SFDC
```

| Path | Mechanism | Freshness |
| - | - | - |
| Your warehouse → allGood | A Snowflake share you publish; allGood reads it and periodically syncs bound tables into records | Periodic |
| Salesforce → allGood, directly | allGood's own Salesforce user reads on a schedule, then the same sync turns it into records | Scheduled |
| Website, forms, email → allGood | Captured as it happens and loaded as activity | Seconds |
| allGood → your warehouse | A Snowflake share allGood publishes; your queries read live views | Live, no sync |
| allGood → Salesforce | The Salesforce sync, over the Salesforce API | When a mapped field changes |

Two things hold on every path in. **Nothing is overwritten.** Each source lands in its own record type (Salesforce Lead, Salesforce Contact, a product-usage table from your warehouse), and trusted values are written by policies, not by a sync. **Every change is logged.** A sync does not update rows; it writes a decision with a reason you can read ("Warehouse sync: Salesforce Lead"), and that log is what reaches your warehouse as `DATA_CHANGE`.

## How your Salesforce data gets into allGood

There are two routes. Both end in the same record types, which the rest of allGood treats identically, but they differ in what you connect and what they enable.

**Through your Snowflake.** If Salesforce already lands in your warehouse (Fivetran, Salesforce Data Cloud, an ETL job), you publish a share containing those tables and allGood mounts it read-only. allGood binds each table to a record type: your `Lead` table becomes Salesforce Lead, `Contact` becomes Salesforce Contact, and so on. No Salesforce API user is involved, and allGood never talks to your org on this route. Setup: [Inbound Sharing](https://docs.allgoodhq.com/ops/snowflake/inbound-sharing.md); which tables to include: [Salesforce data via Snowflake](https://docs.allgoodhq.com/integrations/salesforce/snowflake).

**Directly.** allGood gets its own user in your Salesforce org and reads through the API: a **Dataset** bulk-queries each object on a schedule, and the same warehouse sync turns it into the same record types. This needs an API user with the right object permissions and uses your org's API quota. In return it is also the connection allGood writes back through. Setup: [Salesforce](https://docs.allgoodhq.com/integrations/salesforce.md).

| | Through your Snowflake | Directly |
| - | - | - |
| What you connect | Your Snowflake account | Your Salesforce org |
| Salesforce API user and quota | Not needed | Needed |
| Freshness | Your replication's lag, then the periodic sync | The Dataset schedule, then the periodic sync |
| Write-back to Salesforce | No | Yes |
| Non-Salesforce data too | Yes, anything in your warehouse | No |

The routes aren't exclusive. The usual arrangement is both: the direct integration for write-back and real-time actions, the share for the broader account, territory and product-usage context those actions depend on.

**What the sync does.** For each bound table, allGood needs three things: which table, which column uniquely identifies a row, and which column changes whenever a row is updated (for Salesforce tables, `Id` and `SystemModstamp`). The sync is periodic, on a timer, because turning rows into records means comparing each row with what allGood already holds and logging every difference; reading the share itself is live. Each pass reads the rows whose update column has moved, diffs them field by field, writes one logged decision per changed row, marks records whose source row has gone as deleted (the record and its history stay), then runs identity resolution and wakes any policy that reads a changed field. The records are a copy owned by allGood: they outlive the share, frozen, if you disconnect it.

Each bound record type has a **Sync from Warehouse** tab under **Orchestration → Database → Record Types**: timer or on demand, where it reads from, the key column, the delete behavior, a 30-day chart of records written, and **Sync Now**.

## How allGood data gets back to your warehouse

The outbound share is a Snowflake share allGood publishes to your account. You accept it once, mount it as a database (`ALLGOOD_SHARED_DATA` by default), and your analysts query allGood's data where it sits, joined against whatever else is in your warehouse. Setup, and the full schema, are on [Outbound Sharing](https://docs.allgoodhq.com/ops/snowflake/outbound-sharing.md).

You get six views in the `MK_SHARE` schema:

* `PERSON` and `COMPANY`: one typed, flattened row per live person or company, with the standard fields as columns, Smart Fields as one object, and every other record's fields in another. The place to start.
* `ENTITY` and `RECORD`: the result of identity resolution, and each source record with its current values and the entity it belongs to.
* `ACTIVITY`: every event, with which path captured it.
* `DATA_CHANGE`: the decision log, with the reason for every change.

**It is live, with no sync.** There is no export, no pipeline and no cadence in this direction; Snowflake reads allGood's views from your account at query time. `ACTIVITY` and `DATA_CHANGE` are current the moment you query; `PERSON`, `COMPANY`, `ENTITY` and `RECORD` are summaries that can trail the log by up to about five minutes. A policy's decision is readable in your warehouse as soon as it is applied.

**Cost.** Shared data does not count against your Snowflake storage; you pay only for the compute you spend querying it. The views are secure views, which Snowflake optimizes less aggressively than local tables, so for heavy or repeated analysis materialize what you need into your own account first. The share is meant to feed your modelled layer, not replace it.

## How allGood writes back to Salesforce

Reads are one half. The other half is getting what allGood decides (a normalized title, a lead score, a matched account, a campaign response) back into Salesforce, where your sales team already works. That is the **Salesforce sync**.

> [!NOTE]
>
> Write-back needs Salesforce API credentials from the [Salesforce integration](https://docs.allgoodhq.com/integrations/salesforce.md). If Salesforce reaches allGood only through your Snowflake share, nothing can be written back until the integration is connected. allGood support helps you configure the sync once it is.

**What it writes.** Two things. Changed **fields** on Salesforce Leads, Contacts and Accounts, from a mapping on a record type (usually your Smart Fields). And **campaign membership**: a plan that syncs mirrors its members onto a Salesforce Campaign, either one you link by Id or one allGood creates named after the plan, with each plan state mapped to a Campaign Member Status. The field mapping lives on the record type's **Salesforce Sync** tab:

| Setting | What it does |
| - | - |
| Salesforce object | `Lead`, `Contact` or `Account`. A record type can map to more than one. |
| Field mapping | Which allGood field goes to which Salesforce field, such as a lead score to `Lead_Score__c`. |
| Behavior | **Overwrite** (sent on every change; the default), **Fill if empty** (only when Salesforce's value is blank), or **Create only** (only when the sync creates the record). |
| Blank values | Values to treat as empty, such as `Unknown`, so they never replace a real value. |
| Condition | A formula that must be true for the write to happen, such as only for people in a given region. |
| Create missing Leads | Whether the sync may create a Lead for a person who has none. It never creates one for someone who already has a Contact. |

Mappings are checked against your org when published: every mapped field must exist and be writable, and fields Salesforce requires on create need a default.

**How it runs.** The sync is an action channel. It follows the decision log and acts on each change past the point it last reached: a mapped field changes, or a syncing plan moves someone between states, and the sync wakes. It does not poll Salesforce and does not run on a timer.

```mermaid theme={null}
flowchart LR
    LOG["Decision log"] -- "a mapped field or<br/>plan state changed" --> SYNC["Salesforce sync"]
    SYNC -- "who is this person now?" --> SF["Your Salesforce"]
    SYNC -- "write fields and<br/>campaign members" --> SF
    SYNC -- "sent, skipped or failed,<br/>per field" --> TL["Person's timeline"]
```

Before writing, it asks Salesforce who each person is right now, by the same linkage fields identity resolution uses, rather than trusting its own copy of the Salesforce records. It writes the current allGood value to the current Salesforce target, so a repeated pass never double-writes, and it never writes allGood-derived values into its own Salesforce record types, so nothing loops back. A converted Lead is written through to its Contact. If your org's API usage nears its daily limit, the sync pauses and tries again later.

**Where you see it.** **Settings → Marketing Kernel → Action Channels** lists each channel with an on/off switch; while one is paused, changes queue, and on resume you choose **Execute what's waiting** or **Skip what's waiting**. The Salesforce channel's page shows today's API usage, each mapping's validation, each syncing plan with its status table, and **Re-run this one now** for a failed item. A person's timeline records each field as sent, skipped (with the reason) or failed, so "did this reach Salesforce?" has an answer per field.

This is separate from the Salesforce actions in [Email Reply Management](https://docs.allgoodhq.com/use-cases/erm/salesforce-actions.md), which respond to email replies and are configured on their own.

## Other ways data enters allGood

The paths above carry records. Activities, the things people do, mostly arrive another way:

* **Your website.** Hosted pages, forms, the tracking tag and click redirects are captured as they happen and land in allGood within seconds; a form submit that triggers a plan is typically acted on in about fifteen seconds. See [Web Edge](https://docs.allgoodhq.com/mk/developer/web-edge/index.md).
* **Email.** allGood's own sends (delivered, opened, clicked, unsubscribed) and your provider's delivery receipts.
* **Other tools.** Webinar, demo and ad platforms send events over [webhooks](https://docs.allgoodhq.com/mk/integrations/webhooks.md), each tagged with its source.
* **File uploads.** Attendee lists and content-syndication leads arrive as a spreadsheet; each row becomes a record and, where it describes an event, an activity.

Every activity carries its source, and that column comes back to you in `ACTIVITY`. For which tool uses which path, see [How allGood connects with your other marketing tools](https://docs.allgoodhq.com/mk/integrations/how-allgood-connects.md).

## Good to know

* **Shares are same-region.** allGood has Snowflake accounts available in AWS us-west-2 and AWS us-east-1. Contact allGood support if you need a Snowflake data-share in a different region. The Snowflake Data Sharing settings page shows which region your allGood account is in.
* **Admin role, and ACCOUNTADMIN.** You need the Admin role in allGood to see **Settings → Platform → Snowflake Data Sharing**, and `ACCOUNTADMIN` (or a role that can create shares and databases) on your Snowflake side.
* **One inbound share per workspace, one workspace per Snowflake account.** To share several databases, grant them all to the same share. Views in a share must be secure views.
* **The update column must only move forward.** A column that can go backwards, or that a bulk edit resets, strands rows with no warning. `SystemModstamp` behaves; a hand-maintained `updated_at` sometimes doesn't.
* **A bound record type has one writer: the sync.** Nothing else writes to it, which is what makes delete detection safe. Revoking a grant looks like an empty table, and an empty table deletes nothing.
* **Leaving a plan does not remove the Campaign Member.** The sync adds and updates members; removal isn't supported, so a person who leaves a plan keeps their last status in Salesforce.
* **allGood configures the bindings and mappings.** You run the share scripts and grant access on your side. Contact support with the table, key column and update column for each table you want synced.

> [!NOTE]
>
> Questions? Reach out to [support@allgoodhq.com](mailto:support@allgoodhq.com), or ask your allGood implementation lead.

> [card] [Data Architecture](https://docs.allgoodhq.com/mk/data/architecture.md)
>
> What happens inside: records, entities, identity resolution, activities and the decision log.

> [card] [Orchestration](https://docs.allgoodhq.com/mk/data/orchestration.md)
>
> Policies, Smart Relationships and plans, the things that write the decisions the Salesforce sync carries back.

> [card] [Inbound Sharing](https://docs.allgoodhq.com/ops/snowflake/inbound-sharing.md)
>
> Publish a share so allGood can read your warehouse.

> [card] [Outbound Sharing](https://docs.allgoodhq.com/ops/snowflake/outbound-sharing.md)
>
> Accept allGood's share and the full schema of what you receive.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.