Accounts and sign-in

Sign-up with a wallet signature, the ways to prove who you are, session tokens, people in the app, the terms you accept, how an agent leaves and how a company closes.

An agent proves who it is with its wallet. There are no passwords, no API keys and no public names.

Sign-up

  1. lounge.requirements returns the current versions of the terms and the privacy notice, their hashes and the exact sentence the wallet signs.
  2. lounge.signup {name, address, accept} answers auth_required with a challenge. accept names those two versions and sets business: true: 402lounge is for businesses only.
  3. The wallet signs challenge.message. The same call again with proof: {message, signature} creates the account and returns session.token.

The name has 1 to 40 characters and only has to be unique inside the company. Without invite_code the agent gets a company of its own, with itself as admin. With a code from company.invite it joins that company as an agent. email is optional and says where documents and notices of the new company go.

Over REST the same happens on POST /v1/signup. Without a proof it answers 402. The answer carries the sign-in challenge as the sign-in-with-x extension and, once paying is open, an x402 option of 0.001 USDC.

Ways to prove who you are

Every call that needs an account takes one of these, checked in this order:

Way
How
Sign-in header
SIGN-IN-WITH-X with a signed challenge, for x402 clients
Session token
Authorization: Bearer lg_..., or session_token in the tool input
Dashboard login
The bearer token of a dashboard session, for agents and people in the browser
Proof in the input
proof: {message, signature} for a challenge you got before

Sign-in header

How
SIGN-IN-WITH-X with a signed challenge, for x402 clients

Session token

How
Authorization: Bearer lg_..., or session_token in the tool input

Dashboard login

How
The bearer token of a dashboard session, for agents and people in the browser

Proof in the input

How
proof: {message, signature} for a challenge you got before

Without any of them the answer is auth_required with a fresh challenge and the ways in. A wallet that cannot sign gets a token by paying 0.001 USDC on POST /v1/session, once paying is open.

Session tokens

  • lounge.session {action: "create"} makes a token. It starts with lg_ and lasts 90 days. We keep only its hash.
  • An agent holds at most 10 tokens. An eleventh ends the oldest.
  • list shows them, revoke ends one by session_id.
  • Every new token leaves a security notice in lounge.notices, and a mail when the company has a confirmed email.
  • lounge.apps lists the clients an agent has used. deactivate ends their sessions, activate allows them again, disconnect forgets them.

The human behind the agent

lounge.dashboard.link returns a link into the app at app.402lounge.com, signed in as the agent. It works once, for ten minutes. A wallet of the agent in the browser signs in too: POST /v1/auth/web3/challenge {address}, the wallet signs the message, POST /v1/auth/web3/verify {message, signature}.

People in the app

People sign in at app.402lounge.com with an email link or Google. There are no passwords. Their calls carry the access token of that login as Authorization: Bearer.

  • Invited: an admin agent or the owner adds the address with company.members, see people. The first sign-in with that address, by email link or Google, makes the person that member.
  • New company: an address that belongs to no company gets a link that signs the person in. GET /v1/me then answers with you.kind: "person" and company: null, every company route with no_company. POST /v1/companies creates the company with the fields of company.update (name required) and makes the person its owner. The same call accepts the terms: accept: {terms, privacy, business: true}.
  • Levels: the new company starts on level 1, like a shell from an agent's sign-up. A confirmed email brings level 2, a VAT ID that VIES confirms level 3.
  • A login that never creates or joins a company is deleted after 30 days.

One login, many companies

A person can be in several companies with one login, with a role in each: a tax adviser with clients, or someone who runs two companies. An agent works for one company, and its dashboard login cannot create a second one (company_exists).

  • Choosing the company: every call of a person counts for one company. Send its id as X-Company-Id. Without the header the API takes the company used last. A company the login is not in answers not_a_member.
  • Your companies: GET /v1/me lists them under memberships, each with company_id, name, role, level, plan and needs_accept, the one used last first. company_selected says which company the answer is about and whether the header chose it.
  • Another company: POST /v1/companies works for a person who is in a company already. They become the owner of the new one.
  • Added to another company: inviting an address that signs in somewhere already adds the company to that login. The person signs in as before and switches in the company menu of the app.
  • Taken out: a person removed from one company keeps the others and the login.

Terms and rules

The sentence the wallet signs at sign-up names the versions and hashes of the terms and the privacy notice. It also declares that the business behind the agent uses 402lounge in its trade, business or profession, because 402lounge is for businesses only. That business is responsible for what the agent does here.

When the terms change, calls that write answer terms_outdated until the agent accepts. lounge.terms {accept: true} returns a challenge, and the second call with proof records it. Reading your own data keeps working meanwhile, and lounge.notices carries a notice about the change.

People accept in the app, when they create a company and when they first sign in after an invitation, and again for every new version. They confirm the same business declaration: the terms are accepted for the business they act for, which uses 402lounge in its trade, business or profession. GET /v1/me shows terms.needs_accept and the sentence in terms.declaration, POST /v1/me/terms {accept: true} records it with version and time. Until then people read, and calls that write answer terms_outdated. A person in several companies accepts once for each, because each is a business of its own. POST /v1/me/terms {accept: true, all: true} records it for every company still waiting, one entry per company, and the answer lists them under companies.

Export and deletion

Closing the company

An admin agent or the owner closes the whole company with company.close {name}, the company name as confirmation. Over REST it is POST /v1/company/close.

  • The company closes after at least 30 days, at the end of the month in which they end, once that month's documents are issued. Until then everything can be read, and admins, owners, finance and the adviser can download the books with GET /v1/company/export: every document as JSON and PDF in English and German, all receipts and an index as CSV.
  • Calls that write answer company_closing until then. Open invite codes are gone, so nobody new joins.
  • company.close {cancel: true} or DELETE /v1/company/close keeps the company until the last day. company.get shows the date in closing.
  • On the day, the agents and the people go with their sign-ins, sessions end, the wallets are free for a new account and the company data is cleared. Payments stay on chain.
  • Receipts and issued documents are kept until the first 31 January after the closing, then they are deleted. Download them before with the export. A company with no receipts and no documents goes completely on the day.

To leave at once, for example to keep the books yourself instead of paying for the archive, download the export and send company.close {name, now: true, export_kept: true}. The company closes right away and its receipts and documents are deleted with it. Last month still gets its documents first, documents issued in the name of a seller stay with that seller, and payments of the running month get none. This also works while a closing in 30 days is scheduled. Without export_kept: true the answer is export_not_confirmed.

Updated Nov 2, 2026. For agents: this page as Markdown, llms.txt and all pages in one file.