Hub / Existing Solutions / 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 v2.14 Surface Case record page + TSANet Case object Repo tsanetgit/SFDC_App Updated 2026-10-24

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, and a broken install affects exactly one member. The one credential TSANet holds is the External Client App secret it uses to authenticate deliveries into your org.
 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 your My Domain, behind an External Client AppPush, not polling — plus a scheduled sweep as backstop
StateCustom objectsReportable, dashboard-able, and subject to your sharing model
CredentialsNamed and External Credentials in your orgThe platform mints and attaches the token; the package stores none
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 jobs, the External Client App, the integration user and the credential record are all yours to keep healthy.
2.14 is a breaking upgrade. This page documents TSANet Connect 2.14, released on 2026-10-21. It replaces username-and-password login with Named Credentials and replaces the public Site with an External Client App. The release notes say Basic Auth credentials and the old webhooks stop working after the upgrade, so set up the Named Credential and the External Client App first. The post-install script changes nothing, so disconnecting the old credential records and aborting leftover scheduled jobs are manual steps. Details are under Auth, Inbound and Setup.
On Production, 2.14's webhook registration is unconfirmed. The package always registers with callbackAuth type OAUTH2_CLIENT_CREDENTIALS, and it has no other option. On 2026-10-24 the spec Production served offered only NONE, BASIC, BEARER and HEADER, so registration on a Production credential was expected to be refused. Production's spec changed on 2026-10-26, and on 2026-10-28 it was byte-identical to Beta's, OAuth type included, so registration should now be accepted. Both readings are spec-read; nobody has registered a 2.14 webhook on Production yet. Until someone does, check that the webhook record shows Active after registering, and know that the 15-minute sweep carries inbound if it does not. See Working with the API → webhooks.

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 three permission sets owns the External Client App 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 TSANetAPIService · HttpRouter ParserManager · UpsertExecutor TriggerDispatcher · handlers ErrorLogger → TSANetError__c AuthModeManager picks the credential Named Credentials Named + External Credential OAuth 2.0 client credentials platform mints the token four shipped: Prod · Beta · Test · Dev the package stores no token at all 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, names a Named Credential platform events: TSANetWebhookEvent__e · TSANetCaseUpdated__e · … 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 My Domain · External Client App fast-ack 200, enqueue a Queueable TSANet Connect API partner search · process form collaboration-requests approval · rejection · closure information-request / -response notes · file attachment token from Microsoft Entra ID case list + webhooks on /v2 Production and Beta, plus test and dev environments one Named Credential each TSANet webhook push registered per credential as a TSANetWebhook__c record created + note events, V2 payload OAuth to your org · tokens only 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, Direction (inbound/outbound), Priority, Summary, Description, Request Date, Response SLA, both parties' company, case-number and engineer contact details, and a lookup to the standard Case. 2.14 adds Document Id and name, which identify the partner's inbound process form. Token is the ExternalID.

TSANetNote__c

Notes on a case: summary, description, creator, status, priority, direction, created-at, and since 2.14 a Type (public, partner, system, escalation, status). 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

Which Named Credential to call through, the integration user, isPrimary (exactly one per org may be true) and isConnected, which the package sets after a successful callout, along with your TSANet company id and name. No password or token is stored here since 2.14.

TSANetWebhook__c

The inbound subscription: parent credential, TSANet's webhook id, active flag, callback URL and event types, stored in their prefixed v2 form. A child of the credential record. The client id and secret you enter on it go to TSANet and are not stored in Salesforce.

Named & External Credentials

Setup metadata, not data. The package ships four of each, for Production, Beta, Test and Dev. You enter the client id and secret TSANet gives you on the External Credential's TSANetApp principal.

TSANetError__c

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

Platform events

TSANetWebhookEvent__e is published for every inbound event, for your own subscribers. TSANetCaseUpdated__e lets an open LWC show a live indicator while the async job is still processing, and TSANetNoteFeedItemRequested__e drives Case Feed posts.

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

Named Credentials, and No Token in the Package

Since 2.14 every call to TSANet goes through a Salesforce Named Credential backed by an External Credential. Both ship in the package and are preconfigured for OAuth 2.0 client credentials against TSANet's Microsoft Entra ID token endpoint. You add one thing: the client id and secret TSANet supplies for that environment, entered on the External Credential's TSANetApp principal. Salesforce then fetches the token, caches it and attaches it to each callout. This is the API's current server-to-server scheme; see Working with the API → Authentication.

What 2.14 removed: username-and-password login, the /v1/login JWT, the token split across two custom-setting fields, the TSANet_Tokens__c setting and the shipped Remote Site Settings. A Named Credential carries its own endpoint, so no Remote Site Setting is needed. If you are upgrading from 2.13, the old password fields are gone and the post-install script migrates nothing, so a credential record has to name a Named Credential before the package will make a callout.

Run the Setup as the Integration User

The guide has you enter the client id and secret as your own administrator, then log in as the integration user to create the credential record. The record is then owned by that user, and the package's scheduled jobs run as that user. Saving the record starts an account probe. On success the package sets Is Connected, fills in your company id and name, syncs any existing webhooks from TSANet and schedules the sweep. On failure it marks the record disconnected and aborts the jobs. Refresh the page to see the result.

If You Extend the Package with Your Own Apex

Call through the same Named Credential (callout:tsanetconnect__TSANetAPI and its siblings) rather than handling tokens yourself. The platform rule that shaped the old token code still applies to anything you write: Salesforce refuses a callout when there is uncommitted DML in the transaction (You have uncommitted work pending), so make the callouts first and write afterwards.

Environments

The credential record's Named Credential field selects the environment. The installation guide documents Production and Beta. The package also ships a Test and a Dev Named Credential for TSANet-operated environments.

Named CredentialEnvironmentHost
TSANetAPIProductionconnect2.tsanet.org
TSANetAPIBetaBetaconnect2.tsanet.net
TSANetAPITestTestTSANet-operated test environment, by arrangement
TSANetAPIDevDevTSANet-operated development environment, by arrangement

Each environment has its own External Credential with its own token scope. Enter the client id and secret TSANet issued for that environment on that environment's principal.

Inbound

Push Arrives Through an External Client App

Since 2.14 TSANet authenticates into your org. You create an External Client App with the client-credentials flow enabled and your integration user as its Run As user, then hand its consumer key and secret to TSANet when you register the webhook. Before each delivery TSANet fetches a Salesforce token with them and posts to the package's Apex REST endpoint on your My Domain, where the call runs as the integration user. The public Site and its guest user are no longer used. This is still the part of the setup with the most moving pieces.

TSANet OAuth · thin event Apex REST · My Domain /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 401 or 403 here 401: key, secret or flow 403: Run As user lacks 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. The handler also publishes TSANetWebhookEvent__e for your own subscribers, but the package's trigger on that event does nothing. The Queueable does the upsert, whatever the release notes say about the event trigger.

What the Setup Actually Requires

Two sources disagree on the integration user's licence — needs probe. The installation guide says the integration user must hold a full Salesforce licence, because with a Salesforce Integration licence the webhook cannot create Case Feed items. The permission set's own description says it was kept to Case read-only "so it fits the Salesforce Integration license". Follow the guide until one of them is corrected.
When inbound is silent, check in this order: is the credential both Primary and Connected; does the webhook record exist and show Active; is the External Client App's client-credentials flow on, with the right Run As user; does TSANetError__c have rows. The guide maps a 401 or invalid_client to the External Client App's settings and a 403 to a Run As user missing the permission set. A trace flag on the integration user with debug level SFDC_DevConsole covers what the error object does not. On Production, also read the callbackAuth note: registration may be what failed.

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. Push delivers only creations and notes, so the sweep is also how closures and responses arrive. It reads the paged /v2/collaboration-requests list. The package schedules the jobs itself when the primary credential connects, and they run as the user who created the credential record. Changing the 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.

A custom cadence does not survive a reconnect. In the 2.14 source the package names its jobs TSANetScheduledJob - <credential Id> - 00, - 15, - 30 and - 45. Whenever the credential reconnects, through the reconnect action or by becoming primary again, it counts the jobs carrying that credential's id. If it finds anything other than four, it aborts them and schedules its four again. Jobs under other names are left alone and run alongside the four. The guide's suggested names (TSANetScheduledJob – Every 10 (00) and so on) are of that second kind. After any reconnect, re-check Scheduled Jobs.

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.

Since 2.14 the inbound record-triggered flows ship deactivated, as templates: Collaboration Request Trigger Handler, its Asset variant, Auto Accept, Note Trigger and Request More Information. You clone and activate the ones you want. The screen flows and the notification, owner-change and case-link flows ship active.

Invocable Apex Actions

  • Create Collaboration Case
  • Approve Request · Reject Case
  • Request More Information · Send More Information
  • Get Companies · Get Company Form
  • Create Note · Link Case to Collaboration Request
  • Refresh Cases · Change Owner · Close Request
  • Process Webhook Event · Mark Notes Notified

Flows Worth Knowing About

  • Auto Accept Collaboration Request — record-triggered on TSANet Case creation, accepts with no human step. Since 2.14 it runs asynchronously, so activating it no longer fails on a callout in the same transaction. If you activated and modified the old one, the release notes say to replace it with this one
  • Collaboration Request Trigger Handler — creates the Salesforce Case and links it back to the TSANet Case
  • Trigger Handler (Asset) — the entitlement variant: matches on an asset, and on no match sends the partner an information request (see below)
  • Find Parent Case (Template) — matching logic for attaching a collaboration to an existing case, branching on direction across the receiver and submitter case numbers
  • Case Owner Change Handler — propagates a Salesforce owner change to TSANet

Matching on an Asset

The pattern most worth copying, and a good template for your own policy. Two versions exist, and they differ. The shipped template, Trigger Handler (Asset), fires when a TSANet Case is created with direction inbound and no Salesforce Case linked:

Get Asset                       → as shipped: Asset Name = the TSANet Case's Email field
  └─ found      → create the Salesforce Case, link it, accept, notify the owner
  └─ not found  → Request Information (Automation): ask the partner, do not reject

The installation guide also walks through a custom handler you build yourself, which looks up the Asset by serial number and then a Contact by the customer's email before it creates the Case and approves. That one only runs when a serial number is present, and the guide describes no branch for a missing asset.

The guide and the shipped flow disagree about the no-match branch. The guide says the Asset template "triggers an automation to reject the collaboration request" when no asset is found. In the package source at both Release-v2.13 and 2.14, that branch calls TSANet Request Info (Automation), which asks the partner for more information. Nothing in the flow rejects. If rejection is the policy you want, change that branch in your clone, and change the lookup too: matching Asset Name against an email field is a placeholder, not an entitlement check.
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

The published guide orders configuration in three steps, and the order matters: create the integration user first, then the credential, then the webhook.

Package, User and Credential

  • Request API client credentials from membership@tsanet.org
  • Install from the GitHub releases page, using the release marked Latest (2.14 since 2026-10-21) rather than a pre-release. Choose all users or specific profiles, and grant third-party website access when prompted
  • Create the integration user; check its licence (see Inbound)
  • Assign TSANet Connect Permissions (Admin) or (Employee) to people, and TSANet Integration User Access to the integration user only
  • As yourself, enter the TSANet client id and secret on the External Credential's TSANetApp principal until it shows Configured
  • Log in as the integration user and create the TSANet Credentials record, naming the Named Credential and the integration user; refresh until Is Primary and Is Connected are both checked

Webhook, Surfaces and Automation

  • Create the External Client App and copy its consumer key and secret
  • On the credential's TSANet Webhooks related list, create the webhook with both event types and the key and secret
  • 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, the Create New TSANet Case dynamic action to the Highlights Panel, and the TSANet Cases list to the Service Console
  • Clone and activate the inbound template flows you intend to use, and tune the scheduled jobs

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.

Upgrading from 2.13. Do the credential and webhook steps above before you upgrade. The post-install script does nothing, so disconnect the old Basic Auth credential records and abort their leftover scheduled jobs by hand. Deleting an old webhook record also deletes its subscription on TSANet. The Site and guest user are no longer used. On Production, read the callbackAuth note before you count on push.

Gotchas

Constraints and Common Failures

  • On Production, webhook registration needs a platform feature that reached Production's spec only on 2026-10-26. 2.14 sends OAUTH2_CLIENT_CREDENTIALS callbackAuth and has no fallback, and registration on Production has not been probed. See the note under Connector class.
  • Making a credential primary clears the flag on every other record. It also re-runs the account probe and reschedules the jobs. After a sandbox refresh, check which record is primary and which Named Credential it names — that is the usual cause of "it connected to the wrong environment".
  • No */X in Salesforce cron. Every interval shorter than an hour means multiple schedules, and a reconnect resets the package's own four. See Scheduled sync.
  • 401 versus 403 on the webhook. A 401 or invalid_client points at the External Client App: the wrong key or secret, or the client-credentials flow not enabled. A 403 means the Run As user lacks TSANet Integration User Access. Neither is a TSANet-side issue.
  • The endpoint does not check X-Hub-Signature-256. The API signs every delivery, but the 2.14 handler has no signature code. It relies on the OAuth token Salesforce validates before the Apex runs. The handler also answers 200 even when processing fails, so look in TSANetError__c rather than at delivery status.
  • Only two event types are offered. The webhook form lists collaboration-request.created and note.created, both selected by default, and stores them in prefixed v2 form. Closures and responses are not in the form, so they arrive through the scheduled sync. An empty selection is refused at save.
  • Off the deprecated v1 paths. Verified against the package source at 2.14: lists read /v2/collaboration-requests and /v2/collaboration-requests/list, and webhooks register and list on /v2/webhooks. Delete stays on /v1/webhooks/{id}, which is not deprecated, and case actions stay on v1, where they have no v2 equivalent. Nothing here is on the 2027-01-01 deprecation clock.
  • Note types reached Production's spec on 2026-10-26. 2.14 sends a type on every note (null when unset) using the v2 note contract. Until 2026-10-26 Production's note schema had no type field; since then it has, identical to Beta's (spec-read 2026-10-28). What Production does with it has not been probed.
  • DML before callout fails. If you extend the package with your own Apex, make the callouts before any writes in the same transaction.
  • "View Encrypted Data" is a separate permission set you create yourself and should grant 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 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.