Next.js Integration
This guide covers server-only App Router integration. The five supported example flows and their request semantics live in Core Workflows.
Environment
# .env.local — never prefix this with NEXT_PUBLIC_
PUBLORA_API_KEY=sk_YOUR_API_KEY
PUBLORA_BASE_URL=https://api.publora.com/api/v1
PUBLORA_WEBHOOK_SECRET=your_webhook_secretServer-side client
// lib/publora.ts
const baseUrl = process.env.PUBLORA_BASE_URL ?? 'https://api.publora.com/api/v1';
export async function publora<T>(
path: string,
init: RequestInit = {},
idempotencyKey?: string,
): Promise<T> {
const response = await fetch(`${baseUrl}/${path}`, {
...init,
headers: {
'content-type': 'application/json',
'x-publora-key': process.env.PUBLORA_API_KEY!,
...(idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : {}),
...init.headers,
},
cache: 'no-store',
});
const body = await response.json();
if (!response.ok) throw new Error(body.error ?? `Publora returned ${response.status}`);
return body as T;
}App Router endpoint
Keep the API key on the server and validate your own user's input before forwarding it.
// app/api/social-posts/route.ts
import { NextResponse } from 'next/server';
import { randomUUID } from 'node:crypto';
import { publora } from '@/lib/publora';
export async function POST(request: Request) {
const { content, platforms, publish } = await request.json();
const scheduledTime = publish
? new Date(Date.now() + 5 * 60_000).toISOString()
: undefined;
const result = await publora<{ success: boolean; postGroupId: string }>(
'create-post',
{
method: 'POST',
body: JSON.stringify({ content, platforms, scheduledTime }),
},
randomUUID(),
);
return NextResponse.json(result);
}When publish is false, this intentionally creates a draft. A UI should label those choices “Create Draft” and “Schedule Post”; never call an empty-time submission scheduled.
Raw-body webhook route
// app/api/publora-webhook/route.ts
import crypto from 'node:crypto';
export async function POST(request: Request) {
const raw = Buffer.from(await request.arrayBuffer());
const received = request.headers.get('x-publora-signature') ?? '';
const expected = crypto
.createHmac('sha256', process.env.PUBLORA_WEBHOOK_SECRET!)
.update(raw)
.digest('hex');
const valid = received.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
if (!valid) return new Response(null, { status: 401 });
const envelope = JSON.parse(raw.toString('utf8'));
// Enqueue application work here.
return new Response(null, { status: 204 });
}Do not call request.json() before capturing the raw bytes. See Webhooks.