CRM & Sales8 min read

How to Integrate Close with Your Headless CMS

Connect Close to structured content so sales teams can see campaign context, source details, and follow-up prompts the moment content changes.

Published April 29, 2026
01Overview

What is Close?

Close is a CRM built for sales teams that rely on outbound calling, email, SMS, task queues, and pipeline tracking in one workflow. Teams use it to create leads, contacts, opportunities, activities, and follow-up sequences through the Close app or API. It's common with startups, agencies, and B2B sales teams that need fast rep workflows without a large CRM admin layer.


02The case for integration

Why integrate Close with a headless CMS?

Sales teams lose context when campaign content, landing pages, gated assets, and CRM records live in separate systems. A rep may see a lead in Close with an email address and company name, but not the exact guide they downloaded, the industry page they visited, the persona they matched, or the offer that triggered the conversation. That missing context turns into slower follow-up and generic outreach.


03Architecture

Architecture overview

A typical Sanity and Close integration starts when a content editor publishes or updates a sales-related document in Sanity Studio, such as a campaign, gated asset, partner page, event lead, or sales play. The published document lands in the Content Lake as structured JSON. A Sanity webhook fires on the publish event, or a Sanity Function runs directly on the content mutation. The handler uses GROQ to fetch the document plus any referenced content, such as persona, product, region, CTA, and owner fields. It then calls Close's REST API at https://api.close.com/api/v1 using the Close API key as HTTP Basic auth. Depending on your workflow, the handler can create a lead with contacts, update custom fields, add a note activity, create a task for the assigned rep, or attach source content to an opportunity. The end result is practical: the website still renders from Sanity, while the sales rep sees the right campaign context in Close before making the first call.


04Use cases

Common use cases

📣

Campaign-to-lead context

When a gated campaign is published in Sanity, send the campaign title, URL, persona, and source offer into Close lead fields.

☎️

Rep task creation

Create Close tasks for the right sales owner when a high-intent page, event list, or account-based landing page goes live.

📝

Sales notes from content

Add Close note activities with approved talk tracks, objection handling, and case study links from Sanity Studio.

🎯

Persona-based routing

Map Sanity fields like industry, company size, region, and product interest to Close custom fields for better lead routing.


05Implementation

Step-by-step integration

  1. 1

    Set up Close API access

    Create or sign in to your Close account, then generate an API key from Close settings. Close uses HTTP Basic auth, with the API key as the username and an empty password. Keep the key in an environment variable such as CLOSE_API_KEY.

  2. 2

    Install the Sanity client

    In the app, webhook listener, or Sanity Function that will sync data, install the Sanity client with npm install @sanity/client. Add SANITY_PROJECT_ID, SANITY_DATASET, SANITY_API_VERSION, and a read token if your dataset isn't public.

  3. 3

    Model sales-ready content in Sanity Studio

    Create schemas for the content Close needs, such as campaignLead, gatedAsset, salesPlay, persona, and product. Useful fields include companyName, contactName, email, phone, website, campaign reference, persona reference, sourceUrl, closeLeadId, lifecycleStage, and assignedRepEmail.

  4. 4

    Create the sync trigger

    Use a Sanity webhook filtered to the document types you want to sync, for example _type == 'campaignLead'. For server-side logic inside Sanity, use Functions so the sync runs on content events without a separate job runner.

  5. 5

    Call the Close API

    Use GROQ to fetch the exact fields and references needed for Close, then call the Close Lead API to create a lead, update a lead, add a contact, or create a note activity. If you need idempotency, store the returned Close lead ID back on the Sanity document.

  6. 6

    Test the full sales workflow

    Publish a test document in Sanity Studio, confirm the webhook or Function runs, verify the lead or activity in Close, and open the frontend page to make sure the same content is visible to customers. Test updates and deletes too, not just first publish.


06Code

Code example

typescriptapp/api/sanity-close/route.ts
import {createClient} from '@sanity/client';

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

export async function POST(req: Request) {
  const {_id} = await req.json();

  const lead = await sanity.fetch(`
    *[_id == $id][0]{
      companyName,
      website,
      contactName,
      email,
      phone,
      sourceUrl,
      campaign->{title},
      persona->{name}
    }
  `, {id: _id});

  const auth = Buffer.from(`${process.env.CLOSE_API_KEY!}:`).toString('base64');

  const closeRes = await fetch('https://api.close.com/api/v1/lead/', {
    method: 'POST',
    headers: {
      Authorization: `Basic ${auth}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      name: lead.companyName,
      url: lead.website,
      description: `Source: ${lead.sourceUrl}
Campaign: ${lead.campaign?.title}
Persona: ${lead.persona?.name}`,
      contacts: [{
        name: lead.contactName,
        emails: [{email: lead.email, type: 'office'}],
        phones: lead.phone ? [{phone: lead.phone, type: 'office'}] : [],
      }],
    }),
  });

  if (!closeRes.ok) {
    return Response.json({error: await closeRes.text()}, {status: 500});
  }

  return Response.json(await closeRes.json());
}

07Why Sanity

How Sanity + Close works

Build your Close integration on Sanity

Sanity's AI Content Operating System gives you the structured content foundation, real-time event system, and flexible APIs to connect Close with the channels your team already ships.

Start building free →

08Comparison

CMS approaches to Close

CapabilityTraditional CMSSanity
CRM-ready content structureCampaign and CTA data often live inside page layouts, plugins, or rich text fields, which makes CRM mapping brittle.The Content Lake stores typed JSON with references, so one GROQ query can return campaign, persona, product, and owner data for Close.
Sync timingTeams often rely on form plugins, exports, or scheduled jobs, so Close can lag behind published content.Webhooks or Functions can run on publish, update, and delete events, then call the Close API without polling.
Field-level payload controlThe CRM sync may receive too much page data or not enough structured metadata for sales follow-up.GROQ selects only the fields Close needs, including joined references and computed labels for notes, tasks, and lead descriptions.
Sales context in rep workflowsReps may need to open the website or ask marketing to understand which offer a lead responded to.Sanity Studio can model sales plays, approved talk tracks, source pages, and campaign metadata that sync into Close activities.
Maintenance trade-offLower setup effort if a plugin exists, but custom CRM behavior can become hard to change.Schema-as-code needs developer involvement up front, but it gives versioned models, clear field contracts, and event-driven sync paths.

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 Close and 200+ other tools.