integrations · next.js
Publish engine-written articles on your Next.js site
@itmaster/sdk is a typed Pull-API client plus drop-in App Router handlers. Install it, fill in one key, and the blog, robots.txt, sitemap, RSS, llms.txt and IndexNow key all come from the engine.
reviewed 2026-09-02 · by the IT Master editorial team · how we check facts
setup
- 01Register the site in the IT Master dashboard so the engine mints its per-site pull key, or ask us to register it for you.
- 02Run npx @itmaster/sdk init in the project root and pick the features you want, or run npm install @itmaster/sdk and copy the same files from the README by hand.
- 03Paste the pull key into PUBLISH_PULL_KEY in .env.local; the CLI has already written ITMASTER_API_URL and PUBLISH_SITE for you.
- 04Keep lib/itmaster.ts server-only — it imports server-only on the first line so a client component that touches it fails the build instead of shipping the key.
- 05Render the blog: call listArticles on /blog, and getArticle plus articleMetadata and articleJsonLd on /blog/[slug].
- 06Export the drop-in handlers as one-line route files: createRobotsRoute, createSitemapRoute, createRssRoute and createLlmsRoute.
- 07Add createIndexNowKeyRoute at /api/indexnow-key and the middleware rewrite that maps a 32-hex .txt request to it.
- 08Add the webhook route with createWebhookRoute, set PUBLISH_PUSH_SECRET to the value registered with the engine, and revalidate the blog paths inside it.
- 09Call applyMeta with the site config in your root generateMetadata, and render headTagsFromConfig scripts through next/script.
- 10Deploy, then connect Search Console from the dashboard — the engine serves the verification token in your head tags, submits the sitemap and pings IndexNow on every publish.
What @itmaster/sdk gives a Next.js app
The SDK is the official client for the engine's read-only Pull API. It ships on npm as ESM with full TypeScript declarations, has zero runtime dependencies, and treats next as an optional peer, so it installs cleanly in a project that has not upgraded yet.
Two import paths matter. The root export gives you createClient, and with it listArticles, getArticle, listArticlesPage for an incremental mirror, plus the derived feeds sitemap, rss, llmsTxt, robots, redirects, clusters and config. The /next export gives you the App Router helpers: articleMetadata, articleJsonLd, applyMeta, headTagsFromConfig, deriveIndexNowKey, verifyWebhookSignature and the route factories below.
Nothing about your design is prescribed. An article record arrives as rendered body HTML plus hero image, FAQ pairs, a table of contents, internal links, engine-authored JSON-LD and a canonical path, and you render it in your own components. What the package removes is the layer every site would otherwise rewrite: the authenticated fetch, the feed proxies, the schema builder and the HMAC check.
This site pulls its own blog through the same package.
@itmaster/sdk is a client and a set of route handlers, not a theme: your components stay yours, while the authenticated fetch, the feeds, the schema and the signature checks stop being your problem.
Install, scaffold, and fill in one key
Install with npm install @itmaster/sdk. Then run npx @itmaster/sdk init, which asks yes or no per feature and writes only new files — the shared client at lib/itmaster.ts, a route file for each feature you picked, middleware.ts, and the webhook route. It detects whether your app lives in app or src/app, skips anything that already exists unless you pass --force, and prints the two snippets it refuses to clobber: the root layout head wiring and the blog pages.
It also ensures the env keys in .env.local and .env.example:
- ITMASTER_API_URL — the engine origin, written for you.
- PUBLISH_SITE — your site slug in the engine, written for you.
- PUBLISH_PULL_KEY — the per-site bearer key, left blank because only you should paste it.
- PUBLISH_PUSH_SECRET — added only if you chose the webhook.
So one value is yours to fill in. The generated client starts with an import of server-only, which is what keeps the pull key out of any client bundle. Pass cache with a number of seconds to createClient if you want ISR instead of the default no-store.
Drop-in routes for robots, sitemap, RSS, llms.txt and IndexNow
Each feed is a one-line route file. The factory returns a plain async handler that proxies the engine's generated output and answers 404 rather than an empty body when a feed is not available.
- app/robots.txt/route.ts — createRobotsRoute serves the robots policy from your dashboard, including the single AI-crawler toggle that flips GPTBot, ClaudeBot, PerplexityBot, Google-Extended, CCBot and the rest of the managed list between allow and disallow.
- app/sitemap.xml/route.ts — createSitemapRoute serves the engine's sitemap, honouring the changefreq, priority and exclude settings.
- app/rss.xml/route.ts — createRssRoute.
- app/llms.txt/route.ts — createLlmsRoute publishes the llms.txt manifest.
- app/api/indexnow-key/route.ts — createIndexNowKeyRoute.
The IndexNow route is worth a note. The key is derived locally from a SHA-256 of a fixed salt and your site id, using exactly the formula the engine uses when it submits, so ownership verifies with no stored secret and no round trip. A middleware rule rewrites any 32-hex .txt request to that route, which is where search engines look for the proof.
Head tags the dashboard controls, no redeploy
Verification tokens, analytics IDs, the default OG image, the Twitter handle, the favicon and the title template live in the engine's site config, not in your repository. The SDK applies them at request time.
In the root layout, fetch config and pass your base metadata through applyMeta. That returns a Next Metadata object with the Google and Yandex verification fields set, Bing and Pinterest under verification.other, OG and Twitter defaults filled and the title template or suffix applied. Analytics are separate: headTagsFromConfig returns script descriptors for GA4, GTM, Plausible, Clarity and Meta Pixel, which you render with next/script.
On an article page, compose the two — applyMeta over articleMetadata gives you the canonical, description, keywords, OG and Twitter cards for that post under the site's own title rules. articleJsonLd returns the engine's authored JSON-LD when the article carries it, and otherwise builds an Article plus FAQPage graph.
One trap the README calls out: do not try to inject tags through a Fragment with dangerouslySetInnerHTML inside head. A Fragment has no DOM node, so nothing reaches the document. Use the Metadata API and next/script.
Verification tokens and analytics IDs live in the IT Master site config rather than in the repository, so @itmaster/sdk picks up a change on the next revalidate and a Next.js site never redeploys for a head tag.
Push webhook delivery, and takedowns that stick
If your site registers a push URL and secret, the engine POSTs each publish, update or takedown straight to it. createWebhookRoute reads the raw body, verifies the X-Thoth-Signature header — a hex HMAC-SHA256 of that body, compared in constant time — and answers 401 on a bad signature and 400 on malformed JSON before any of your code runs.
After the check it dispatches. Publish and update events go to onUpsert; unpublish, delete and takedown events go to onDelete, as does any article whose status is already unpublished. Then it calls your revalidate closure, which is where you put revalidatePath for the post and the index. That is the whole path from the engine passing its checks to the page being live.
Prefer polling? listArticlesPage returns a page of articles and a next_since cursor, tombstones included, so a mirror can add, update and delete in one pass. Either way, a takedown removes the article from your site rather than leaving an orphan.
The same signature scheme covers createSeoPatchRoute, which lets the engine propose rewritten titles, meta and JSON-LD for pages you already have.
What has already happened before your route sees an article
By the time an article reaches your Next.js app it has been through the whole pipeline. Topics come from measured demand — DataForSEO plus your own Search Console queries. Research reads the competing pages, forums and, where it exists, your first-party data. A Claude model writes it. Then judges from a different vendor, Gemini and GPT, run fact-check, novelty against the ranking pages, E-E-A-T critique and AI-tell detection, with up to three revision rounds; see cross-model validation.
Stated as measured, not promised. Across 186 Standard-tier runs, 51% of drafts passed every check first time; the rest were revised or rejected. Pro-tier judges are stricter: 25% first-pass, across a small sample of 16 runs.
You pay per published article from a prepaid balance, with no subscription; a draft that fails the checks is not charged at the full rate. Current rates are on pricing, and the route recipes above are written out in full in the docs.
Not on Next.js? The same engine serves WordPress through a plugin.
questions people ask
Does the pull key ever reach the browser?
No, provided you keep the client where the scaffolder puts it. The generated lib/itmaster.ts opens with an import of the server-only package, so if any client component imports it, the build fails rather than the key shipping in a bundle. Every call the SDK makes sends the key as a bearer token from your server to the engine. The IndexNow key route is the one piece that needs no key at all: it derives the value locally from your site id.
Does it work with the Pages Router, or only the App Router?
The route factories, applyMeta and articleMetadata are shaped for the App Router, which is what the scaffolder generates. The core client is framework-agnostic and works anywhere in Node or an edge runtime, so a Pages Router app can still call listArticles and getArticle, build its own head from renderHeadHtml, and serve the feeds from API routes. The package declares next as an optional peer and imports nothing from it, so neither router is a hard requirement.
How fresh is the content on my pages?
That is your choice. By default createClient fetches with no-store, so every render sees the current article. Pass a number as the cache option and the SDK uses Next ISR revalidation with that many seconds instead. If you also wire the webhook route, the engine pushes each publish and takedown to you and your revalidate closure clears exactly the affected paths, so a long revalidate window and near-instant updates are not in conflict.
Can the engine change pages I wrote myself, not just the articles?
Only if you mount the route for it and only as far as your handler allows. createSeoPatchRoute receives proposed titles, meta descriptions, Open Graph values and JSON-LD, plus prose rewrites, each HMAC-verified the same way as the publish webhook. Your handler decides what to persist and can decline a patch by returning false. A content patch marked for staging should be saved for review rather than published. The engine never receives credentials for your database.
What arrives in each article record?
Rendered body HTML, title, description and meta description, an excerpt, a hero image with alt text, FAQ question and answer pairs, a table of contents, engine-authored JSON-LD, the canonical path and URL, published and updated timestamps, word count and reading time, the primary and secondary keywords, tags, author details, Open Graph and Twitter fields, internal links into your other posts, and outbound citations. Status is published or unpublished, and an unpublished record is a tombstone you should act on.
Do I have to use the SDK at all?
No. The SDK is a convenience over a plain REST API. Any stack can call the Pull API endpoints directly with the site's bearer key, render the body HTML, inject the JSON-LD and serve the sitemap and RSS straight through, polling with a since cursor for changes. The signed push webhook is equally open: verify the hex HMAC of the raw body against your secret in whatever language you like. The SDK exists so Next.js sites need not write that twice.
See what search engines and AI assistants find on your site
Free, no account. Type your address and we show you what is missing and what we would write first.