Sign users in
Your page draws the sign-in form and sends what the user types to EdgeKit. EdgeKit sends the emails, checks the code or token, creates the user when the address is new, and sets the session cookie.
The samples below call your tenant host. From a page, call the same path under /edgekit/ on your own domain, which
is what the browser client does: id.signIn.emailOtp, id.signIn.verifyEmailOtp, id.signIn.magicLink,
id.consumeMagicLink and id.signIn.google on
createClient.
The routes that send a code or a link, and the routes that sign in with a code or with Google, answer only where
EdgeKit sign-in is open: at an edgekit_only site, or at a dual site where EdgeKit has turned it on. Everywhere else
they answer 404 not_found. See Choose who signs users in.
1. Read the site’s sign-in settings
Section titled “1. Read the site’s sign-in settings”GET /v1/id/config tells your page what to render. It needs no credential and answers any origin.
curl https://your-tenant-host/v1/id/config \ -H 'x-edgekit-site-id: site_example'const response = await fetch('https://your-tenant-host/v1/id/config', { headers: { 'x-edgekit-site-id': 'site_example' },});
const config = await response.json();{ "methods": [ { "name": "email-otp", "registrationTraits": [] }, { "name": "magic-link", "registrationTraits": [] } ], "captcha": null, "flow": { "otpLength": 6, "otpExpiresIn": 300, "magicLinkExpiresIn": 300, "resendCooldown": 60 }, "operations": [ "sign-in", "sign-out", "get-session", "change-identifier", "update-profile" ], "idMode": "edgekit_only"}methods: the sign-in methods to offer, in order.email-otpcarries the traits a new user can answer at sign-up, andgoogle, when present, carries theclientIdyour page passes to Google.methodsis empty where EdgeKit sign-in is closed. It tells your page which buttons to draw; it does not switch a method off.captcha: a widget and site key for your page to show, ornull. EdgeKit does not check a captcha answer on any sign-in route.flow: the code length, the code and link lifetimes in seconds, andresendCooldown, how many seconds your page should wait before offering to send again. EdgeKit enforces its own limit per address, below, not the cooldown.operations: what this site serves. See Choose who signs users in.idMode: this site’s id mode.
The answer is cached for up to 5 minutes by shared caches and 1 minute by the browser, so a change to these settings
can take 5 minutes to reach your pages. A site that is not active or not in your newest deployment answers
404 not_found, Unknown site..
2. Sign in with an emailed code
Section titled “2. Sign in with an emailed code”The email code is the method that can record a new user’s traits, and the one with no link to open.
-
Send the code. EdgeKit answers
200whether or not a user holds the address, so the route never tells anyone which addresses exist.Terminal window curl -X POST https://your-tenant-host/v1/id/email-otp/send-verification-otp \-H 'x-edgekit-site-id: site_example' \-H 'content-type: application/json' \-d '{"email": "ada@example.com"}' -
Send the code the user typed. On success EdgeKit answers
200with the user and the session, and sets the session cookie.const response = await fetch('https://your-tenant-host/v1/id/sign-in/email-otp',{method: 'POST',credentials: 'include',headers: {'x-edgekit-site-id': 'site_example','content-type': 'application/json',},body: JSON.stringify({email: 'ada@example.com',otp: '482913',firstName: 'Ada',lastName: 'Lovelace',userTraits: { job_industry: '01926f3a-8b51-7e99-9aaa-8b9c0d1e2f3a' },}),},);const { user, session } = await response.json();
If no user holds the address, this request creates one, verified, and attaches them to this site. firstName,
lastName (1 to 100 characters each) and userTraits are read on that request only: a user who already exists
keeps the name and traits they have. A list trait’s value is the id of one of its values, not its label. When the
request carries userTraits, they are checked against your traits, including required ones, and a failure answers
422 unprocessable_entity. A request without userTraits registers the user with no traits checked, so your
sign-up form must ask the questions. See Ask users about themselves.
A user who already exists is signed in, and an unverified address becomes verified.
3. Sign in with a magic link
Section titled “3. Sign in with a magic link”-
Send the link.
nameis optional, and is the new user’s name when the address is new.callbackURLis optional and is carried in the link for your page to use afterwards. It must be a relative path, or an absolute URL on one of the site’s Redirect origins.Terminal window curl -X POST https://your-tenant-host/v1/id/sign-in/magic-link \-H 'x-edgekit-site-id: site_example' \-H 'content-type: application/json' \-d '{"email": "ada@example.com", "callbackURL": "/account"}' -
The email’s link opens on the host EdgeKit received the send request on: your tenant host when your forwarder sets
Hostto it, as Mount /edgekit/ on your domain says. Its path is/edgekit/v1/id/magic-link/verify?token=…&callbackURL=…for a request that came through the mount, and/v1/id/magic-link/verify?…otherwise. EdgeKit answers both404 not_found, and no setting points the link at a page you host, so a user who opens it cannot finish signing in. Offer the email code instead. -
A page that holds a token redeems it with
POST /v1/id/magic-link/consume. Redeem it when the user selects a button, not on page load: mail scanners open emailed links, and a scanner that redeems the token leaves the user a link that no longer works.async function consumeMagicLink(token: string) {return fetch('/edgekit/v1/id/magic-link/consume', {method: 'POST',credentials: 'include',headers: {'x-edgekit-site-id': 'site_example','content-type': 'application/json',},body: JSON.stringify({ token }),});}
A token works once. A used, unknown or expired token answers 400 bad_request. A new address becomes a verified
user with the name sent in step 1. A magic link records no traits, and a trait sent with it is dropped without an
error. While magic link is offered on any of your sites, no trait can be marked required.
4. Sign in with Google
Section titled “4. Sign in with Google”Google sign-in is offered when EdgeKit holds your organization’s own Google OAuth client. GET /v1/id/config then
lists google with its clientId. Users see your Google client’s consent screen.
-
Your page loads Google Identity Services and renders Google’s button with that
clientId. The browser client does not load it for you. -
Send the
credentialGoogle hands your page asidToken. Sendnonceonly if you asked Google for one, with the same value.const response = await fetch('/edgekit/v1/id/sign-in/google', {method: 'POST',credentials: 'include',headers: {'x-edgekit-site-id': 'site_example','content-type': 'application/json',},body: JSON.stringify({ idToken: googleCredential }),});
There is no redirect flow. A new address becomes a user with the first and last name from Google. The route records
no traits: a Google user answers them later, through
PATCH /v1/id/user-details. Retrying with the same token while it is valid is
safe, and each call creates a new session.
Where EdgeKit sign-in is closed, or your organization has no Google client, the route answers 404 not_found.
Limits
Section titled “Limits”| What | Limit |
|---|---|
| Sign-in emails to one address | 3 a minute, counting codes, magic links and email-change codes together |
| Code attempts for one address | 3 a minute, across every code sent to it |
| Wrong guesses at one code | 3, then the code is spent |
| Email code | 6 digits, valid for 5 minutes |
| Magic link | Valid for 5 minutes, one use |
The per-address limits ignore a +tag and dots before the @, so ada+news@example.com and a.da@example.com
share ada@example.com’s allowance. A limit reached answers 429 too_many_requests with Retry-After: 60.
Errors
Section titled “Errors”| Status | Code | Message | Cause |
|---|---|---|---|
400 |
bad_request |
That code is not valid. |
The code is wrong, or no code is outstanding. |
400 |
bad_request |
That code has expired. Request a new one. |
The code is older than 5 minutes. |
400 |
bad_request |
That sign-in could not be verified. Start again. |
A Google token or magic-link token did not verify. |
400 |
bad_request |
That address already has an account. Sign in with an emailed code once, then Google will work. |
Google sign-in for an existing user whose address is not verified yet. |
400 |
bad_request |
That Google account has no email address to sign in with. |
The Google account has no address. |
403 |
forbidden |
That redirect target is not registered for this site. |
callbackURL is on an origin not in Redirect origins. |
404 |
not_found |
No route matches this path. |
EdgeKit sign-in is closed at this site, or no Google client. |
422 |
unprocessable_entity |
Request validation failed. |
A malformed address, a code that is not 6 digits, a callbackURL that is not a path or an http(s) URL, or traits that fail their checks. |
429 |
too_many_requests |
Too many sign-in emails for this address. Try again in a minute. |
The send limit for this address. |
429 |
too_many_requests |
Too many code attempts for this address. Try again in a minute. |
The attempt limit for this address. |
429 |
too_many_requests |
Too many attempts. Request a new code. |
Three wrong guesses at this code. |
500 |
internal_error |
Varies | EdgeKit could not send the email, or failed in another way. |
Every refusal is in the error code reference. The browser client answers a refusal as
failed with its code, and no answer at all as unavailable.
The emails
Section titled “The emails”The emails’ wording is fixed. The code arrives with the subject Your sign-in code, and the link with Sign in. They
go out from EdgeKit’s own address until your organization sets its transactional mail settings, which set the sender
name and address: see Site settings. You set them on any site’s
screen in Delivery › Delivery › Sites, with the Owner or Admin role. They apply to every site in your
organization and take effect at once.