Feeding ERP and CRM integrations from SharePoint-held records

One SQL copy of a SharePoint list for every ERP and CRM integration to read, so none of them calls Graph or parses lookup IDs.

Book a call

Book a call to talk through your use case, see further walkthroughs or demos, and set up a trial in your own environment.

Last updated

The list an integration needs sits in SharePoint, and every integration ends up calling the Graph API on its own schedule, hitting the same throttling and lookup problems.

When three systems need the same SharePoint list, the usual result is three integrations polling Graph on three schedules. Each handles throttling its own way, each receives lookup columns as IDs, and each breaks separately when the list changes. An approved supplier list kept in SharePoint so buyers can edit it is a typical case: the ERP needs it so purchase orders go to approved suppliers, the CRM team wants it too, and an ERP job’s paging can fail the week the list passes 5,000 items.

One copy for every system that reads the list

SPSync replicates the lists an integration depends on into tables in your database and refreshes them on a schedule. The ERP job, the CRM connector and any stored procedure read SQL. SPSync is the only thing still talking to Graph, so a third or fourth reader adds no load on SharePoint.

Tables an integration can join

Each list is a table with its calculated, person, choice and lookup columns; managed metadata columns are not replicated yet. A lookup is a key column into the table for the target list, so a purchase order line joins to its supplier like any two SQL tables. A multi-choice column becomes a child table with one row per value, so a stored procedure never parses a delimited string; multi-value lookups follow the same pattern once checked against a live tenant. People fields resolve into one sp_users table shared by every replicated list.

Deleted items stay in the table with sp_deleted_at set. An integration that reconciles against its previous run keeps the evidence that a supplier existed and was removed.

Getting the integration onto the replica

Grant only what the integration needs. Sign in with delegated access to the sites it reads, or have an admin grant SPSync Sites.Selected app permissions to just those sites, and pick the lists themselves; the whole site is rarely needed. Point SPSync at SQL Server, Azure SQL or Postgres and it creates and maintains the schema. Then change the integration’s job or connector to read the replicated tables.

For a small integration owned by one group, the desktop app is often enough. When the replication should sit next to the integration code, the command line keeps its config in the same repository, reviewed in the same pull requests.

When the list changes shape

A buyer adds a column and it appears in the table on the next run. A column whose type changes moves into its own _v2 column on SQL Server, PostgreSQL, MySQL, ClickHouse, Oracle, SQLite and DuckDB, so the integration’s next run reads what it read yesterday. Other destinations do not fork a retyped column today. Large lists are supported, and SPSync never writes to SharePoint or installs anything there.

Desktop, Azure or command line

All three editions run the same engine, write the same tables and use the same licence key, so the choice comes down to who looks after the sync.

Desktop app

For macOS and Windows. Sign in with Microsoft, tick the lists, choose a database and sync from your own machine on the schedule you set. Delegated sign-in needs no admin unless your tenant requires admin consent for apps.

In your Azure

SPSync in your Azure, a managed application for your own subscription, is deployed with you on a call while the Marketplace listing is prepared. Once deployed it is always on, so the sync keeps running with every laptop closed.

Command line

Every replication is a YAML file you can review and commit. Run it from a server, a container or a CI pipeline, and re-run it safely any time.

Set it up

Ready to run this against your own site? The setup guide takes you from install to a first replication, and the SharePoint connection guide covers signing in.

Script it

Everything you can click, you can script. The CLI, YAML config and automation below reproduce this scenario exactly. See the CLI reference.

spsync auth login --client-id 00000000-0000-0000-0000-000000000000
spsync export-config --output replication.yaml --pipeline-name erp_crm_integration --prefix erp_crm_integration --destination-kind mssql --connection-string-env SPSYNC_DB_CONNECTION --dataset-name erp_crm_integration --client-id 00000000-0000-0000-0000-000000000000 --site-url https://contoso.sharepoint.com/sites/example --all-lists
spsync doctor replication.yaml
spsync run replication.yaml
pipeline_name: erp_crm_integration
prefix: erp_crm_integration
auth:
  mode: delegated
  client_id: 00000000-0000-0000-0000-000000000000
  tenant_id: common
destination:
  kind: mssql
  connection_string_env: SPSYNC_DB_CONNECTION
  dataset_name: erp_crm_integration
scopes:
  - site_url: https://contoso.sharepoint.com/sites/example
    mode: entire_site
reconcile_interval_days: 7
create_reporting_views: false

Cron (SQL Server nightly):

# every night at 02:00
0 2 * * * cd /opt/spsync && SPSYNC_DB_CONNECTION="$(cat /etc/spsync/db.env)" uv run spsync run replication.yaml >> /var/log/spsync/run.log 2>&1

GitHub Actions:

# .github/workflows/spsync-run.yml
name: SPSync
on:
  schedule:
    - cron: "0 2 * * *"
  workflow_dispatch: {}
jobs:
  run:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: astral-sh/setup-uv@v3
      - run: uv sync
      - run: uv run spsync run replication.yaml
        env:
          SPSYNC_DB_CONNECTION: ${{ secrets.SPSYNC_DB_CONNECTION }}
          SPSYNC_LICENCE_KEY: ${{ secrets.SPSYNC_LICENCE_KEY }}

Azure DevOps:

# azure-pipelines.yml
schedules:
  - cron: "0 2 * * *"
    branches: { include: [main] }
    always: true
steps:
  - script: uv sync
  - script: uv run spsync run replication.yaml
    env:
      SPSYNC_DB_CONNECTION: $(SPSYNC_DB_CONNECTION)
      SPSYNC_LICENCE_KEY: $(SPSYNC_LICENCE_KEY)

Windows (PowerShell + Task Scheduler):

# Windows Task Scheduler action (see /docs/cli/automation/)
$env:SPSYNC_DB_CONNECTION = (Get-Content C:\spsync\db.env)
uv run spsync run C:\spsync\replication.yaml
schtasks /Create /SC DAILY /ST 02:00 /TN "SPSync" /TR "powershell -File C:\spsync\run.ps1"

macOS (launchd):

# ~/Library/LaunchAgents/io.spsync.run.plist, then: launchctl load ~/Library/LaunchAgents/io.spsync.run.plist

Secrets (licence key, connection strings) come from the environment, so never write them into the YAML. See scheduling and secrets.

Book a call

Book a call to talk through your use case, see further walkthroughs or demos, and set up a trial in your own environment.