Documentation Index
Fetch the complete documentation index at: https://docs.neuraldraft.io/llms.txt
Use this file to discover all available pages before exploring further.
This snippet wires Neural Draft into Nuxt 3 with two pieces: a typed
composable for client and server use, and a webhook handler with HMAC
verification.
Install
npm install @neuraldraft/sdk
NEURAL_DRAFT_API_KEY=ndsk_live_...
NEURAL_DRAFT_WEBHOOK_SECRET=whsec_...
Runtime config
export default defineNuxtConfig({
runtimeConfig: {
neuralDraftApiKey: process.env.NEURAL_DRAFT_API_KEY,
neuralDraftWebhookSecret: process.env.NEURAL_DRAFT_WEBHOOK_SECRET,
public: {
neuralDraftPublicApiUrl: "https://api.neuraldraft.io/v1",
},
},
});
Server-side composable
Server-only — uses the secret API key. Importable from ~/server/api/* and
plugins.
import { NeuralDraft } from "@neuraldraft/sdk";
import { createHmac, timingSafeEqual } from "node:crypto";
export function getNeuralDraft(event?: any) {
const config = useRuntimeConfig(event);
return new NeuralDraft({ apiKey: config.neuralDraftApiKey });
}
export function verifyNeuralDraftSignature(
body: string,
header: string,
secret: string,
toleranceSeconds = 300
): boolean {
const parts = Object.fromEntries(
header.split(",").map((p) => p.split("=") as [string, string])
);
const t = Number(parts.t);
const v1 = parts.v1;
if (!Number.isFinite(t) || !v1 || !secret) return false;
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - t) > toleranceSeconds) return false;
const signed = `${t}.${body}`;
const expected = createHmac("sha256", secret).update(signed).digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(v1, "hex");
return a.length === b.length && timingSafeEqual(a, b);
}
A blog index server route
export default defineEventHandler(async (event) => {
const nd = getNeuralDraft(event);
const { data } = await nd.blogPosts.list({
status: "published",
lang: "en",
per_page: 50,
});
return data;
});
A blog page
<script setup lang="ts">
const { data: posts } = await useFetch("/api/blog");
</script>
<template>
<main class="prose mx-auto py-12">
<h1>Blog</h1>
<ul>
<li v-for="post in posts" :key="post.id">
<NuxtLink :to="`/blog/${post.slug}`">
<h2>{{ post.title }}</h2>
<p>{{ post.excerpt }}</p>
</NuxtLink>
</li>
</ul>
</main>
</template>
<script setup lang="ts">
const route = useRoute();
const { data: post } = await useFetch(`/api/blog/${route.params.slug}`);
</script>
<template>
<article v-if="post" class="prose mx-auto py-12">
<h1>{{ post.title }}</h1>
<img v-if="post.featured_image" :src="post.featured_image" :alt="post.title" />
<div v-html="post.content" />
</article>
</template>
export default defineEventHandler(async (event) => {
const slug = getRouterParam(event, "slug");
if (!slug) throw createError({ statusCode: 404 });
const nd = getNeuralDraft(event);
return await nd.blogPosts.bySlug(slug, { lang: "en" });
});
Webhook handler
export default defineEventHandler(async (event) => {
const config = useRuntimeConfig(event);
const body = await readRawBody(event, "utf8");
if (!body) throw createError({ statusCode: 400, statusMessage: "missing body" });
const sig = getHeader(event, "x-neural-draft-signature") ?? "";
if (!verifyNeuralDraftSignature(body, sig, config.neuralDraftWebhookSecret)) {
throw createError({ statusCode: 401, statusMessage: "invalid signature" });
}
const payload = JSON.parse(body) as { type: string; data: any };
// Acknowledge fast; do work async if needed.
switch (payload.type) {
case "blog_post.published":
case "content.changed":
// For ISR/SSG hosts, hit your platform's deploy hook here.
break;
case "order.paid":
// Send fulfilment email, sync to CRM, etc.
break;
}
return { received: true };
});
A composable for client-side public reads
For unauthenticated reads (e.g. public product catalogue), use the public API
directly from the browser.
export function useNeuralDraftPublic() {
const config = useRuntimeConfig();
const baseUrl = config.public.neuralDraftPublicApiUrl;
return {
listProducts: () => $fetch(`${baseUrl}/public/products`),
productBySlug: (slug: string) => $fetch(`${baseUrl}/public/products/${slug}`),
};
}
Notes
- Always read raw body in the webhook handler.
readBody() parses JSON
and breaks the HMAC; use readRawBody().
- Public endpoints don’t accept your API key — use
useNeuralDraftPublic
directly from the browser only for /v1/public/* routes.
- For multi-language sites, parameterise
lang in your fetches and key your
cache by locale.