Migrate from Stytch

Migrate from Stytch

Stytch B2B's data model is similar to Authio's — shared members across organizations by email. The migration is mostly a 1:1 mapping.

Credit where it's due: Stytch B2B got the multi-org-by-email model right. If you're migrating from Stytch, it's not because of the data model — it's usually about pricing, the breadth of features (SCIM, custom domains, the risk engine), or the desire to consolidate consumer + B2B under one provider.

What you'll keep, what you'll lose, what you'll gain

  • Keep: Organizations + slugs, members + roles (admin → Authio admin), trusted / untrusted metadata, MFA enrolled flag, SSO connection records.
  • Lose: Stytch sessions (users re-auth), AuthKit passwords (Authio is passwordless), TOTP secrets (only the enrolled flag preserved).
  • Gain: Custom-domain hosted UI, the audit stream as a separate service, the risk engine, fine-grained authorization.

B2B vs Consumer

Stytch ships two products: B2B (orgs + members) and Consumer (flat users). The importer auto-detects:

  • B2B export — a JSON object with organizations[] at the top, each with members[] inside. Maps 1:1 to Authio orgs + users + memberships.
  • Consumer export — a JSON array of users. All users land under one synthetic Default Authio org.

Sessions

Stytch sessions are dropped. Users sign in via passkey or magic-link on next visit.

Step-by-step: CLI (B2B)

# 1) Fetch orgs + members.
H="Authorization: Bearer $STYTCH_SECRET"
H2="content-type: application/json"

orgs=$(curl -s -X POST "https://api.stytch.com/v1/b2b/organizations/search" -H "$H" -H "$H2" -d '{}')

# 2) For each org, fetch members and attach.
echo "$orgs" | jq -c '.organizations[]' | while read o; do
  oid=$(echo "$o" | jq -r .organization_id)
  members=$(curl -s -X POST "https://api.stytch.com/v1/b2b/organizations/$oid/members/search" -H "$H" -H "$H2" -d '{}')
  echo "$o" | jq --argjson m "$members" '. + {members: $m.members}'
done | jq -s '{organizations: ., sso_connections: []}' > stytch.json

# 3) Dry-run.
authio import stytch --input ./stytch.json --dry-run | jq '.stats'

# 4) Apply.
authio import stytch --input ./stytch.json

Step-by-step: CLI (Consumer)

curl -s -X POST "https://api.stytch.com/v1/users/search" \
  -H "Authorization: Bearer $STYTCH_SECRET" \
  -H "content-type: application/json" -d '{}' \
  | jq '.results' > stytch.json
authio import stytch --input ./stytch.json

Step-by-step: dashboard wizard

Visit app.authio.com/migrate/stytch.

Live import (paste an API token)

Skip the bundle assembly. Paste your Stytch Project ID + Project Secret into the dashboard wizard and Authio walks every B2B organization and member for you.

  1. Stytch dashboard → API Keys → copy your live Project ID and Project Secret (used as HTTP Basic auth).
  2. In Authio: Migrate from Stytch → Connect with API token. Paste both. The probe hits /v1/b2b/organizations/search.
  3. Start the job; progress updates per-org as the worker fans out into members/search.
Tokens are encrypted at rest with envelope encryption and the credential row is auto-deleted within 24 hours.

Live import via the CLI

authio import stytch \
  --live-token "$STYTCH_PROJECT_SECRET" \
  --stytch-project-id "$STYTCH_PROJECT_ID" \
  --stytch-secret "$STYTCH_PROJECT_SECRET" \
  --dry-run

Post-migration checklist

  • Swap Stytch SDK for the Authio SDK; the patterns are similar.
  • Recreate SSO connections (the plan lists Stytch's connection IDs for cross-reference).
  • Send migration emails.
Stytch's B2B trusted_metadata and untrusted_metadata distinction is preserved in Authio user metadata under those exact keys, so your app code can continue to use the same access pattern.