Bring your own storage (BYOS)
With BYOS, your workspace’s translation data lands in your Azure storage account instead of TrueIdiom’s platform store — your approved translations and your document files, in your tenant, under your access policies. Setting it up is self-serve: bind your account from Storage in the workspace, or over the API, by following this guide.
What moves to your account
Once your binding is verified, two data planes route to your storage:
| Plane | What lands there | Default prefix |
|---|---|---|
| Golden | Text translations you published or approved — autonomous passes too, not only human-reviewed ones — plus a daily manifest | golden/ |
| Documents | Document jobs: source file, layouts, quality report, translated output | documents/ |
A binding also carries a third prefix, terminology/, which is
reserved for a future plane. Nothing writes to it today:
terminology and translation memory currently stay in the platform control plane
for every workspace, BYOS or not. A custom terminology prefix is accepted and
stored on your binding, but it simply sits unused until that plane ships.
Approving a document does not add a record to golden/ in your
account. It writes a counts-only audit record — document id, reviewer, language
pair, how many segments, no text — to our side of the line. The document’s own
files land in documents/ as usual.
Routing is fail-closed: data goes to your account only after the
binding is verified, and nothing silently falls back to platform storage when your
storage becomes unreachable. What happens to the affected job depends on which
pipeline it came from — a text translation job parks with a
persistence_failed status and is replayed for you once the binding is
verified again, while a document job is marked failed
and has to be resubmitted after you fix the binding. Scratch copies
of the files that go to your account are removed once the upload has been attempted,
and downloads are served from your account.
One artifact never leaves our side: the pipeline telemetry log. It is execution internals end to end — engine routing, per-stage model calls, per-stage token cost — with nothing in it you would read, so it is retained platform-side only and is never uploaded to your account. The layout JSONs, the quality report and the golden records that are written to your account are redacted copies: your text, geometry, review signals, scores and decisions are kept, while model names, token usage, reasoning and per-step agent metadata are stripped. Our full-fidelity copy stays in the platform control plane.
What stays with TrueIdiom
BYOS moves the translation data plane, not the whole service. Regardless of your binding, the platform control plane keeps your workspace and user accounts, billing and usage records, job status and job metadata (including checksums and review history), terminology and translation memory, the binding record itself, and — if you chose a secret-based access mode — the encrypted credential.
BYOS is a guarantee about data at rest. Translation and review still run on TrueIdiom compute, so text in flight is processed on our side before the approved result is written to your account.
Prepare the storage account
- Create (or pick) a StorageV2 account with hierarchical namespace enabled — that is, Azure Data Lake Storage Gen2.
- Create a container to receive the data, for example
golden. This becomes the binding’s filesystem.
Choose an access mode
Option A — Managed identity (recommended, keyless)
Grant TrueIdiom’s service principal the Storage Blob Data
Contributor role on the container (or the account). Read the
principal’s application ID straight from the API —
GET /api/storage/binding returns it as
principal_client_id, and works before you have bound anything:
curl -sS "$BASE_URL/api/storage/binding?api-version=2026-09-01" \
-H "Authorization: Bearer $TOKEN" | jq -r .principal_client_id
On the rare deployment where that comes back null, the identity is
one we cannot name from configuration —
ask us rather than guessing, because a role granted to the
wrong principal fails as a plain permission error with nothing to point at it.
az role assignment create \
--assignee <trueidiom-sp-app-id> \
--role "Storage Blob Data Contributor" \
--scope "/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.Storage/storageAccounts/<acct>/blobServices/default/containers/<container>"No secret ever changes hands — and revoking the role later cuts off TrueIdiom’s access. That is your kill switch, and it is entirely in your hands. Azure takes the same few minutes to propagate a revocation as it does a grant, so expect access to stop shortly after you revoke rather than on the instant.
Option B — SAS token
Generate a SAS token for the container with read, write, create, delete, and list permissions and a sensible expiry. You paste it once when binding; it is stored encrypted immediately and is never returned by any API or page afterwards.
Option C — Connection string
Handled exactly like a SAS token: stored encrypted, referenced indirectly, never displayed again.
Bind the account
In the workspace, open Storage and enter: provider
adls, your account URL (https://<acct>.dfs.core.windows.net),
the container name, and the access mode — pasting the secret if you chose SAS or
connection string. Save.
Always fill in the container name. The documents plane uses it verbatim, so a blank value leaves document jobs with nowhere to write.
Prefer the API? The same binding as a call:
curl -sS -X PUT "$BASE_URL/api/storage/binding?api-version=2026-09-01" \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{
"provider": "adls",
"account_url": "https://myacct.dfs.core.windows.net",
"filesystem": "golden",
"auth_mode": "managed_identity"
}'
For sas or connection_string, include a
"secret" field — the response only ever confirms
has_secret: true. An optional "prefixes" object remaps
the per-plane directories if you want something other than the defaults shown above.
This call is a full replace, not a patch: anything you leave out
falls back to its default, so omitting prefixes resets your custom
directories. Send the complete desired state every time. The full endpoint
reference — reading the binding, replacing it, verifying, deleting, and the
error shapes to branch on — is in the
Storage API documentation.
Custom prefixes are API-only. The in-app Storage
panel has no field for them, and because saving from it is the same full replace,
a panel save silently resets all three prefixes to the defaults. If you set custom
prefixes over the API, re-PUT them after anyone saves from the panel —
and prefer the API for those workspaces.
Verify the binding
Verification is a real write/read/delete round-trip against your
account using the binding’s credentials — not a formatting check. Wrong account
URLs, missing role assignments, and expired SAS tokens all fail here. A failed
probe is reported as a result, not as a transport error: the binding comes back
with status error and a last_error field carrying
Azure’s own message. Trigger it from the Storage section, or:
curl -sS -X POST "$BASE_URL/api/storage/binding/verify?api-version=2026-09-01" \
-H "Authorization: Bearer $TOKEN"On success:
- the binding flips to
verifiedand live routing begins; - any text translation jobs that parked as
persistence_failedwhile your storage was unreachable are replayed automatically, and the response includes a replay summary. Document jobs are not replayed — those you resubmit.
One caveat worth knowing before you scope permissions narrowly: the probe only
writes under the golden/ prefix. Grant access to the whole container
— or to every prefix you configure — so a passing verify actually means
document artifacts will land too.
Every edit to a binding resets it to unverified. Re-run verify after any change — data does not route to your account until you do.
Day-to-day use
Nothing about the workflow changes. Translate, review, and approve as usual:
- text translations you publish or approve land in
golden/in your container — one JSON record each plus a daily manifest; - document uploads and translated outputs land in
documents/<job-id>/; - document downloads are fetched from your account.
One thing does change, in your favour: platform storage caps do not apply to you. Workspaces on platform storage can be given a ceiling on the document bytes they keep with us, and uploads past it are refused. Once your workspace is bound to your own account, that admission check is skipped entirely — those bytes rest in your account, on your subscription, and capacity planning is yours from there.
Opting out
Switch the binding back to the platform provider, or delete the binding entirely — either from the Storage section or via the API. Both drop any stored secret. Data already written to your account stays there: leaving BYOS never migrates, copies back, or deletes what your account already holds. New work resumes on platform storage; the historical records remain yours to keep or clear on your own schedule.
Of the two, deleting the binding is the one that always works.
Switching back to the platform provider provisions your managed platform container
first and can fail with a 502 if that provisioning does not succeed —
a clean failure that changes nothing, leaving you on the binding you had. Deleting
is never blocked that way, so reach for it if the switch keeps failing.
Deletion you ask for is the one exception, and it is deliberate. When you delete a translation job or a document from TrueIdiom, the cascade also reaches into your bound account to remove that item’s records and artifacts there. That reach is best-effort and honestly counted: the removals it manages are reported in the response counts, and a failure on your side never blocks the platform-side delete. So if your storage is unreachable or our access has been revoked when you issue the delete, the copies in your account remain — restore access and re-run the delete to clear them.
Troubleshooting
| Symptom | Cause / fix |
|---|---|
| Verify fails with an authorization or role error | The TrueIdiom service principal lacks Storage Blob Data Contributor on the container, or Azure role propagation (up to ~5 minutes) hasn’t finished. Wait and retry. |
| Verify fails with name resolution or a 404 | Wrong account URL — it must be the dfs endpoint
(https://<acct>.dfs.core.windows.net) — or the container
doesn’t exist. |
| Verify fails after it used to work | SAS token expired. Update the binding with a fresh secret, then re-verify. |
| Verify passes, but document jobs still fail | The probe only exercises the golden/ prefix. Widen the role
assignment to the container (or to every prefix you configured), and check
that the binding actually carries your container name. |
Text jobs stuck in persistence_failed |
Your storage was unreachable at approval time (fail-closed by design). Fix the binding and re-verify — the verify response replays the parked text jobs. |
Document jobs failed while storage was down |
Document jobs do not park and are not replayed: a job that could not
upload its artifacts is marked failed and its scratch purged.
Fix the binding, re-verify, then resubmit the document. |
| Binding shows unverified after an edit | Expected: every change forces re-verification. Run verify again. |
Stuck on something not listed here? Contact us and we will help you get storage set up.