Documentation Index Fetch the complete documentation index at: https://stytch.com/docs/llms.txt
Use this file to discover all available pages before exploring further.
Email OTP (One-Time Passcode) provides a passwordless authentication method where users receive a 6-digit code via email. OTPs are valid for 10 minutes and can only be used once.
Implement Email OTP
Discovery flow
Organization flow
The Discovery flow is designed for centralized login pages where users authenticate before selecting which Organization to access.
Send the OTP
Send a one-time passcode to the user’s email address: curl --request POST \
--url https://test.stytch.com/v1/b2b/otps/email/discovery/send \
--header 'Content-Type: application/json' \
--user 'PROJECT_ID:SECRET' \
--data '{
"email_address": "user@example.com"
}'
Parameters:
email_address: The user’s email address
discovery_expiration_minutes: (Optional) Expiration time in minutes (default: 10)
login_template_id: (Optional) Custom email template ID
locale: (Optional) Language for the email (e.g., “en”, “es”)
Response: {
"status_code" : 200 ,
"request_id" : "request-id-test-b05c992f-ebdc-489d-a754-c7e70ba13141"
}
Authenticate the OTP
After the user receives and enters the code, authenticate it: curl --request POST \
--url https://test.stytch.com/v1/b2b/otps/email/discovery/authenticate \
--header 'Content-Type: application/json' \
--user 'PROJECT_ID:SECRET' \
--data '{
"email_address": "user@example.com",
"code": "123456"
}'
Parameters:
email_address: The user’s email address
code: The 6-digit OTP code
Response: {
"status_code" : 200 ,
"request_id" : "request-id-test-b05c992f-ebdc-489d-a754-c7e70ba13141" ,
"intermediate_session_token" : "SeiGwdj5lKkrEVgcEY3QNJXt6srxS3IK2Nwkar6mXD4=" ,
"email_address" : "user@example.com" ,
"discovered_organizations" : [
{
"organization" : {
"organization_id" : "organization-test-..." ,
"organization_name" : "Acme Corp" ,
"organization_slug" : "acme-corp"
},
"membership" : {
"type" : "eligible_to_join_by_email_domain" ,
"details" : {}
}
}
]
}
Exchange the intermediate session
After the user selects an organization, exchange the intermediate session token for a full session: curl --request POST \
--url https://test.stytch.com/v1/b2b/discovery/intermediate_sessions/exchange \
--header 'Content-Type: application/json' \
--user 'PROJECT_ID:SECRET' \
--data '{
"intermediate_session_token": "SeiGwdj5lKkrEVgcEY3QNJXt6srxS3IK2Nwkar6mXD4=",
"organization_id": "organization-test-...",
"session_duration_minutes": 60
}'
Response: {
"status_code" : 200 ,
"request_id" : "request-id-test-..." ,
"member_id" : "member-test-..." ,
"session_token" : "mZAYn5aLEqKUlZ_Ad9U_fWr38GaAQ1oFAhT8ds245v7Q" ,
"session_jwt" : "eyJhbGc..." ,
"member" : { /* member object */ },
"organization" : { /* organization object */ },
"session" : { /* session object */ }
}
The Discovery flow returns an intermediate_session_token which must be exchanged for a full session after the user selects their organization.
The Organization flow is for organization-specific login pages where the organization context is already known (e.g., acme.yourapp.com).
Send the OTP
Send a one-time passcode for a specific organization: curl --request POST \
--url https://test.stytch.com/v1/b2b/otps/email/login_or_signup \
--header 'Content-Type: application/json' \
--user 'PROJECT_ID:SECRET' \
--data '{
"organization_id": "organization-test-07971b06-ac8b-4cdb-9c15-63b17e653931",
"email_address": "user@example.com"
}'
Parameters:
organization_id: The organization’s ID
email_address: The user’s email address
login_expiration_minutes: (Optional) Expiration for existing members (default: 10)
signup_expiration_minutes: (Optional) Expiration for new members (default: 10)
login_template_id: (Optional) Custom email template for login
signup_template_id: (Optional) Custom email template for signup
locale: (Optional) Language for the email
Response: {
"status_code" : 200 ,
"request_id" : "request-id-test-b05c992f-ebdc-489d-a754-c7e70ba13141" ,
"member_id" : "member-test-32fc5024-9c09-4da3-bd2e-c9ce4da9375f" ,
"member_created" : false ,
"member" : { /* member object */ },
"organization" : { /* organization object */ }
}
Authenticate the OTP
After the user receives and enters the code, authenticate it: curl --request POST \
--url https://test.stytch.com/v1/b2b/otps/email/authenticate \
--header 'Content-Type: application/json' \
--user 'PROJECT_ID:SECRET' \
--data '{
"organization_id": "organization-test-07971b06-ac8b-4cdb-9c15-63b17e653931",
"email_address": "user@example.com",
"code": "123456",
"session_duration_minutes": 60
}'
Parameters:
organization_id: The organization’s ID
email_address: The user’s email address
code: The 6-digit OTP code
session_duration_minutes: (Optional) Session lifetime in minutes (default: 60)
session_custom_claims: (Optional) Custom claims to add to the session JWT
Response: {
"status_code" : 200 ,
"request_id" : "request-id-test-b05c992f-ebdc-489d-a754-c7e70ba13141" ,
"member_id" : "member-test-32fc5024-9c09-4da3-bd2e-c9ce4da9375f" ,
"organization_id" : "organization-test-07971b06-ac8b-4cdb-9c15-63b17e653931" ,
"session_token" : "mZAYn5aLEqKUlZ_Ad9U_fWr38GaAQ1oFAhT8ds245v7Q" ,
"session_jwt" : "eyJhbGc..." ,
"member_authenticated" : true ,
"method_id" : "member-email-test-..." ,
"member" : { /* member object */ },
"organization" : { /* organization object */ },
"session" : { /* session object */ }
}
If MFA is required for the Member, member_authenticated will be false and an intermediate_session_token will be returned instead. This token must be used to complete the MFA step.
Next steps
Sessions Learn about session management
Organizations Understand the B2B data model