Tips & Tricks

Claude Code x SaaS Integration Guide: Notion, Slack, Linear, GitHub, Figma

A hands-on guide to driving your everyday SaaS stack directly from Claude Code. Full working code and real-world patterns for the five biggest SaaS integrations.

Still thinking of Claude Code as just a code editor? Hook it up to the SaaS tools you use every day and it becomes a full-blown “work orchestrator”. Write a Notion page, post to Slack, file a Linear ticket, open a GitHub PR, pull values from Figma — all from a single natural-language prompt.

In this article we walk through the five SaaS integration patterns we actually use to run ClaudeCodeLab, complete with working code and setup steps. By the end you will have your own personal automation hub.

The three basic integration architectures

There are essentially three ways to connect Claude Code to a SaaS product:

ApproachImplementation costFlexibilityBest for
MCP serverMediumHighFrequent integrations, reused across projects
CLI wrapperLowMediumOne-off scripts, simple integrations
Webhook receiverHighHighBidirectional integrations triggered from the SaaS side

Beginners should start with a CLI wrapper — it is the shortest path. You just let Claude Code call an API through the Bash tool, so all you really need is an API key in an environment variable. Once you are comfortable, the standard move is to promote it into an MCP server.

1. Notion integration: mass-produce pages with AI

Use cases

  • Drop meeting minutes straight into Notion
  • Auto-generate weekly reports
  • Let the AI keep expanding your internal knowledge base

Implementation

// scripts/notion-create-page.mjs
import { Client } from "@notionhq/client";

const notion = new Client({ auth: process.env.NOTION_TOKEN });

async function createPage(databaseId, title, markdown) {
  const blocks = markdown.split("\n\n").map((para) => ({
    object: "block",
    type: "paragraph",
    paragraph: {
      rich_text: [{ type: "text", text: { content: para } }],
    },
  }));

  const page = await notion.pages.create({
    parent: { database_id: databaseId },
    properties: {
      Name: { title: [{ text: { content: title } }] },
      Status: { select: { name: "Draft" } },
    },
    children: blocks,
  });

  return page.url;
}

const [, , dbId, title, ...bodyParts] = process.argv;
const url = await createPage(dbId, title, bodyParts.join(" "));
console.log(`Created: ${url}`);

Using it from Claude Code

claude -p "
Read the meeting minutes below and summarize them into three sections
(decisions / action items / next-meeting agenda), then post to
the Notion minutes database:

$(cat ~/inbox/meeting-raw.txt)

Use database_id: abc123...
After posting, send the URL to slack-channel-general.
"

Claude Code calls notion-create-page.mjs through the Bash tool and pipes the resulting URL into Slack — all in a single prompt.

2. Slack integration: automating notifications and posts

Use cases

  • Deploy-complete notifications
  • Weekly error summary posts
  • Formatting long reports into threads

Implementation (Incoming Webhook — the easiest route)

// scripts/slack-notify.mjs
const webhookUrl = process.env.SLACK_WEBHOOK_URL;
const message = process.argv.slice(2).join(" ");

const res = await fetch(webhookUrl, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    text: message,
    blocks: [
      { type: "section", text: { type: "mrkdwn", text: message } },
    ],
  }),
});

console.log(res.ok ? "✓ Sent" : `✗ ${res.status}`);

Slack Bolt SDK variant (thread replies, DMs, etc.)

// scripts/slack-post-thread.mjs
import { WebClient } from "@slack/web-api";

const client = new WebClient(process.env.SLACK_BOT_TOKEN);

async function postThread(channel, parent, replies) {
  const main = await client.chat.postMessage({ channel, text: parent });
  for (const reply of replies) {
    await client.chat.postMessage({
      channel,
      text: reply,
      thread_ts: main.ts,
    });
  }
  return main.ts;
}

const channel = process.argv[2];
const [parent, ...replies] = process.argv.slice(3);
await postThread(channel, parent, replies);

Real-world usage with Claude Code

claude -p "
Run scripts/deploy.sh.
If it succeeds, post 'Production deploy complete 🚀' to #dev,
and as thread replies send:
1. Commit hash
2. Number of files changed
3. Build duration
If it fails, send a red-highlighted alert to #dev-alerts.
"

3. Linear integration: auto-filing tickets

Use cases

  • Turning bug discussions into tickets
  • Batch-filing issues from a design review
  • Splitting code review comments into discrete tasks

Implementation

// scripts/linear-create-issue.mjs
const LINEAR_API_KEY = process.env.LINEAR_API_KEY;
const LINEAR_TEAM_ID = process.env.LINEAR_TEAM_ID; // e.g. "ENG"

async function createIssue(title, description, priority = 2) {
  const query = `
    mutation CreateIssue($input: IssueCreateInput!) {
      issueCreate(input: $input) {
        success
        issue { id identifier url }
      }
    }
  `;
  const res = await fetch("https://api.linear.app/graphql", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: LINEAR_API_KEY,
    },
    body: JSON.stringify({
      query,
      variables: {
        input: {
          teamId: LINEAR_TEAM_ID,
          title,
          description,
          priority, // 0=None, 1=Urgent, 2=High, 3=Medium, 4=Low
        },
      },
    }),
  });
  const json = await res.json();
  return json.data.issueCreate.issue;
}

const [, , title, ...desc] = process.argv;
const issue = await createIssue(title, desc.join(" "), 2);
console.log(`Created: ${issue.identifier} - ${issue.url}`);

Example: bulk-filing from PR reviews

claude -p "
Read every review comment on GitHub PR #234 and, for any comment that
requires a fix, create an individual Linear ticket.

Each ticket should have:
- Title: a short summary of the issue
- Body: original comment + PR URL + filename:line
- Priority: 1 for 'must fix', 3 for 'suggestions'
"

4. GitHub integration: lean on the gh CLI

For GitHub, the gh CLI is dramatically easier than the raw API. Just let Claude Code hit it through the Bash tool.

Key patterns

# Create a PR
gh pr create --title "feat: add cache layer" --body "$(cat desc.md)"

# File an issue
gh issue create --title "Bug: user logout fails" --label "bug,priority-high"

# View PR review
gh pr view 234 --comments

# Check CI status
gh run list --workflow deploy.yml --limit 5

# Cut a release
gh release create v1.2.0 --notes "Changelog here"

Example: incident response

claude -p "
We are seeing 500 errors spiking in production. Walk through these steps:

1. Read the last hour of logs/prod-*.log and extract error patterns
2. Identify the suspect commit from the stack trace using git log
3. Open a PR that reverts that commit (gh pr create)
4. File an incident ticket in Linear
5. Post a status update to #incident-response on Slack

Target: complete within 15 minutes.
"

5. Figma integration: turn designs into code

Use cases

  • Reading the component structure of a Figma file
  • Converting Figma values (colors, spacing, font sizes) into CSS
  • Screenshot-based visual diff testing

Implementation (Figma REST API)

// scripts/figma-extract.mjs
const FIGMA_TOKEN = process.env.FIGMA_TOKEN;
const fileKey = process.argv[2];
const nodeId = process.argv[3];

const res = await fetch(
  `https://api.figma.com/v1/files/${fileKey}/nodes?ids=${nodeId}`,
  { headers: { "X-Figma-Token": FIGMA_TOKEN } }
);
const data = await res.json();
const node = data.nodes[nodeId].document;

// Extract colors, typography, and layout
const tokens = {
  colors: extractColors(node),
  typography: extractTypography(node),
  spacing: extractSpacing(node),
};

console.log(JSON.stringify(tokens, null, 2));

function extractColors(node) { /* implementation omitted */ }
function extractTypography(node) { /* implementation omitted */ }
function extractSpacing(node) { /* implementation omitted */ }

Using it from Claude Code

claude -p "
Read nodeId=456:789 (the card component) from Figma file abc123
and implement it as the React component components/Card.tsx.

- Match Tailwind tokens (pick colors from theme.colors)
- Props: children, variant (primary/secondary)
- Also generate a Storybook story
- Use scripts/figma-extract.mjs to pull Figma values as reference
"

Credential management: .env + gitignore is non-negotiable

An iron rule common to every integration:

# .env (NEVER commit this to git)
NOTION_TOKEN=secret_xxx
SLACK_BOT_TOKEN=xoxb-xxx
SLACK_WEBHOOK_URL=https://hooks.slack.com/xxx
LINEAR_API_KEY=lin_api_xxx
LINEAR_TEAM_ID=ENG
FIGMA_TOKEN=figd_xxx
# .gitignore
.env
.env.*
!.env.example

Document it in CLAUDE.md to prevent accidents

## Handling secrets
- Never commit .env
- Read values inside scripts via process.env
- Never include API keys in prompts or output
- Never print the Authorization header in error messages

Five common pitfalls

1. Ignoring rate limits Slack allows one call per second, Notion three per three seconds, and Linear enforces GraphQL complexity limits. Always sleep between batched calls.

async function batchPost(items) {
  for (const item of items) {
    await postToNotion(item);
    await new Promise((r) => setTimeout(r, 500)); // 500ms spacing
  }
}

2. Skipping webhook signature verification When you build a webhook receiver, skipping signature verification opens you up to replay attacks. Both Slack and GitHub publish their signing schemes — always implement them.

3. Forgetting whose permissions are used It is easy to post as a bot and then be surprised that your name does not appear. Document permission scopes in the README.

4. Duplicate execution on retry A network blip triggers a retry and suddenly you have three identical pages in Notion. Use an idempotency key to guarantee at-most-once behavior.

5. No fallback when services go down If Slack is down, your deploy notification is lost and manual ops go unattended. Set up two primary notification paths (e.g., Slack + email) for peace of mind.

Putting it together: five services driven by a single prompt

The endgame. A multi-SaaS workflow in one shot:

claude -p "
Prep this week's release:

1. Extract unreleased commits on master with gh log
2. Generate release notes in Markdown
3. Create a new page in the Notion 'Releases' database
4. Count issues closed this week in Linear
5. Export 5 screenshots of the changes from the Figma 'v2.0 Design' page
6. Post a Slack summary to #team
   (summary in 3 lines, details in the thread)
7. Run gh release create v2.0.0 for the official release

Report a concise log of each step.
"

If this works, the weekly release process that used to take an hour becomes a 5-minute hands-off task for Claude Code.

Summary

SaaSQuickest integrationAdvanced form
NotionCLI wrapper + APIPromote to MCP server
SlackIncoming WebhookBolt SDK + thread management
LinearDirect GraphQL callsWrap as an MCP tool
GitHubThe gh CLI is enoughCombine with Actions
FigmaREST APIPlugin + MCP

Drop the “Claude Code = code-writing tool” mindset and the horizon expands dramatically. Start with Slack Incoming Webhook — it’s the lowest-cost first step, done in 10 minutes. Add Notion or Linear next, and you should be able to automate around 30% of your work.

References

#claude-code #saas #notion #slack #linear #github #figma #integration

Level up your Claude Code workflow

50 battle-tested prompt templates you can copy-paste into Claude Code right now.

Free

Free PDF: Claude Code Cheatsheet in 5 Minutes

Just enter your email and we'll send you the single-page A4 cheatsheet right away.

We handle your data with care and never send spam.

Masa

About the Author

Masa

Engineer obsessed with Claude Code. Runs claudecode-lab.com, a 10-language tech media with 2,000+ pages.