# Step 1: Sign in and choose an organization ## First time: register `POST /auth/register` creates the user. If that user does not already belong to an organization, it also creates one, with the user as its owner. - Required: `email`, `password`, `name`. - `userType` is optional free text; the Hearsay apps send `professional` or `individual`. - `termsAccepted` must be `true`; without it the call returns 400. - `organizationName` names the new organization. If you leave it out, the name comes from the user's name. A name that is already taken returns 400. - Optional: `phone` (a list of numbers) and `inviteEmails` (colleagues to invite). - On success you are already signed in: the response carries an access token, a refresh token, the user's profile and the organization. You do not need to call login straight after. - Optional pre-check: `POST /auth/check-email-availability` tells you whether an email can sign up. It needs no token and allows only 10 calls per minute. ## Returning user: log in `POST /auth/login` with `email` and `password` returns: - `accessToken`: send it as `Authorization: Bearer `. - `refreshToken`: keep it to get new access tokens. - `userProfile`: includes `userOrganizations`, the organizations this user belongs to. Wrong credentials and inactive accounts return 401. ## Choose the organization Each entry of `userProfile.userOrganizations` has an `organization` (with its `id` and `name`) and a `permission` (with your `role`). Take `organization.id` and send it in the `organization` header on every later call. If the user belongs to several organizations, pick by name, or ask a human. Never guess. `GET /organization/user/organizations` returns the same list at any time. ## Keep the session alive - When a call returns 401 because the access token expired, call `POST /auth/refresh` with the `refreshToken`. You get a new access token; the refresh token stays the same. Then retry the original call once. - If the refresh itself fails (404 for an unknown token, 400 for an expired one), log in again. - `POST /auth/logout` ends the current session. ## Long-running agents: API tokens For an unattended agent, a long-lived token is often easier than storing a password. While signed in, call `POST /auth/generate-token`. There are two kinds: | Kind | How to get it | What it can do | Lifetime | |---|---|---|---| | **Scoped automation token** | Send `scopes` (and optionally `label` and `ttlDays`) | **Only** the routes its scopes allow. Every other route returns 403. | `ttlDays`, 1 to 400 days (default about 365) | | **Full-access token** | Send no `scopes`, and no `label` or `ttlDays` (those return 400 without `scopes`) | Everything the user can do | 5 years | The scopes and the routes each one unlocks: | Scope | Routes it unlocks | |---|---| | `cases:create` | `POST /case` | | `codes:create` | `POST /case/{caseId}/extraction` | | `exports:create` | `POST /case/{caseId}/export` and `POST /case/{caseId}/export/{exportId}/rerun` | > warn > A scoped token cannot list or update cases, list or update extraction codes, or list or download exports. An integration that runs the full workflow in this guide needs either a normal login session (recommended) or a full-access token (see the token rules below). Use scoped tokens for agents that only create things. Token rules: - The token value is shown **once**, in the generate-token response. Store it in a secret store, never in logs or prompts. - `GET /auth/tokens` lists your scoped tokens (never their values). `DELETE /auth/tokens/{id}` revokes one, and `PATCH /auth/tokens/{id}/scopes` changes its scopes; an empty scope list disables the token without deleting it. - **Full-access tokens are not listed** by `GET /auth/tokens` and have no id, so `DELETE /auth/tokens/{id}` cannot revoke them. The only way to revoke one is to call `POST /auth/logout` *with that token*. If it is lost or leaked, you cannot revoke it yourself; contact Hearsay. Prefer a normal login session for the full workflow, and use a full-access token only if you must. - Scoped tokens can be switched off on Hearsay's side. If generate-token returns 403, ask Hearsay to enable them for you. - You still send the `organization` header with an API token. ## Example Log in: ```sh curl -X POST "https://api.usehearsay.com/auth/login" \ -H "Content-Type: application/json" \ -d '{"email": "jane.doe@example.com", "password": "..."}' ``` ```json { "data": { "accessToken": "eyJhbGciOi...", "refreshToken": "b7c1e2f0a9d8...", "userProfile": { "id": "507f1f77bcf86cd799439011", "name": "Jane Doe", "userOrganizations": [ { "organization": { "id": "507f1f77bcf86cd799439012", "name": "Example Law LLP" }, "permission": { "role": "owner" } } ] } }, "statusCode": 200, "timestamp": "2026-01-15T10:15:00.000Z" } ``` Refresh an expired access token: ```sh curl -X POST "https://api.usehearsay.com/auth/refresh" \ -H "Content-Type: application/json" \ -d '{"refreshToken": "'"$REFRESH_TOKEN"'"}' ``` Create a scoped automation token: ```sh curl -X POST "https://api.usehearsay.com/auth/generate-token" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"label": "Intake sync", "scopes": ["cases:create", "codes:create"], "ttlDays": 90}' ```