Signed URLs
Generate short-lived capture URLs without exposing an API key to a browser or client.
Signed URLs authenticate GET /v1/capture with a project signing secret instead of an API key. They are useful when a client should trigger a render directly but must not see your API key.
When to use signed URLs
Requirements
- Create or choose a project.
- Read the project signing secret from the dashboard or API.
- Mint a URL on your backend.
- Send only the signed URL to the browser.
The signing secret belongs on your server. Treat it like an API key.
Query parameters
Signed capture URLs include normal GET /v1/capture query parameters plus:
| Parameter | Description |
|---|---|
project_id | Project id that owns the signing secret. |
expires | Unix timestamp in seconds. |
signature | Hex HMAC-SHA256 signature over the canonical payload. |
Canonical payload
Build the signature from:
txtMETHODPATHsorted=urlencoded&query=params&excluding=signature
For GET /v1/capture, the first two lines are:
txtGET/v1/capture
Sort all query parameter pairs by their encoded key=value string and exclude signature.
JavaScript example
javascriptimport crypto from "node:crypto";function signCaptureUrl({ secret, params }) { const url = new URL("https://screenframed.com/v1/capture"); for (const [key, value] of Object.entries(params)) { url.searchParams.set(key, String(value)); } const pairs = []; for (const [key, value] of url.searchParams.entries()) { if (key === "signature") continue; pairs.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`); } pairs.sort(); const canonical = ["GET", url.pathname, pairs.join("&")].join("\n"); const signature = crypto.createHmac("sha256", secret).update(canonical).digest("hex"); url.searchParams.set("signature", signature); return url.toString();}const signedUrl = signCaptureUrl({ secret: process.env.SCREENFRAMED_PROJECT_SIGNING_SECRET, params: { project_id: "prj_01K...", expires: Math.floor(Date.now() / 1000) + 300, site: "https://example.com/pricing", device: "browser-macos", bg: "midnight", shadow: "soft", aspect_ratio: "16:9", },});
Payload query parameter
For complex payloads, encode the JSON body as a base64url payload query param. Explicit query parameters still override values inside payload.
json{ "url": "https://example.com/pricing", "device": "browser-macos", "background_preset": "midnight", "shadow": "float"}
Rotation
Rotate the project signing secret if it is exposed or no longer trusted:
bashcurl -X POST "https://screenframed.com/v1/projects/prj_01K.../signing-secret/rotate" \ -H "Authorization: Bearer $SCREENFRAMED_API_KEY"