Hub / Connectors / Salesforce

Salesforce

A managed package that installs entirely into your own org: custom objects, Apex services, Lightning Web Components, and a library of Flows you can clone. The most configurable connector TSANet ships — and the one where the automation surface is a Flow library you clone and edit, where Dynamics gives you a configuration table and Zendesk its triggers.

Class In-tenant Status GA · managed package Surface Case record page + TSANet Case object Repo tsanetgit/SFDC_App Updated 2026-09-13

Connector class

The reference in-tenant connector

TSANet connectors divide by one question: will the platform run your code inside the member's own tenant? Salesforce is the fullest yes on the list — Apex is real server-side code, running on Salesforce's infrastructure, inside your org, under your governance. Intercom is the opposite pole, where nothing can run in-tenant at all and TSANet hosts a gateway; Zendesk sits in between, with client-side apps and declarative flows but no arbitrary server code.

Everything below runs in the member's org. TSANet operates nothing, holds no credentials, and a broken install affects exactly one member.
 Where it runsWhat that buys you
Business logicApex, in your orgReal server-side code with governor limits, tests and debug logs
AutomationFlow, in your orgClone and edit any shipped flow without writing Apex
Inbound deliveryApex REST on a public SitePush, not polling — plus a scheduled sweep as backstop
StateCustom objectsReportable, dashboard-able, and subject to your sharing model
CredentialsCustom object in your orgNever leaves your tenant
The consequence worth planning for is the inverse of the gateway trade-off. Nothing here is shared infrastructure, so nothing here fails for everyone at once — but equally, nobody operates it for you. The scheduled job, the Site, the guest-user permissions and the credential record are all yours to keep healthy.

Architecture

What the package installs

Four layers, all inside the org: Lightning components the agent works in, a Flow library that drives the business logic, Apex services that talk to TSANet, and custom objects that hold the state. Two integration points cross the org boundary — an outbound scheduled sync and an inbound Apex REST endpoint.

Service agent Case record page TSANetApplication LWC TSANet Cases related list quick actions on the case Service Console list view Salesforce admin clones and edits shipped Flows assigns the two permission sets owns the Site + guest user tunes the scheduled job cron Reports & dashboards custom objects are reportable case distribution, SLA metrics no extra plumbing needed the member's own Salesforce org · managed package · TSANet operates nothing Lightning components tsaNetApplication · caseCard caseCreator · caseNotes actionModal + form + fields companySelector · uploadAttachment plus Aura quick actions for each verb Flow library inbound · outbound · common screen flows + autolaunched record-triggered auto-accept every one clonable and editable invocable Apex actions as building blocks Apex services TSANetService · HttpClient ParserManager · UpsertExecutor TriggerDispatcher · handlers ErrorLogger → TSANetError__c EndpointManager resolves the environment Auth & token storage AuthManager · CredentialManager TokenManager caches in memory AccessTokenSplitter: 2 × 175 chars commit AFTER callouts, never before a JWT does not fit one custom setting field Custom objects · the durable state, and it is yours TSANetCase__c — status · type · priority · token (ExternalID) · Case lookup TSANetNote__c · TSANetResponse__c · TSANetError__c · TSANetWebhook__c TSANet_Credentials__c (one primary per org) · TSANet_Tokens__c (org defaults) TSANetCaseUpdated__e · TSANetNoteFeedItemRequested__e — platform events TSANetEnvironment__mdt and TSANetCustomField__mdt carry configuration, not data TSANetScheduledJob 15-minute sweep by default reconciles what push missed cron has no */X — list minutes 15 min = four separate schedules Apex REST endpoint /services/apexrest/ tsanetconnect/webhook public Site + guest user fast-ack 200, enqueue a Queueable TSANet Connect API · /v1 partner search · process form collaboration-requests approval · rejection · closure information-request / -response notes · file attachment login returns a JWT, ~60 min case list — version not re-probed BETA and PRODUCTION hosts, plus a developer environment selected per credential record TSANet webhook push registered per credential as a TSANetWebhook__c record collaboration request + note events thin payload: tokens, not content Partner member works the same case from their own connector or the web app 1 2 3 4 invoke apex actions token upsert by ExternalID Queueable → upsert configures reports on
Everything inside the outlined box is installed by the managed package into the member's own org. Accent edges leave the org; plain edges are in-org calls. The green band is the durable state, held in custom objects the member owns, reports on, and governs with their own sharing model.

Data model

Custom objects, and why that matters

As on Dynamics, and unlike Zendesk and Fin / Intercom, collaboration state here lands in first-class Salesforce objects. That is the single biggest practical difference: the data is reportable, dashboard-able, subject to your sharing rules, and addressable from any Flow or Apex you write yourself.

TSANetCase__c

The collaboration record. Status, Type (inbound/outbound), Priority, Summary, Description, Request Date, Response SLA, both parties' company and engineer contact details, and a lookup to the standard Case. Token is the ExternalID.

TSANetNote__c

Notes on a case — summary, description, creator, status, priority, created-at. Token is the ExternalID, which is what makes upserts idempotent.

TSANetResponse__c

Formal replies and actions on inbound cases: type, note, engineer email and phone, created-at.

TSANet_Credentials__c

Username, password, environment (DEVELOPER, BETA, PRODUCTION), an integration-user designation, and isPrimary — of which exactly one per org may be true.

TSANet_Tokens__c

Org-default custom setting holding the access token in two parts plus its expiry. Not something you edit by hand.

TSANetWebhook__c

The inbound subscription: parent credential, active flag, callback URL and selected event types. A child of the credential record.

TSANetError__c

Where ErrorLogger writes. First place to look when a webhook or a sync cycle misbehaves.

Platform events

TSANetCaseUpdated__e and TSANetNoteFeedItemRequested__e. The former lets an open LWC show a live indicator while an async job is still processing an inbound event.

TSANetEnvironment__mdt and TSANetCustomField__mdt are custom metadata, not data — they carry environment hosts and field-mapping configuration, and they deploy with the package rather than living in your data.

Authentication

Two Salesforce platform limits shape the token handling

Auth is basic authentication against a TSANet API user on the member account, exchanged for a JWT that lives about 60 minutes. That is the API's legacy scheme: OAuth 2.0 client credentials is the current server-to-server path, and it removes the token handling described below — see Working with the API → Authentication. The managed package as documented here uses the login path. Storing and using that token runs into two Salesforce constraints that are worth understanding before you debug anything in this area.

A JWT does not fit in one custom setting field

Custom setting text fields cap well below the length of a Connect JWT, so AccessTokenSplitter splits the token at 175 characters into AccessTokenPart1__c and AccessTokenPart2__c, and TokenManager concatenates them on read. If you are inspecting stored credentials and see a truncated-looking token, that is why — check both parts.

DML before a callout is not allowed

Salesforce refuses a callout when there is uncommitted DML in the transaction. A naive implementation — log in, save the token, then immediately call the API — hits You have uncommitted work pending on the very first request. TokenManager therefore holds a freshly minted token in a static in-memory field and defers persistence until after the callouts complete.

// getAccessToken() returns the pending token without touching the database
// commitTokenIfPending() runs after the callouts, not before
String token = TokenManager.getAccessToken();   // no DML
... callouts ...
TokenManager.commitTokenIfPending();            // DML now safe

If you extend the package with your own Apex callouts, follow the same ordering. Persisting the token first is the natural thing to write and it will fail.

Environments

The environment is selected on the credential record and resolved through TSANetEnvironment__mdt at runtime. Each environment also needs its matching Remote Site Setting, which the package ships — a callout to a host with no Remote Site Setting is refused by the platform before it leaves the org.

Credential environmentHost
BETAconnect2.tsanet.net
PRODUCTIONconnect2.tsanet.org
DEVELOPERTSANet-operated development environment, by arrangement

Inbound

Push arrives through a public Salesforce Site

TSANet cannot authenticate into your org, so inbound events land on an Apex REST endpoint exposed through a Salesforce Site running as a guest user. This is the part of the setup with the most moving pieces and by far the most common source of installation failures.

TSANet thin event: tokens Apex REST · Site guest /apexrest/tsanetconnect /webhook always 200, even on error TSANetCaseUpdated__e LWC shows a live indicator enqueue Queueable fetch case by token the fetch is the truth async — off the request thread Upsert by ExternalID TSANetCase__c · Note__c idempotent on redelivery Token is the ExternalID record-triggered Flow create Case · maybe auto-accept 403 here is almost always a guest user missing the permission set
The event carries identifiers, not content, so the fetch is what establishes truth — the same shape as the other connectors. Upserting on the token as ExternalID is what makes a redelivered event harmless.

What the setup actually requires

When inbound is silent, check in this order: is the Site active; is the guest user assigned the permission set; are event types selected on the webhook record; does TSANetError__c have rows. A trace flag on the integration user with debug level SFDC_DevConsole covers what the error object does not.

Scheduled sync

The 15-minute sweep, and Salesforce cron

TSANetScheduledJob reconciles Salesforce against TSANet every 15 minutes by default, catching anything the push path missed. Changing that interval runs into a platform quirk that surprises everyone who has written a crontab.

Salesforce cron does not support */X or 0/X increments. There is no "every 15 minutes" expression. A single schedule fires at most once per hour, at one specific minute — so a 15-minute cadence is four separate scheduled jobs, a 10-minute cadence is six, and a 5-minute cadence is twelve.
# Every 15 minutes — four schedules, not one
0 0  * * * ?
0 15 * * * ?
0 30 * * * ?
0 45 * * * ?

# Use ? for Day-of-Month or Day-of-Week when the other is specified

Manage them under Setup → Environments → Jobs → Scheduled Jobs. Before shortening the interval, weigh it against your org's Apex governor limits — every schedule is an independent job making its own callouts.

Automation

The Flow library is the extension point

This is what makes the Salesforce connector different in kind from the others. The package ships a library of Flows covering every lifecycle action, in both screen and autolaunched form, and every one of them is clonable. A member can change routing, approval policy, notification recipients and case-matching logic without writing Apex — the Apex is exposed as invocable actions that Flow calls as building blocks.

Invocable Apex actions

  • Create Collaboration Case
  • Approve Request · Reject Case
  • Request More Information · Send More Information
  • Get Companies · Get Company Form
  • Create Note · Attach File
  • Refresh Cases · Change Owner · Close Request

Flows worth knowing about

  • Auto Accept Collaboration Request — record-triggered on TSANet Case creation, accepts eligible requests with no human step
  • Collaboration Request Trigger Handler — creates the Salesforce Case and links it back to the TSANet Case
  • Trigger Handler (Asset) — the entitlement variant: matches on asset, and rejects when there is no match
  • Find Parent Case (Template) — matching logic for attaching a collaboration to an existing case
  • Case Owner Change Handler — propagates a Salesforce owner change to TSANet

Auto-approval by asset serial number

The most-copied pattern in the library, and a good template for your own policy. The record-triggered flow fires when a TSANet Case is created with no Salesforce Case linked, direction inbound, and a serial number present:

Get Asset by Serial Number      → no match? reject the request
Get Contact by Customer Email   → optional enrichment
Create Salesforce Case          → Asset, Contact, Subject, Description
Update TSANet Case              → attach the new Case Id
Approve Collaboration Request   → invocable Apex, with engineer details
Send Custom Notification        → to the case owner

Rejecting on no asset match is the entitlement check: a partner escalating about hardware you have no record of does not become a case your team has to triage. Adapt the matching step — contract, entitlement, account tier — and the rest of the flow is unchanged.

Custom flows that send notifications need a Custom Notification Type to exist first; the shipped flows reference one named TSANet_Notification. A flow that silently does nothing at the notification step is usually a missing notification type.

Setup

Installation order

Package and access

  • Request credentials from membership@tsanet.org
  • Install the latest release package from the GitHub releases page, choosing all users or specific profiles
  • Grant third-party website access when prompted
  • Assign TSANet Connect Permissions (Admin) and (Employee) to the right users
  • Create the credential record, pick the environment, mark it Primary, designate the integration user
  • Enable Enhanced Profile User Interface under User Management Settings

Surfaces and automation

  • Add the TSANetApplication component to the Case record page in Lightning App Builder, then activate and assign
  • Add the TSANet Case related list to the Case page layout
  • Add the Create New TSANet Case dynamic action to the Highlights Panel
  • Add the TSANet Cases list to the Service Console
  • Register the Sites domain, create and activate the Site, assign the guest permission set, create the webhook record
  • Activate the inbound and outbound flows you intend to use, and tune the scheduled job

Organizations that would rather not use the LWC can reach the same actions through page layouts instead — override the predefined actions in Salesforce Mobile and Lightning Experience Actions and add the TSANet Case Menu.

Gotchas

Constraints and common failures

  • Exactly one primary credential per org. Marking a second one primary is the usual cause of "it authenticated as the wrong company" after a sandbox refresh.
  • No */X in Salesforce cron. Every interval shorter than an hour means multiple schedules. See above.
  • A 403 on the webhook is a guest-user permission problem nine times out of ten — not a TSANet-side issue and not a Site problem.
  • Delivery signing is not described here. The API signs every delivery with X-Hub-Signature-256, and the Dynamics, Zendesk and Fin / Intercom pages each verify a secret or signature; whether the package verifies it has not been re-probed. The only controls documented on this endpoint are Site activation and guest permissions.
  • Empty event types on the webhook record deliver nothing, silently. Select both. On v1 those two — collaboration-request.created and note.created — are the whole set the platform delivers; the three v2-only types (closed, response.created, response.updated) never reach a v1 subscription, so closures and responses arrive through the scheduled sync.
  • Remote Site Settings are required per environment. A callout to an unregistered host never leaves the org.
  • DML before callout fails. If you extend the package with your own Apex, defer token persistence until after the callouts, as TokenManager does.
  • The stored token is split across two fields. Neither half is a valid token on its own.
  • "View Encrypted Data" is a separate permission set and should be granted narrowly — it exposes encrypted field values wherever they appear.
  • Cases auto-close after 30 days of inactivity. Worth knowing before you build reporting that assumes a manual close.
  • Two v1 paths are on a 2027-01-01 sunset. GET /v1/collaboration-requests and the /v1/webhooks subscription paths. The API page records this connector on v1 webhooks (documented, not re-probed), and the deprecated v1 list accepts updatedAfter too, so the sync keeps working until the sunset; which list path the package calls has not been re-probed. See deprecation clocks.
  • Two API rules this page does not repeat. Rejections arrive as HTTP 500 unless the caller sends Accept: application/problem+json, and there is no idempotency key on create — a retried create is a second case. Both on Working with the API.
  • Check TSANetError__c first. It is where ErrorLogger writes, and it is faster than a trace flag for most failures.