Puzzle links can be simple, or they can be signed by your server. The right choice depends on how your site publishes media and how much control you want over where puzzle links can be used. Link security is configured separately for each media source.
Standard links are the easiest to set up and are suitable for many public media sources. They are a good starting point when your media is already public, when you are testing the integration, or when you want the fewest moving parts.
A Standard link includes client_id, media_url, and optional player_id. No server-side signing code is required.
Production integration is link-based, not iframe-based. If your media is already protected by your own paywall or member session, you may not need signed puzzle links. In that setup, players follow a normal link to a puzzle subdomain of your site, and your media server allows that subdomain to request media with the player's existing session.
For protected media, puzzlebaddie works best when media access is authorized by same-site cookies or by signed temporary media URLs. localStorage-based auth is not shared across subdomains and requires the partner to issue a playback URL or token.
Protected media requires a puzzle subdomain on your domain, with a DNS CNAME such as puzzles.yoursite.com pointing to puzzlebaddie.com. This is required when protected media is served through a CDN; the integration will not work without the CNAME. The puzzle page must be on your domain so the browser can send the viewer's media access cookie when it loads protected images or videos.
The puzzle subdomain must have valid HTTPS coverage. You can use a certificate issued specifically for
puzzles.yoursite.com, a SAN certificate that includes it, or a wildcard certificate such as
*.yoursite.com. If puzzlebaddie manages certificates for your
puzzle host with ACME, the CNAME must be in place before certificate issuance and renewal can succeed.
Your media server should allow credentialed CORS requests from the puzzle subdomain. For example, if the puzzle page is served from https://puzzles.yoursite.com, media responses should include:
Access-Control-Allow-Origin: https://puzzles.yoursite.com
Access-Control-Allow-Credentials: true
The browser uses GET to load media. HEAD is only used by our server to determine the type when the link omits media_type. OPTIONS is only needed if a browser request triggers a CORS preflight; normal image and video loads do not require one. If your CDN returns different allowed origins for different requests, also return Vary: Origin.
The cookie that protects media must be available to the jigsaw subdomain. Some sites do this with their existing session cookie scoped to the parent domain, such as .yoursite.com. A tighter option is to issue a separate media-access cookie that gives only enough permission to read puzzle media.
This approach keeps your media behind your own paywall while avoiding expiring media URLs that could break a long puzzle after a refresh. It does require DNS, HTTPS, cookie scope, and CORS to be configured correctly on your side.
The puzzle subdomain is intended for puzzle pages, player API calls, and required static assets. Partner dashboard and management pages remain on the puzzlebaddie management host. puzzlebaddie does not inject management-site analytics into partner puzzle pages.
A technical demo is available at https://TODO so your technical team can inspect the request flow and response headers.
We need to know whether a media URL is an image or a video before choosing puzzle settings. For a new media URL, we use these methods in order:
media_type=image or media_type=video. We use that value and do not send a HEAD request to the media URL. For a signed link, add media_type before signing so it is covered by the signature. Use this method if your CDN blocks our server's HEAD request.media_type, our server sends a HEAD request and reads the response's Content-Type. This request does not include the viewer's browser cookie. It checks the type without downloading the media.HEAD request returns 401 or 403, we create the game with an unknown type. The viewer's browser then tries to load it as a video and, if that fails, as an image. If one succeeds, we save the detected type and continue. This still requires the browser to be able to access the CDN media.If the URL already has a saved media type, we reuse it. A media_type value that conflicts with the saved type is rejected. Other HEAD failures, such as a missing file or unsupported Content-Type, do not trigger browser detection.
Check these items in order when public media works but protected media does not.
client_id.
media_url begins with the registered media-source
URL and opens for a signed-in user on the media site.
https:// and any
non-default port. The media response must return that origin in
Access-Control-Allow-Origin, not * when credentials are used.
Access-Control-Allow-Credentials: true and ensure that
image, video, audio, or fetch requests include credentials.
Secure
setting, and SameSite policy. Log in again after changing cookie settings.
Access-Control-Allow-Origin header indicates a
CORS configuration problem. A 401 response means the request reached the media server but
its session credential was missing or rejected.
High Security uses shared-key signing with HMAC-SHA256. puzzlebaddie generates a shared secret for the media source, and your server uses that secret to sign puzzle links before sending players to the puzzle page.
This prevents another website from copying one of your puzzle links, changing the media URL, and hosting puzzles with their own content or a different URL. If the link values change, the signature no longer matches.
Signed links must include a short expiration time and a fresh launch ID. The shared secret should stay only in server-side configuration.
Ultimate Security uses public-key signing with Ed25519. Your server signs puzzle links with a private key that stays on your server. puzzlebaddie stores only the public key, which can verify a link but cannot create one.
This gives the strongest separation: puzzlebaddie can confirm that a link was created by your system without needing the private key used to sign it.
Only the PUBLIC key should be pasted into puzzlebaddie. The private key should remain in your own server-side configuration or secret manager and should never be included in browser JavaScript.
Signing creates a short proof that the link was created by someone who has the right secret or private key. The proof is sent in the URL as sig.
puzzlebaddie verifies that proof before creating a game. The signature covers every query parameter other than sig, including client_id, media_url, launch_id, expires, and any player, clip, or audio options.
If someone changes the media URL, client ID, player ID, or expiration time after the link is generated, verification fails.
launch_id for each intended game, even when the media URL is the same.expires to a Unix timestamp no more than five minutes ahead. puzzlebaddie allows 30 seconds of clock tolerance.key_id; this integration uses one active key per media source.sig, sort keys by name, and query-encode the key/value pairs using UTF-8, + for spaces, and uppercase percent escapes. Prefix the result with jigembed-play-v1 followed by one newline byte. Sign those bytes.sig. For Ultimate Security, sign with Ed25519 and add the unpadded base64url signature. Store the matching Ed25519 public key as PEM SubjectPublicKeyInfo or unpadded base64url of its 32 raw bytes.Generate links on every page view or Play action. A signed link can create one game. Reopening it before expiry returns that game; after expiry, use its redirected game_uuid URL to resume. Changing the media source key immediately invalidates outstanding links.
HMAC test vector (fixed example, not a live link):
secret: example-shared-secret-for-test-only
message (two lines):
jigembed-play-v1
audio=none&client_id=11111111-1111-4111-8111-111111111111&expires=2000000000&launch_id=22222222-2222-4222-8222-222222222222&media_url=https%3A%2F%2Fmedia.example.test%2Fvideo.mp4&start_time=12.5
sig: 0f14c32849cf61506f6f4f8f0c3b9ad2cdf42474e5e20198003924c8e6bad7ff
A signature proves that a link was valid when your server created it. An expiration time says how long that link should remain valid.
Without expiry, a copied signed link could remain useful for a long time. With expiry, copied links naturally age out, which is usually what partners want for member pages, paid content, or time-sensitive access.