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.
Before you begin
You need an Azure subscription with a storage account you administer: a StorageV2 account with hierarchical namespace enabled — that is, Azure Data Lake Storage Gen2 — and a container in it to receive the data. Prepare the storage account below covers both.
You also decide how TrueIdiom reaches that account: a role assignment for the recommended keyless managed identity, or a SAS token or connection string you paste once. See Choose an access mode.
Binding, verifying and removing the account are admin actions: they need a
workspace admin, or the workspace’s API key. Every member can see the
binding and its status, but a member’s change is refused with
403, and each change is recorded in the audit log with who made
it — never the secret.
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. While the binding is not verified — including right
after any edit — or cannot be used because its stored secret is missing or
unreadable, new translations are refused up front with 409
storage_binding_not_verified, before any translation runs or any job
is created, so nothing is charged; jobs that would have gone to review are refused
too. What happens to a job already under way when your storage becomes
unreachable depends on which pipeline it came from — a text
translation job whose published result cannot be written parks with a
persistence_failed status and is replayed for you once the binding is
verified again (a parked job is billed: its translation is final and readable in
the meantime), while a document job is marked failed
and has to be resubmitted after you fix the binding — unless its
translated file had already reached your account, in which case it is billed and
its failure message says it does not need resubmitting. 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, the job records themselves — including the source text and final translation of text jobs and the extracted text segments of documents — with their status and 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.
So BYOS decides where your published outputs and document files rest; it does not take your content out of the service’s own records. Translation and review still run on TrueIdiom compute, so text 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>"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
As a workspace admin, open Storage (under Administration) in the
workspace and enter:
provider Azure Data Lake (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.
The account URL has to be your storage account’s own endpoint: the
dfs form above, or the account’s blob endpoint,
which is saved as the dfs one — over https, with no
port, path or query. Anything else, and any account TrueIdiom itself writes to, is
refused with a 422 when you save. Saving checks the URL’s
form, not that the account exists; that is what verify is
for.
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 — with an admin’s access token,
or the workspace API key in X-API-Key:
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. On a later save with the same access mode you may
leave the secret out to keep the one already stored. 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
— the stored secret aside, as above — 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.
409
storage_binding_not_verified.
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.
Of the two, deleting the binding is the one provisioning never
blocks. Switching back to the platform provider may first have to provision your
platform storage container, and fails with a 502 if that does not
succeed — a clean failure that changes nothing, leaving you on the binding you
had — so reach for delete if the switch keeps failing. Either way out publishes
any text jobs parked in persistence_failed to platform storage, with
the same replay summary in the response as a verify.
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 — and since
the platform-side delete still completes, a second delete finds nothing to act
on. Remove those copies from your account yourself: the job’s record under
golden/ (and its line in that day’s manifest), or the
document’s documents/<job-id>/ folder.
Troubleshooting
| Symptom | Cause / fix |
|---|---|
Saving is refused with 422 “account_url must be an
Azure Storage endpoint” |
Use your account’s dfs (or blob)
endpoint over https, with no port, path or query —
https://<acct>.dfs.core.windows.net. |
| 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 name — saving checks only the URL’s form, not that the account exists — 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 — or the binding not verified — when the job published (fail-closed by design). Fix the binding and re-verify — the verify response replays the parked text jobs. Switching back to platform storage, or deleting the binding, replays them too. |
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 —
unless its failure message says the translated document was already
delivered to your storage account: that one has been billed and does not
need resubmitting. |
New translations are refused with 409
storage_binding_not_verified |
The binding is not verified — it is new, was just edited, or its last verify failed — or its stored secret is missing or unreadable. Verify it (re-entering the secret first if it is gone), or switch back to platform storage, then resubmit; nothing was created or charged. |
The stored secret is reported missing or unreadable — a submission, or a
document download, review or approval, is refused with 409 |
Re-enter the SAS token or connection string by saving the binding again with the secret, then verify. |
A save, verify or delete is refused with 403 “admin
role required” |
Changing the binding needs a workspace admin, or the workspace API key. Nothing changed — ask an admin to make the change. |
A save, verify or delete — or a submission, or a document download,
review or approval — fails with 503 “secret storage is
temporarily unavailable” |
A temporary problem on our side: nothing about your binding changed, a verified binding stays verified, and nothing was created or charged. Try again shortly. |
| 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.
What's next
- Storage API reference — read, replace, verify and delete a binding, with the error shapes to branch on.
- Pricing & billing FAQ — what a character is, and how plans, trials and top-ups work.
- Getting started — the translate, approve and re-translate loop your golden files come from.