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. userTypeis optional free text; the Hearsay apps sendprofessionalorindividual.termsAcceptedmust betrue; without it the call returns 400.organizationNamenames 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) andinviteEmails(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-availabilitytells 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 asAuthorization: Bearer <accessToken>.refreshToken: keep it to get new access tokens.userProfile: includesuserOrganizations, 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/refreshwith therefreshToken. 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/logoutends 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 |
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/tokenslists your scoped tokens (never their values).DELETE /auth/tokens/{id}revokes one, andPATCH /auth/tokens/{id}/scopeschanges its scopes; an empty scope list disables the token without deleting it.- Full-access tokens are not listed by
GET /auth/tokensand have no id, soDELETE /auth/tokens/{id}cannot revoke them. The only way to revoke one is to callPOST /auth/logoutwith 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
organizationheader with an API token.
Log in:
curl -X POST "https://api.usehearsay.com/auth/login" \
-H "Content-Type: application/json" \
-d '{"email": "jane.doe@example.com", "password": "..."}'
{
"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:
curl -X POST "https://api.usehearsay.com/auth/refresh" \
-H "Content-Type: application/json" \
-d '{"refreshToken": "'"$REFRESH_TOKEN"'"}'
Create a scoped automation token:
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}'