login
Log a Wexa user in with the email and password they use for the console, and get an SDK session for one project. The session acts as that user, with that user's current role in the project, so every call made with it is checked and audited as them. Use it when you build your own login page on top of Wexa; use an API key when a service calls Wexa as itself.
The response carries an access token, which lasts one hour, and a refresh token, which lasts 30 days and changes every time you use it. Both SDKs keep the pair in memory, refresh the access token before it runs out, and hand you every new pair so you can save it. See refresh-session and logout.
What it is called on each surface
login runs before any tool call, so it is not on the Wexa MCP server: MCP clients sign in through OAuth instead. On the other surfaces it creates the client itself.
| Surface | Name |
|---|---|
| REST API | POST /v1/auth/login |
| TypeScript SDK | Fabric.login({ workspace, email, password, projectId }) |
| Python SDK | Fabric.login(workspace, email, password, project_id) |
Arguments
| Argument | Type | Required | Notes |
|---|---|---|---|
email | string | Required | The user's Wexa email address. |
password | string | Required | The user's Wexa password. |
project_id | string | Required | The project the session is locked to. projectId in the TypeScript SDK. The user must be a member of it. |
grants | string[] | Optional | Narrows the session to fewer grants than the user's role gives, for example ["fabric:query.read"]. Every grant named must be in the user's role. |
The SDKs also take workspace, the URL of your Wexa deployment, and an optional token callback, onTokens in TypeScript and on_tokens in Python, which receives every new token pair, starting with this one.
In a browser, login and every later call work only from origins your project admin lists under Developer access → Allowed origins in the console. Calls from a server are not affected.
What it returns
A real response, captured from a live call to login. The tokens are cut short here:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…",
"refresh_token": "fab_srt_Ja2bvA…",
"token_type": "Bearer",
"expires_in": 3600,
"org_id": "6a79a770b6c128ccc24bfca8",
"project_id": "6aa9beb7ec8121a43b6c791b",
"session_id": "sess_Ss5LcvniPbkIkWERMiY8YUAO",
"grants": [
"fabric:query.read",
"fabric:docs.read",
"fabric:ontology.write",
"fabric:orchestrate.read",
"fabric:orchestrate.write",
"fabric:agent.run",
"fabric:skill.write",
"fabric:model.write",
"fabric:model.run",
"fabric:connector.run",
"fabric:flow.delete",
"fabric:kb.delete",
"fabric:table.read",
"fabric:table.write"
]
}expires_at is the access token's expiry in seconds since the epoch. whoami on the new client reports auth_kind: "session" and the session_id.
Calling it
curl -sS https://fabric.wexa.ai/v1/auth/login \
-H "content-type: application/json" \
-d '{"email": "ada@example.com", "password": "'"$FABRIC_PASSWORD"'", "project_id": "6aa9beb7ec8121a43b6c791b"}'Resume a saved session
Build the client from the saved pair instead of logging in again. It refreshes on its own when the access token runs out. Over REST there is no client: send the saved access token as the bearer, and when a call comes back 401 with invalid_token, trade the refresh token for a new pair and save it.
curl -sS https://fabric.wexa.ai/v1/whoami \
-H "authorization: Bearer $FABRIC_ACCESS_TOKEN"
# after a 401 invalid_token: get a new pair, then save both tokens
curl -sS https://fabric.wexa.ai/v1/auth/refresh \
-H "content-type: application/json" \
-d '{"refresh_token": "'"$FABRIC_REFRESH_TOKEN"'"}'Ask for less than the role gives
curl -sS https://fabric.wexa.ai/v1/auth/login \
-H "content-type: application/json" \
-d '{"email": "ada@example.com", "password": "'"$FABRIC_PASSWORD"'", "project_id": "6aa9beb7ec8121a43b6c791b", "grants": ["fabric:query.read"]}'Errors
Both SDKs raise the same typed errors, chosen from the gateway's own error string first and its HTTP status second. The wire code is in the error's message.
| Error | Status | Raised when |
|---|---|---|
AuthError | 401 | The email or the password is wrong (invalid_credentials). The two cases look the same on purpose. |
ForbiddenError | 403 | The user is not a member of the project (forbidden), is suspended in its organization (account_suspended), or the call came from a browser origin that is not allowed (origin_not_allowed). |
ValidationError | 400 | A required argument is missing, project_id is not a project id, or a requested grant is not in the user's role (invalid_request). |
QuotaExceeded | 429 | Too many failed logins for this email or address. retry_after / retryAfter says how many seconds to wait. |
UpstreamError | 502 | The account service behind the gateway could not be reached. Retrying later can help. |
ConfigurationError | — | Raised in the SDK before any request: a required option is missing, or an API key and a token pair were both given. |
An unverified email can log in for seven days after signup, the same as in the console; after that, login is refused until the email is verified.
Governance
Not a governed tool: it runs before any tool call. Every call the new session makes afterwards runs through the full lifecycle as the logged-in user.
See also
refresh-session, logout, the tool index, and errors.