How to Integrate Smartling with Your Headless CMS
Connect Smartling to your headless CMS so source content can move from publish to translation workflows, review, and localized delivery without copy-and-paste handoffs.
What is Smartling?
Smartling is an enterprise localization platform for translating digital content, product copy, websites, apps, documents, and support content. It combines translation workflow, translation memory, glossaries, machine translation, human review, visual context, and APIs for teams that ship content across multiple languages. Localization, marketing, product, and content operations teams use Smartling when translated content needs approvals, quality checks, and repeatable workflows.
Why integrate Smartling with a headless CMS?
If your team publishes in 5, 12, or 40 locales, translation handoffs can become the slowest part of the release. A product page goes live in English, someone exports fields to a spreadsheet, another person uploads files to Smartling, translators ask which headline belongs to which page section, and then localized copy gets pasted back into the wrong field. That works for a few pages. It breaks when you have hundreds of articles, product descriptions, legal notices, and campaign pages changing every week.
Connecting Smartling to a headless CMS lets you send structured source content directly into translation workflows. Instead of sending full pages or raw HTML, you can send the exact fields translators need, such as title, excerpt, body, CTA label, SEO title, and alt text. With Sanity's AI Content Operating System, content in the Content Lake is typed JSON, so Smartling receives clean field names and predictable JSON files. GROQ can select only translatable fields, including referenced content like product names or category labels.
The alternative is disconnected work: manual exports, folder conventions, stale translation memory context, and no reliable way to know whether a translation matches the current source. Webhooks and Functions reduce that risk. A publish event can trigger a Smartling upload within seconds, and a Smartling callback can write completed translations back to the right localized document. You still need to define locale rules, review workflows, and fallback behavior. The integration won't decide those for you. But once those rules are clear, the handoff becomes repeatable.
Architecture overview
A typical Sanity and Smartling integration starts when an editor publishes source-language content in Sanity Studio. A Sanity webhook filters for the document types and events you care about, for example published articles where language == "en". The webhook sends the document ID to a Sanity Function or your own webhook endpoint. That server-side code fetches the full source document from the Content Lake with GROQ. The query can project only the fields Smartling should translate, such as title, slug, body, SEO fields, CTA labels, image alt text, and referenced category names. The function then authenticates with Smartling's Auth API using a user identifier and user secret, creates a JSON payload, and uploads it to the Smartling Files API at /files-api/v2/projects/{projectId}/file with fileType=json, fileUri set to a stable path like articles/my-post.json, and authorize=true when you want Smartling to start translation authorization immediately. From there, Smartling routes the file through the project's workflow, translation memory, glossary, machine translation, human review, and quality checks. When translations are ready, your integration can use a Smartling callback or scheduled check to download each locale's translated file from the Files API. The final step writes localized fields back into Sanity as locale-specific documents or field-level translations. Your frontend then queries the right locale from the Content Lake and renders localized content for web, mobile, or any other channel.
Common use cases
Global marketing launches
Send campaign pages, SEO fields, CTA labels, and image alt text to Smartling when English content is published, then return approved translations to locale-specific Sanity documents.
Product copy with placeholders
Translate JSON content while preserving variables, product names, ICU messages, and structured fields that shouldn't be edited by translators.
Help center localization
Route articles, categories, and related links through Smartling workflows so support content stays aligned across languages after source updates.
Translation review with context
Send page metadata, preview URLs, and field labels from Sanity so Smartling reviewers can understand where each string appears.
Step-by-step integration
- 1
Set up Smartling project access
Create or choose a Smartling project, confirm the source locale and target locale IDs, and generate API credentials from Smartling. You'll need userIdentifier, userSecret, and projectId. If your team uses Smartling's SDK for your runtime, install it. The example below calls Smartling's REST API directly, which is the clearest path for a small webhook handler.
- 2
Model localizable content in Sanity Studio
Add fields that make translation state explicit. For example: language, translationGroup, title, slug, body, seoTitle, seoDescription, smartlingFileUri, smartlingStatus, and references to localized variants. Keep non-translatable fields, such as IDs, SKUs, and routing keys, separate from copy fields.
- 3
Create a publish trigger
Create a Sanity webhook that fires on publish events for source-language documents, or use a Sanity Function if you want the sync logic to run inside Sanity's server-side event system. Configure the webhook payload to include at least the document _id and _type.
- 4
Fetch the exact fields Smartling needs
Use @sanity/client and GROQ in the handler to fetch translatable fields, join referenced labels, and omit internal fields. This avoids sending draft metadata, workflow fields, or HTML that translators shouldn't touch.
- 5
Upload JSON to Smartling
Authenticate with Smartling's Auth API, then upload the JSON file to the Files API with a stable fileUri. Use fileType=json so Smartling can parse the payload, apply translation memory, and keep translated keys aligned with the source.
- 6
Test the localized frontend path
Publish one source document, confirm the file appears in Smartling, complete a test translation, download or receive the translated JSON, write it back to Sanity, and query it from your frontend by locale. Test fallback behavior when one locale is missing or still in review.
Code example
import { createClient } from '@sanity/client';
import { NextRequest, NextResponse } from 'next/server';
const sanity = createClient({
projectId: process.env.SANITY_PROJECT_ID!,
dataset: process.env.SANITY_DATASET!,
apiVersion: '2025-02-06',
token: process.env.SANITY_API_TOKEN!,
useCdn: false,
});
export async function POST(req: NextRequest) {
const { _id } = await req.json();
const doc = await sanity.fetch(`*[_id == $id][0]{
_id, title, "slug": slug.current, body, seoTitle, seoDescription,
"category": category->title
}`, { id: _id });
if (!doc?.slug) return NextResponse.json({ skipped: true });
const authRes = await fetch('https://api.smartling.com/auth-api/v2/authenticate', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
userIdentifier: process.env.SMARTLING_USER_IDENTIFIER,
userSecret: process.env.SMARTLING_USER_SECRET,
}),
});
const auth = await authRes.json();
const accessToken = auth.response.data.accessToken;
const fileUri = `articles/${doc.slug}.json`;
const form = new FormData();
form.set('file', new Blob([JSON.stringify(doc)], { type: 'application/json' }), `${doc._id}.json`);
form.set('fileUri', fileUri);
form.set('fileType', 'json');
form.set('authorize', 'true');
const uploadRes = await fetch(
`https://api.smartling.com/files-api/v2/projects/${process.env.SMARTLING_PROJECT_ID}/file`,
{ method: 'POST', headers: { authorization: `Bearer ${accessToken}` }, body: form }
);
if (!uploadRes.ok) return NextResponse.json({ error: await uploadRes.text() }, { status: 502 });
return NextResponse.json({ fileUri, smartling: await uploadRes.json() });
}How Sanity + Smartling works
Build your Smartling integration on Sanity
Sanity gives you the structured content foundation, real-time event system, and flexible APIs you need to connect Smartling to your localization workflow.
Start building free →CMS approaches to Smartling
| Capability | Traditional CMS | Sanity |
|---|---|---|
| Translation payload structure | Often exports page HTML or plugin-specific formats, which can mix layout, copy, and metadata. | Uses GROQ to send a single, typed JSON payload with only the fields Smartling should translate. |
| Sync timing after publish | Often depends on scheduled exports, editor actions, or translation plugin queues. | Webhooks and Functions can start the Smartling upload when source content is published. |
| Locale modeling | Locale structure is often tied to pages, folders, or plugin rules that are hard to change later. | Schema-as-code lets you model field-level locales, document-level locales, translation groups, and fallback rules. |
| Translator context | Context may be limited to page URLs or exported screenshots. | Can include preview URLs, field labels, categories, and related content in the Smartling JSON payload. |
| Writing translations back | Translated content may be pasted into pages or imported through plugin-specific mappings. | APIs can write translated fields back into locale documents while preserving references, drafts, and editorial workflows. |
| Multi-channel delivery | Localized content is often tied to one website delivery path. | The same localized structured content can serve web, mobile, search, Smartling sync, and AI agents. |
Keep building
Explore related integrations to complete your content stack.
Sanity + Lokalise
Connect Sanity with Lokalise to translate app strings, product UI copy, and structured content across release cycles.
Sanity + Phrase
Use Phrase with Sanity to route structured source content through translation memory, review, and locale delivery.
Sanity + Crowdin
Pair Crowdin with Sanity for community translation workflows, developer-friendly localization files, and structured content sync.