Hub / Design Docs / TSANET-2026-001
TSANET-2026-001 DRAFT

The Canonical Case Model, and How Connectors Map onto It

Every TSANet connector maps the same canonical collaboration case onto a different platform's data model. This document specifies that canonical model, the places where the write schema and the read schema deliberately diverge, and the mapping decisions each connector has to make. It is for anyone building or reviewing an integration against the Connect API.

Date2026-10-28
Version0.4
AudienceMember integrators
Verified againstConnect API OpenAPI spec as served by Production and Beta, 2026-10-28: byte-identical, last modified 2026-10-26 (Production) and 2026-10-19 (Beta). Earlier: Production (v3.2.3) and Beta (v3.2.5), 2026-10-20. Spec-read; Production not probed

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.

Related Behavioural guidance for working with these endpoints is on Working with the Connect API. Platform-specific mappings are on the individual connector pages. File transfer is specified separately in TSANET-2026-002; every endpoint there is scoped by the 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

ConceptWrite (create request)Read (case status)
Short problem titleproblemSummarysummary
Full problem textproblemDescriptiondescription
Your own case numberinternalCaseNumbersubmitterCaseNumber or receiverCaseNumber, depending which side you are
Submitter contactsubmitterContactDetails objectflattened onto the case
Counterparty identityimplied by the form targetsubmitCompanyName / receiveCompanyName and their IDs
Test flagtestSubmissiontestCase
Constraint A mapping layer written from the create request alone will fail silently on read-back. Model the two schemas separately and map between them explicitly, rather than assuming one round-trips into the other.

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.

Pilot fields Both environments' specifications add two fields to case create and read, and to notes: 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:

SectionCarries
PROBLEM_SECTIONThe problem itself — summary, description, severity context
CONTACT_SECTIONWho is asking, and how to reach them
COMMON_CUSTOMER_SECTIONThe 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.

Constraint Fetch the form template fresh for every submission. Partners change their forms, and the 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.

Constraint A 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.

ApproachExampleConsequence
State on the platform's own recordZendesk — custom fields and comments on the ticketFewer objects, native to the agent's view, but collaboration state is entangled with the member's own ticket lifecycle
State in dedicated objectsSalesforce and Dynamics — dedicated case, note and response tables linked to the standard recordThe 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.

Canonical case token is the join key Member A connector own fields, own storage, own note-visibility model Member B connector different platform, same canonical case
Figure 1 — One case, two representations. Neither member sees the other's mapping; the token is what makes them the same case.

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:

FieldEarlier spec (v3.2.3; Production until 2026-10-26)Current spec (v3.2.5; Beta, and Production since 2026-10-26)
companyNamepresentpresent; 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 createrequiredoptional

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.

Constraint The specification sets three rules for reading a type. The set of types can grow without a release, so fetch the list rather than hard-coding it. A null or unknown type must not be treated as public. And the type is a label, not an access-control filter: it does not decide who can see the note.

6Revision History

VersionDateChange
0.42026-10-28Production 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.32026-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.22026-09-13Add the testSubmission / testCase row to the divergence table; cross-reference TSANET-2026-002.
0.12026-08-15Initial draft. Field names verified against the OpenAPI specification rather than taken from connector source.