Generate cinematic faceless explainer videos from a one-line idea. Public REST API designed agent-first: every endpoint is documented in OpenAPI 3.1, errors are self-describing, POSTs accept Idempotency-Key for safe retries.
vsk_live_…Authorization: Bearer <key> on every request.POST /api/v1/videos.GET /api/v1/videos/{id} every 10-20s until status === "ready".final_video_url.curl -X POST https://vidshark.ai/api/v1/videos \
-H "Authorization: Bearer $VIDSHARK_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"idea": "Why the Persian Empire fell — Pixar-style animated explainer",
"category": "story",
"duration": 16,
"aspect_ratio": "9:16"
}'Returns 202 with a Video resource. Cost: duration / 8 credits up front. Failed scenes auto-refund.
async function waitForVideo(id: string) {
const key = process.env.VIDSHARK_API_KEY!;
while (true) {
const r = await fetch(`https://vidshark.ai/api/v1/videos/${id}`, {
headers: { Authorization: `Bearer ${key}` },
});
const { video } = await r.json();
if (video.status === "ready") return video.final_video_url;
if (video.status === "failed") throw new Error("Render failed");
await new Promise(r => setTimeout(r, 10_000)); // 10s
}
}Instead of polling, register an HTTPS endpoint and we'll push events as the render moves through its states. Each request is signed with HMAC-SHA256 so you can trust the sender without IP allowlists.
Create a subscription:
curl -X POST https://vidshark.ai/api/v1/webhooks \
-H "Authorization: Bearer $VIDSHARK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "n8n production",
"url": "https://hooks.example.com/vidshark",
"events": ["video.ready", "video.failed"]
}'
# Response includes "secret": "whsec_…" — store it now, shown once.Event types:
video.created — render queued.video.scene_ready — one scene finished (multi-clip videos emit one per scene).video.ready — final stitched MP4 ready.video.failed — render hit an unrecoverable error.* — wildcard, subscribe to everything.Example payload (video.ready):
{
"id": "evt_b1c2…",
"type": "video.ready",
"created_at": "2026-05-13T10:23:14.001Z",
"data": {
"video_id": "8e9f7a…",
"final_video_url": "https://r2.vidshark.ai/…/final.mp4?sig=…",
"narration_audio_url": "https://r2.vidshark.ai/…/narration.mp3?sig=…",
"clip_count": 2
}
}Verify the VidShark-Signature header (format t=<unix>,v1=<hex>):
import { createHmac, timingSafeEqual } from "node:crypto";
const TOLERANCE_S = 300; // reject anything older than 5 minutes
export function verifyVidSharkWebhook(
rawBody: string,
header: string,
secret: string,
): 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) return false;
if (Math.abs(Date.now() / 1000 - t) > TOLERANCE_S) return false;
const expected = createHmac("sha256", secret)
.update(`${t}.${rawBody}`)
.digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(v1, "hex");
return a.length === b.length && timingSafeEqual(a, b);
}Delivery is at-least-once. We retry non-2xx responses at 1m / 5m / 30m / 2h / 6h / 24h, then auto-disable the subscription after 6 consecutive failures. Respond 2xx within 10s — slower responders look like failures. Inspect recent attempts with GET /api/v1/webhooks/{id}/deliveries or fire a synthetic test with POST /api/v1/webhooks/{id}/test.
Every error has the same shape:
{
"code": "insufficient_credits",
"message": "Need 6 credits, you have 2. Top up or pick a shorter length.",
"docs_url": "https://vidshark.ai/docs/api#insufficient_credits",
"details": { "credits_required": 6, "credits_remaining": 2 }
}Pattern-match on code — it's stable. Full enum + per-endpoint codes are in the OpenAPI spec.
GET /llms.txt — human-readable summary tuned for LLM ingestion.GET /api/v1/openapi.json — full machine-readable contract.GET /.well-known/openapi.json — alias to the spec for agents that probe the standard location.Idempotency-Key.