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.
Connector class
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.
| Where it runs | What that buys you | |
|---|---|---|
| Business logic | Apex, in your org | Real server-side code with governor limits, tests and debug logs |
| Automation | Flow, in your org | Clone and edit any shipped flow without writing Apex |
| Inbound delivery | Apex REST on your My Domain, behind an External Client App | Push, not polling — plus a scheduled sweep as backstop |
| State | Custom objects | Reportable, dashboard-able, and subject to your sharing model |
| Credentials | Named and External Credentials in your org | The platform mints and attaches the token; the package stores none |
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
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.
TSANetCase__c — Accept, Reject, Request Information, Send Information, Create Note, Close, Upload File — each launch either a screen flow or an Aura action. Close is valid on outbound cases only: the API rejects a closure from the receiving company.callout:tsanetconnect__TSANetAPI, or the Beta, Test or Dev equivalent, whichever Named Credential the primary credential record names. The External Credential behind it fetches and attaches the OAuth token, so no Apex in the package handles one.TSANetScheduledJob sweeps every 15 minutes by default, reconciling anything the push path missed.Data model
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.
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.
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.
Formal replies and actions on inbound cases: type, note, engineer email and phone, created-at.
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.
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.
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.
Where ErrorLogger writes. First place to look when a webhook or a sync cycle misbehaves.
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
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.
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.
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.
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 Credential | Environment | Host |
|---|---|---|
TSANetAPI | Production | connect2.tsanet.org |
TSANetAPIBeta | Beta | connect2.tsanet.net |
TSANetAPITest | Test | TSANet-operated test environment, by arrangement |
TSANetAPIDev | Dev | TSANet-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
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.
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.TSANet Integration User Access permission set. It covers the webhook classes, upserts on the TSANet objects, Case Feed posts and Case read-only access, with no View All.api and full scopes and only the client-credentials flow, and the integration user set as Run As under its OAuth policies. The callback URL is required by the form but never called.TSANetWebhook__c record under the primary credential, with the External Client App's consumer key and secret as Client Id and Client Secret. The callback URL is filled in for you as <My Domain>/services/apexrest/tsanetconnect/webhook. It must use My Domain, not a Site URL. Saving registers the subscription on /v2/webhooks with a V2 payload and OAuth client-credentials callbackAuth. The key and secret are sent to TSANet and not kept in Salesforce.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
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.
*/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.
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
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.
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.
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.TSANet_Notification. A flow that silently does nothing at the notification step is usually a missing notification type.Setup
The published guide orders configuration in three steps, and the order matters: create the integration user first, then the credential, then the webhook.
membership@tsanet.orgTSANetApp principal until it shows ConfiguredTSANetApplication component to the Case record page in Lightning App Builder, then activate and assignOrganizations 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
OAUTH2_CLIENT_CREDENTIALS callbackAuth and has no fallback, and registration on Production has not been probed. See the note under Connector class.*/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.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.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.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./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.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.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.TSANetError__c first. It is where ErrorLogger writes, and it is faster than a trace flag for most failures.