# Lockedin.me: the full guide for people and AI assistants > Lockedin.me is a link-in-bio product (https://lockedin.me). A person or business publishes one fast, customizable page at lockedin.me/{handle}. This file is the complete reference for an AI assistant that sets up, changes or explains a person's page. Short index: https://api.lockedin.me/llms.txt. Paste-ready assistant instructions: https://lockedin.me/ai/ai-instructions.md. Human guide: https://lockedin.me/help. If this file and a tool's own description disagree, trust the tool: the service enforces the rules, this file only explains them. ## 1. What Lockedin.me is - A published page is a small static file with no database behind it. Lockedin.me's own visit counter sets no cookies and keeps no IP address in its counts. Visitor IP addresses are processed only in security and request logs (Cloudflare and Microsoft Azure Application Insights) to prevent abuse and fraud; they are never shown to page owners or used for advertising. The page also loads Google Analytics (all pages are measured together), plus the owner's own tags if they added any. It opens quickly on a phone. - The address is https://lockedin.me/{handle}. The page's owner is a person (a Lockedin.me account); a page can also belong to a business the person manages. - Pages have a Draft copy and a Live copy. Every change an assistant makes is saved to the Draft. Nothing reaches the public until the page is published. - A page has: a profile, a theme, optional design overrides, an optional background, SEO fields, optional tracking ids, and an ordered list of items. - Sign-up: https://lockedin.me (choose an address, confirm an email address with a 6-digit code; no password and no card). - Reserved words (the site's own pages and the paths any website needs), well-known company and product names, and names held back for their real owners all read as "taken"; the API never says why, so do not try to work out whether a name is reserved. Suggest other addresses instead. ## 2. Plans (what is allowed) - Free plan: no card, no end date, no trial period. one page for an account that signed up on lockedin.me. Every item type. All five themes and every design control. The owner's own Google Analytics 4 and Meta Pixel ids. Analytics, QR code, email sign-ups, import, and this assistant access. - The paid plan (an upgrade the person chooses in their dashboard) adds: a photo or video background, removing the small "Made with Lockedin.me" footer line, and more pages (three pages in all). Never quote a price for it and never invent one: refer the person to their dashboard at https://lockedin.me/app . - Call `lockedin_get_plan` (REST: `GET /v1/links/plan`) before suggesting anything that may need the paid plan. Paid options a plan does not include are saved on the Draft but left out of the published page; publishing lists them in `warnings`. Tell the person. - A complimentary grant from Lockedin.me staff behaves like the paid plan. - A page cannot be created while the account is suspended; a suspended account cannot publish. ## 3. The page model | Part | What it is | Limits | |---|---|---| | handle | The address: lockedin.me/{handle} | 3-30 characters: lower-case letters, digits and single hyphens; cannot start or end with a hyphen or contain two in a row. Reserved words, well-known company or product names and names held back for their owners are refused or read as taken. | | displayName | The person's name (for a business page: the person who runs it) | max 80 | | businessName | A business page's title | max 80 | | headline | One line under the name | max 120 | | bio | A short paragraph | max 500 | | location | For example Austin, TX | max 80 | | category | What they are, for example Web design agency | max 60 | | kind | `person` or `business` | default theme: midnight for a person, storefront for a business | | theme | midnight, paper, pop, cover, storefront | see section 5 | | design | Colors, fonts, button style on top of the theme | see section 6 | | background | theme, color, gradient, or a Media Studio asset | the photo or video background is a paid option | | SEO | title, description, noIndex | see section 8 | | tracking | the owner's own GA4 id and Meta Pixel id, and hideBranding | ids work on every plan; hiding the footer line is a paid option | | items | Up to 60 per page, in order | see section 4 | Page status: Draft (only the owner sees it), Live, Paused, Suspended (an administrator hold). ## 4. Items Add items with `lockedin_add_item` (REST: `POST /v1/links/pages/{pageId}/items`). Common fields: `type`, `title` (max 100), `subtitle` (max 200), `url`, `icon`, `visible`, `featured`, `addUtm`, `startsUtc`, `endsUtc`, `data` (REST: a JSON object; MCP: `dataJson` as text). Some types have shorter limits, listed below. A page holds up to 60 items. | type | Good for | What to send | Per-page limit and specifics | |---|---|---|---| | link | A button to any web address | title (required), url (http or https), optional subtitle and icon; `featured: true` turns it into a large picture card; `addUtm: true` adds UTM tags to the address | title max 100, subtitle max 200 | | booking | A highlighted "Book a call" style button | same as link | same as link | | header | A small title that groups the items under it | title (required) | title max 60 | | text | A few lines in the person's own words | title and/or subtitle (at least one) | title max 60, body (subtitle) max 400 | | social | Round icons for Instagram, TikTok, YouTube and so on | `icon` = the network key, `url` = a handle or address | up to 16 per page. Networks: instagram, tiktok, x, linkedin, facebook, youtube, threads, whatsapp, telegram, pinterest, snapchat, twitch, github, spotify, discord, reddit, medium, email, phone, website | | video | A video that plays when tapped | url = a YouTube or Vimeo address | up to 10 per page | | music | A music player that loads when tapped | url = a Spotify, Apple Music or SoundCloud address | up to 10 per page | | contact | One tap to call, text or email | url = `tel:+15125550123`, `sms:+15125550123`, `mailto:name@example.com` or a wa.me / whatsapp.com link | default titles: Call, Text us, Email us, WhatsApp | | map | An address with a Get directions button | subtitle = the address (max 200); optional url (web address); optional title | 1 per page, title max 40 | | email | A sign-up form that collects addresses | optional title (max 60) and subtitle (max 200); data `{"button":"Subscribe","consent":"...","thanks":"...","name":true,"confirm":true,"notify":true}` | up to 3 per page. button max 24, consent line max 200, thank-you max 120, `name` asks for a name too, `confirm` (default true) asks each person to confirm by e-mail, `notify` (default true) e-mails the owner about new sign-ups | | textsignup | A text-message sign-up that collects US mobile numbers with consent (capture only, nothing is sent; adults only) | optional title (max 60) and subtitle (max 200); data `{"button":"Text me","brand":"","frequency":"Msg frequency varies.","thanks":"...","confirm":false}` | up to 2 per page. button max 24, brand max 60 (empty = the page name), frequency max 60. The consent sentence next to the unchecked box is added by the platform and cannot be changed or removed. Teens (13 to 17) cannot add it (`teen_restricted`) | | hours | Opening hours with an "Open now" badge | data `{"tz":"America/Chicago","days":[{"d":1,"o":"09:00","c":"17:00","x":false}],"note":""}` where d is 0 (Sunday) to 6, times are 24-hour HH:mm, x = closed | 1 per page, title max 40, note max 160. Days left out count as closed. | | reviews | A star rating and a link to read or leave reviews | data `{"rating":4.8,"count":212}` (rating 0.1 to 5); optional url (web address) | 1 per page, title max 40. Numbers only: no review text is shown or stored. | | faq | Questions and answers that open in place | data `{"items":[{"q":"...","a":"..."}]}` | up to 3 per page, up to 12 questions each, question max 140, answer max 600, title max 60 | | divider | A thin line between groups | nothing | none | Notes: - URLs are checked: only http, https, tel, sms and mailto are accepted, and a refused address comes back with the reason. - A link's thumbnail or a featured card's picture is set with `lockedin_set_image` (slot `item`). - Hide an item without deleting it with `visible: false` (`lockedin_update_item`). Deleting is permanent for the Draft. - Change the order with `lockedin_reorder_items`: list the item ids first to last; items left out go after the listed ones. - `update_item` changes only the fields passed. The item's type cannot change. When you change `data`, send the complete data object (read the item first with `get_page`). ### Scheduling - `startsUtc` and `endsUtc` (ISO 8601) show an item only between two moments. The end must be after the start. - Any item except a divider or a social icon can also be shown only on certain days and times in a time zone: put `"when":{"tz":"America/Chicago","days":[{"d":1,"o":"09:00","c":"17:00","x":false}]}` inside the item's `data` (for example a Call button only while the business is open). The live page checks the clock when it is opened, so a scheduled item appears and disappears by itself; there is no need to publish again. ## 5. Themes | key | Look | Good for | |---|---|---| | midnight | Dark glass with a soft aurora glow | Creators, founders, developers | | paper | Warm and editorial, hairline borders, a serif voice | Writers, consultants, designers, coaches | | pop | Bold color, chunky buttons, a bounce | Musicians, makers, shops, events | | cover | A full-width photo hero with glass buttons over it | Photographers, coaches, speakers, restaurants (set a cover picture) | | storefront | Call, directions, hours and Google rating up front | Local businesses: salons, restaurants, trades, clinics, shops | Changing the theme keeps colors and fonts the person already chose. In the editor each theme has six ready-made color sets; a page that departs from them is shown as "Custom, based on {theme}". ## 6. Design keys and allowed values `lockedin_update_design` (REST: `PATCH /v1/links/pages/{pageId}/design`). Only the fields passed change. An empty string (or -1 for dim) returns a field to the theme's own value. Unknown values are refused. | key | Allowed values | |---|---| | accent | #rgb or #rrggbb. Buttons, links, avatar ring. | | accent2 | #rgb or #rrggbb. Second color of gradients and glows (derived from accent when empty). | | background | #rgb or #rrggbb. Page background. | | background2 | #rgb or #rrggbb. Far end of the background gradient (derived when empty). | | text | #rgb or #rrggbb. It is nudged until it is readable (contrast at least 4.5:1) on the background. | | buttonStyle | solid, outline, soft, glass, hard | | buttonShape | pill, round, square | | font | sans, serif, rounded, mono, display | | avatarShape | circle, rounded, square | | animation | none, subtle, lively | | dim | 0 to 80. How much a photo or video background is darkened so text stays readable. | Ask the person for their brand colors; do not invent a brand. "Warmer" or "more like a bakery" is a request for you to pick values and tell them which. ## 7. Pictures and backgrounds - Slots: `avatar` (profile picture, shown square or round), `hero` (the cover picture the Cover theme and the share card use), `item` (a link's thumbnail or a featured card's picture; also pass `itemId`). - Send `imageUrl` (a PUBLIC web address of an image; the server downloads it, up to 15 MB; private and local addresses are refused) or `imageBase64` (the bytes). REST also accepts the raw image bytes with an `image/*` content type. JPG, PNG, WebP or GIF. Pictures are re-encoded to WebP and their metadata is removed. - Prefer `imageUrl`. MCP requests larger than 1 MB are refused for Agent Keys, so large base64 pictures do not fit; use an address instead. - Only use pictures the person owns or gave you. Remove a picture with `lockedin_remove_image` after the person confirms. - Backgrounds are never uploaded. Solid color and gradient backgrounds (`kind` color or gradient, using the design's background colors) and the theme's own (`kind` theme) are free. A photo or video background (`studioAssetId` of a finished asset) is a paid option. ## 8. SEO and link previews - `lockedin_update_seo` (REST: `PATCH /v1/links/pages/{pageId}/seo`): `title` (up to 70 characters accepted), `description` (up to 200 accepted), `noIndex` (true asks search engines not to list the page; people with the address can still open it). - Aim for a title of about 60 characters and a description of about 155 so search results and link previews are not cut off. - Leave a field empty to use the default: the title falls back to the person's name and their one line about themselves, the description to their bio. - A page read (`lockedin_get_page`, `GET /v1/links/pages/{pageId}`) also returns what search engines get now (`seoTitleEffective`, `seoDescriptionEffective`) and an optimized pair built from the page's own profile (`seoTitleSuggested`, `seoDescriptionSuggested`). Show the suggestion to the person and, if they agree, send those values to `lockedin_update_seo` as they are. - The cover picture is used as the share image when there is one. - The page address of a live page is canonical. Pages are indexable unless `noIndex` is on. ## 9. Analytics and privacy - Counted by Lockedin.me's own counter, with no cookies and no IP address in its counts (IP addresses appear only in security and request logs, kept only as long as needed for security, never shown to owners): views, unique visitors (one per person per day), taps on each item, mobile views, tap rate, the source of each visit, and the previous period for comparison. A visitor is a one-way hash that changes every day and is deleted after two days; only totals per page, day, source and item are kept. - Every published page also loads Google Analytics. For visitors outside the EU, the UK and Switzerland it may set its own cookies and receives the visit; its advertising features are off and it respects the Global Privacy Control signal. So the page as a whole is not cookie-free, only the page counter is. - Sources: instagram, tiktok, facebook, linkedin, x, youtube, threads, pinterest, snapchat, whatsapp, reddit, google, search, email, qr, other, direct. The tagged address for each app is on the editor's Share tab; scanning the QR code counts as `qr`. Crawlers are ignored. - `lockedin_get_analytics` (REST: `GET /v1/links/pages/{pageId}/analytics?days=1..90`, default 30). Report numbers as given; do not extrapolate or guess causes. - The owner may add their own Google Analytics 4 id (G-ABC123XYZ) and Meta Pixel id (8-20 digits) with `lockedin_update_tracking`. These are free on every plan, belong to the owner, and are labeled as theirs. Only use ids the person gave you. They may set their own cookies and share visits with Google or Meta, so the owner needs a notice for their visitors. ## 10. Email sign-ups (leads) - A `textsignup` item collects US mobile numbers behind an unchecked consent box (capture only; nothing is sent). The numbers belong to the page owner: `GET /v1/links/pages/{pageId}/text-signups` lists them masked to the last four digits, `.../text-signups.csv` has the full numbers with the exact consent wording and time, `POST .../text-signups/{id}/opt-out` records a STOP, `DELETE .../text-signups/{id}` removes one. Same access rule as the e-mail list. Never repeat a number unless asked. - An `email` item collects addresses with the wording on the form. The addresses belong to the page owner. - `lockedin_list_leads` (REST: `GET /v1/links/pages/{pageId}/leads?take=1..500`, newest first) returns each sign-up's e-mail address, date and source. They are personal data of the owner's subscribers: summarize counts and trends, and repeat an address only when the person asks. - In the editor (Analytics tab) the owner can export all sign-ups as a CSV or delete one. REST does the same: `GET /v1/links/pages/{pageId}/leads.csv` (columns Email, Name, Status, Source, Signed up (UTC), Confirmed (UTC); same access rule as the list) and `DELETE /v1/links/pages/{pageId}/leads/{leadId}` (the person asked to be removed, or it was a mistake; needs `links:write`). There is no MCP tool for either. - Each sign-up has a `status`: `confirmed` (the person pressed Confirm in the e-mail we sent), `pending` (waiting for them to) or `stored` (no confirmation was asked: the form has it turned off, or it is from before confirmations). Only `confirmed` and `stored` addresses are subscribers: do not count or suggest mailing `pending` ones. "Ask people to confirm by email" and "Email me about new sign-ups" are on every sign-up item by default (`confirm` and `notify` in the item's data); when confirmation is on, a form's thank-you message is shown on the confirmation page, not right after the sign-up. - A page accepts up to 500 new addresses a day (an address already on the list does not count again; the form then says to try again tomorrow). This limit is per page per day and has nothing to do with the number of pages a person may have. ## 11. Addresses, moved pages and removal - Changing a live page's address (`lockedin_change_handle`; REST: `POST /v1/links/pages/{pageId}/handle` with body `{"handle":"new-address"}`; needs `links:write`) publishes the page at the new address at once and leaves a small "moved" page at the old address that sends visitors to the new one; the old address stays reserved for the page. Confirm the new address with the person first: links they already shared keep working only through that notice. - Taking a page offline (`lockedin_unpublish`) turns it back into a Draft and its address stops answering. Nothing is deleted. Ask the person first. - Deleting a page (`lockedin_delete_page`; REST: `DELETE /v1/links/pages/{pageId}`; needs `links:write`) removes the page from the public site, then its items, pictures, numbers and sign-ups, and the address becomes free for anyone. Only the page's owner may (a business Manager cannot). It cannot be undone: ALWAYS ask the person to confirm by name before calling it. Assistants cannot delete an account. - Deleting the Lockedin.me account removes all of the person's link pages. - Each page has a QR code (PNG and SVG) on the editor's Share tab; REST: `GET /v1/links/pages/{pageId}/qr.svg` (an SVG of the public address tagged utm_source=qr, so scans show as their own source in the analytics). ## 12. Filling a page from its Google listing If a page is tied to a business that has a Google listing (its business's listing, or one its owner has verified), its details can be filled from that listing instead of retyped. This is offered only for such pages: in the editor the Content tab shows a **Google** button and card, and the setup wizard's Connect step has a toggle. Without a listing the page has nothing to fill from. - `lockedin_get_google_listing` (REST: `GET /v1/links/pages/{pageId}/google-listing`) reads the listing and returns, for each part (profile, website, phone, map, hours, reviews), the exact values that would be added, or why a part is unavailable. It changes nothing. `hasListing: false` means the page has no listing: do not guess or invent the business's details; ask the person. - Show the person the values and ask which parts they want, then call `lockedin_apply_google_listing` (REST: `POST /v1/links/pages/{pageId}/google-listing/apply`, optional body `{"profile":true,"website":true,"phone":true,"map":true,"hours":true,"reviews":true}`; send false to leave a part out). Only empty profile fields are filled, an item the page already has is skipped, and the result lists what was added and what was skipped. It needs `links:write`, saves to the Draft and never publishes. It is idempotent: running it again adds nothing twice. - Never add hours, ratings, addresses or descriptions from your own memory. ## 13. Importing existing links In the editor (Content tab, "Bring your links over") the person pastes the address of an existing Linktree, Beacons, bio.link or similar page. Lockedin.me reads that public page once, lists what it found, and adds only the links the person ticks. Social networks become round icons. This is a screen action; assistants add items one by one. ## 14. Connecting: REST, MCP and authentication - MCP server (Streamable HTTP, stateless): https://api.lockedin.me/mcp/links . Server card: https://api.lockedin.me/.well-known/mcp/server-card.json - REST API base: https://api.lockedin.me/v1/links . OpenAPI 3.0 (public, no sign-in): https://api.lockedin.me/openapi/links.json - Authentication: `Authorization: Bearer `. The page owner makes the key (an "Agent Key") at https://lockedin.me/ai while signed in, names it after the tool, picks an expiry and chooses "May edit and publish" or "Edit drafts only". The key is shown once. One key per tool; the owner can revoke any key in one tap. - Scopes: any Lockedin.me key reads (list pages, get a page, plan, analytics, leads and the leads CSV, the QR code). `links:write` changes things (create pages, edit profile, theme, design, SEO, tracking, background, items, pictures, change the address, delete a page, delete a sign-up). `links:publish` is needed to publish and unpublish. A key made as "Edit drafts only" has `links:write` and is not shown the publish tools. - Anonymous routes (section 16a) need no key at all: the site's own sign-up form and its published pages use them. They are not for assistants. - A Lockedin.me key works only on `/v1/links` and `/mcp/links`; every other path answers 404. It can only touch pages its owner can edit. It has no access to billing or the account. - No custom header, no credits and no developer plan are needed. Rate limit: 120 requests a minute per person (HTTP 429 when exceeded: wait, do not retry in a loop). - Never print, log or repeat a key. If a key is lost, the owner makes a new one. ### Setup recipes (replace YOUR_KEY) - Claude Code: `claude mcp add --transport http lockedin https://api.lockedin.me/mcp/links --header "Authorization: Bearer YOUR_KEY"` - Claude Desktop: in claude_desktop_config.json add `"lockedin": {"command":"npx","args":["-y","mcp-remote","https://api.lockedin.me/mcp/links","--header","Authorization:${AUTH_HEADER}"],"env":{"AUTH_HEADER":"Bearer YOUR_KEY"}}` under `mcpServers` (needs Node.js). - Cursor: `.cursor/mcp.json` with `{"mcpServers":{"lockedin":{"url":"https://api.lockedin.me/mcp/links","headers":{"Authorization":"Bearer YOUR_KEY"}}}}` - VS Code: `.vscode/mcp.json` with `{"servers":{"lockedin":{"type":"http","url":"https://api.lockedin.me/mcp/links","headers":{"Authorization":"Bearer YOUR_KEY"}}}}` - Gemini CLI: `~/.gemini/settings.json` with `{"mcpServers":{"lockedin":{"httpUrl":"https://api.lockedin.me/mcp/links","headers":{"Authorization":"Bearer YOUR_KEY"}}}}` - ChatGPT (custom GPT): Configure, Actions, Import from URL `https://api.lockedin.me/openapi/links.json`, Authentication: API Key, auth type Bearer, paste the key. - Any REST tool: `curl https://api.lockedin.me/v1/links/pages -H "Authorization: Bearer YOUR_KEY"` - Claude.ai and ChatGPT's built-in connector directories are not supported yet. ## 15. MCP tools (all under https://api.lockedin.me/mcp/links) Their names are exact; call `tools/list` for the live descriptions. Reads (any key): - `lockedin_get_plan`: the plan, page limit, and which paid options are on. - `lockedin_list_pages`: the person's pages with pageId, handle, status, theme, address, unpublished-changes flag and view and tap totals. Always call this first. - `lockedin_get_page`: one page in full (profile, theme, design, SEO, tracking ids, items with ids). Read before changing. Give pageId or handle. - `lockedin_get_analytics`: views, visitors, taps, tap rate, sources and top items for 1-90 days. - `lockedin_list_leads`: email sign-ups, newest first (privacy rules in section 10). - `lockedin_get_google_listing`: what the page's linked Google listing would add, per part, with the exact values (reads only; section 12). Writes (key needs `links:write`): - `lockedin_create_page`: new Draft page (handle, displayName, optional kind and theme). - `lockedin_change_handle`: change the page's address (pageId, handle); a live page moves at once and a "moved" notice stays at the old address. Confirm the new address with the person first. - `lockedin_delete_page`: delete a page for good (owner only). ALWAYS ask the person to confirm by name first; it cannot be undone. - `lockedin_update_profile`: displayName, businessName, headline, bio, location, category, kind. - `lockedin_set_theme`: midnight, paper, pop, cover or storefront. - `lockedin_update_design`: colors, buttonStyle, buttonShape, font, avatarShape, animation, dim. - `lockedin_update_seo`: title, description, noIndex. - `lockedin_update_tracking`: GA4 id, Meta Pixel id, hideBranding (hiding the footer line is a paid option). - `lockedin_set_background`: kind theme, color or gradient, or a Media Studio asset (paid). - `lockedin_add_item`: add an item at the end. - `lockedin_update_item`: change an item, hide it, schedule it. - `lockedin_delete_item`: delete an item (confirm with the person first). - `lockedin_reorder_items`: set the order. - `lockedin_set_image`: avatar, hero or item picture from imageUrl (best) or imageBase64. - `lockedin_remove_image`: remove one of those pictures (confirm first). - `lockedin_apply_google_listing`: add the chosen parts of the page's Google listing (profile, website, phone, map, hours, reviews) to the Draft; call `lockedin_get_google_listing` first and ask the person which parts they want. Publishing (key needs `links:publish`): - `lockedin_publish`: make the saved Draft live; lists any paid options left out in `warnings`. - `lockedin_unpublish`: take the page offline (confirm first). ## 16. REST routes (all under https://api.lockedin.me/v1/links) - `GET /plan`, `GET /pages`, `GET /pages/{id}`, `GET /pages/by-handle/{handle}`, `POST /pages` (body `{"handle":"sunny-bakes","displayName":"Sunny Bakes","kind":"business","theme":"storefront"}`) - `PATCH /pages/{id}/profile`, `PUT /pages/{id}/theme`, `PATCH /pages/{id}/design`, `PATCH /pages/{id}/seo`, `PATCH /pages/{id}/advanced` (ga4Id, metaPixelId, hideBranding), `PUT /pages/{id}/background` - `POST /pages/{id}/handle` (body `{"handle":"new-address"}`), `DELETE /pages/{id}` (the owner's decision; a business Manager cannot), `GET /pages/{id}/qr.svg` (image/svg+xml) - `POST /pages/{id}/items`, `PATCH /pages/{id}/items/{itemId}`, `DELETE /pages/{id}/items/{itemId}`, `PUT /pages/{id}/items/order` (body `{"itemIds":[3,1,2]}`) - `PUT|DELETE /pages/{id}/avatar`, `PUT|DELETE /pages/{id}/hero-image`, `PUT|DELETE /pages/{id}/items/{itemId}/image` - `GET /pages/{id}/google-listing`, `POST /pages/{id}/google-listing/apply` - `POST /pages/{id}/publish`, `POST /pages/{id}/unpublish`, `GET /pages/{id}/analytics?days=30`, `GET /pages/{id}/leads?take=100`, `GET /pages/{id}/leads.csv` (text/csv), `DELETE /pages/{id}/leads/{leadId}` - Responses are `{"success":true,"data":...}`. Failures are `application/problem+json` with `title` (the reason, written for a person) and `status`. ### 16a. Anonymous routes (no key; for the site's own sign-up form and its published pages, not for assistants) All under https://api.lockedin.me/v1/links, rate-limited per client address in ten-minute sliding windows. - `GET /handle-check?handle=` (120 per 10 minutes): is this address free? Answers `{"handle","available","message","suggestions":[up to four free alternatives]}`. A pasted address is reduced to its last segment. Reserved words, brands and names held for their owners read as taken. - `POST /signup/start` (5 per 10 minutes), body `{"email":"...","handle":"optional-claimed-address"}`: e-mails a 6-digit code (fifteen minutes, five attempts) and answers `{"status":"sent","challengeId","maskedEmail","resendAfterSeconds","expiresInMinutes"}`. Every deliverable address gets the same answer whether or not an account exists; an undeliverable address is a 400. Sign-ups paused: 503. Earlier codes of the address stay valid until they expire (a resend adds a code); an address that asked three times in ten minutes gets `{"status":"wait","maskedEmail","retryAfterSeconds"}` and no mail. - `POST /signup/verify` (15 per 10 minutes), body `{"challengeId":"...","code":"123456","handle":"optional","deviceInfo":"optional"}`: a right code signs the person in, creating the account when there is none, and answers `{"status":"verified","created","pageId","handle","tokens":{"accessToken","refreshToken","expiresAtUtc"}}`; the claimed handle is reserved as a draft page when it is free (`pageId` and `handle` are null when not). An account with an authenticator answers `{"status":"two-factor","twoFactorToken","expiresAtUtc"}`: finish on `POST /api/otp/verify-2fa`. Other answers: `{"status":"invalid","attemptsLeft"}`, `{"status":"expired"}`, `{"status":"unavailable"}`. - `POST /public/t` (900 per 10 minutes): the page's view and tap beacon, a small text body the published page's own script sends; always 204. `POST /public/lead` (12 per 10 minutes): the page's e-mail sign-up form (form fields), answers `{"ok","message"}`. `POST /public/report` (6 per 10 minutes): the "Report this page" form, redirects to the site's report page. `GET|POST /public/confirm/{token}`: the page behind a sign-up confirmation e-mail (GET shows a button, POST confirms). ## 17. Rules for assistants 1. Call `lockedin_list_pages` first. Identify the page by pageId or handle. If there are several pages and the person did not say which, ask. 2. Read (`lockedin_get_page`) before you change, so you edit what is really there. 3. Change the Draft, then tell the person plainly what changed. Ask before publishing unless they asked you to publish. Ask before unpublishing and before deleting or removing anything. 4. Use only facts the person gave you. Never invent prices, hours, addresses, phone numbers, reviews, ratings, awards, testimonials, colors of a brand or social handles. If something is missing, ask. 5. Use the exact values this guide and the tools list. A refused value comes back with a reason: read it, fix it, and do not guess around it. 6. Respect the plan. Check `lockedin_get_plan` before suggesting paid options; never quote a price. 7. Treat email sign-ups as the owner's private data. Summarize; repeat an address only when asked. 8. Do not poll or loop. Stay inside the rate limit. 9. Never print or store the Agent Key. 10. An assistant cannot: delete the account, change billing or the plan, upload a background, or touch anything outside the person's link pages. It can change a page's address and delete a page, but only after the person confirmed (by name, for a delete). ## 18. Errors and troubleshooting | What you see | Meaning | What to do | |---|---|---| | 401 | No key, a wrong key, an expired key or a revoked key | Ask the person to make a new key at https://lockedin.me/ai | | 403 | The key lacks `links:write` or `links:publish` | Tell the person which grant is missing; they make a new key with it | | 404 | No such page, item or handle, or it is not this person's, or the route is not part of this service | Call list_pages again and use the ids it returns | | 400 with a reason | A value was refused (reserved handle, bad address, over a limit, a paid-only option) | Read the reason, correct the value, retry once | | 409 or "That address is taken" | The handle is taken, or held back for its owner | Suggest other handles and let the person choose; do not try to find out why | | 413 or "request too large" | A picture sent as base64 was too big | Use `imageUrl` with a public address | | 429 | More than 120 requests a minute | Wait a minute | | A page limit message | The page limit of the plan | Offer to edit an existing page; the paid plan allows more | | Publish `warnings` | Paid options were left out of the live page | Tell the person what was left out | | "Publishing is not available right now" | A temporary hosting problem | Try again in a minute; tell the person if it keeps happening | Handle taken: suggest similar handles (add a word such as the city or the trade) and let the person choose. Picture problems: JPG, PNG, WebP or GIF under 15 MB from a public address. ## 19. A worked example (an illustration; the details belong to the example person) Person: "Set up a page for my bakery, Sunny Bakes, in Austin. I want the handle sunny-bakes." 1. `lockedin_list_pages` returns no pages. `lockedin_get_plan` shows the Free plan. 2. `lockedin_create_page` {handle: "sunny-bakes", displayName: "Maria Lopez", kind: "business", theme: "storefront"} (the assistant asked first for the owner's name and confirmed the handle). 3. `lockedin_update_profile` {pageId, businessName: "Sunny Bakes", headline: "Fresh bread daily in East Austin", location: "Austin, TX", category: "Bakery"}. Every word came from the person's message. 4. The person gives a phone number and an ordering link. `lockedin_add_item` {type: "contact", title: "Call us", url: "tel:+15125550123"} and {type: "link", title: "Order online", url: "https://sunnybakes.example/order", featured: true}. 5. Hours: the person says Tuesday to Saturday 7 to 3. `lockedin_add_item` {type: "hours", dataJson: "{\"tz\":\"America/Chicago\",\"days\":[{\"d\":0,\"x\":true},{\"d\":1,\"x\":true},{\"d\":2,\"o\":\"07:00\",\"c\":\"15:00\"},{\"d\":3,\"o\":\"07:00\",\"c\":\"15:00\"},{\"d\":4,\"o\":\"07:00\",\"c\":\"15:00\"},{\"d\":5,\"o\":\"07:00\",\"c\":\"15:00\"},{\"d\":6,\"o\":\"07:00\",\"c\":\"15:00\"}]}"}. 6. Picture: the person pastes a public photo address. `lockedin_set_image` {pageId, slot: "avatar", imageUrl: "https://sunnybakes.example/logo.jpg"}. 7. The assistant summarizes: "I created the page as a draft with your name and tagline, a Call us button, an Order online card, your hours and your logo. Want me to publish it?" 8. The person says yes. `lockedin_publish` returns the address https://lockedin.me/sunny-bakes and no warnings. The assistant reports the address. 9. Later: "How did last week go?" `lockedin_get_analytics` {days: 7} gives views, taps and sources, which the assistant reports as given. Questions or problems the assistant cannot solve: send the person to https://lockedin.me/help .