Integration Guide

This guide explains how to integrate the ShipKit Docs API into your application and receive Webhook notifications.


Authentication

All API requests require an API Key, with two supported methods:

  • Query parameter: ?apikey=sdxxxxxxxxxxxxxxxx
  • Authorization header: Authorization: Bearer sd_xxxxxxxxxxxxxxxx

Next.js App Router Integration

Embed ShipKit documentation content into your Next.js 14+ application.

1. Create an API Client

// lib/shipkit.ts

const SHIPKITBASE = process.env.SHIPKITAPI_URL || 'https://your-shipkit.vercel.app'

const SHIPKITKEY = process.env.SHIPKITAPI_KEY!

interface DocPage {

path: string

title: string

}

interface SiteIndex {

site_title: string

navigation: unknown[]

pages: DocPage[]

updated_at: string

}

interface PageContent {

title: string

content: string

updated_at: string

}

export async function fetchSiteIndex(siteId: string): Promise<SiteIndex> {

const res = await fetch(

\\${SHIPKITBASE}/api/docs/\${siteId}?apikey=\${SHIPKIT_KEY}\,

{ next: { revalidate: 60 } }

)

if (!res.ok) throw new Error(\Failed to fetch site index: \${res.status}\)

return res.json()

}

export async function fetchPage(siteId: string, path: string): Promise<PageContent> {

const res = await fetch(

\\${SHIPKITBASE}/api/docs/\${siteId}/\${path}?apikey=\${SHIPKIT_KEY}\,

{ next: { revalidate: 60 } }

)

if (!res.ok) throw new Error(\Failed to fetch page: \${res.status}\)

return res.json()

}

2. Documentation List Page

// app/docs/page.tsx

import { fetchSiteIndex } from '@/lib/shipkit'

import Link from 'next/link'

export default async function DocsIndexPage() {

const site = await fetchSiteIndex('my-site-slug')

return (

<div>

<h1>{site.site_title}</h1>

<ul>

{site.pages.map((page) => (

<li key={page.path}>

<Link href={\/docs/\${page.path}\}>{page.title}</Link>

</li>

))}

</ul>

</div>

)

}

3. Documentation Detail Page

// app/docs/[...path]/page.tsx

import { fetchPage, fetchSiteIndex } from '@/lib/shipkit'

import { notFound } from 'next/navigation'

export async function generateStaticParams() {

const site = await fetchSiteIndex('my-site-slug')

return site.pages.map((page) => ({

path: page.path.split('/'),

}))

}

export default async function DocPage({

params,

}: {

params: Promise<{ path: string[] }>

}) {

const { path } = await params

const pagePath = path.join('/')

try {

const page = await fetchPage('my-site-slug', pagePath)

return (

<article>

<h1>{page.title}</h1>

<div dangerouslySetInnerHTML={{ __html: page.content }} />

<p>Last updated: {new Date(page.updated_at).toLocaleString()}</p>

</article>

)

} catch {

notFound()

}

}


Webhook Integration (Next.js revalidatePath)

When ShipKit documentation updates, trigger Incremental Static Regeneration (ISR) via Webhook.

1. Create Webhook Endpoint

// app/api/shipkit-webhook/route.ts

import { NextRequest } from 'next/server'

import { createHmac } from 'crypto'

import { revalidatePath } from 'next/cache'

const WEBHOOKSECRET = process.env.SHIPKITWEBHOOK_SECRET!

function verifySignature(body: string, signature: string): boolean {

const expected = createHmac('sha256', WEBHOOK_SECRET)

.update(body)

.digest('hex')

return expected === signature

}

export async function POST(request: NextRequest) {

const body = await request.text()

const signature = request.headers.get('x-shipkit-signature')

if (!signature || !verifySignature(body, signature)) {

return Response.json({ error: 'Invalid signature' }, { status: 401 })

}

const payload = JSON.parse(body)

if (payload.event === 'page.updated' || payload.event === 'page.created') {

revalidatePath(\/docs/\${payload.data.path}\)

revalidatePath('/docs')

}

if (payload.event === 'site.updated') {

revalidatePath('/docs', 'layout')

}

return Response.json({ received: true })

}

2. Configure Webhook in ShipKit

Add your Webhook URL and Secret in site settings, then test:

curl -X POST https://your-shipkit.vercel.app/api/webhooks/test \

-H "Content-Type: application/json" \

-d '{

"webhook_url": "https://your-app.vercel.app/api/shipkit-webhook",

"webhook_secret": "your-secret-here"

}'


Express Integration

1. Install Dependencies

npm install express node-fetch

2. Complete Example

// server.js

const express = require('express')

const crypto = require('crypto')

const app = express()

app.use(express.json({

verify: (req, _res, buf) => { req.rawBody = buf }

}))

const SHIPKITBASE = process.env.SHIPKITAPI_URL

const SHIPKITKEY = process.env.SHIPKITAPI_KEY

const WEBHOOKSECRET = process.env.SHIPKITWEBHOOK_SECRET

// Simple in-memory cache

const cache = new Map()

async function fetchFromShipKit(path) {

const cached = cache.get(path)

if (cached && Date.now() - cached.ts < 60_000) return cached.data

const res = await fetch(\\${SHIPKIT_BASE}\${path}\, {

headers: { Authorization: \Bearer \${SHIPKIT_KEY}\ },

})

if (!res.ok) throw new Error(\ShipKit API error: \${res.status}\)

const data = await res.json()

cache.set(path, { data, ts: Date.now() })

return data

}

// Documentation list

app.get('/docs', async (req, res) => {

try {

const site = await fetchFromShipKit('/api/docs/my-site-slug')

res.json(site)

} catch (err) {

res.status(500).json({ error: err.message })

}

})

// Documentation detail

app.get('/docs/*', async (req, res) => {

const pagePath = req.params[0]

try {

const page = await fetchFromShipKit(

\/api/docs/my-site-slug/\${pagePath}\

)

res.json(page)

} catch (err) {

res.status(500).json({ error: err.message })

}

})

const PORT = process.env.PORT || 3001

app.listen(PORT, () => console.log(\Server running on port \${PORT}\))

3. Environment Variables

# .env

SHIPKITAPIURL=https://your-shipkit.vercel.app

SHIPKITAPIKEY=sd_xxxxxxxxxxxxxxxx

SHIPKITWEBHOOKSECRET=your-webhook-secret


API Reference

GET /api/docs/:siteId

Returns site navigation and page list.

Response:
{

"site_title": "My Documentation",

"navigation": [

{ "label": "Getting Started", "path": "getting-started" }

],

"pages": [

{ "path": "getting-started", "title": "Getting Started" },

{ "path": "api-reference", "title": "API Reference" }

],

"updated_at": "2026-03-22T10:00:00Z"

}

GET /api/docs/:siteId/:path

Returns specific page content. Includes Cache-Control: public, max-age=60 header.

Response:
{

"title": "Getting Started",

"content": "<h2>Introduction</h2><p>Welcome to...</p>",

"updated_at": "2026-03-22T10:00:00Z"

}

POST /api/webhooks/test

Send a test webhook to verify your endpoint configuration.

Request:
{

"webhook_url": "https://your-app.com/webhooks/shipkit",

"webhook_secret": "your-secret"

}

Response:
{

"success": true,

"status": 200,

"message": "Test webhook delivered successfully."

}