1Background
TSANet Connect exposes one canonical case model. A collaboration between two members is a single case in the platform, and each side sees it through their own connector — as a Salesforce custom object, a Zendesk ticket, a Dataverse row, or an Intercom back-office ticket. The connectors differ in almost everything except this: they all map to and from the same canonical shape.
That makes the canonical model the one contract worth understanding before any platform-specific work. It also makes its sharp edges worth writing down, because every connector has independently had to discover them.
token this document introduces.
2Two Schemas, One Case
The most consequential property of the model is that the shape you write is not the shape you read. Case creation takes a request schema; retrieval returns a status schema; the field names differ between them. This is not a defect, and it is not documented as a mapping anywhere — an integrator meets it when a read-back returns nothing under the names they just sent.
2.1 Field-Name Divergence
| Concept | Write (create request) | Read (case status) |
|---|---|---|
| Short problem title | problemSummary | summary |
| Full problem text | problemDescription | description |
| Your own case number | internalCaseNumber | submitterCaseNumber or receiverCaseNumber, depending which side you are |
| Submitter contact | submitterContactDetails object | flattened onto the case |
| Counterparty identity | implied by the form target | submitCompanyName / receiveCompanyName and their IDs |
| Test flag | testSubmission | testCase |
2.2 Fields That Only Exist on Read
The status schema carries lifecycle state the write schema has no concept of: token, status, direction, createdAt and updatedAt, respondBy, and responded. Two of these deserve particular attention.
tokenis the join key for everything. It appears in every subsequent URL and in every webhook payload. Persist it at creation, before anything else in your flow can fail.respondedis a boolean, and it — not the status name — is what gates the response SLA. Once it is true the platform has stopped trackingrespondBy, so any countdown built on the status name alone will display a deadline nobody is measuring.
encrypted (a boolean, default false) and manifest (an opaque value the platform passes through untouched). Beta's has carried them since 2026-10-19 and Production's since 2026-10-26. They belong to an end-to-end encryption pilot; see End-to-end encryption. Don't design around them yet.
3Custom Fields Are Data, Not Schema
Beyond the canonical fields, each partner defines their own intake form. Those fields arrive as a typed template from the form endpoints and are submitted back as a customFields[] array. No connector hardcodes them — the mapping is driven by the template fetched at submission time.
3.1 Form Sections
The template groups fields into sections, which is what lets a connector lay the form out sensibly rather than as a flat list:
| Section | Carries |
|---|---|
PROBLEM_SECTION | The problem itself — summary, description, severity context |
CONTACT_SECTION | Who is asking, and how to reach them |
COMMON_CUSTOMER_SECTION | The shared end customer, where both members serve the same one |
That third section is the mechanism for identifying the end customer a collaboration is about. Because it is a partner-defined section rather than a fixed field, a connector that wants to resolve an incoming case to a local account record has to read it as a custom field and map it — there is no canonical endCustomer to bind to.
documentId you must submit alongside comes from that same fetch. A cached template means a stale documentId and a rejected case.
3.2 Field Types the Template Can Specify
Custom fields are typed — STRING, TEXT, EMAIL, PHONE, INTEGER, URL, SELECT and HIDDEN among them — and a connector is expected to render each appropriately rather than treating everything as free text.
SELECT field's options are newline-delimited, not comma-delimited, and the structured selections[] array is frequently empty. Splitting on commas collapses every choice into a single option — a bug that renders perfectly and is wrong. Parse selections[].value first, fall back to splitting on newlines, and only then on commas.
4Where Connectors Put the Data
The canonical model says nothing about storage, and the shipped connectors have made genuinely opposite choices. Both are correct; the difference is worth understanding before designing a new one.
| Approach | Example | Consequence |
|---|---|---|
| State on the platform's own record | Zendesk — custom fields and comments on the ticket | Fewer objects, native to the agent's view, but collaboration state is entangled with the member's own ticket lifecycle |
| State in dedicated objects | Salesforce and Dynamics — dedicated case, note and response tables linked to the standard record | The platform's own record is never modified; collaboration data is separately reportable and separately governed |
The second approach has proven easier to reason about at scale, mostly because it keeps a member's existing queues and reporting untouched. The first is lighter to install. Neither is a platform requirement.
5Known Mapping Gaps
Two mappings were not cleanly expressible on the platform until October 2026. Both are recorded here so integrators design around them deliberately rather than discovering them mid-build. The second is solved in the specification both environments now serve, and §5.2 and §5.3 say how. On Production that rests on the specification alone, which it has served since 2026-10-26; nothing there has been probed.
5.1 Priority Is Three Levels; Most Members Use Four
The canonical model has LOW, MEDIUM and HIGH. Members commonly run a four-level severity scale (S1–S4), so a three-to-four mapping is required in both directions, and it has to stay synchronised across the case lifecycle for both sides' service levels to hold. There is no canonical answer; each connector currently decides its own collapse, which means a case can read as a different severity to each party.
5.2 Note Direction: from a Case Property to a Note Property
In the earlier specification, which Production served until 2026-10-26, the note object carries no direction field. Notes are written under a synchronisation identity, so author fields are identical regardless of who sent one. The only available signal is companyName, which is ambiguous when both organisations have similar names. Direction is reliably derivable from the case, not from the note. A connector that labels notes "sent" or "received" should derive that from case direction and its own record of what it posted. Keep that fallback until direction has been seen on Production notes.
In the current specification, the gap is closed. Beta served it first. Production has served the same specification since 2026-10-26, spec-read and not probed. The note object there carries:
| Field | Earlier spec (v3.2.3; Production until 2026-10-26) | Current spec (v3.2.5; Beta, and Production since 2026-10-26) |
|---|---|---|
companyName | present | present; absent on system notes |
companyId | — | the authoring company; absent on system notes |
direction | — | relative to the reader, not the author; absent when companyId is |
type | — | {code, displayName}, or null |
priority on create | required | optional |
Because direction is relative to whoever reads the note, the same note reads as one direction to the submitter and the opposite to the receiver. Store it per side; never copy it across.
5.3 Note Types Replace a Convention
Before note types, the only way to tag a note was a convention both sides had to agree to: a structured event type in the note summary ("status change", "severity change", "new public note") and the human content in description. Until Production's specification changed on 2026-10-26, that convention was the only option there. Keep it readable until note types have been seen working on Production.
In the current specification, the platform carries the tag itself. GET /v1/note-types lists the available types, which on Beta today are partner, public, escalation and status. Creating a note takes an optional type: {code}, matched case-insensitively. An unknown code, system, or an empty object is rejected with 400, and leaving the type out creates the note with none. Those answers were seen on Beta; Production's list and answers have not been read.
public. And the type is a label, not an access-control filter: it does not decide who can see the note.
6Revision History
| Version | Date | Change |
|---|---|---|
| 0.4 | 2026-10-28 | Production now serves the same specification as Beta (byte-identical on 2026-10-28; Production's last modified 2026-10-26). §2.2 and §5 re-framed from Production-versus-Beta to earlier-versus-current specification, with Production spec-read and unprobed. §2.2 links the end-to-end encryption topic. |
| 0.3 | 2026-10-20 | §5.2 split Production from Beta: the Beta note object carries companyId, reader-relative direction and type, and makes priority optional. New §5.3 on note types. Beta-only encrypted / manifest fields noted in §2.2. Verified-against names both environments. |
| 0.2 | 2026-09-13 | Add the testSubmission / testCase row to the divergence table; cross-reference TSANET-2026-002. |
| 0.1 | 2026-08-15 | Initial draft. Field names verified against the OpenAPI specification rather than taken from connector source. |