Next.js
Markieren Sie jede KI-Datei, die Ihre Next.js-App erzeugt, mit einer serverseitigen Hilfsfunktion. Der Schlüssel bleibt auf dem Server, große Dateien und Videos werden berücksichtigt, und eine Datei, die sich nicht markieren lässt, wird nie ausgeliefert.
Auf einen Blick
- Wo: ein Route Handler oder eine Server Action, nie Client-Code.
- Was: eine Hilfsfunktion,
lib/kennmarq.ts, für Bilder, Songs und Videos. - Felder: Ihr Unternehmen, Ihre App und die Märkte, die Sie bedienen: die fünf Angaben für Kalifornien und ein Nachweis je Datei.
- Wenn es fehlschlägt: die Anfrage schlägt fehl, und das unmarkierte Original wird nie gespeichert oder angezeigt.
1. Der Schlüssel
Erstellen Sie im Dashboard einen Schlüssel (Schnellstart) und legen Sie ihn in Ihrer Umgebung ab: auf Vercel in den Umgebungsvariablen des Projekts, lokal in .env.local.
# .env.local: never NEXT_PUBLIC_, so it never reaches the browser
KENNMARQ_API_KEY=sk_test_…2. Die Hilfsfunktion
Eine Funktion, mark(), für Bilder, Songs und Videos. Dateien über 4 MB werden zuerst über einen Upload hochgeladen, und ein Video wird als Auftrag markiert, auf den sie wartet. Sie braucht nichts außer fetch.
// lib/kennmarq.ts: server code only (npm install server-only)
import "server-only";
const API = "https://api.kennmarq.com/v1";
const auth = { Authorization: `Bearer ${process.env.KENNMARQ_API_KEY}` };
// Your company and your AI system: with them, every mark carries California's five items.
const fields: Record<string, string> = {
source: "created", // or "edited", "composite"
provider: "Acme Inc.",
app_name: "Acme Studio",
app_version: "2.1",
markets: "eu,us-ca",
};
async function failed(res: Response): Promise<never> {
const { error } = await res.json();
throw new Error(`Kennmarq.com ${res.status} ${error.code}: ${error.message}`);
}
/** The marked copy of an image, song or video. Throws rather than hand back the original. */
export async function mark(file: Blob, filename: string, extra: Record<string, string> = {}): Promise<Blob> {
const form = new FormData();
for (const [k, v] of Object.entries({ ...fields, ...extra })) form.append(k, v);
// A request carries at most 4 MB: a bigger file goes up first.
if (file.size < 3_900_000) form.append("file", file, filename);
else form.append("upload", await upload(file));
const res = await fetch(`${API}/mark`, { method: "POST", headers: auth, body: form });
if (res.status === 202) return finished(await res.json()); // a video: marked as a job
if (!res.ok) return failed(res);
return res.blob();
}
async function upload(file: Blob): Promise<string> {
const res = await fetch(`${API}/uploads`, {
method: "POST",
headers: { ...auth, "Content-Type": "application/json" },
body: JSON.stringify({ bytes: file.size }),
});
if (!res.ok) return failed(res);
const { id, url } = await res.json();
// No key here: the URL is the permission. fetch sends the Content-Length it's signed for.
const put = await fetch(url, { method: "PUT", body: file });
if (!put.ok) throw new Error(`Kennmarq.com upload: ${put.status}`);
return id;
}
async function finished(job: { url: string }): Promise<Blob> {
for (;;) {
await new Promise((r) => setTimeout(r, 5_000));
const res = await fetch(job.url, { headers: auth });
if (!res.ok) return failed(res);
const m = await res.json();
if (m.status === "failed") throw new Error(`Kennmarq.com: ${m.error?.code}: ${m.error?.message}`);
if (m.status === "completed") return (await fetch(m.download.url)).blob();
}
}Sie möchten lieber keine Hilfsfunktion pflegen? Das JavaScript-SDK erledigt dasselbe mit einem Aufruf, mit Typen: npm install @kennmarq/sdk.
3. Markieren, was Ihr Modell erzeugt
In einem Route Handler, direkt nach dem Generieren:
// app/api/generate/route.ts
import { mark } from "@/lib/kennmarq";
import { generate } from "@/lib/model"; // your model: Replicate, fal, OpenAI, your own ComfyUI…
// Marking takes a second or two; leave room for a video, or for a slow first call.
export const maxDuration = 60;
export async function POST(request: Request) {
const { prompt } = await request.json();
const image: Blob = await generate(prompt);
const marked = await mark(image, "image.png", { model: "FLUX.1 schnell" });
// Store or return the marked copy only. If mark() threw, this request fails instead.
return new Response(marked, { headers: { "Content-Type": marked.type || "image/png" } });
}Oder in einer Server Action, die das Ergebnis speichert:
// app/actions.ts
"use server";
import { put } from "@/lib/storage"; // S3, R2, Vercel Blob…
import { mark } from "@/lib/kennmarq";
import { generate } from "@/lib/model";
export async function createImage(prompt: string) {
const marked = await mark(await generate(prompt), "image.png");
return put(`images/${crypto.randomUUID()}.png`, marked); // never the unmarked original
}Videos
Ein Video wird als Auftrag markiert: Der Aufruf antwortet mit 202, und die Hilfsfunktion fragt alle fünf Sekunden nach, bis er fertig ist, meist nach ein bis zwei Minuten. Lassen Sie sich in der Produktion stattdessen von einem Webhook (mark.completed) benachrichtigen, und halten Sie die Anfrage kurz.
Wenn ein Aufruf fehlschlägt
429 rate_limited: zu viele Aufrufe in dieser Minute für Ihren Plan; warten Sie dieRetry-AfterSekunden ab.402 quota_exceeded: Die Markierungen dieses Monats sind aufgebraucht; upgraden Sie, oder warten Sie bis zum Monatsersten.400 invalid_request: Die Meldung nennt, was fehlt;markets=us-cabrauchtprovider,app_nameundapp_version.
Alle Fehlercodes stehen auf der Seite Fehler. Um das Ergebnis zu sehen, ziehen Sie ein markiertes Bild auf das Prüftool. Sie nutzen das Vercel AI SDK? Seine Anleitung verwendet dieselbe Hilfsfunktion.