Minting a signed URL
POST /v1/projects/{slug}/signed-url with an API key that has urls:sign (or a dashboard session). The body is { file_slug, transform_chain?, kind?, expires_in_seconds? }; expires_in_seconds is 60 to 3600, default 300. The project must be active and the file ready. A chain must pass the grammar, and its watermark presets must exist; on a public project it must also be in allowed_transform_chains (a protected project signs any valid chain).
public_url is the unsigned equivalent, or null on a protected project. This endpoint has its own per-project limit on issued URLs (60 per minute by default, configurable per deployment); refused requests do not count against it. Cache signed URLs for their lifetime instead of minting one per page view.