Reseller JWT Authentication
Scanifly can authenticate your users from a JSON Web Token that you sign. You embed Scanifly
in an iFrame, mint a token for the user who is already signed in to your own product, and send
it as a bearer token. Scanifly verifies the signature against your JWKS endpoint and signs the
user in — no separate Scanifly password, and no second login for your user.
This document covers the token format, the claims you can send, and how to have Scanifly create
users automatically on their first visit.
How it works
- You register a JWKS endpoint with Scanifly. It must serve an RS256 public key.
- Your server signs a short-lived JWT for the user, including a
kidheader matching a key in
your JWKS. - You send that token to Scanifly as
Authorization: Bearer <token>. - Scanifly fetches your JWKS, verifies the signature, identifies the user from the claims, and
authorizes the request.
Tokens are verified on every request, not once per session. Expiry is enforced — an expired
token is rejected, so keep your token lifetime short and re-mint as needed.
Token requirements
| Requirement | Value |
|---|---|
| Algorithm | RS256. Nothing else is accepted. |
Header kid | Must match a key ID in your JWKS |
exp | Required and enforced |
Claims
| Claim | Type | Required | Meaning |
|---|---|---|---|
iss_scanifly_company_id | string | yes | Your company ID in Scanifly. Tells us whose JWKS endpoint to verify against. |
sub | string | see below | The user's email address. |
sub_scanifly_user_id | string | see below | The user's Scanifly user ID, if you have stored it. |
sub_scanifly_company_id | string | for user creation | The Scanifly company the user belongs to. See Automatic user creation. |
firstName | string | no | Used only when creating a new user. |
lastName | string | no | Used only when creating a new user. |
phone | string | no | Used only when creating a new user. |
position | string | no | Used only when creating a new user. |
iss, aud, iat | — | standard | Standard JWT claims. |
iss_scanifly_company_id vs sub_scanifly_company_id
iss_scanifly_company_id vs sub_scanifly_company_idThese are two different companies and it is easy to confuse them.
iss_scanifly_company_idis your company — the reseller issuing the token. It is how we
find your JWKS endpoint. Always required.sub_scanifly_company_idis the company the end user belongs to — one of the customer
companies that has authorized you. Required only when you want the user created automatically.
Identifying the user
Scanifly resolves the user in this order:
- By
sub_scanifly_user_id, if present. - By
sub(email), if the first did not match.
You may send either or both. Sending the email alone is the simplest option and is usually what
you want.
Important:
sub_scanifly_user_idmust be a Scanifly user ID, or omitted entirely.It is a 24-character hexadecimal Scanifly identifier. If you send anything else — your own
internal user ID, a UUID, a username, an empty string — the request is rejected with a 401,
and the email fallback does not run. The token will fail even when thesubemail would
have matched a real user perfectly well.This is the single most common integration mistake. If logins are failing with a 401 and you
are sendingsub_scanifly_user_id, remove it and send onlysubfirst to confirm.
Automatic user creation
By default, a token for someone who does not exist in Scanifly is rejected. Every user has to be
provisioned first, through the reseller user API.
Scanifly can instead create the user on their first visit. This is off by default and is
enabled per customer company — the company's admin grants it to you, and it stays off until they
do. Ask your Scanifly contact to arrange it for the companies you need.
What has to be true
All of the following, or the request is rejected exactly as it is today and nothing is created:
- Neither
sub_scanifly_user_idnorsubmatched an existing user. subis present and is a valid email address.sub_scanifly_company_idis present.- Your reseller account is active.
- That company has authorized you.
- That company has granted you automatic user creation.
What gets created
| Field | Value |
|---|---|
Your sub claim, lowercased | |
| Company | Your sub_scanifly_company_id claim |
| First / last name | Your claims, or not provided if omitted |
| Phone | Your claim, or 1234567890 if omitted |
| Position | Your claim, or designer if omitted or unrecognized |
| Role | Standard user. Never an administrator, whatever the token says. |
| Permissions | Simple Design (technical) |
The user is created as an active member of that company and the original request proceeds
normally, so your user reaches the page they asked for on their very first click.
position, if you send it, must be one of: designer, surveyor, financeProcurement,
salesMarketing, operationsProjectManager, installer, executiveOwnerCEO,
thirdPartyEngineer, dronePilot, maintenanceTech, guest. An unrecognized value is not an
error — we fall back to designer and the login still succeeds, so a typo never locks anyone out.
Note that position is recorded on the user but does not grant permissions. Every
automatically created user gets the same permission set regardless of the position you send.
What it does not do
- It never creates companies.
sub_scanifly_company_idmust already exist and must already
have authorized you. - It never makes administrators. Nothing in a token can grant an admin role.
- It never updates an existing user. The identity claims are used on creation only. If you
send a newphoneorlastNamefor someone who already exists, it is ignored — update them
through the user API instead. - It sends no email. No welcome message, no password setup. Your users reach Scanifly only
through your product. - It does not create a second account for a known email. If the email already belongs to
someone, we sign in that existing user — or reject the request if their company has not
authorized you.
Concurrent first requests
A page load usually fires several requests at once, all carrying the same token. When that page
is a brand-new user's first visit, every one of those requests arrives before the user exists.
You do not need to do anything about this. Exactly one user is created and all of the requests
succeed. You will not see some of the first page's requests fail with a 401 while others work.
Mobile handoff
To hand a user to the Scanifly mobile app, exchange your token for a native Scanifly session:
POST /auth/reseller/exchange
{ "token": "<your RS256 JWT>" }
The response contains an access/refresh pair the app can hold and refresh on its own, which
matters because the app cannot refresh a token you signed.
A token used here must be short-lived: no more than 120 seconds between iat and exp
(with 30 seconds of clock-skew tolerance), and its iat cannot be in the future. Mint a
dedicated short token for the handoff rather than reusing a long-lived iFrame token. The token
is single-use — a second exchange with the same token is rejected.
Automatic user creation works here too, under the same conditions.
Troubleshooting a 401
Work down this list in order:
- Are you sending
sub_scanifly_user_idwith a non-Scanifly ID? This rejects the request
before the email is ever tried. Remove the claim and retry withsubalone. - Is the signature verifiable? Confirm
algisRS256, thekidmatches a key currently
served by your JWKS endpoint, and the endpoint is reachable from the public internet. - Has the token expired? Expiry is enforced on every request.
- Is
iss_scanifly_company_idyour company? It is the issuer's company, not the end user's. - Does the user already exist, and has their company authorized you? An existing user in a
company that has not authorized you is rejected, and no account is created for them. - If you expect automatic creation: confirm
sub_scanifly_company_idis present and is a
company that has both authorized you and granted automatic user creation. Missing either is
a 401 with nothing created.
If a user is an administrator of their company, they are signed in with standard-user privileges
unless that company has separately granted you permission to authenticate their admins.

