How to track leads and sales from Your own app
Measure signups from your own SaaS or site, attributed to the link that brought them.
AT A GLANCE
| What NEMO tracks | Leads · Sales |
|---|---|
| Connection | Native — activated from the NEMO app |
| Method | “The hidden form field” · Server-side · highest reliability |
| Steps | 3 in total, with a one-minute check at the end |
| Worth knowing | THE nm_id IS OPTIONAL: if your backend doesn't have it at hand, drop it from the body. The signup still lands and shows up on Home, just with «no source» — it is never an error. To actually have it, store the value of window.nemo.id() with the signup (a hidden form field, or one more line in your fetch body). |
With Your own app, NEMO tracks which link brings each lead and which link brings each sale with no pixels and no Google Analytics. Know which of your links brought each person who signs up or buys in your own application, without relying on Kit or a payment gateway. Two steps. You paste the script on your site and, once the signup is confirmed, your server tells NEMO. That's it: NEMO hashes the email with your workspace key, and there is no secret for you to compute. The method is “The hidden form field” (server-side · highest reliability): Your form carries an invisible field (nemo_click_id) that travels with every signup to your email platform. NEMO receives it server to server: the lead arrives already attributed to its source link. THE nm_id IS OPTIONAL: if your backend doesn't have it at hand, drop it from the body. The signup still lands and shows up on Home, just with «no source» — it is never an error. To actually have it, store the value of window.nemo.id() with the signup (a hidden form field, or one more line in your fetch body). The full connection is 3 steps and you verify it with a single test click.
How does each Your own app lead arrive with its source?
The hidden form field · Server-side · highest reliability
Two steps. You paste the script on your site and, once the signup is confirmed, your server tells NEMO. That's it: NEMO hashes the email with your workspace key, and there is no secret for you to compute.
- Paste the NEMO script on your site — on the landing and in the app, once per page:
<script src="https://nemolink.app/nemo.js" defer></script> - When your server confirms the signup, tell NEMO. Your ingest URL carries the token inside (you'll find it on the «Your own app» card in Integrations) — treat it as a secret:
await fetch("https://nemolink.app/api/ingest/YOUR_TOKEN", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ event: "lead", email: usuario.email, nm_id: alta.nm_id }), });
Note: THE nm_id IS OPTIONAL: if your backend doesn't have it at hand, drop it from the body. The signup still lands and shows up on Home, just with «no source» — it is never an error. To actually have it, store the value of window.nemo.id() with the signup (a hidden form field, or one more line in your fetch body).
Note: IF YOUR APP LIVES ON ANOTHER DOMAIN than your landing (the landing on criptoterminal.com and the app on app.other.com), declare the hop once on your site: window.nemo.hosts(['app.other.com']). Between subdomains of the same domain you don't need to.
Note: THE IDENTITY IS THE EMAIL, in clear. Since the call is authenticated by your token, NEMO hashes it with YOUR workspace key (the same identity Stripe or Kit webhooks already use) and never stores it in clear. You compute nothing.
Note: ADVANCED — if you'd rather NOT send emails: send an OPAQUE `external_id` instead (HMAC of your internal id with a secret of yours, in hex; never the email, a numeric id, or a uuid from the payload). It's optional: the email is the normal path.
Note: WHAT TO STORE in your database: the nm_id NEXT TO the user's record (a column beside the signup). The UTMs are YOURS — NEMO doesn't need them; keep them for your own analytics if you want.
Note: HOW LONG IT LASTS: the source anchor lives 90 days in the browser (what /legal/cookies declares). On the free plan you see 30 days of history in the panel; the data is still stored — it's the visible window that shrinks.
Note: THE own-hosts LIMIT uses a TRIMMED public-suffix list (the real-world families: .co.uk, .com.br, .com.mx, .com.ar…), not the full list. It covers virtually every domain; if yours is a rare ccTLD that doesn't hop on its own, declare it with window.nemo.hosts(['your.domain']).
Note: RATHER HAVE YOUR CODING ASSISTANT wire it? The «Your own app» card in Integrations carries a message ready to paste into Claude Code or Cursor, with your real URL already inside.
Note: PRIVACY — YOURS, AND HERE IT'S TWO PROCESSINGS: you're responsible for the one on your WEBSITE (where nemo.js runs) and the one in your BACKEND (where you store the nm_id beside the user). The nm_id in your database is PSEUDONYMOUS PERSONAL DATA as long as you can cross it with your users table: name it in your privacy policy and your record of processing activities, give it a lawful basis and a retention period, and treat it with the same care as the rest of the user's record. NEMO receives the email from you to hash it with your workspace key — it is never persisted in clear — plus the click's source.
How is each Your own app sale attributed?
The hidden form field · Server-side · highest reliability
The same call, with event: "sale" and the amount in cents. It's an AUTHENTICATED source (your token), so the sale counts for real — it moves money in your panel.
- When your backend confirms a charge, tell NEMO just like the signup but as a sale:
await fetch("https://nemolink.app/api/ingest/YOUR_TOKEN", { method: "POST", headers: { "content-type": "application/json" }, // the SAME email as the signup, and the amount in cents body: JSON.stringify({ event: "sale", email: user.email, amount_cents: 4900, nm_id: signup.nm_id }), });
Note: The same email (or the same external_id, in the advanced case) for the signup and the purchase: that's how NEMO ties a person's lead and sale together. The email is hashed; never stored in clear.
Check it in one minute
- On the «Your own app» card in Integrations, hit «Send test»: it runs the real circuit and tells you what was accepted.
- With an nm_id that resolves, the response carries "attributed": true; if it carries "attributed": false, the nm_id is what fails (expired or not captured), not your call.
- A test signup from your backend returns {"ok":true} and shows up on Home, attributed to its link. Send another one WITHOUT nm_id: it also returns {"ok":true} and lands with «no source».
Frequently asked questions
- What do I do if the signup lands but arrives with no source (attributed:false)?
- The nm_id didn't arrive or doesn't resolve. Check the frontend captures it (window.nemo.id()) and passes it to your backend, and that the link was a NEMO one. The call is fine; it's the source that's missing.
- What do I do if the response asks you to send the person's email?
- The `email` field is missing (or arrived empty or malformed). It's the only identity field you need: NEMO hashes it with your workspace key and never stores it in clear. If you're on the advanced path, `external_id` has to be genuinely opaque — the response tells you how.
- What do I do if the response says that ingest URL does not exist?
- Your URL's token isn't valid (or you rotated it). Copy the current URL from the «Your own app» card.
- What do I do if the nm_id doesn't hop from my landing to my app on another domain?
- nemo.js only decorates hosts on your SAME registrable domain. If the app lives on another domain, declare it: window.nemo.hosts(['app.other.com']).
OFFICIAL SOURCES
Guide verified against the official documentation on 2026-09-11.
ALSO INTEGRATES WITH
Track Your own app from your very first link.
Free during early access. Early users get special terms when paid plans launch.