What we touch, and what we keep.

A migration reads your entire customer history out of one system and writes it into another. That is worth a page of specifics rather than a badge, so here are the mechanisms, the subprocessors, and the disclosure most tools leave out.

0
records sent to a model
6
subprocessors, all named
3
things that never leave

How your data is held

OAuth, not pasted credentials

Attio, HubSpot, Pipedrive and Salesforce all connect over OAuth with scoped, revocable tokens — revoke us from your own admin and we are out, no ticket required. Affinity has no OAuth, so it takes an API key, and an Attio API key is offered only as a clearly-marked fallback. Every connection belongs to one migration, and its token is deleted 14 days after the migration finishes.

Tokens encrypted with a key we do not store beside them

Every credential is sealed under its own data key, which is itself wrapped by a master key held outside the database. One module in the codebase can decrypt, and one table holds token material. A database dump on its own opens nothing.

Background jobs carry an id and nothing else

The durable job that runs your migration receives a single migration id. Credentials and records are fetched inside the job, from encrypted storage, and never travel through a queue payload, a log line or a dashboard. Secrets and personal data are never logged at all.

Every request re-checks who owns the row

Ownership is enforced at the data layer on each request rather than in the page that renders it, and a migration belonging to someone else answers the same way one that does not exist does — so an id cannot be probed for.

Your CSV is deleted when the full run finishes

A CSV imports in full, with no sample, and the file is removed when the import finishes or you roll it back; a paused or failed run keeps it until you resume, retry or roll back. A CSV sample started before CSV imports ran in full keeps its file for up to 30 days, and a daily sweep deletes it after that. What we keep is the structure the run used, the field mapping, the id map and the provenance ledger — the bookkeeping that makes a re-run idempotent and a rollback complete. Not your rows.

Reconnecting to a different Attio workspace stops the run

Every id we hold is scoped to one workspace. If the connected workspace changes, resume, retry, full-run and rollback are all refused at the door rather than writing this migration's records into somebody else's Attio.

What reaches Claude, exactly

Shuttio uses a model in two places: to guess the type of each column in a CSV you upload, and in the help chat. This is the whole of what it receives.

What the model sees

Only a CSV you upload, and only a little of it: each column's name, at most three sample values, each truncated to 80 characters, and, for a column with 25 or fewer distinct values, how many there are. That is what lets it tell an email column from a date or a fixed list of choices. A connected CRM sends it nothing.

What the help chat sees

The help chat, shown to signed-in users, sends Claude our product documentation, the messages you type in it (up to 20 turns, kept in your browser only), and, on a migration page, a summary of that migration: its state, source and destination, the options chosen, each object's counts and status, and error groups by object, kind and field. Never a record value, name, email, source id, error text or API response. Nothing from a chat is stored on our servers. The text of earlier turns comes back from your browser, so it is yours to edit; it never reaches another account.

What the model never sees

The import itself, and your records. Nothing designs your structure with a model — it is mirrored from your CRM's own schema by code — and the writer never calls one. Your records stream from your CRM into Attio without passing through Claude, whether you are moving fifty rows or five million.

Values that could not be written, kept 14 days

When a single value could not be written to your destination (a malformed email, a number that is not a number), the migration's Errors tab lists it with the object, the field, the source record's id, our own explanation, the destination's error code and HTTP status, and up to 200 characters of the value. The destination's error message is never stored. The value is deleted 14 days after the migration finishes, at the same time as its credentials; the rest of the line stays with the migration.

What the model can decide

Nothing, on its own. It guesses the type of each column in a CSV you upload; you see every guess on the field review and can change it before a single write reaches Attio. The help chat only answers questions and changes nothing.

Three things that are never migrated anywhere

For any reason, on any plan, at any record count.

Your record rows

Never leaves

The mirror and the writer are code, with no model in either. Records stream from your CRM into Attio without passing through one.

Your uploaded CSV

Never leaves

Deleted when the import finishes or you roll back; a paused or failed run keeps it until you resume, retry or roll back. A CSV imports in full, with no sample. What we keep is the structure, the mapping, the id map and the provenance ledger — not your rows.

Your Odoo import package

Never leaves

An import package is kept encrypted and deleted 7 days after it is built, or when you finish the load guide, whichever comes first. Its download links work for 24 hours, and only for you.

Your records on the way to the Odoo app

Never leaves

If you use our Odoo app, the records waiting for your Odoo are kept encrypted, and deleted as soon as your Odoo confirms them, and after 24 hours at most. The app's access is stored only as a fingerprint, and you can disconnect it from Odoo or from Shuttio at any time.

Credentials

Never leaves

Sealed under a per-record data key wrapped by a master key held outside the database, and never placed in a job payload, a log line or a dashboard, and deleted 14 days after the migration finishes (revoked with the provider where it allows).

Who else processes it

The full list. Shuttio is operated by Venturise OÜ in Tallinn, Estonia.

NeonPostgres database
VercelApplication hosting
InngestDurable job execution — receives migration ids only
AnthropicClaude, for CSV column types and the help chat, described above
GoogleSign-in only, via OAuth
ResendCompletion emails, when enabled

Getting back to where you were

Cancelling a migration deletes every record, note, task, meeting, list entry, attribute and select option that migration created, in reverse order, working from a ledger written as each one was made. Anything that was in your workspace beforehand is never touched.

Custom objects the migration created are deleted too, last, once its own records are gone and the object is empty. One that holds records the migration did not create (yours, or another migration's) is left in place and named, with its record count. Attio's standard objects and any object you already had are never touched. One thing a rollback leaves in place is the lists it created: Attio's API cannot delete a list. They are listed for you by name at the end of a rollback rather than quietly left behind, so the cleanup is a few clicks in Attio and not a discovery six months later.

Read it, forward it, then start with fifty records.

Free at any record count. Nothing is written to Attio until you have reviewed the objects and fields, and every run starts as a free 50-record sample.