Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.dualentry.com/llms.txt

Use this file to discover all available pages before exploring further.

Salesforce Integration: OAuth, settings, and revenue objects

Connect Salesforce to DualEntry so business accounts, contacts, products, and opportunities at a configured stage flow into DualEntry as customers, items, and draft revenue contracts. Each opportunity line item becomes a performance obligation on the resulting contract, which the rev rec engine picks up after you activate the contract manually. At this point, the connector only syncs from Salesforce into DualEntry. It does not write any information back into Salesforce. DualEntry uses Salesforce’s OAuth 2.0 authorization-code flow against login.salesforce.com, stores and refreshes credentials in compliance with the connected org’s session policy. The minimum version of the Salesforce server REST API supported is 58.0.

Prerequisites

Confirm the following before connecting Salesforce:
  • Admin access in DualEntry with permission to manage integrations.
  • Permission in your Salesforce org to authorize OAuth apps (System Administrator, or a profile with “API Enabled” and Connected App access).
  • The DualEntry Connected App installed in your Salesforce org. Salesforce has required Connected Apps to be packaged and installed in the customer’s org before OAuth succeeds since the September 2025 platform change; this applies to every edition. DualEntry publishes the package; your Salesforce admin installs it and approves the api and refresh_token scopes before you connect.
  • The DualEntry company that will own synced contracts and items already exists in DualEntry. Sync writes to a single accounting entity per Salesforce integration; consolidation across entities is handled by your existing multi-entity setup, not by the connector.
  • A chart of accounts that already contains the GL accounts you will pick in integration settings (accounts receivable, deferred revenue, income, expense).
  • Agreement with your revenue team on which Salesforce opportunity stage triggers sync into DualEntry. The default is Closed Won, and the trigger-stage dropdown is populated at connect time from your org’s active opportunity stages.
  • Active products tied to the Standard Pricebook in Salesforce. The connector imports Product2 records that have at least one PricebookEntry on the Standard Pricebook; products without one are skipped.

Step 1: Connect with OAuth

DualEntry uses the OAuth 2.0 authorization-code flow against Salesforce’s standard auth host.
  1. Navigate to Company → Integrations, find Salesforce and click Connect.
  2. Approve the DualEntry app in Salesforce. Salesforce will redirect to DualEntry’s callback URL with the authorization.
  3. DualEntry exchanges credentials from Salesfoce, as well as the per-org instance_url, and validates that the REST API version your org advertises that meets or exceeds 58.0.
After a successful exchange, the integration’s status moves to connected and the settings drawer becomes available. If your org exposes no API version at or above the connector minimum, the integration moves to error with status UNSUPPORTED_API_VERSION. You can still reconnect after your Salesforce admin restores a supported release. Salesforce Developer Edition orgs also authenticate through login.salesforce.com. For platform outages, see Salesforce Trust. For details about the Salesforce API behavior, see the Salesforce REST API Developer Guide.

Step 2: Configure required integration settings

These parameters configure the synchronization process to know which DualEntry company owns the imported records and which GL accounts to apply to new items. Sync will not produce clean records until every required setting resolves to a valid record.
SettingPurpose
Company (accounting entity)The DualEntry company that owns synced contracts and items.
Accounts receivable accountAR account on synced customers; used when invoice writeback ships in a later release.
Deferred revenue accountDeferred revenue account applied to service-type items so obligations defer correctly.
Income accountDefault income account on items created from Salesforce products.
Expense accountDefault expense account on the same items.
Opportunity trigger stageSalesforce opportunity stage that promotes it into a draft contract. The default is Closed Won. The dropdown lists your org’s active stages.
Open Company → Integrations → Salesforce and then click Settings and complete each field. The salesforce_api_version setting is detected at connect time and not user-editable and records the negotiated REST API version. The historical sync window defaults to 90 days through the salesforce_historical_sync_days setting. Sync uses that window only on the first run for each record type. Subsequent syncs honor the per-record-type LastModifiedDate watermark described under Incremental sync.

Step 3: Run sync

Trigger a sync from Company → Integrations → Salesforce → Sync. See the next section for details on the synchronized entities.

What syncs into DualEntry

The connector imports four Salesforce object types, each mapping to a specific DualEntry record. Sections below describe how each object resolves and how subsequent syncs update it.

Products to items

Salesforce Product2 records with at least one Standard Pricebook entry become DualEntry items. The item carries the Salesforce product name, description, and a SKU derived from ProductCode. When a product has no ProductCode, the Salesforce record ID is used as the SKU instead. Income, expense, and deferred revenue accounts come from the values you set in Step 2.

Accounts to customers

Each Salesforce business account becomes a company-type DualEntry customer. Billing address, phone, and website propagate from the account when present. Person Accounts are excluded from V1 and do not sync; contacts belonging to a Person Account are also skipped on the contact pull.

Contacts to individual customers

Each Salesforce Contact whose AccountId matches a synced account becomes an individual-type DualEntry customer with email, phone, and mailing address. DualEntry’s customer model is flat (no separate contact entity under a parent customer), so contacts that share an account do not collapse into a single customer record; each contact is its own individual customer. The contact pull is scoped to WHERE AccountId IN (...) so unrelated contacts do not import.

Opportunities to draft contracts

Each opportunity at the configured trigger stage becomes a draft contract:
  • Customer resolves from AccountId via the account integration record. If the account has not been synced or has no DualEntry customer mapping, the opportunity is flagged with MISSING_ACCOUNT_MAPPING.
  • Contract date and start date are both set to Opportunity.CloseDate in V1. Custom-field overrides ship in a later release.
  • Currency is the configured company’s default currency.
  • Performance obligations are built one per OpportunityLineItem, with quantity from Quantity, rate from UnitPrice, and standalone_selling_price set to rate * quantity. Recognition strategy defaults to immediate; pick the final strategy during contract activation.
  • Item on each obligation resolves from Product2Id via the product integration record. Lines that reference a product without a mapping surface as MISSING_PRODUCT_MAPPING.
For revenue recognition after activation, see Revenue recognition.
Contracts are always created in draft status. The revenue recognition engine does not pick them up until you review each contract, set the final recognition strategy and end dates, and activate it manually in DualEntry.

Incremental sync

After the first full sync, each subsequent run pulls only records modified since the last successful sync. The watermark is computed per source type as the maximum LastModifiedDate across previously synced records of that type, using Salesforce’s own timeline rather than DualEntry’s local clock. This avoids a gap where records modified during a previous sync’s fetch window would be missed by a clock-based watermark. Salesforce does not propagate Opportunity.LastModifiedDate when only a child OpportunityLineItem changes. The connector compensates by issuing a second query for line items modified since the watermark, then fetching any parent opportunities not already covered by the first query. The result is the same as if Salesforce supported a single query with a semi-join, but split into two steps because SOQL rejects that syntax. The first synchronization for each source type fetches using 90 days as the default watermark. Once the watermark identifies which opportunities to refresh, the connector pulls the full current set of OpportunityLineItem rows for those opportunities. Rebuilding each contract’s performance obligations requires the complete line-item state, including deletions, so a removed Salesforce line still flows through and updates the draft contract.

Current limitations

Several aspects of the connector in its current version are worth knowing:
  • One-way only. DualEntry does not push contracts, customers, items, or accounting state into Salesforce. There is no writeback path.
  • Person Accounts excluded. the connector filters to IsPersonAccount = false. Contacts whose parent is a Person Account do not sync either.
  • Stage regression does not unwind. Once an opportunity has been promoted to a draft contract, demoting it back to a non-trigger stage in Salesforce does not delete or archive the draft contract. Cancel the draft contract manually in DualEntry if needed.
  • Custom-field mapping not yet supported. Mapping Salesforce custom fields on opportunities or line items to DualEntry classifications or custom fields is on the roadmap. V1 supports standard Opportunity and OpportunityLineItem fields only.
  • Line items drive contract value. Opportunity.Amount is logged as a warning if it diverges from the sum of the total prices from all OpportunityLineItem, but the contract is still built from line items.
  • Standard Pricebook only. Products without a PricebookEntry on the Standard Pricebook are skipped.
  • Rate limits enforced per Salesforce org. Sync paces requests against your org’s daily API allocation and pauses when usage crosses 95% of the allocation to accommodate for higher priority interactions.

Troubleshoot sync errors

When a record fails, the error appears in the Integration Errors log under Settings → Integrations → Salesforce. Per-record codes are stored verbatim on IntegrationRecord.import_error; integration-wide failures (authentication, API version negotiation, rate-limit trip) set Integration.status = ERROR with a status message on the integration itself instead. The most common cases:
Code or symptomCauseResolution
MISSING_ACCOUNT_MAPPINGThe opportunity’s Salesforce account has no DualEntry customer mapping.Run a full sync so the account syncs first; if the account is a Person Account, switch it to a business account or skip the opportunity.
MISSING_PRODUCT_MAPPINGAn opportunity line item references a Product2 with no DualEntry item.Verify the Salesforce product is active and has a Standard Pricebook entry, then re-run sync.
MISSING_PRODUCT_ON_LINE_ITEMAn OpportunityLineItem has no Product2Id.Attach a product to the line in Salesforce, or remove the line.
MISSING_LINE_ITEMSThe opportunity has zero OpportunityLineItem rows.Add at least one line item in Salesforce, or move the opportunity out of the trigger stage.
MISSING_COMPANYThe Company setting is empty or points at a deleted DualEntry company.Open Settings → Integrations → Salesforce → Settings and pick a valid company.
MISSING_INCOME_ACCOUNT, MISSING_EXPENSE_ACCOUNT, MISSING_DEFERRED_REVENUE_ACCOUNTThe matching GL account setting is empty or invalid.Pick a valid GL account in integration settings.
UNSUPPORTED_API_VERSION (integration-level)The Salesforce org exposes no REST API version at or above the connector minimum. The status is set on the integration, not on individual records.Ask your Salesforce admin to confirm the org is on a supported release, then reconnect.
”Salesforce daily request limit reached.” (integration-level)The org’s daily API allocation reached the 95% trip threshold; internally logged as RATE_LIMIT_BREAKER_OPEN.Wait for the cooldown to elapse; investigate whether other tools are competing for the same allocation.
OAuth fails immediatelyMismatched redirect URI, missing or unapproved Connected App, or the user lacks “API Enabled”.Verify the Connected App is installed and approved in your org, check the redirect URI matches the deployment, and confirm the user’s profile allows API access.
Repeated 401 mid-syncRefresh token revoked or session policy tightened in Salesforce.Reconnect OAuth in DualEntry to obtain new tokens.

Result

After OAuth, settings, and a successful sync, Salesforce business accounts and their contacts appear as customers, Salesforce products appear as items, and opportunities at your configured stage appear as draft contracts with performance obligations linked to those items and customers. Activate each draft contract in DualEntry to push it into Revenue recognition. To compare CRM connectors, see the HubSpot integration. To connect more systems, return to Integrations. For Salesforce’s own platform reference, see the Salesforce Developer documentation.
Last modified on May 28, 2026