Everstrat Docs home
DevelopersApp

Referral registration and access

Personal invite codes, wallet-signature registration, durable attribution, private referral reads, and the boundary with on-chain access.

Referrals are an application registration feature backed by PostgreSQL. The app records who invited a wallet independently of the wallet's browser session and on-chain mint history. For user instructions and the registration diagram, see Referrals.

Reward accounting, reward balances, commission rates, and payouts are not implemented.

Codes and ownership

Every registered wallet has one permanent five-character personal code. New codes use uppercase letters and digits, omitting 0, 1, I, and O. Input is trimmed and uppercased. Codes are generated in PostgreSQL from cryptographic UUID bytes and reserved atomically.

Personal and administrative codes share a reservation namespace, so a code cannot identify both kinds of invitation. Reservations survive deletion and prevent later reassignment. Personal code allocation retries collisions, and a database trigger rejects changes to an allocated five-character code. These public codes are not authentication credentials.

Both kinds use /invite and /invite/<CODE>, with server-side dispatch by code kind. A personal code is reusable across new wallets. An administrative invite can be claimed by one wallet.

Registration sequence

  1. Issue a challenge. POST /api/invites/challenge accepts { code, address }. It checks request origin, applies IP/wallet rate limits, normalizes the code, and resolves its kind for the configured chain and Whitelist deployment. Personal registration requires a different, unregistered wallet.
  2. Sign for the referral purpose. The server issues a five-minute Sign-In with Ethereum (SIWE) challenge stating Join Everstrat through a referral. Its resource is urn:everstrat:referral:<codeHash>. The signature binds the wallet, chain, origin, invitation hash, nonce, expiry, and purpose. Invite redemption and access restoration use separate purposes, so their signatures cannot be substituted.
  3. Verify and register atomically. POST /api/invites/verify accepts { code, message, signature } and determines the kind again on the server. After signature verification, a transaction locks registration for the wallet/deployment, consumes the matching unexpired nonce, inserts the member with its referrer and personal code, and creates a claimed invite_kind = 'referral' access record. Failure does not leave a partial registration.
  4. Establish access. The access record has an independent random on-chain inviteId and a hashed private session token. The response sets the access cookie and returns { ok: true, redirectTo: "/portfolio" }. Administrative redemption returns /mint.

The nonce is single-use and stored as a hash, bound to the parent and registering wallet. Both initial administrative claims and personal registration take the same transaction-scoped advisory lock. The first successful registration fixes attribution; later claims or sign-ins do not allocate another member or change its parent.

See the pinned registration repository and SIWE implementation.

Durable data and invariants

RecordResponsibility
referral_membersWallet, chain, Whitelist deployment, permanent code, optional referrer_id, and registration time
referral_noncesHashed single-use challenge, referrer, wallet, and expiry
invite_code_reservationsShared unique code hashes for administrative and personal invitations
invite_codesIndividual access claim, random inviteId, status, and session-token hash; referral claims have no email

(wallet, chain_id, whitelist_address) is unique. A composite foreign key requires the parent to belong to the same deployment, and referrer_id < id requires an earlier member. Together these constraints prevent self-referrals and cycles. There is no API for reparenting a member.

Initial administrative claims create members without a parent. Revoking or deleting an access invite does not remove the member, code, or attribution. Retained attribution does not itself grant a valid access session.

Private referral API

GET /api/referrals?wallet=<address>&cursor=<id> requires an app access session. The requested wallet must match the session wallet. The server uses its configured chain and Whitelist address; the caller cannot select another deployment through query parameters.

Response fieldMeaning
walletChecksummed owner address
referralCodeOwner's five-character personal code
totalCountTotal direct referrals, independent of the current page
referralsUp to ten entries containing string id, checksummed wallet, and UTC ISO registeredAt
nextCursorLast returned ID when more entries exist; otherwise null

The first request omits cursor. Pass nextCursor back unchanged to get the next page. IDs are strings; do not convert them to JavaScript numbers. Pagination uses id < cursor, ordered by descending ID, and accepts positive PostgreSQL bigint cursors. The owner, count, and page are read in one database statement. Only immediate children are returned, with no recursive downline, email, or financial data.

StatusMeaning
401Missing or invalid access session
403Missing, invalid, or mismatched wallet parameter
400Invalid cursor
503Session is valid but no member exists for this deployment
500Referral data could not be loaded

Responses use Cache-Control: private, no-store. The client scopes queries by wallet and cursor, remounts on wallet changes, aborts pending sign-ins on unmount, and hides previous data after authorization or fetch failures. Refresh resets pagination to the first page. Errors must not be rendered as zero referrals.

Browser session and on-chain access

The access cookie is HttpOnly, SameSite=Strict, and Secure in production, with a 14-day lifetime. Its session must also be valid in the database. Registered wallets restore access via /api/invites/access/challenge and /api/invites/access/verify, using the access SIWE purpose.

Referral registration does not transact with Whitelist or AMM. The public personal code is never used as an on-chain invite identifier. A first mint uses the POST /api/whitelist/voucher route and the individual access record's inviteId; the signer must be configured and authorized on-chain. Registration alone therefore does not establish that minting is available. See Mint UI and AMM entry points.

Last updated on

On this page