Resellers

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

  1. You register a JWKS endpoint with Scanifly. It must serve an RS256 public key.
  2. Your server signs a short-lived JWT for the user, including a kid header matching a key in
    your JWKS.
  3. You send that token to Scanifly as Authorization: Bearer <token>.
  4. 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

RequirementValue
AlgorithmRS256. Nothing else is accepted.
Header kidMust match a key ID in your JWKS
expRequired and enforced

Claims

ClaimTypeRequiredMeaning
iss_scanifly_company_idstringyesYour company ID in Scanifly. Tells us whose JWKS endpoint to verify against.
substringsee belowThe user's email address.
sub_scanifly_user_idstringsee belowThe user's Scanifly user ID, if you have stored it.
sub_scanifly_company_idstringfor user creationThe Scanifly company the user belongs to. See Automatic user creation.
firstNamestringnoUsed only when creating a new user.
lastNamestringnoUsed only when creating a new user.
phonestringnoUsed only when creating a new user.
positionstringnoUsed only when creating a new user.
iss, aud, iat—standardStandard JWT claims.

iss_scanifly_company_id vs sub_scanifly_company_id

These are two different companies and it is easy to confuse them.

  • iss_scanifly_company_id is your company — the reseller issuing the token. It is how we
    find your JWKS endpoint. Always required.
  • sub_scanifly_company_id is 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:

  1. By sub_scanifly_user_id, if present.
  2. 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_id must 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 the sub email 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 sending sub_scanifly_user_id, remove it and send only sub first 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:

  1. Neither sub_scanifly_user_id nor sub matched an existing user.
  2. sub is present and is a valid email address.
  3. sub_scanifly_company_id is present.
  4. Your reseller account is active.
  5. That company has authorized you.
  6. That company has granted you automatic user creation.

What gets created

FieldValue
EmailYour sub claim, lowercased
CompanyYour sub_scanifly_company_id claim
First / last nameYour claims, or not provided if omitted
PhoneYour claim, or 1234567890 if omitted
PositionYour claim, or designer if omitted or unrecognized
RoleStandard user. Never an administrator, whatever the token says.
PermissionsSimple 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_id must 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 new phone or lastName for 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:

  1. Are you sending sub_scanifly_user_id with a non-Scanifly ID? This rejects the request
    before the email is ever tried. Remove the claim and retry with sub alone.
  2. Is the signature verifiable? Confirm alg is RS256, the kid matches a key currently
    served by your JWKS endpoint, and the endpoint is reachable from the public internet.
  3. Has the token expired? Expiry is enforced on every request.
  4. Is iss_scanifly_company_id your company? It is the issuer's company, not the end user's.
  5. 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.
  6. If you expect automatic creation: confirm sub_scanifly_company_id is 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.