Reference
Auth
13 routes, generated from Morse’s own OpenAPI document. Every path below hangs off the base URL, and every one needs the bearer header.
The examples are built from each route’s schema, so the shapes and types are exactly what the API declares. The values are illustrative, and no one has run them.
Finish signing up, setting a password, or resetting one: send the 6-digit code from the email, and a new password where the flow asked for one. Signs the browser in on success.
/auth/confirmSigning in here is deliberate. They have just proved they hold the mailbox
and, for two of the three purposes, chosen the password — a login form
immediately afterwards would add a step and no security.
**What the code is for is decided here, not sent by the caller.** The purpose
is read from the live row (auth_codes.purpose_of), so a reset_password
code cannot be presented as a verify_email one. The HMAC is bound to the
purpose as well, so even a caller that could choose would get nowhere.
**And it is not told to the caller either, except by answering.** Since
register gives the same 202 to a new address and an existing one, the
confirm page cannot know whether to ask for a password — and a redirect
carrying the purpose would rebuild exactly the oracle the 202 closed. So the
page sends the code alone; a verify_email code finishes, and anything else
comes back password_required with the code still live, to be sent again
with the password. Only someone already holding a valid code learns which
flow they are in, and the email told them that already.
POST/auth/confirm
curl -X POST "$MORSE/auth/confirm" \
-H "Authorization: Bearer $MORSE_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "code": "4KJ9P2", "email": "priya@neuralarc.ai" }'{
"email": "priya@neuralarc.ai",
"id": "3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60",
"name": "Priya Shah"
}Signing in connects the calendar too (plan 009 D14): one consent screen, once. Only where calendars are configured — asked of a server with no key to keep the answer in, it would be consent for nothing.
/auth/googleOffline access, and no prompt. prompt=consent would show the screen on
every sign-in; without it Google shows it only while something is still
ungranted, and returns a refresh token then. Someone already connected signs
in without seeing it. include_granted_scopes keeps a grant made through
/api/calendar/connect in the token rather than replacing it.
GET/auth/google
curl "$MORSE/auth/google" \
-H "Authorization: Bearer $MORSE_TOKEN"Where Google sends a browser back after signing in: finds or makes the account, signs the browser in and returns to the app.
/auth/google/callbackNot for scripts.
GET/auth/google/callback
curl "$MORSE/auth/google/callback" \
-H "Authorization: Bearer $MORSE_TOKEN"Google Sign-In for the mobile app.
/auth/google/mobileThe browser flow above cannot work in-app: /google is a 302 to
accounts.google.com, and Google blocks OAuth inside embedded WebViews. The
device SDK hands the app a signed ID token instead, so this route verifies
that token and ends exactly where google_callback ends — same checks,
same cookie — leaving nothing downstream able to tell the two apart.
The audience is our **Web** OAuth client ID, which is also what the client
must pass as serverClientId. A platform client ID on either side fails
every token with an audience mismatch.
POST/auth/google/mobile
curl -X POST "$MORSE/auth/google/mobile" \
-H "Authorization: Bearer $MORSE_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "id_token": "mp_YOUR_MORSE_TOKEN" }'{
"email": "priya@neuralarc.ai",
"id": "3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60",
"name": "Priya Shah"
}Sign a browser in with an email and password.
/auth/login403 with reason: "unverified" for an account that has never confirmed its
address, so the page can offer Resend instead of repeating "incorrect
password" at someone whose password is correct. That does reveal that an
unverified account exists — accepted, and only after the right password was
supplied, because the alternative is a dead end a real user cannot escape.
POST/auth/login
curl -X POST "$MORSE/auth/login" \
-H "Authorization: Bearer $MORSE_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "email": "priya@neuralarc.ai", "password": "…" }'{
"email": "priya@neuralarc.ai",
"id": "3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60",
"name": "Priya Shah"
}Sign a browser out by clearing its session cookie.
/auth/logoutTokens are unaffected: revoke those in Settings.
POST/auth/logout
curl -X POST "$MORSE/auth/logout" \
-H "Authorization: Bearer $MORSE_TOKEN"Who's signed in: your profile and preferences.
/auth/meWith a token, the token's owner.
GET/auth/me
curl "$MORSE/auth/me" \
-H "Authorization: Bearer $MORSE_TOKEN"{
"accent": "…",
"avatar_choice": "…",
"avatar_original": true,
"avatar_uploads": true,
"avatar_url": "https://onmorse.com/priya",
"avatar_version": "…",
"background": "…",
"bio": "…",
"bookable": true,
"booking_handle": "…",
"clock": "…",
"company": "…",
"email": "priya@neuralarc.ai",
"extra_language": "en",
"extra_language_default": "en",
"id": "3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60",
"is_admin": true,
"name": "Priya Shah",
"onboarded_at": "2026-10-02T10:30:00+05:30",
"organisation_id": "3f9c1a24-5e6f-4b31-9a77-1b2c3d4e5f60",
"share_tasks": true,
"theme": "…",
"timezone": "Asia/Kolkata",
"title": "Pricing review"
}Lets the sign-in pages render only the methods this deployment accepts, rather than offering a form that would 404.
/auth/methodsGET/auth/methods
curl "$MORSE/auth/methods" \
-H "Authorization: Bearer $MORSE_TOKEN"{
"google": true,
"password": true
}Start a password reset: we email a 6-digit code. Finish it at
POST /auth/confirm with that code and the new password — there is no
second endpoint, because a reset and a first password are the same act.
/auth/password/forgotAlways 202. An account that signs in with Google and has no password gets an email saying so rather than a code: the alternative is a flow that promises a code which cannot exist, and teaches the person nothing.
POST/auth/password/forgot
curl -X POST "$MORSE/auth/password/forgot" \
-H "Authorization: Bearer $MORSE_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "email": "priya@neuralarc.ai" }'{
"detail": "…"
}A just-issued ID token for *this* account's Google identity.
/auth/reauth/googleAny valid token is not enough: a token for another account proves nothing about this session, and an old one proves only that the app once held it.
POST/auth/reauth/google
curl -X POST "$MORSE/auth/reauth/google" \
-H "Authorization: Bearer $MORSE_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "id_token": "mp_YOUR_MORSE_TOKEN" }'{
"expires_at": "2026-10-02T10:30:00+05:30",
"token": "mp_YOUR_MORSE_TOKEN"
}Re-enter your password for a five-minute proof that it's you, sent as X-Morse-Recent-Auth
to delete a voice note permanently.
/auth/reauth/passwordOnly where password sign-in is on; an API token needs no proof.
POST/auth/reauth/password
curl -X POST "$MORSE/auth/reauth/password" \
-H "Authorization: Bearer $MORSE_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "password": "…" }'{
"expires_at": "2026-10-02T10:30:00+05:30",
"token": "mp_YOUR_MORSE_TOKEN"
}Start an account with an email and a password. We email a 6-digit code;
nothing is signed in until POST /auth/confirm carries that code back.
/auth/register202 and not 201, and no session: at this point all we know is that someone
typed an address. 202 and not 409 for an address that already has an
account, either — the 409 this route used to return was an account-existence
oracle, which the comment above BAD_CREDENTIALS has always said login must
not be. Registration had been left out of that rule (plan 036 §5.1).
POST/auth/register
curl -X POST "$MORSE/auth/register" \
-H "Authorization: Bearer $MORSE_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "email": "priya@neuralarc.ai", "name": "Priya Shah", "password": "…" }'{
"detail": "…"
}Send another 6-digit code, for whichever flow the person is part-way through. The previous code stops working.
/auth/resendAlways 202, with the same body as register — including for an address with
no account, an unknown purpose, and a purpose with nothing to resend. Each
of those answering differently would be the oracle again, in a route that
needs no authentication at all.
**purpose in the body is a hint and nothing more.** The client cannot have
been told which flow it is in — that is the point of register's identical
202 — so it is guessing, and a guess that issued the wrong kind of code
would be a dead end: the person would hold a verify_email code for an
account that needs a password. What they already have decides, and failing
that, what the account looks like.
POST/auth/resend
curl -X POST "$MORSE/auth/resend" \
-H "Authorization: Bearer $MORSE_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "email": "priya@neuralarc.ai" }'{
"detail": "…"
}