SOS Inventory technical guide
Everything an engineer needs to connect SOS Inventory to Salesforce: architecture, the exact build steps with real code, field mapping, the data model, security, monitoring, and the pitfalls we design out.
A two-way Salesforce and SOS Inventory order-to-cash pipeline. We have shipped it across 1 client project and 13 build tasks.
The value is one source of truth: both systems stay current, records reconcile automatically, and every error is logged for review.
We build it the Salesforce-native way: a Connected App and Named Credentials so no secrets ever live in code, field mappings that respect your data model, and record-triggered automation that does the work.
Every SOS Inventory build is delivered by a senior Salesforce architect on a fixed price, tested end to end in a sandbox, deployed to your org, and backed by 30 days of hypercare. You own the result: documented, source-controlled, and free of black-box middleware lock-in.
How SOS Inventory connects to Salesforce
The real connection surface: how it authenticates, what it is built on, the endpoints and events in play, and where the reference docs live.
- Connects via
- Custom Apex REST callouts (via Named Credential) to the SOS Inventory v2 REST APIScheduled Queueable Apex running updatedSince delta pullsOAuth 2.0 app registered via the SOS developer portal
- Package
- Custom build (no managed package)
- Authentication
- OAuth 2.0 (authorization code grant); Bearer access token in the Authorization header; auto-refresh using the stored refresh token
- API type
- REST
https://api.sosinventory.com/api/v2/- Reference
- Official developer docs
Key endpoints
/estimate/salesorder/invoice/item/customerBuild this with AI agents
Copy the full playbook as a prompt for any coding agent, or install it as a Claude Code skill. Either way your agent builds with our exact approach: the architecture, the Apex code, the field mapping, the rate limits, and the pitfalls to design out. Free, no signup.
Loading the SOS Inventory playbook...What we build for a SOS Inventory integration
An end-to-end bi-directional Salesforce and SOS Inventory integration: OAuth with automatic token refresh, the full quote-to-invoice pipeline, scheduled delta pulls, and bi-directional deletion sync.
Self-healing OAuth
Built OAuth with a SOSInventoryAuth class and a token scheduler that checks expiry and refreshes the access token before it lapses, keeping the sync uninterrupted.
Quote-to-invoice pipeline
Mapped Quote to SOS Estimate (only when the Quote is Accepted), synced Orders back from SOS, and mapped Invoices to Salesforce with child Invoice Line Items.
Scheduled delta pulls
Queueable jobs with an updatedSince filter pull Customers to Accounts every two hours, plus Products and Invoices, upserting by SOS id.
Invoice-driven pipeline moves
Each matched invoice flips an Opportunity to Closed Won or creates one, with SOS invoice lines mapped to child records.
Deletion sync and validation
Added bi-directional deletion sync (delete an Account in Salesforce and the SOS Customer is deleted) and validated the whole API surface in a dedicated Postman workspace.
Real components we ship
What you will need
What we confirm on both sides before writing a line of code.
From trigger to record, end to end
The production runtime flow, with what happens in each system.
- 01
Change in either system
Either systemA change in Salesforce or in SOS Inventory starts the sync.
$Salesforce side via flow or Change Data Capture; vendor side via webhook or scheduled pull. - 02
Transform and route
In transitThe integration layer maps and transforms the payload between the two data models.
$DataWeave or Apex converts between the Salesforce and vendor schemas. - 03
Push and scheduled pull
In transitRecords push on trigger and pull on a schedule so both sides stay current.
$An updatedSince delta filter keeps each batch small and current. - 04
Reconciled both ways
Both systemsRecords reconcile across systems, with every error logged for review.
$Conflicts resolved by rule; failures land in an Error Log object for replay.
How the data actually flows
Left to right: sources, the integration layer, Salesforce, and the outcomes it drives.
// sources feed the integration layer, Salesforce persists, outcomes ship
The objects behind the integration
The Salesforce objects we read and write, what each one is for, and the fields that carry the load.
| Object | Purpose | Key fields |
|---|---|---|
Invoice | The primary Salesforce record SOS Inventory data maps onto. | External_Id__c, Name, Status |
Account | Matched or created for the customer or company behind the record. | Name, External_Id__c |
Error_Log__c (custom) | Captures every request, response, and failure so anything can be replayed. | Payload__c, Status__c, Related_Id__c |
Sync_Log__c (custom) | Tracks the last-synced timestamp per record for delta pulls. | Last_Synced__c, Direction__c |
Salesforce objects typically in play for SOS Inventory
Build the SOS Inventory integration
Every step we follow to ship a production-grade build, with the code that matters.
Plan the integration and prerequisites
Before any code, we lock down access and design how SOS Inventory and Salesforce will talk.
- A Salesforce edition with API access (Enterprise, Unlimited, or Developer) and a dedicated sandbox track
- A SOS Inventory account on a plan with API access, plus admin rights on both systems
- A dedicated Salesforce integration user with a minimum-access permission set, never a personal admin login
- Decide direction (inbound, outbound, or bidirectional) and cadence (real-time callouts vs scheduled batch)
- Budget the daily API request allocation and per-transaction callout limits up front
Register the app and scope OAuth in SOS Inventory
We create the connection on the SOS Inventory side and collect exactly the access Salesforce needs.
- Create an OAuth app or scoped API token in SOS Inventory to get the Client ID and Client Secret
- Grant the minimum OAuth scopes required, and note the API base URL and version
- Whitelist the Salesforce callback URL if the tool uses the authorization-code flow
Store secrets with External and Named Credentials
We use the modern Salesforce auth stack, so no secret ever lives in code or metadata.
- Create an External Credential (OAuth 2.0 or custom) with a named principal
- Create a Named Credential pointing at the SOS Inventory base URL and enable the generated authorization header
- Grant the External Credential through a permission set, so only the integration user can call out
- This replaces legacy Remote Site Settings and hard-coded tokens entirely
Pro tip: Named Credentials, not code
Named Credentials keep the secret and endpoint out of your Apex and metadata, so nothing sensitive ships in a deployment or lands in version control.
Design the data model and external IDs
We make SOS Inventory records land cleanly on the right Salesforce object with no duplicates.
- Map every SOS Inventory field to the Invoice and related objects, documenting type, picklist values, and record types
- Add a unique, external-id, case-insensitive field on each object as the match key
- Define owner assignment, required-field defaults, and relationship lookups
Build inbound ingestion
If SOS Inventory data flows in, we ingest it safely and idempotently.
- Expose an Apex REST resource (@RestResource) or subscribe to SOS Inventory's push or Platform Events
- Authenticate and parse the payload, then Database.upsert on the external id in bulk
- Make it idempotent, so a retried or duplicate payload never creates a second record
@RestResource(urlMapping='/inbound/records/*')
global with sharing class InboundRecordApi {
@HttpPost
global static void upsertRecords() {
List<Row> items = (List<Row>) JSON.deserialize(
RestContext.request.requestBody.toString(), List<Row>.class);
List<MyObject__c> rows = new List<MyObject__c>();
for (Row r : items) {
rows.add(new MyObject__c(External_Id__c = r.id, Name = r.name, Amount__c = r.amount));
}
upsert rows External_Id__c; // bulk + idempotent on the external id
RestContext.response.statusCode = 200;
}
global class Row { global String id; global String name; global Decimal amount; }
}Build outbound sync
If Salesforce drives SOS Inventory, we call out the right way.
- A record-triggered flow to invocable Apex, or an Apex trigger handing off to a Queueable
- Triggers cannot call out synchronously, so the HTTP callout runs asynchronously
- Serialize with JSON.serialize and call SOS Inventory via callout:NamedCredential, handling every status code
public class SyncToServiceQueueable implements Queueable, Database.AllowsCallouts {
private List<Id> ids;
public SyncToServiceQueueable(List<Id> ids) { this.ids = ids; }
public void execute(QueueableContext ctx) {
for (MyObject__c rec : [SELECT Id, Name, External_Id__c FROM MyObject__c WHERE Id IN :ids]) {
HttpRequest req = new HttpRequest();
req.setEndpoint('callout:Service_NC/v1/records'); // secret lives in the Named Credential
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json');
req.setBody(JSON.serialize(new Map<String,Object>{
'externalId' => rec.External_Id__c, 'name' => rec.Name }));
HttpResponse res = new Http().send(req);
if (res.getStatusCode() != 200) ErrorLog.capture(rec.Id, res);
}
}
}Engineer for scale and governor limits
We build it to survive real volume, not just a demo.
- Bulkify everything: no SOQL, DML, or callouts inside loops (100 SOQL and 150 DML per transaction)
- Use Queueable, Batchable, or Scheduled Apex for volume, and chain jobs for large syncs
- Add retry with backoff and a dead-letter Error_Log__c record for anything that fails
global class NightlySyncBatch implements Database.Batchable<SObject>, Database.AllowsCallouts {
global Database.QueryLocator start(Database.BatchableContext bc) {
return Database.getQueryLocator([SELECT Id, External_Id__c FROM MyObject__c WHERE Needs_Sync__c = true]);
}
global void execute(Database.BatchableContext bc, List<MyObject__c> scope) {
ServiceClient.sync(scope); // one callout per 200-record chunk stays under limits
}
global void finish(Database.BatchableContext bc) { /* chain the next job or log the run */ }
}Watch out: governor limits
Salesforce caps SOQL, DML, and callouts per transaction. Bulkify everything and move volume to Queueable or Batch Apex, or the integration will fail at scale.
Lock down security and compliance
We give the integration exactly the access it needs and nothing more.
- A least-privilege permission set, field-level security, and sharing for the integration user
- Rotate secrets on a schedule, and add Shield Platform Encryption for sensitive fields where required
Test like production
We prove it works before it ships.
- Apex tests with Test.setMock(HttpCalloutMock) covering success, failure, and a 200-record bulk case
- At least 75 percent coverage, plus sandbox UAT and a parallel run against the live system
@IsTest
private class SyncToServiceTest {
private class Mock implements HttpCalloutMock {
public HttpResponse respond(HttpRequest req) {
HttpResponse res = new HttpResponse();
res.setStatusCode(200); res.setBody('{"ok":true}');
return res;
}
}
@IsTest static void syncsInBulk() {
Test.setMock(HttpCalloutMock.class, new Mock());
List<MyObject__c> recs = new List<MyObject__c>();
for (Integer i = 0; i < 200; i++)
recs.add(new MyObject__c(Name = 'Row ' + i, External_Id__c = 'EXT-' + i));
insert recs;
Test.startTest(); // proves the callout is bulk-safe under governor limits
System.enqueueJob(new SyncToServiceQueueable(new List<Id>(new Map<Id,MyObject__c>(recs).keySet())));
Test.stopTest();
}
}Deploy, monitor, and hand over
We ship it safely and keep it healthy.
- Deploy via change sets or an SFDX and CI pipeline, and assign the permission sets
- Turn on monitoring and alerting on the Error Log, and optionally Event Monitoring
- Hand over with 30 days of hypercare and failure alerting
Example field mapping
How SOS Inventory data lands on your Salesforce records. We tailor the full mapping to your org.
| SOS Inventory | Salesforce | Notes |
|---|---|---|
| SOS Inventory record id | Invoice.External_Id__c | Unique external id, upsert key |
| SOS Inventory number | Invoice.Name | |
| SOS Inventory customer | Account | Matched or created |
| SOS Inventory amount / total | Invoice.Total__c | |
| SOS Inventory status | Invoice.Status | Picklist value mapping |
| Created / updated at | LastModifiedDate | Enables delta sync and audit |
| Owner or rep | Invoice.OwnerId | Assignment rules or a default owner |
Rate limits and governor limits
The platform constraints we design around, so the integration stays fast and never falls over at scale.
Specific to SOS Inventory
Salesforce platform limits
Secure by design
How we keep the integration safe, least-privilege, and compliant.
Monitoring, retries, and reliability
What keeps the integration trustworthy in production, and how you know the moment something needs attention.
How we test, deploy, and hand it over
The quality gates every build clears before it touches your production org.
Common pitfalls we design out
The mistakes that quietly break integrations, and how we avoid each one.
Duplicate records on retry
Upsert on a unique external-id field so retried payloads are idempotent.
Hitting governor limits at volume
Bulkify and move work to Queueable or Batch Apex; never call out inside a loop.
Callouts failing when a token expires
Use Named Credentials so Salesforce refreshes the OAuth token automatically.
No visibility when it breaks
We log every call and surface failures on a dashboard with alerts, so an issue never goes unnoticed.
Reporting drifts from reality
External-id keys and a delta timestamp keep Salesforce and the source reconciled, so reports stay trustworthy.
Gotchas specific to SOS Inventory
SOS Inventory integration: technical FAQs
How do you authenticate SOS Inventory with Salesforce?
We connect SOS Inventory using OAuth and store every secret in Salesforce Named Credentials with a permission set, so nothing is hard-coded or shipped in metadata.
Does the SOS Inventory integration handle bulk volume?
Yes. All Apex is bulkified, volume moves to Queueable or Batch Apex, and we respect the Salesforce governor limits (SOQL, DML, and callout caps per transaction).
How do you prevent duplicate records?
We upsert on a unique external-id field, so a retried or duplicate payload is idempotent and never creates a second Invoice.
How is the integration tested and deployed?
Apex tests with HttpCalloutMock cover the success, failure, and a 200-record bulk case (75 percent plus coverage). We deploy via change sets or an SFDX and CI pipeline.
What happens if SOS Inventory or Salesforce is briefly down?
Failed calls retry with backoff and land in an Error Log object with alerting, so nothing is lost and any event can be replayed.
Inbound, outbound, or both?
We build whichever direction you need: an Apex REST endpoint for inbound, record-triggered flows or Queueable callouts for outbound, or both for a two-way sync.
Want us to build your SOS Inventory integration?
Skip the build. In a free 30-minute call we will map your SOS Inventory flow and hand you a clear, fixed-price plan.
