Skip to main content
Email Magic Links are a secure passwordless authentication option that create a low-friction login experience for users. When a user logs in via an Email Magic Link, Stytch generates a unique, one-time-use token embedded in a URL and sends it to the user’s email address. The user authenticates their identity by successfully receiving and clicking on this link before the link expires, at which point Stytch will either issue a Session or prompt the user to perform MFA if they are enrolled.
The Discovery flow is designed for centralized login pages where users authenticate before selecting which Organization to access.
1

Send the Magic Link

Send a magic link to the user’s email address:
curl --request POST \
  --url https://test.stytch.com/v1/b2b/magic_links/email/discovery/send \
  --header 'Content-Type: application/json' \
  --user 'PROJECT_ID:SECRET' \
  --data '{
    "email_address": "user@example.com",
    "discovery_redirect_url": "https://yourapp.com/authenticate"
  }'
Parameters:
  • email_address: The user’s email address
  • discovery_redirect_url: Where to redirect after clicking the magic link
Response:
{
  "status_code": 200,
  "request_id": "request-id-test-...",
  "intermediate_session_token": "DOYoip3rvIMMW5lgItikFK-Ak1CfMsgjuiCyI7uuU94="
}
2

Authenticate the Magic Link

When the user clicks the link, they’ll be redirected to your discovery_redirect_url with a token query parameter. Exchange this token for session information:
curl --request POST \
  --url https://test.stytch.com/v1/b2b/magic_links/authenticate \
  --header 'Content-Type: application/json' \
  --user 'PROJECT_ID:SECRET' \
  --data '{
    "magic_links_token": "TOKEN_FROM_QUERY_PARAM"
  }'
Response:
{
  "status_code": 200,
  "request_id": "request-id-test-...",
  "member_id": "member-test-...",
  "intermediate_session_token": "DOYoip3rvIMMW5lgItikFK...",
  "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": {}
      }
    }
  ]
}
3

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": "DOYoip3rvIMMW5lgItikFK...",
    "organization_id": "organization-test-...",
    "session_duration_minutes": 60
  }'
Response:
{
  "status_code": 200,
  "request_id": "request-id-test-...",
  "member_id": "member-test-...",
  "session_token": "WJtR5BCy38Szd5AfoDpf0iqFKEt4EE5JhdpRWT...",
  "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.

Next steps