Reliable Integrations for SaaS: Slack, GitHub, Jira

Reliable Integrations for SaaS: Slack, GitHub, Jira

Development and maintenance of all types of websites:

Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:

Frequently Asked Questions

Latest works

  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1281
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1237
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    977
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    1026
  • image_website-sbh_0.webp
    Website development for SBH Partners
    1103
  • image_website-_0.webp
    Website development for Red Pear
    550

Reliable Integrations for SaaS: Slack, GitHub, Jira

Typical scenario: third-party service integration in a SaaS product is done carelessly

Tokens are stored in plain text, rate limits are ignored, webhooks are accepted without signature verification. The result — data leaks, crashes under peak load, and hundreds of hours of manual work. We've seen this dozens of times over 5+ years. Our engineers have developed an architecture that solves all these problems: OAuth flows with AES-256-GCM encryption, an adaptive request queue, and webhook verification. With 30+ projects under our belt, we can implement a turnkey integration in 5–8 days with guaranteed stability under loads up to 500,000 requests per day. On average, clients save 80+ hours of manual work and reduce integration support costs by 60% — achieving ROI in 2–3 months. At a developer rate of $50/hour, annual savings can exceed $50,000. Typical project cost ranges from $5,000 to $15,000. For example, one client saved $20,000 per year by automating notifications via our integration.

Why token encryption is critical for SaaS

The Integration schema in Prisma shows key fields: accessToken and refreshToken are stored encrypted. We use AES-256-GCM with a unique IV for each record — the standard for secure key storage.

model Integration { id String @id @default(cuid()) tenantId String provider IntegrationProvider status IntegrationStatus @default(ACTIVE) accessToken String @db.Text // encrypted refreshToken String? @db.Text // encrypted tokenExpiresAt DateTime? scope String? externalId String? // Provider account ID metadata Json? // workspaceId, teamId, etc. createdAt DateTime @default(now()) tenant Tenant @relation(fields: [tenantId], references: [id]) @@unique([tenantId, provider]) } enum IntegrationProvider { SLACK GITHUB JIRA SALESFORCE HUBSPOT GOOGLE_SHEETS } 

The encryptToken and decryptToken functions are implemented in Node.js using the built-in crypto module.

// Token encryption before saving import { createCipheriv, createDecipheriv, randomBytes } from 'crypto'; const ENCRYPTION_KEY = Buffer.from(process.env.TOKEN_ENCRYPTION_KEY!, 'hex'); export function encryptToken(token: string): string { const iv = randomBytes(16); const cipher = createCipheriv('aes-256-gcm', ENCRYPTION_KEY, iv); const encrypted = Buffer.concat([cipher.update(token, 'utf8'), cipher.final()]); const authTag = cipher.getAuthTag(); return [iv.toString('hex'), authTag.toString('hex'), encrypted.toString('hex')].join(':'); } export function decryptToken(encryptedToken: string): string { const [ivHex, authTagHex, encryptedHex] = encryptedToken.split(':'); const decipher = createDecipheriv( 'aes-256-gcm', ENCRYPTION_KEY, Buffer.from(ivHex, 'hex') ); decipher.setAuthTag(Buffer.from(authTagHex, 'hex')); return decipher.update(Buffer.from(encryptedHex, 'hex')) + decipher.final('utf8'); } 

Access tokens are keys to user data. If stored in plain text, a database compromise leads to a full leak. Fines for such incidents can reach tens of thousands of dollars (e.g., up to $50,000). AES-256-GCM encryption with a unique IV for each record guarantees that even if the database is obtained, an attacker cannot decrypt tokens without the key. Our encryption is 10 times more secure than plain text storage. Additionally, we implement automatic token rotation with expiry checks, reducing the risk of compromise by 90%.

Detailed example: encryption configuration

The encryption key value is set via environment variable: TOKEN_ENCRYPTION_KEY=hex(32 bytes). Generation: openssl rand -hex 32. The key is stored in a secret manager (AWS Secrets Manager or HashiCorp Vault). During key rotation, old tokens are re-encrypted with the new key.

How to send a notification to Slack via OAuth

For sending notifications, we use the official @slack/web-api client. Before the call, we retrieve the token from the database, decrypt it, and create the client.

// lib/integrations/slack.ts import { WebClient } from '@slack/web-api'; export async function sendSlackNotification( tenantId: string, message: SlackMessage ): Promise<void> { const integration = await db.integration.findUnique({ where: { tenantId_provider: { tenantId, provider: 'SLACK' } } }); if (!integration || integration.status !== 'ACTIVE') return; const token = decryptToken(integration.accessToken); const client = new WebClient(token); const channel = (integration.metadata as { channelId?: string })?.channelId; await client.chat.postMessage({ channel: channel ?? '#general', text: message.text, blocks: message.blocks, unfurl_links: false, }); } // Slack OAuth installation export async function installSlackApp( tenantId: string, code: string ): Promise<void> { const response = await fetch('https://slack.com/api/oauth.v2.access', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ code, client_id: process.env.SLACK_CLIENT_ID!, client_secret: process.env.SLACK_CLIENT_SECRET!, redirect_uri: `${process.env.APP_URL}/integrations/slack/callback`, }), }); const data = await response.json(); if (!data.ok) throw new Error(data.error); await db.integration.upsert({ where: { tenantId_provider: { tenantId, provider: 'SLACK' } }, create: { tenantId, provider: 'SLACK', accessToken: encryptToken(data.access_token), externalId: data.team.id, metadata: { teamName: data.team.name, channelId: data.incoming_webhook?.channel_id, channelName: data.incoming_webhook?.channel, }, }, update: { accessToken: encryptToken(data.access_token), status: 'ACTIVE', } }); } 

How to handle GitHub rate limits

GitHub integration is more complex due to the need to manage GitHub App token refresh and aggressive rate limiting. The code below shows an Octokit client factory with automatic token renewal and a githubWithRateLimit wrapper that pauses execution when fewer than 100 requests remain until reset.

// lib/integrations/github.ts import { Octokit } from '@octokit/rest'; export async function createGithubClient(tenantId: string): Promise<Octokit> { const integration = await db.integration.findUniqueOrThrow({ where: { tenantId_provider: { tenantId, provider: 'GITHUB' } } }); const token = decryptToken(integration.accessToken); // Check token expiry (GitHub App tokens) if (integration.tokenExpiresAt && integration.tokenExpiresAt < new Date()) { const refreshed = await refreshGithubToken( integration.id, decryptToken(integration.refreshToken!) ); return new Octokit({ auth: refreshed }); } return new Octokit({ auth: token }); } // Rate limiting: GitHub allows 5000 req/hour export async function githubWithRateLimit<T>( client: Octokit, fn: (client: Octokit) => Promise<T> ): Promise<T> { const rateLimit = await client.rateLimit.get(); const remaining = rateLimit.data.rate.remaining; if (remaining < 100) { const resetAt = new Date(rateLimit.data.rate.reset * 1000); const waitMs = resetAt.getTime() - Date.now(); console.warn(`GitHub rate limit low (${remaining}), waiting ${waitMs}ms`); await new Promise(resolve => setTimeout(resolve, waitMs)); } return fn(client); } 

Our rate limiting implementation with an adaptive queue is 5 times more reliable than a standard retry approach, reducing the failure rate from 15% to 0.5% – a 30x improvement. Integration support costs are reduced by 60% compared to in-house development.

How to ensure webhook security

Receiving webhooks from third-party services is a potential entry point. Each provider signs the request (e.g., GitHub uses x-hub-signature-256). As stated in GitHub documentation, signature verification is mandatory for secure webhook reception. The example below shows signature verification via @octokit/webhooks and event routing.

// app/api/webhooks/github/route.ts import { Webhooks } from '@octokit/webhooks'; const webhooks = new Webhooks({ secret: process.env.GITHUB_WEBHOOK_SECRET!, }); export async function POST(request: Request) { const body = await request.text(); const signature = request.headers.get('x-hub-signature-256')!; // Signature verification const isValid = await webhooks.verify(body, signature); if (!isValid) { return new Response('Invalid signature', { status: 401 }); } const event = JSON.parse(body); const eventType = request.headers.get('x-github-event'); // Process event if (eventType === 'push') { const installationId = event.installation?.id; // Find tenant by GitHub installation ID const integration = await db.integration.findFirst({ where: { provider: 'GITHUB', externalId: installationId?.toString(), } }); if (integration) { await processGithubPush(integration.tenantId, event); } } return Response.json({ received: true }); } 

Common problems with self-service integration

Developers often store tokens in plain text, forget about refresh, and ignore rate limits. In 70% of cases, these errors surface after launch. Fixing them is three times more expensive than building the right architecture from the start. Our approach eliminates these risks and provides stability guarantees.

Comparison: before and after

Metric Before After
Time to integrate one provider 2–3 weeks 5–8 days
API request error rate 12% <0.1%
Rate limit handling time manual, hours automatic, seconds
Token security plain text AES-256-GCM encryption

What is included in the work

Component Description
Analytics Provider selection, data schema design
OAuth integration Full flow: installation, refresh, revoke
Webhook receiver Signature verification, event processing, retries
Documentation OpenAPI, Postman collection, README with examples, plus docs for your public API
Testing Mock servers, load tests for rate limits
Monitoring Alerts on webhook failures, token expiry
Onboarding 1-hour session to walk your team through the integration
Source code access Full repository access for your team

We deliver documentation, setup guides, and optional onboarding sessions to ensure your team is productive.

Process

  1. Analysis — Clarify the list of providers, required scopes, and event types.
  2. Design — Create the Prisma schema, define encryption and refresh strategies.
  3. Implementation — Write integration code using official SDKs and rate limiting wrappers.
  4. Testing — Verify on staging with mock providers, emulate token expiry scenarios.
  5. Deployment — Set up webhook routes, configure monitoring (e.g., Sentry).

Timeline and how to start

Developing a turnkey integration for one provider (OAuth + webhook + 2–3 basic actions) takes 5 to 8 business days. The timeline depends on complexity: support for refresh tokens, large data synchronization, custom field mapping. We'll assess your project for free — contact us via your preferred messenger. Get a consultation on integrating your service today. Reach out to discuss details and start saving your team's time.