Authentication
The Picked API supports four ways to authenticate. They are a session from an email address and a password, an OAuth provider, a magic link, and an API key. This guide covers each one. It also covers how to control a session and how to use multi-factor authentication (MFA).
Use a session
After the caller has a session, it puts the access token in the Authorization
header of every request that needs authentication:
Authorization: Bearer <accessToken>
The expiresAt field of the session gives the time at which the access token
expires. The caller must refresh the session before that time.
Authenticator assurance levels
The session holds an aal field, and it gives the strength of the authentication:
| Level | Meaning |
|---|---|
AAL1 | One factor: an email address and a password, OAuth, or a magic link |
AAL2 | Two factors: the user verified a code from an authenticator app |
Picked staff, and every member of an organization that requires MFA, must use AAL2.
For these users, an AAL1 session can only do these operations:
- Read
user. - Call the MFA mutations.
- Call
passwordUpdate, if the user has no verified authenticator. Therefore, a user can set a password before they add an authenticator.
Every other operation returns the error code MFA_REQUIRED. Read session.user.mfa after
sessionCreate to find out whether the user must verify, and see
How to set up MFA for the steps.
The API checks the level when it creates a token. An OAuth token for an MCP client or the
CLI is created at consent, which needs AAL2 for these users. The token stays valid
until it is revoked.
The mutations
| Mutation | What it does |
|---|---|
sessionStart | Starts a sign-in from an email address alone. Returns PASSWORD when the account has a password, and sessionCreate completes the sign-in. Otherwise sends an email with a six-digit code and a sign-in link, and returns EMAIL_CODE, and magicLinkVerify completes the sign-in. An address with no account also gets EMAIL_CODE, but no email. Too many requests from one client return RATE_LIMITED. |
sessionCreate | Authenticates an email address and a password. Returns a session at AAL1. session.user.mfa says whether the user must verify. |
sessionRefresh | Exchanges a refresh token for a new session. Needs no Authorization header. |
accountCreate | Creates an account. metadata takes any JSON. |
accountVerifyEmail | Completes email verification with the token from the email. |
accountResendVerification | Sends the verification email again. |
oauthAuthorize | Returns the authorization URL for a provider. Google is supported. |
oauthCallback | Exchanges an OAuth code for a session. |
magicLinkSend | Sends an email with a six-digit code and a sign-in link to an account. Does not create an account. Returns success: true also for an address with no account, and sends no email to it. |
magicLinkVerify | Exchanges the six-digit code from the email for a session. |
passwordResetSend | Sends a password reset email. |
passwordUpdate | Sets or changes the password. Needs authentication. Needs AAL2 when the user has a verified authenticator, also when the user does not have to use MFA. |
mfaFactorEnroll | Adds an authenticator app. A new factor is UNVERIFIED. Returns the QR code and the key one time. |
mfaChallengeCreate | Opens a challenge against a factor. |
mfaChallengeVerify | Verifies the code from the authenticator app. Moves the session to AAL2. |
mfaFactorUnenroll | Removes an authenticator. Needs AAL2 for a verified factor. |
apiKeyCreate | Creates an API key. Only a user with a session can call it. |
apiKeyRevoke | Invalidates a key immediately. |
Credentials
| Credential | Header |
|---|---|
| Session access token | Authorization: Bearer <accessToken> |
| API key | x-api-key: pk_live_abc123... |
