powered by

Sign in users: email, tokens and API keys

Register and sign in users with email and password, manage sessions with access and refresh tokens, reset passwords, and know when to use an API key.

Last updated

Every request your app makes to the Anythink REST API is made as someone: a signed-in user or an API key. This page covers the user side. A user signs in with an email address and password, gets an access token and a refresh token, and sends the access token with each request. Anythink identifies the user from it and applies their role's permissions and any row-level security, on every call.

Authentication answers who is calling. What they may do is decided by roles and permissions, and which records they may see by groups and row-level security.

Task Endpoint
Register POST /org/{project_id}/auth/v1/register
Confirm an email address POST /org/{project_id}/auth/v1/confirm-email
Sign in POST /org/{project_id}/auth/v1/token
Refresh a session POST /org/{project_id}/auth/v1/refresh
Sign out POST /org/{project_id}/auth/v1/logout
Get the signed-in user GET /org/{project_id}/users/me
Reset a forgotten password POST /org/{project_id}/auth/v1/send-password-reset then POST /org/{project_id}/auth/v1/confirm-password-reset
Change your password PUT /org/{project_id}/users/me/password
Accept an invitation POST /org/{project_id}/users/invites/{invite_token}

Your project ID is the number after /org/ in the dashboard address. The examples use https://api.my.anythink.cloud as the API URL. The register, sign-in, refresh, sign-out, confirmation, reset and invitation endpoints need no token, because the caller isn't signed in yet.

Other ways in:

  • Users who already have an Apple, Google, GitHub or LinkedIn account can use social sign-in. It returns the same tokens as this page.
  • React apps can use the Anythink SDK, which wraps every call below and refreshes the session for you.

Let users register

Self-registration is controlled by three project settings. They apply to the register endpoint, and to nothing else: you can always create users yourself, whatever these say.

Setting What it does
Allow registrations Turns the register endpoint on. Off, it returns 400 Organisation does not allow registrations.
Require email confirmation for new users On, a new user gets a confirmation email and can't sign in until they confirm. Off, they can sign in straight away.
Default role for new users The role every self-registered user gets. Required when registrations are on.

Pick a default role that holds only what a new user should be able to do. Everything a user can read or write comes from their role. See Roles and permissions.

In the Anythink dashboard

  1. Open Settings › Users.
  2. In the User access card, tick Allow registrations.
  3. Choose a Default role for new users. Administrator roles aren't offered here.
  4. Tick Require email confirmation for new users if users must confirm their address before they sign in.
  5. Click Save.

With the CLI

Note: These project settings are managed in the dashboard. Use the steps above.

With an AI assistant (MCP)

Note: These project settings are managed in the dashboard. Your assistant can walk you through the steps above.

Register with email and password

A registration creates the user in your project with the default role. The response is plain text, not tokens: the user signs in as a separate step.

In the Anythink dashboard

To add a user yourself rather than let them register:

  1. Open Settings › Users and click Create Project User.
  2. Enter the First Name, Last Name and Email, and choose a Role.
  3. Click Create Project User. Anythink emails the user an invitation to set their own password. See Invite people and team members.

With the CLI

The CLI has no command for end-user registration. Call the endpoint with anythink fetch, which adds the active project's path for you:

bash
anythink fetch /auth/v1/register --method POST --body '{
  "first_name": "Alice",
  "last_name": "Smith",
  "email": "alice@example.com",
  "password": "Str0ng!passphrase"
}'

With an AI assistant (MCP)

text
Register alice@example.com as a user in this project, first name Alice, last name Smith.

The assistant uses the cli tool to fetch /auth/v1/register. To create a user without choosing a password for them, ask it to invite them instead: it creates the user with fetch /users, which sends the invitation. Connect an AI assistant sets up the MCP server.

The raw request:

http
POST /org/{project_id}/auth/v1/register
Content-Type: application/json

{
  "first_name": "Alice",
  "last_name": "Smith",
  "email": "alice@example.com",
  "password": "Str0ng!passphrase"
}
text
Registration successful

Optionally add referral_code, or a signup_context object with utm_source, utm_medium, utm_campaign, utm_term, utm_content, referrer and signup_country. Anythink records the context against the new user.

The rules:

Field Rule
first_name, last_name Required, up to 50 characters.
email Required, a valid address, up to 255 characters.
password At least 8 characters, with an uppercase letter, a lowercase letter, a digit and a special character.

A request that breaks a rule returns 400 with an errors object listing each failed field, for example:

json
{
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "Password": [
      "Password must be at least 8 characters long",
      "Password must contain at least one uppercase letter"
    ]
  }
}
Status Body Cause
200 Registration successful The user was created.
400 Organisation does not allow registrations Allow registrations is off.
400 Organisation does not have a default role for new users No default role is set, or the role no longer exists.
400 validation errors A field broke a rule above.

Confirm an email address

With Require email confirmation for new users on, the new user is sent an email containing a confirmation token. The token lasts 24 hours. Until the user confirms, sign-in returns 401 User email address is not confirmed.

Your app passes the email address and token from the email to Anythink:

http
POST /org/{project_id}/auth/v1/confirm-email
Content-Type: application/json

{
  "email": "alice@example.com",
  "token": "7F3A9C1E5B2D4A6890E1C3B5D7F92A4C"
}
text
Confirmation successful

An unknown or expired token returns 400 Invalid confirmation token.

The email comes from your project's confirmation template. You can restyle it in Email templates.

To send a fresh confirmation email, call the resend endpoint. It always answers 204 No Content, whether or not the address exists, and sends at most one email a minute per user:

http
POST /org/{project_id}/auth/v1/resend-confirm-email?email=alice@example.com

In the Anythink dashboard

An administrator can fix a stuck account without a token. Open the user under Settings › Users, then click Resend Confirmation Email or Mark as Confirmed.

With the CLI

bash
anythink fetch '/auth/v1/resend-confirm-email?email=alice@example.com' --method POST

To confirm a user without an email, an administrator can call anythink fetch /users/42/mark-as-confirmed --method POST, using the user's ID from anythink users list.

With an AI assistant (MCP)

text
Resend the confirmation email to alice@example.com, or mark user 42 as confirmed.

Sign in

Signing in exchanges an email and password for a session.

In the Anythink dashboard

Your own app provides the sign-in screen, so there is nothing to configure per sign-in. To check that an account can sign in, open Settings › Users. A user who has confirmed their address and has an active account shows Active. A user who hasn't accepted an invitation shows Invite Sent.

With the CLI

bash
anythink fetch /auth/v1/token --method POST --body '{
  "email": "alice@example.com",
  "password": "Str0ng!passphrase"
}'

The CLI itself signs in as you, not as an end user. Use anythink users me to see which account the CLI is using. See Get started with the CLI.

With an AI assistant (MCP)

text
Sign in alice@example.com with her password and tell me when the access token expires.

The assistant uses the cli tool to fetch /auth/v1/token. Anything you paste into a chat passes through the assistant, so use a test account, not a real user's password.

The raw request:

http
POST /org/{project_id}/auth/v1/token
Content-Type: application/json

{
  "email": "alice@example.com",
  "password": "Str0ng!passphrase"
}
json
{
  "access_token": "eyJhbGciOiJIUzI1NiIs…",
  "refresh_token": "OW+kSomn10…",
  "expires_in": 1800
}
Status Body Cause
200 access_token, refresh_token, expires_in Signed in.
401 Invalid email or password No user with that email, or the password is wrong.
401 User email address is not confirmed The user hasn't confirmed their address yet.
401 User is not active The account is inactive.

Sessions: access and refresh tokens

A sign-in returns a pair of tokens.

Token Lifetime Used for
Access token 30 minutes (expires_in is 1800 seconds) Sent as Authorization: Bearer … on every API call.
Refresh token 30 days Exchanged for a new pair when the access token expires.

The access token is a signed JWT. Treat it as opaque in your app: read the user from GET /users/me, not from the token's claims.

Refresh tokens are single use. Each refresh returns a new access token and a new refresh token, and revokes the one you sent. Always store the newest pair. A refresh token is tied to the project that issued it. Keep it somewhere only your app can read, such as an HTTP-only cookie or the device's secure storage, because anyone holding it can open a session until it expires.

A request with a missing, malformed or expired access token returns 401 Unauthorized. That is your cue to refresh, replay the request once with the new token, and send the user back to sign in if the refresh also fails.

Call the API as the user

Send the access token as a bearer token:

http
GET /org/{project_id}/orders
Authorization: Bearer eyJhbGciOiJIUzI1NiIs…

What comes back depends on the user's role and any row-level security on the entity. See the REST API overview for the endpoints themselves.

Get the signed-in user

In the Anythink dashboard

Open Settings › Users to see each user's role, status and last login. Open a user to see their confirmation and invitation details.

With the CLI

bash
anythink users me

This shows the account the CLI is using. To see another user, run anythink users get 42.

With an AI assistant (MCP)

text
Show me the current user, and the user with ID 42.

The raw request:

http
GET /org/{project_id}/users/me
Authorization: Bearer eyJhbGciOiJIUzI1NiIs…
json
{
  "id": 42,
  "gid": "368f7b33-940e-4aad-a3d8-5a28215935bd",
  "first_name": "Alice",
  "last_name": "Smith",
  "email": "alice@example.com",
  "role_id": 104,
  "role_name": "Standard User",
  "is_active": true,
  "confirmed_at": "2026-10-04T13:31:50.114391Z",
  "last_login_at": "2026-10-04T13:31:50.307407Z",
  "auth_provider": null,
  "permissions": [
    { "name": "orders:read", "description": "…", "is_active": true }
  ]
}

The response carries more fields than shown. permissions lists everything the user's role allows, so your app can show or hide controls. These related endpoints take the same bearer token:

Endpoint Returns
GET /users/me/permissions Just the permissions list.
GET /users/me/has-permission?permissionName=orders:read true or false.
GET /users/me/has-role?roleName=Standard User true or false.
GET /users/me/is-admin true or false.
PUT /users/me Updates first_name and last_name. Returns 204.

Without a valid token, GET /users/me returns 401.

Refresh a session

With the CLI

bash
anythink fetch /auth/v1/refresh --method POST --body '{ "token": "OW+kSomn10…" }'

With an AI assistant (MCP)

text
Refresh this session with the refresh token in my REFRESH_TOKEN variable.

In the Anythink dashboard

The dashboard refreshes its own session automatically. There is nothing to do.

The raw request:

http
POST /org/{project_id}/auth/v1/refresh
Content-Type: application/json

{
  "token": "OW+kSomn10…"
}
json
{
  "access_token": "eyJhbGciOiJIUzI1NiIs…",
  "refresh_token": "d4Qm1xZ8…",
  "expires_in": 1800
}

The body field is token, and it holds the refresh token. An unknown, revoked or expired refresh token, or one from a different project, returns 401 Invalid refresh token. Sending the same refresh token a second time also returns it, because the first use revoked it.

Sign out

Signing out revokes the refresh token so it can't open another session. Your app should also discard both tokens on the device.

With the CLI

bash
anythink fetch /auth/v1/logout --method POST --body '{ "token": "d4Qm1xZ8…" }'

anythink logout is different: it removes the CLI's own saved profile, and doesn't sign an end user out.

With an AI assistant (MCP)

text
Sign out the session with the refresh token in my REFRESH_TOKEN variable.

In the Anythink dashboard

Choose Sign Out in the user menu to end your own dashboard session.

The raw request:

http
POST /org/{project_id}/auth/v1/logout
Content-Type: application/json

{
  "token": "d4Qm1xZ8…"
}

It returns 200. Refreshing with that token afterwards returns 401 Invalid refresh token.

Reset and change passwords

Reset a forgotten password

A reset has two steps. Your app asks for a reset email, then submits the code from that email with the new password.

In the Anythink dashboard

Your app provides the forgot-password screen, so use the endpoints below.

With the CLI

bash
anythink fetch '/auth/v1/send-password-reset?email=alice@example.com' --method POST

anythink fetch /auth/v1/confirm-password-reset --method POST --body '{
  "email": "alice@example.com",
  "token": "482913",
  "password": "An0ther!passphrase"
}'

With an AI assistant (MCP)

text
Send a password reset email to alice@example.com.

The assistant uses the cli tool to fetch /auth/v1/send-password-reset. Finish the reset in your app, so the new password never passes through the chat.

The raw requests:

http
POST /org/{project_id}/auth/v1/send-password-reset?email=alice@example.com

This always returns 204 No Content, whether or not the address belongs to a user, so it can't be used to find out who has an account. It sends at most one email a minute per user.

http
POST /org/{project_id}/auth/v1/confirm-password-reset
Content-Type: application/json

{
  "email": "alice@example.com",
  "token": "482913",
  "password": "An0ther!passphrase"
}
Status Body Cause
204 none The password was changed. The user signs in with it.
400 Invalid password reset token The code is wrong, or has been used.
400 Password reset token has expired The code is older than an hour. Request another.
400 validation errors The new password breaks the password rules.

The reset code is single use. Requesting a new reset replaces any earlier code. The email comes from your project's password reset template (Email templates).

Change your own password

A signed-in user can change their password with their access token.

In the Anythink dashboard

Your app provides the change-password screen, so use the endpoint below.

With the CLI

bash
anythink fetch /users/me/password --method PUT --body '{
  "current_password": "Str0ng!passphrase",
  "new_password": "An0ther!passphrase"
}'

This changes the password of the account the CLI is signed in as.

With an AI assistant (MCP)

Change passwords in the dashboard or in your app, not in a chat.

The raw request:

http
PUT /org/{project_id}/users/me/password
Authorization: Bearer eyJhbGciOiJIUzI1NiIs…
Content-Type: application/json

{
  "current_password": "Str0ng!passphrase",
  "new_password": "An0ther!passphrase"
}

It returns 204 No Content. The next sign-in uses the new password. Choose a new password that meets the password rules.

Invite people and team members

Invite people when you want to choose who joins, rather than let anyone register. The invited user gets an email with a link to set their name and password. The invitation lasts 7 days.

In the Anythink dashboard

  1. Open Settings › Users and click Create Project User.
  2. Enter the name and Email, choose a Role, and click Create Project User. The invitation is sent.
  3. To follow up, open the user and use Resend Invitation Email. The users list shows Invite Sent until they accept.

With the CLI

bash
anythink fetch /users --method POST --body '{"first_name":"Alice","last_name":"Smith","email":"alice@example.com","role_id":104}'

Find role IDs with anythink roles list.

With an AI assistant (MCP)

text
Invite alice@example.com, Alice Smith, as a Standard User.

The assistant runs roles list to find the role, then creates the user with fetch /users.

When the user opens the link, your app (or the hosted page) completes the invitation. Both calls need no token:

http
GET /org/{project_id}/users/invites/{invite_token}

This returns the invited user's name, email and role, so you can pre-fill your form. An unknown or expired token returns 404.

http
POST /org/{project_id}/users/invites/{invite_token}
Content-Type: application/json

{
  "first_name": "Alice",
  "last_name": "Smith",
  "password": "Str0ng!passphrase"
}

It returns 204 No Content, and the user can then sign in with that email and password. A bad token returns 400 Invalid invite token. The password follows the password rules.

For teams, roles, project switching and access across several projects, see Manage app users and team members.

API keys or user sessions

Use a user session when a person is using your app, so each request carries that person's identity, role and row-level access. Use an API key when code you run yourself calls the API with nobody signed in: a nightly job, a CI step, a backend service. You send it in the x-api-key header instead of a bearer token:

http
GET /org/{project_id}/customers
x-api-key: ak_your_key_here
User session API key
Who it represents One signed-in person The user who created the key
Credential Authorization: Bearer <access token> x-api-key: ak_…
Lifetime 30 minutes, renewable for 30 days 30 to 365 days, chosen at creation
Where it belongs Browsers and mobile apps Servers, scripts and CI

Never put an API key in a browser or mobile app. Anyone who opens the app can read it.

Limits and trade-offs

Limit Detail
Access token 30 minutes.
Refresh token 30 days, single use.
Confirmation token 24 hours. Resend limited to one email a minute per user.
Password reset code 1 hour, single use. One reset email a minute per user.
Invitation 7 days.
Password rules 8 or more characters with an uppercase letter, a lowercase letter, a digit and a special character.
Names and email 50 characters for each name, 255 for the email.
Registration settings Managed in the dashboard.

Next steps