Operator login — email + password
const url = 'http://localhost:3000/operator/login';const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: '{"email":"hello@example.com","password":"example"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url http://localhost:3000/operator/login \ --header 'Content-Type: application/json' \ --data '{ "email": "hello@example.com", "password": "example" }'Request Bodyrequired
Section titled “Request Bodyrequired”object
Examplegenerated
{ "email": "hello@example.com", "password": "example"}Responses
Section titled “Responses”A session token and the operator’s public profile.
object
Operator session token. Bearer-authenticate with it as operatorToken.
An operator as returned by login, invite acceptance, and GET /operator/me — the same shape everywhere so a dashboard hitting any of the three sees one model. Never carries password_hash.
object
Null for platform staff.
Example
{ "operator": { "role": "staff" }}Malformed body — not an email, missing password, or either field over its length bound. Rejected by validation before any credential is examined, so this is distinguishable from a failed login and says nothing about the account.
object
object
Present only for VALIDATION_FAILED.
object
Example
{ "error": { "code": "VALIDATION_FAILED" }}Unknown email, wrong password, a not-yet-accepted invite, or a disabled operator — all four answer identically, in the same time, so none is distinguishable from the others.
object
object
Present only for VALIDATION_FAILED.
object
Example
{ "error": { "code": "VALIDATION_FAILED" }}Rate limit exceeded.
object
object
Present only for VALIDATION_FAILED.
object
Example
{ "error": { "code": "VALIDATION_FAILED" }}