Automation & Integration8 min read

How to Integrate Tray.io with Your Headless CMS

Connect Tray.io to your headless CMS so content changes can trigger CRM updates, product syncs, approval workflows, and customer notifications without manual copy-paste.

Published April 29, 2026
01Overview

What is Tray.io?

Tray.io is an automation and integration platform used by operations, RevOps, IT, and product teams to connect SaaS apps, internal systems, APIs, and data workflows. Teams build automations in Tray.io using visual workflows, connectors, callable triggers, branching logic, and API calls. It sits in the same category as Workato, Zapier, Make, and n8n, with a strong fit for business-critical workflows that need more control than simple one-step automations.


02The case for integration

Why integrate Tray.io with a headless CMS?

If your content changes need to kick off work in Salesforce, HubSpot, Slack, Jira, Airtable, Snowflake, or an internal API, Tray.io is often where that logic already lives. Connecting it to a headless CMS lets a publish event do real work: create a launch checklist, notify a regional sales team, update a product catalog, send a localization request, or start a compliance review.


03Architecture

Architecture overview

A typical Tray.io integration starts when an editor publishes or updates a document in Sanity Studio. Sanity writes that structured JSON document to the Content Lake. A GROQ-powered webhook filters the event, for example only published product pages where status == "live", and sends the document ID to a webhook handler or Sanity Function. That server-side handler fetches the latest document from the Content Lake with @sanity/client and GROQ. This keeps the Tray.io payload small and current. The query can join references, such as author, product family, region, or campaign, so Tray.io receives one clean JSON object. The handler then calls a Tray.io Callable Trigger URL with an HTTP POST request. In Tray.io, that callable workflow can map the incoming fields, call connectors like Salesforce or Slack, branch based on locale or content type, and write results to other systems. The end user might see a Slack notification, a CRM record update, a Jira task, a product feed update, or a customer-facing page that reflects the same published content.


04Use cases

Common use cases

🚀

Launch operations

When a campaign page is published in Sanity, Tray.io can create Jira tasks, notify Slack channels, update Salesforce campaign records, and log the launch in Airtable.

🛒

Product data routing

Send structured product copy, SKU metadata, release dates, and regional availability from Sanity to commerce, PIM, ERP, and spreadsheet workflows through Tray.io.

🌍

Localization handoffs

Trigger Tray.io workflows that send approved source content to translation tools, route locale-specific reviews, and write translation status back to your operations stack.

📣

Sales and support updates

Use Tray.io to push newly published help articles, release notes, or pricing updates from Sanity into Slack, Zendesk, HubSpot, or internal enablement tools.


05Implementation

Step-by-step integration

  1. 1

    Create the Tray.io workflow

    In Tray.io, create a new workflow with a Callable Trigger. Copy the generated trigger URL. Add steps for the downstream systems you need, such as Slack, Salesforce, HubSpot, Jira, Google Sheets, or an HTTP Client call to an internal API. If the trigger should be private, require an Authorization header and store the token as a Tray.io config value.

  2. 2

    Model the content in Sanity Studio

    Define schema fields that Tray.io can route without guessing. For a product launch, include fields like title, slug, sku, launchDate, regions, lifecycleStatus, owner, relatedCampaign, and integrations.syncToTray. Keep IDs stable, use references for related documents, and avoid hiding workflow-critical data inside freeform rich text.

  3. 3

    Create a Sanity webhook or Sanity Function

    Use a Sanity webhook when you want Sanity to call your own endpoint on publish, update, or delete. Use a Sanity Function when you want the sync logic to run inside Sanity's server-side event system. In both cases, filter events with GROQ so Tray.io isn't called for drafts, internal notes, or content types that don't need automation.

  4. 4

    Fetch the exact payload with GROQ

    In your handler, use @sanity/client to fetch the current document from the Content Lake. Project only the fields Tray.io needs, and join references in the same query, for example author->name, categories[]->title, or productFamily->slug.current.

  5. 5

    Post the payload to Tray.io

    Call the Tray.io Callable Trigger URL with fetch, set content-type to application/json, and include your auth header if configured. In Tray.io, map the JSON fields to connector steps, add conditions for content type or region, and handle failures with Tray.io's workflow logs and retry settings.

  6. 6

    Test the full path

    Publish a test document in a non-production Sanity dataset, confirm the webhook fires, inspect the request body in your handler logs, and check the Tray.io run history. Then test update and delete events, not just first publish. Frontend apps should read from Sanity as usual, while Tray.io handles the operational side effects.


06Code

Code example

typescriptapi/sanity-to-tray.ts
import {createClient} from '@sanity/client'
import type {VercelRequest, VercelResponse} from '@vercel/node'

const sanity = createClient({
  projectId: process.env.SANITY_PROJECT_ID!,
  dataset: process.env.SANITY_DATASET!,
  apiVersion: '2025-02-19',
  token: process.env.SANITY_READ_TOKEN,
  useCdn: false,
})

export default async function handler(req: VercelRequest, res: VercelResponse) {
  if (req.method !== 'POST') return res.status(405).end()

  const {documentId} = req.body
  const id = String(documentId).replace(/^drafts\./, '')

  const doc = await sanity.fetch(
    `*[_id == $id][0]{
      _id,
      _type,
      title,
      "slug": slug.current,
      sku,
      launchDate,
      lifecycleStatus,
      "regions": regions[],
      "owner": owner->name
    }`,
    {id}
  )

  if (!doc) return res.status(204).end()

  const trayRes = await fetch(process.env.TRAY_CALLABLE_TRIGGER_URL!, {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
      authorization: `Bearer ${process.env.TRAY_TRIGGER_TOKEN}`,
    },
    body: JSON.stringify({event: 'sanity.content.published', document: doc}),
  })

  if (!trayRes.ok) {
    const body = await trayRes.text()
    return res.status(502).json({error: 'Tray.io trigger failed', body})
  }

  return res.status(200).json({ok: true})
}

07Why Sanity

How Sanity + Tray.io works

Build your Tray.io integration on Sanity

Sanity gives you the structured content foundation, real-time event system, and flexible APIs you need to connect Tray.io workflows to the rest of your business systems.

Start building free →

08Comparison

CMS approaches to Tray.io

CapabilityTraditional CMSSanity
Triggering Tray.io workflowsOften needs plugins, scheduled exports, or custom code tied to page publish events.GROQ-powered webhooks and Functions can trigger Tray.io only for the documents and fields that matter.
Payload shapeContent is frequently mixed with layout markup, which makes routing fields into Tray.io steps harder.GROQ can return one clean JSON payload with projected fields and referenced data in a single query.
Server-side sync logicCustom sync code often lives in theme code, plugins, or a separate job runner.Functions can run event-driven logic close to content changes, which reduces the extra infrastructure needed for Tray.io syncs.
Content model controlModels are often page-centric, so operational fields like lifecycleStatus or region can become ad hoc.Schema-as-code lets teams version fields that Tray.io depends on, such as sku, owner, locale, and sync flags.
Multi-channel deliveryAutomations may be tied to website pages, which makes reuse across apps and workflows harder.One structured back end can serve web, mobile, Tray.io, and AI agents from the same modeled content.
Trade-offsFast for simple page publishing, but automation can become fragile as systems multiply.Best results require thoughtful schemas and stable operational fields, especially when Tray.io workflows depend on specific values.

09Next steps

Keep building

Explore related integrations to complete your content stack.

Ready to try Sanity?

See how Sanity's Content Operating System powers integrations with Tray.io and 200+ other tools.