Sanity Integration
Connect your Sanity project to publish articles written in Asky straight into your content lake, as real documents of your own type, with your own fields.
What You Get
| Feature | Description |
|---|---|
| Content Publishing | Publish articles into any document type in your schema |
| Your Fields | Every field on the type appears in Asky, driven by your deployed schema |
| Drafts | Save to Sanity as an unpublished draft, exactly as if someone had started editing in Studio |
| Translations | Each language publishes as its own document, joined so Studio’s language switcher works |
| Status Tracking | Asky notices when a document is published, unpublished or deleted in Studio |
Prerequisites
- A Sanity project and dataset
- A robot token with Editor permission (sanity.io/manage → your project → API → Tokens)
- Your schema deployed to the dataset (see below)
Your Studio can be hosted anywhere: on Sanity, on your own Vercel project, or only on a developer’s laptop. Asky talks to the Content Lake API directly, so a self-hosted Studio needs no CORS entry and no plugin.
Deploying your schema
Asky reads your deployed schema to know which document types exist and what fields they have. This is the one setup step that is not inside Asky:
npx sanity schemas deployRun it from your Studio project. Without it, Asky can connect but has no types to offer.
It needs re-running whenever your schema changes, which is worth automating: see Keeping your schema in sync below.
Keeping your schema in sync
This is the one step that isn’t inside Asky, and the one most likely to be forgotten, so it’s worth understanding why it exists.
Three things in Sanity are more independent than they look:
- The Content Lake is schemaless. Documents are just JSON. Changing a schema doesn’t migrate or validate existing content.
- Your site queries the dataset with GROQ, and GROQ never reads the schema. A schema change has no effect on your live site by itself.
- Your Studio is where a schema change lands for editors, and that happens when you rebuild and redeploy the Studio.
sanity schemas deploy is none of those. It writes a machine-readable description of your content model into the dataset, and Asky is what reads it.
Because nothing else in your stack depends on it, forgetting it is silent. Your Studio works, your site works, and only Asky drifts. If you rename a field without redeploying, Asky keeps writing to the old name and reports the field as skipped after publishing.
What happens if it’s stale
| Change | Effect |
|---|---|
| Added a field | Asky doesn’t offer it until you redeploy. Harmless. |
| Renamed or removed a field | Asky skips it and says so in the publish notes, but that content isn’t written. |
| Changed a field’s type | Asky writes the old shape. Redeploy and republish. |
Asky shows how long ago your schema was deployed, and after a publish it samples your real documents to tell you when they contain fields the deployed schema doesn’t.
Automate it in CI
Running it by hand works. Automating it means it can’t be forgotten.
A Sanity-hosted Studio deployed with sanity deploy extracts the schema as part of that command. A self-hosted Studio (Vercel, Netlify, your own CI) usually builds with sanity build, which does not write to the dataset, so it needs the extra step.
Add it to your build command:
sanity build && sanity schemas deployOr as its own GitHub Actions step:
- name: Deploy Sanity schema
run: npx sanity schemas deploy
env:
SANITY_AUTH_TOKEN: ${{ secrets.SANITY_AUTH_TOKEN }}CI needs its own token with deploy permission, set as SANITY_AUTH_TOKEN. This is separate from the token you gave Asky, and it doesn’t need to be the same one.
If your build output isn’t ./dist/static, pass --manifest-dir to point at the right directory.
Connecting Sanity
Navigate to Connections
Go to Settings → Connections in Asky and find the Sanity card.
Enter your project details
You will need your project ID and dataset name, both visible in sanity.io/manage .
Paste a robot token
Create a token with Editor permission and paste it in. Asky encrypts it at rest and never returns it to the browser.
Sanity’s Editor permission is project-wide across datasets unless you are on Enterprise with custom roles. That is a smaller ask than the WordPress application password Asky already accepts, but it is worth knowing.
Nothing to pick for the workspace
Asky reads the workspace name straight from your deployed schema, so there is nothing to choose. It is often not called default — a Studio names its workspaces freely.
Tell Asky where your Studio is
One optional field, and you can change it later.
Studio URL powers the “Open in Studio” link beside each published article, so you can jump straight from Asky to editing the document:
- Self-hosted: your own address, e.g.
https://studio.example.com - Sanity-hosted:
https://<your-project>.sanity.studio
Leave it blank only if you don’t want those links — Asky can’t infer the address either way.
Asky doesn’t ask for your site’s public URLs. Sanity has no concept of them, and a single pattern would be wrong the moment you publish more than one document type. Use your own site or Sanity’s Studio to find a published article.
Check what Asky found
The last step lists which of your document types can receive an article, and where hyperlinks will land in each. A type with no rich text field can’t hold an article body, and a rich text field with no link annotation can’t store hyperlinks, so this is your chance to spot both before publishing anything.
You don’t pick a type here. That’s chosen per article in the content editor, the same way a Webflow collection is.
Publishing
Open any draft in the Content Library, choose Sanity as the destination, pick a document type, and fill in the fields. Then Publish Now, or Save as Draft to CMS to leave it unpublished in Studio.
Asky writes only the fields it manages. Anything an editor set in Studio (SEO objects, contributors, related posts, an image hotspot) is left untouched, because Asky patches rather than replaces the document.
Links
Portable Text has no built-in link type; each schema declares its own annotation. Asky asks which one is your external link during setup and shows the resolved path in the publishing panel. If your rich text has no annotation that can hold a link, Asky blocks publishing for any article containing one rather than silently dropping the hrefs.
Images
Images in the article are uploaded into your own asset store and referenced normally. Sanity content-addresses assets, so republishing the same image does not create duplicates.
Two things about your schema decide how much survives, and the publishing panel tells you both before you publish:
- If the rich text field declares no image member, pictures cannot be stored at all and are dropped. Everything else still publishes.
- If the image member declares no alt field, the picture publishes and the alt text is dropped, because Asky writes alt only where your schema has somewhere to put it. It looks for
alt,altTextoralternativeText, and deliberately never repurposescaptionorattribution— those render as visible text on your site, so filling one with an alt attribute would add a caption you never wrote.
A bare defineArrayMember({type: 'image'}) is Sanity’s default and declares neither, so adding an alt field is usually a one-line change:
defineArrayMember({
type: 'image',
fields: [defineField({name: 'alt', type: 'string', title: 'Alt text'})],
})Either way the publish report names what was dropped, so this never happens quietly.
Translations
With @sanity/document-internationalization, each language is its own document with a language field, joined by a translation.metadata document.
That is exactly how Asky models translations, so each language publishes independently. A language joins the switcher when it goes live, which also avoids Sanity’s rule that a document cannot publish while it references an unpublished one. See Translations for how a language version is created in the first place.
If your project uses field-level localization (sanity-plugin-internationalized-array) instead, publishing still works, but every language lives in one document and they cannot be published separately. Asky publishes single-language documents and leaves your arrays alone.
Editing in both places
Asky is a one-way publisher: it writes to Sanity, and does not read your Studio edits back into the draft.
If a document changed in Studio since Asky last published it, Asky says so before you republish. Sanity also rejects the write server-side if the document changed while the publish was in flight, so a genuine race fails loudly instead of quietly overwriting someone.
Troubleshooting
| Problem | Cause |
|---|---|
| ”No schema deployed” | Run npx sanity schemas deploy from your Studio project |
| A field is missing in Asky | The schema was changed but not redeployed |
| ”Read-only token” | The token has Viewer permission; create one with Editor |
| Publishing blocked on a link | The chosen rich text field declares no annotation that can hold a link |
| Publishing blocked on the body | The document type has no rich text field; pick another type |
| Wrong workspace’s types | Re-connect and pick the workspace your content model lives in |