# Valentine Captcha (d-solve.de) > A fake reCAPTCHA that turns into a personalised Valentine: the recipient ticks "I'm not a robot", selects the right squares in a 3×3 picture grid, drags a slider all the way to yes and is told they are someone's Valentine. English or German, every text replaceable. This file is the complete instruction for an AI assistant that builds a Valentine with a user: how to work with them, every parameter, pictures, the long link, and the optional short link through the API or the MCP server. Nothing else needs to be read first. Operator: dSolve (Felix Paul), https://d-solve.de. Legal: https://d-solve.de/impressum/ (Impressum), https://d-solve.de/datenschutz/ (privacy, German). Everything is free to use. ## Addresses - Page: https://d-solve.de/projects/valentine-captcha/? (the Valentine itself; identical for everyone, all content in the query string) - Link builder for people (live preview, edit in place, "Save online"): https://d-solve.de/projects/valentine-captcha/customize/ - Finished example: https://d-solve.de/projects/valentine-captcha/demo/ - Documentation for people: how it works, every parameter with a live example, self-hosting: https://d-solve.de/projects/valentine-captcha/docs/ - API documentation for people: endpoints, limits, curl walkthrough, errors, accounts: https://d-solve.de/projects/valentine-captcha/api/ - API base: https://d-solve.de/valentine/api · OpenAPI 3: https://d-solve.de/valentine/api/openapi.json · live limits: https://d-solve.de/valentine/api/limits - MCP server (Streamable HTTP, no login): https://d-solve.de/valentine/mcp - Short links look like https://d-solve.de/valentine/; uploaded pictures like https://d-solve.de/valentine/img/ ## How to work with the user 1. Work in everything you already know about the sender and the recipient right away. Start with public placeholder pictures (for example from Wikimedia Commons) so there is a good result fast; find the pictures yourself. 2. Ask only for what is missing, a few things at a time, briefly. Start with the most important: language, the sender's name, the recipient's name, the tone (sweet, funny, nerdy, inside joke). 3. Then the challenge: what the recipient has to find in the squares, which squares are the "right" ones (that becomes `cells`), whether a hint is needed. 4. Offer to use the user's own pictures; ask actively, personal pictures make the Valentine much more special. 5. Fill `title`, `subtitle`, `question`, `scale`, `readouts`, `reveal` and `msg` to match the tone and their story: warm and specific, without exaggerating. 6. Make sure the challenge is solvable: `prompt` and `help` must match `cells` and the pictures. 7. Give the current link at every intermediate step so the user can open and test it. Show the final version with a short overview of the decisions, ask whether anything should change, and point to the link builder for manual changes. 8. At the end ask once: long link without storage (default) or short link with storage on a d-solve.de server? Never store anything online without asking. 9. Talk to the user in the language they write in. ## What the recipient sees 1. Intro: big recipient name, a headline, a mock security notice and an "I'm not a robot" checkbox. A "decline" button runs away. 2. Picture grid: 3×3 squares numbered 1–9 (left to right, top to bottom). The recipient selects the right squares and presses VERIFY. 3. Slider: a question, labels under a slider, phrases that change while dragging. Only at 100 % can they confirm. 4. Reveal: ", you are my valentine." plus a message and the sender's signature. ## Parameters Every parameter is optional; a missing or empty one falls back to the built-in text of the chosen language. The reference with a working example link per parameter: https://d-solve.de/projects/valentine-captcha/docs/#parameters - `lang`: "en" (default) or "de". Language of every built-in text. - `to`: recipient's name. - `from`: sender's name. - `title`: intro headline, shown after ",". Default: "you've received *a Valentine.*" - `subtitle`: the mock security notice under it. - `select`: bold first line above the grid. Default: "Select all squares with". - `prompt`: smaller second line above the grid, always visible: what to find plus any hint. Default: "a heart. If there are none, click verify." - `help`: text shown only when the recipient presses the "?" button. - `cells`: which squares must be selected to pass: "all" (default: every square secretly contains a heart), "any", or an exact list such as "1,3,5". With a list, exactly those squares must be selected, no more and no fewer. - `img1` … `img9`: picture for each square. Unset squares show a built-in drawn heart. - `question`: headline above the slider. Default: "On a scale of *0 to yes*,\nwill you be my valentine?" - `scale`: comma-separated labels under the slider, left to right. Default: "NO,MEH,OK,SURE,YES". - `readouts`: comma-separated phrases shown while the slider moves from 0 % to 100 %; the last one appears only at 100 %. - `reveal`: final headline, shown after ",". Default: "you are my valentine." - `msg`: the last line under the final headline. Formatting: in `title`, `subtitle`, `prompt`, `question` and `reveal`, *asterisks* put a phrase in the italic accent colour and \n makes a line break. Everything else is shown literally; HTML is never interpreted. `scale` and `readouts` are split at commas, so their entries must not contain commas. Old parameter names still accepted: `name` (to), `by` (from), `message` (msg), `hint` (appended to prompt). ## Pictures Each `img` value must be a public https:// link that points directly at an image file and works without login (test: open it in a private window; only the bare picture should appear). Good sources: Wikimedia Commons (right-click the picture, "Copy image address", upload.wikimedia.org/…); Google Drive shared as "Anyone with the link", turning drive.google.com/file/d/FILE_ID/view into https://drive.google.com/thumbnail?id=FILE_ID&sz=w1000; Imgur (i.imgur.com/…); the user's own website. Google Photos and iCloud links are album pages and never work. Only use picture links you actually found and checked; never make one up. Only a stored Valentine (short link) can hold uploaded pictures; in link mode there are picture links only. ## The long link (default) - Start from exactly https://d-solve.de/projects/valentine-captcha/ and append the parameters as a query string. - URL-encode every value as encodeURIComponent would: space → %20, comma → %2C, & → %26, = → %3D, ? → %3F, # → %23, line break → %0A, umlauts and emoji as UTF-8 percent escapes. A picture link is a value too, so its own :// / ? & = must be encoded. If you can run code, build the link with code. - Leave out every parameter that keeps its default. Over roughly 18 000 characters the link does not open in Safari on iPhone; never embed pictures as data: URIs. If the link gets that long, suggest the short link. - Nothing is stored anywhere, nothing expires, but the link is long and everyone who has it can read its content. ## The short link (optional, after the user said yes) The same parameters are stored as JSON on a d-solve.de server (Cloudflare, D1 and R2), the Valentine gets a short link, pictures can be uploaded, everything can be changed later and is deleted automatically after a retention period (without an account 7, 30 or 90 days; default 30; current options in `retention.options` of GET /limits). Whoever has the link sees the Valentine there too. Tell the user the period and the deletion date. Two ways, same implementation. If you have an MCP client, use the MCP server; if you can make HTTP requests, use the API; if you can do neither, hand the user the finished `curl` command or the long link. ### MCP Add https://d-solve.de/valentine/mcp as a remote MCP server (Streamable HTTP, JSON-RPC 2.0, no authentication, stateless). Tools, one per API call: `valentine_limits`, `valentine_create` {data, retentionDays}, `valentine_get` {id}, `valentine_update` {id, editKey, data?, retentionDays?}, `valentine_delete` {id, editKey}, `valentine_add_image` {id, editKey, url}, `valentine_delete_image` {id, editKey, imageId}, `valentine_rotate_key` {id, editKey}. `data` is the flat parameter object described above, not URL-encoded. Results are JSON (also as structuredContent); errors carry `status`, `error` and sometimes `code`. ### API Base https://d-solve.de/valentine/api, JSON in and out, `Authorization: Bearer ` for writes, CORS open, no bot protection. Full reference: https://d-solve.de/projects/valentine-captcha/api/ and https://d-solve.de/valentine/api/openapi.json 1. Optional GET /limits: `features.cloud` must be true; it lists limits, `retention.options` and today's usage. 2. POST /sites with {"data": {...}, "retentionDays": 30}. `data` holds the parameters above as a flat object name → text, not URL-encoded (a line break is \n inside the JSON string); only those names are accepted, typos are rejected with 400; no data: URIs. Response 201: `url` (short link), `id`, `editKey`, `expiresAt`. 3. Pictures: for each one POST /sites//images with Bearer and {"url": "https://…"}; the server fetches it (JPEG, PNG, WebP, GIF, AVIF; at most 9 per Valentine, size in `limits.maxImageBytes`). Put the returned `url` into `img1` … `img9`, then PUT /sites/ with the complete `data`. 4. Later changes with the key the user gives you: GET /sites/, change, PUT /sites/ with the complete `data` (PUT replaces everything). Retention: PUT with {"retentionDays": 90} restarts the period from now. Delete: DELETE /sites/. New key: POST /sites//rotate-key. 5. Without HTTP: print `curl -sS -X POST https://d-solve.de/valentine/api/sites -H "content-type: application/json" -d '{"data": {...}, "retentionDays": 30}'` with the real JSON, or let the user paste the long link into the link builder under "Start from an existing link" and click "Save online" there. ### Rules for storing - Show the short link and the edit key once, clearly set apart in a code block, and ask the user to save the key now (password manager): it is the only way to change or delete the Valentine later, it is never shown again, and you will not remember it. Never put the key into a link, into `data` or into a request URL. - Name the deletion date (from `expiresAt`). - Always write in full: GET first, edit, then PUT or `valentine_update` with the whole object. - On 429 (`rate`) or 503 (`capacity`, `daily-cap`) fall back to the long link and tell the user briefly why. On 409 `image-limit` use fewer pictures. On 400 the `error` field names the problem; fix it and retry once. - Accounts (Google sign-in) and premium exist for people who manage several Valentines in the builder; agents do not need them. Details: https://d-solve.de/projects/valentine-captcha/api/#accounts ## Delivering the result Give the finished link once as plain text in a code block and once as a clickable link. If the Valentine was stored, the short link is the link and the edit key comes with it once. Remind the user to test it (solve their own challenge, drag the slider to 100 %), and that the link builder (https://d-solve.de/projects/valentine-captcha/customize/) can load the link under "Start from an existing link" and change everything in the preview. Ask whether anything should change, and give the complete link again after every change.