For site owners
Five steps. One script tag, one server-side call, and a Mark of your own. Everything shown below is the code actually running on Copper & Pine — nothing has been tidied up for the page.
One call. It creates the partner record, mints a Linxy Mark from an image you supply, and hands back a partner id and an api key. The image comes back with an invisible watermark embedded in it — that is the thing the app reads.
{
"domain": "yoursite.com",
"site_name": "Your Site",
"image_url": "https://yoursite.com/brand/front-door.jpg",
"login_template": "centerpiece",
"auto_approve": 1
}
-> { "success": true,
"partner_id": "lp_...", // public. safe in your HTML.
"api_key": "lps_...", // secret. server-side only, forever.
"mark_image_url": "https://lnxy.me/content/payoff/..." } Supply the image at the size you intend to display it. A Mark is embedded at the pixel dimensions of the file you send, so marking a large image and then shrinking it on the page destroys the pattern. This site’s Mark was made at 560 px square and is shown at 560 px square.
Six lines. The partner id is public, so this is all client-side and there is nothing here worth stealing. The SDK draws the Mark, opens a session, and waits.
<div id="lp-slot"></div>
<script src="https://lnxy.me/static/linxypass.js"></script>
<script>
LinxyPass.renderMark('#lp-slot', {
partnerId: 'lp_fcb10c3a6a4eb901',
template: 'centerpiece',
onSuccess: function (r) {
fetch('/auth', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ session_id: r.session_id })
}).then(function (res) { return res.json(); })
.then(function (d) { window.location = d.redirect; });
}
});
</script> Notice what the callback does not do: it does not sign anyone in. It hands a session id to your own server and waits to be told what happened.
A session id arriving from a browser proves nothing — anyone can post one. So your server asks Linxy about it over a separate channel authenticated with your api key, and believes only the answer that comes back there.
$ch = curl_init('https://lnxy.me/api/fusion/login/status?session_id=' . urlencode($sessionId));
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$d = json_decode(curl_exec($ch), true);
if (($d['status'] ?? '') !== 'confirmed') return null;
if (empty($d['scoped_fusion_id'])) return null;
// $d['scoped_fusion_id'] is your user. Map it to your own accounts table. That identifier is scoped to your site. The same person signing in somewhere else gets a different one, so no two sites using LinxyPass can compare notes about who visits them.
Once someone has an account, they can open your site from the Linxy app or from an icon on their home screen and arrive already identified. Your site sees ?lp_at= on the query string and exchanges it, server to server, exactly once.
$ch = curl_init('https://lnxy.me/api/fusion/login/appconsume');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $this->apiKey,
'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode(['token' => $token]),
]);
// Good for sixty seconds. Burned by this call whether or not you use it. There is one rule here that is not optional. Do not set a session the moment a token arrives. The fingerprint proved who minted the token; it says nothing about whose browser the link opened in. An attacker can mint a perfectly valid token for their own account and get someone else to open it — and a silent session would drop that person inside the attacker’s account, where everything they type belongs to the attacker. Ask for one gesture instead.
After that gesture, store an opaque proof in a cookie: an HMAC of the identifier under a secret only your site holds. Never the identifier itself. On the next arrival the cookie matches and the gate is skipped.
private function token($fusionId) {
return hash_hmac('sha256', 'lp_ob|' . $fusionId, $this->secret);
}
public function browserRemembers($fusionId) {
$have = $_COOKIE['lp_ob'] ?? '';
return strlen($have) === 64 && hash_equals($this->token($fusionId), $have);
} Call rememberBrowser() after every successful sign-in, including the enrollment that created the account. That is what makes a member’s first One-Tap tap-free rather than only their second.
The work that disappears is larger than the work that appears.
No hashing choices, no rotation policy, no breach-notification exposure for credentials you never held.
No reset emails, no expiring links, no help-desk process that can be talked into handing over an account.
The factor is the device and the fingerprint. There is no seed to enrol, print, lose, or migrate to a new phone.
Fuller answers live in the Linxy help centre; each question links to its article.
A sign-in method that replaces the password with a device the person already carries. Your site shows a Linxy Mark; their phone reads it and approves with a fingerprint or a face; your server is told who it is. Nothing is typed and nothing is stored that could be stolen and reused elsewhere.
They open the Linxy App, point it at the Mark on your page, and approve. From then on they can also open your site straight from the app’s Connections list, or from an icon pinned to their home screen, and arrive already signed in.
Approval can happen on a watch as well as a phone, which matters for people whose hands are busy.
Yes. The sign-in request appears on the wrist and can be approved there. It is the fastest path when the phone is in a bag or a pocket, and it is the reason the demonstration sites are worth trying with a watch on.
The same call shown in step one above, or the WordPress plugin if you run WordPress. You supply a domain and an image; you get back a partner id, an api key, and a Mark of your own.
Every site has a console listing its members. You can revoke a member’s connection there, and optionally block re-enrollment. Revocation takes effect immediately across every path, including One-Tap and the home-screen icon.
One opaque identifier, scoped to your site. Not an email address, not a phone number, not a name, unless you ask for those yourself for your own reasons. The same person on a different site has a different identifier there, so sites cannot correlate visitors with each other.
Their account is recovered through Linxy rather than through your help desk, using their other devices or a Recovery Mark they set up in advance. You are not in the business of judging whether the person on the phone is who they claim to be.
The three demonstration sites are fictional, but the sign-in is completely real. Each one has its own partner record, its own api key and its own Linxy Mark, and the sign-in you just used ran through exactly the same endpoints a paying partner uses. Nothing here is simulated.
Everything you just used took one afternoon to add: a script tag, one server-side call, and a Mark of your own. No password database, no reset emails, no seed secrets to lose.