Skip to content

Switching from Salesforce

Salesforce data moves into Tessule as files: one CSV per object, the object’s own metadata beside it, and the file bodies. Nothing connects to your org, and no Salesforce credential is handed over. This guide is the checklist for producing that export, the definition of a complete set, what each part becomes in Tessule, and what stays behind.

There are two ways in:

  • A few objects, through the import wizard. Export the objects, run sf sobject describe for each, and the wizard types every column from the describe, links the objects to each other and proposes the tables. See Typing from Salesforce metadata.
  • A whole org, as a bundle. The export checklist below produces a folder the migration converter turns into schema plans, import plans, saved views, record layouts, a permissions skeleton and a coverage report, applied to a workspace in one order with owners, dates, authors, stage history, activities and files carried over. The converter is run with Tessule on your bundle — contact support@tessule.com.

Both routes keep the Salesforce Id of every record in a source_id field, so a second run updates what the first created and links resolve across files whether an Id is 15 or 18 characters long.

  1. Clean up. Dedupe, retire unused picklist values and fields, and decide which objects move. Formula and roll-up fields are never in an export; Tessule recomputes them.
  2. Note who is active. Records owned by deactivated users keep their owner; the owner becomes a pending member who has no account until invited. Decide whether those people are invited.
  3. Freeze for the final run. Pause flows and integrations once the final export is taken, and keep the org read-only until the workspace is verified.

Each step names the native Salesforce tool. Everything runs in your org, with your access.

  1. Users. Setup → Users → export, or sf data export bulk --query "SELECT Id, Username, Email, Name, IsActive FROM User" --output-file users.csv --result-format csv. Owners, creators and editors are matched to people by email, so the user file is what gives every record its owner.
  2. Object metadata, the minimum. For every object you move: sf sobject describe --sobject Account --json > describe/Account.json. This is what types the fields exactly: labels, required and unique, picklist values, lookup targets. sf org list metadata --metadata-type CustomObject lists the custom objects.
  3. Configuration, the full package. Recommended: sf project generate manifest --from-org <alias> --output-dir . then sf project retrieve start --manifest package.xml --target-org <alias> --output-dir metadata/. The package carries list views, layouts, validation rules, flows, profiles and permission sets, which become saved views, record layouts, constraints and the inventory of what to rebuild. Large orgs need several retrieves; keep package.xml with the export.
  4. Data. Setup → Data Export → Export Now with the encoding set to UTF-8, “Include images, documents, and attachments” and “Include Salesforce Files and Salesforce CRM Content document versions” ticked, and either “Include all data” or the objects you move plus User, RecordType, OpportunityHistory, Task, Event, Note, Attachment, ContentDocument, ContentVersion, ContentDocumentLink, CampaignMember, OpportunityContactRole, OpportunityLineItem and PricebookEntry. Download within 48 hours and unzip into data/ (the CSVs) and files/ (the bodies). At scale, or for deleted and archived rows, export per object with sf data export bulk --query "SELECT … FROM Task" --all-rows; archived tasks and events only come out with --all-rows.
  5. The manifest. Write bundle.json: the org id, the API version from package.xml, the export date, the object list, a row count per object (Setup → Storage Usage) and a SHA-256 per file. The converter checks the bundle against it first, so an incomplete set is caught before anything is uploaded.

Things that catch people out:

  • The Data Export link expires after 48 hours.
  • A UTF-16 export (the “Unicode” choice) is refused with the iconv command that converts it. Choose UTF-8.
  • Deleted records come out only with “Export All” or --all-rows; archived activities only with --all-rows.
  • Field history is kept for 18 months in Salesforce and comes out as the *History objects; only stage history is written into Tessule (below).
  • Inactive users still own records. They are matched like active ones.

A complete, restorable export is one folder:

Path What Required
data/<Object>.csv One Data Export or Bulk API CSV per object, UTF-8 yes
describe/<Object>.json sf sobject describe --json per object yes (without one, a standard field is typed from Salesforce’s published object reference and every other column of that object is text)
metadata/ The retrieved Metadata API package, source or mdapi format recommended
files/ Attachment and ContentVersion bodies, at the paths the CSVs name when files move
users.csv or data/User.csv The user directory one of the two
bundle.json The manifest: objects, row counts, a SHA-256 per file recommended

Keep the bundle. It is the off-platform archive of the org as it was at cutover, and the only form the migration can be re-run from. After the import, the workspace backup is the restorable artifact (see Backups); Salesforce’s own terms give a window after termination to export, and the bundle predates it.

In Salesforce In Tessule
Standard and custom objects One table per object, with source_id holding the Salesforce Id, required and unique
Fields Typed from the describe: text and long text, email, phone, URL, integer and number, currency, percent, date, datetime, time, boolean, picklist and multi-select picklist with their values, geolocation; labels, help text, required and unique as set. A standard field the describe does not list is typed from Salesforce’s published object reference, with a picklist’s values taken from the exported rows; a custom field the describe does not list is text
Lookups and master-detail Reference fields between the moved objects; master-detail as a required reference; self-references (ParentId) included; a lookup to several moved objects becomes a polymorphic reference to them
Junction objects Child tables with two references and a composite unique constraint
OwnerId, CreatedById, LastModifiedById The record’s owner and authors, matched to people by email; a person not yet a member is created as a pending member, claimed when they accept an invitation
CreatedDate, LastModifiedDate The record’s own created and updated instants, not the import’s
StageName, Status on Opportunity, Case and Lead Stage fields with Closed Won and Closed Lost as categories
OpportunityHistory and stage field history The record’s stage history, every move dated as it happened, written as migrated history
Task, Event, Note Activities of type task, meeting or call, and note, attached to the records their WhoId and WhatId named
Attachment, ContentVersion (latest version) Files on their records, one per field, overflow in a <table>_files child table
RecordTypeId A record_type picklist on the table
Validation rules on one record check constraints, where the formula is in the supported grammar
Roll-up summaries, same-row formulas Rollup and formula fields, where the operation maps
List views Saved views: columns, filters whose operators map, sort
Page layouts Record layouts: sections, field order, related lists
Profiles, permission sets, public groups Groups with their members, an empty custom role per profile or permission set, private visibility where every non-admin profile was denied, and a report of the grants to set by hand; nothing is granted automatically
Flows, Process Builder, workflow rules, Apex, approval processes, reports, dashboards, email templates, assignment and sharing rules An inventory, each with its Tessule equivalent, sorted active first
Item Why What to do
Flows, Process Builder, workflow rules, Apex as running automation A different execution model Rebuild from the inventory in the flow editor; the AI assistant can draft from the inventory rows
Approval processes Approvals here are rules on updates Rebuild as approval rules and a flow on the decision
Reports and dashboards A different query model Rebuild as saved views with grouping, queries and dashboard tiles
Layout assignment per profile or record type One layout per audience Audience-specific layouts and conditional sections
Sharing rules, role hierarchy, queues, assignment and escalation rules Not modelled Roles, groups and record shares; round-robin as a flow
Field history other than stage moves No API writes historical change entries Keep the *History CSVs in the bundle
Amount and close-date snapshots in OpportunityHistory Only the stage moves are history Keep the CSV in the bundle
Dependent picklists and global value sets Every value is offered regardless of the controlling field The dependency is noted in the field description
Cross-object, $User and CASE formulas Outside the supported grammar Rebuild by hand from the report
Encrypted fields No encrypted type Review, then import as text or exclude
File versions, one file shared by many records One file per field, no sharing The latest version, copied per record
Email threads, Chatter, Knowledge, Communities No model Archive only
Multi-currency conversion Amounts keep their currency code; sums are not converted Report in one currency

The converter reads the bundle and writes the documents that build the workspace, applied in this order: tables and fields, then the data (one import job per object file, updating on source_id), then activities and files, then the stage configuration, the stage history and the dated baseline for records the history left out, then constraints, saved views and record layouts. Every step is safe to run again. The principal applying the plan is a workspace admin who also holds org:members:manage in the workspace’s organization (to create pending members), records:importProvenance (to set the original dates, authors and owners) and records:backfillHistory (to write the stage history as migrated history, through the job’s stage-history file or POST /tables/{tableId}/stage-transitions/seed).

People are never invited by the import. Everyone the data names becomes a pending member with no account and no seat, holding their records; an organization admin invites them from the organization console when the time comes, singly or with POST /organizations/{organizationId}/invites/bulk, and an accepted invitation claims the pending member, records and all.

Then verify: record counts per table against the manifest, a sample of owners, links, stages and files, and the coverage report, which lists every object and field with what it became and every compromise made on the way. Sign off before decommissioning the org.

Open the app