createClient
createClient<
S,T>(options):object
Create the browser client for one site.
import { createClient } from '@edgekitcloud/client-browser';import type { EdgeKitDecisions } from './edgekit/generated/decisions';import type { EdgeKitTraits } from './edgekit/generated/identity';
const ek = createClient<EdgeKitDecisions['site_example'], EdgeKitTraits>({ siteId: 'site_example' });S is the site’s schema and T the tenant’s traits, both written by
edgekit types pull. T is optional and defaults to an untyped record. It
is a separate argument because a trait applies to every site of the tenant,
while a feature belongs to one site.
The client holds no credential. The tenant accepts its requests from the origins registered on the site, and the session cookie is the only way it identifies a user.
Type Parameters
Section titled “Type Parameters”S extends SiteSchema
T extends TraitValues = TraitValues
Parameters
Section titled “Parameters”options
Section titled “options”Returns
Section titled “Returns”newsletters
Section titled “newsletters”newsletters:
object
The newsletter lists this site offers, and where the user stands on each.
The user is whoever the session cookie names. With no session, as
through an absolute baseUrl that does not carry one, subscriptions()
answers unknown for every list. Throws EdgeKitConfigError on
400, 401, 403 or 404; a 404 usually means a list id from an outdated generated
newsletters.ts, so run edgekit types pull again. A 429 is failed,
so a sign-up form can tell the user to wait.
newsletters.lists()
Section titled “newsletters.lists()”lists(
options?):Promise<CallResult<NewsletterListsResponseBody>>
Every list this site has deployed, without the user’s standing. A disabled list is not deployed, so it never appears and there is no flag to filter on. Before the first deploy the answer is an empty set, the same as a site with no newsletters.
Parameters
Section titled “Parameters”options?
Section titled “options?”NewsletterOptions = {}
Returns
Section titled “Returns”Promise<CallResult<NewsletterListsResponseBody>>
newsletters.replace()
Section titled “newsletters.replace()”replace(
input,options?):Promise<CallResult<NewsletterSubscriptionsResponseBody>>
Sets the user’s lists. A full replacement, not a patch: every list
this site deploys that subscribed omits becomes unsubscribed. The
response is the full resulting set, so you can see what was turned
off.
A list that is pending stays pending whether named or omitted. 200
and 202 both arrive as ok; read the returned statuses to see which
lists await confirmation.
With only an email and no session, the call adds and never
removes. Omitted lists are left as they are, and the response is what
an address never seen before would get: named lists pending or
subscribed, the rest unknown. Unsubscribing needs a signed-in user.
Parameters
Section titled “Parameters”options?
Section titled “options?”NewsletterOptions = {}
Returns
Section titled “Returns”Promise<CallResult<NewsletterSubscriptionsResponseBody>>
newsletters.subscriptions()
Section titled “newsletters.subscriptions()”subscriptions(
options?):Promise<CallResult<NewsletterSubscriptionsResponseBody>>
Where the user stands on every deployed list: the one call a
preference centre needs. A list the user never touched answers
unknown, so there is nothing to join against lists(). A signed-out
user gets unknown for every list rather than a refusal, so the same
form renders either way.
Parameters
Section titled “Parameters”options?
Section titled “options?”NewsletterOptions = {}
Returns
Section titled “Returns”Promise<CallResult<NewsletterSubscriptionsResponseBody>>
Get Signature
Section titled “Get Signature”get id():
object
EdgeKit ID: sign-in, the session, and the user’s own details.
Built on first access, so a page that only calls decide sets up no
session store, no cross-tab sync and no session request.
Nothing here throws on an HTTP status: a refusal (a wrong code, a sign-in
method this site does not offer) is failed, for the sign-in screen to
render.
Returns
Section titled “Returns”changeIdentifier
Section titled “changeIdentifier”changeIdentifier:
object
changeIdentifier.confirm
Section titled “changeIdentifier.confirm”confirm: (
input) =>Promise<CallResult<OkResult>>
Completes the change with the code sent to newEmail. On success
every session of the user ends, this one included: send the user
back through sign-in.
Parameters
Section titled “Parameters”newEmail
Section titled “newEmail”string
string
Returns
Section titled “Returns”Promise<CallResult<OkResult>>
changeIdentifier.request
Section titled “changeIdentifier.request”request: (
newEmail) =>Promise<CallResult<OkResult>>
Starts changing the signed-in user’s email address: sends a code to
newEmail. Only at an edgekit_only site. In dual and
external_only mode your identity provider owns the address, and this
answers failed with a 404.
Answers success even when another user holds the address, so it
cannot be used to test which addresses exist; confirm reports the
conflict.
Parameters
Section titled “Parameters”newEmail
Section titled “newEmail”string
Returns
Section titled “Returns”Promise<CallResult<OkResult>>
config
Section titled “config”config: () =>
Promise<CallResult<IdConfig>>
The site’s public sign-in settings: the sign-in methods it offers, code and link lifetimes, and registration traits. Sent without cookies and never cached by this client.
Returns
Section titled “Returns”Promise<CallResult<IdConfig>>
consumeMagicLink
Section titled “consumeMagicLink”consumeMagicLink: (
token) =>Promise<CallResult<SignInResult>>
Redeems a magic-link token, from a page you host.
A token works once. Mail scanners open emailed links, so call this from a step the user takes, such as a button on that page, rather than on page load; otherwise a scanner can use up the token and the user is told a link they never opened has expired.
Parameters
Section titled “Parameters”string
Returns
Section titled “Returns”Promise<CallResult<SignInResult>>
exchangeSession
Section titled “exchangeSession”exchangeSession: () =>
Promise<CallResult<SignInResult>>
Exchanges your identity provider’s credential for an EdgeKit session.
Only at a dual site; in any other mode it answers failed with a
404.
Returns
Section titled “Returns”Promise<CallResult<SignInResult>>
getSession
Section titled “getSession”getSession: () =>
Promise<CallResult<{session:SessionRef|null;user:IdUser|null; }>>
Reads the session once, without the session store. Signed out is ok
with {user: null, session: null}.
Returns
Section titled “Returns”Promise<CallResult<{ session: SessionRef | null; user: IdUser | null; }>>
getUserDetails
Section titled “getUserDetails”getUserDetails: () =>
Promise<CallResult<UserDetails<T>>>
The signed-in user’s record and the traits they set, to pre-fill a preferences form. No session payload carries traits; this is the call that reads them.
It does not touch the session store, and it does not replace
getSession: for a session whose user no longer exists it answers
failed with a 404, where getSession answers signed out.
Values come back as stored. A list item your organization has since
disabled is still returned but missing from traits(): render it as
unset.
Returns
Section titled “Returns”Promise<CallResult<UserDetails<T>>>
signIn
Section titled “signIn”signIn:
object
signIn.emailOtp
Section titled “signIn.emailOtp”emailOtp: (
Promise<CallResult<OkResult>>
Emails a one-time sign-in code to email. Redeem it with
verifyEmailOtp.
Parameters
Section titled “Parameters”string
Returns
Section titled “Returns”Promise<CallResult<OkResult>>
signIn.google
Section titled “signIn.google”google: (
input) =>Promise<CallResult<SignInResult>>
Signs in with a Google ID token your page already holds.
This package does not load Google Identity Services. Your page
loads it, renders Google’s button, and passes the credential from
its callback as idToken. Read the Google client id from the
google entry of config().methods (clientId) rather than building
it into your page.
There is no redirect flow: this is an ordinary request, answering the
same {user, session} as verifyEmailOtp. Pass nonce only when you
asked Google for one, with the same value.
Parameters
Section titled “Parameters”idToken
Section titled “idToken”string
nonce?
Section titled “nonce?”string
Returns
Section titled “Returns”Promise<CallResult<SignInResult>>
signIn.magicLink
Section titled “signIn.magicLink”magicLink: (
input) =>Promise<CallResult<OkResult>>
Emails a sign-in link to email. It answers before the user opens
the link; the page the link opens calls consumeMagicLink.
Parameters
Section titled “Parameters”callbackURL?
Section titled “callbackURL?”string
string
string
Returns
Section titled “Returns”Promise<CallResult<OkResult>>
signIn.verifyEmailOtp
Section titled “signIn.verifyEmailOtp”verifyEmailOtp: (
input) =>Promise<CallResult<SignInResult>>
Signs in with the emailed code, and registers the user on their first
sign-in. firstName, lastName and userTraits are the registration
fields. This is the only sign-in call that accepts traits; a trait
sent through the magic-link flow is dropped without an error.
userTraits is T whole, not a patch: registration is where a
required trait is answered, so each required trait is a required key
once you pass the object. Omitting userTraits entirely still
registers the user, with no traits checked, so your sign-up form must
ask the questions.
Parameters
Section titled “Parameters”string
firstName?
Section titled “firstName?”string
lastName?
Section titled “lastName?”string
string
userTraits?
Section titled “userTraits?”T
Returns
Section titled “Returns”Promise<CallResult<SignInResult>>
signOut
Section titled “signOut”signOut: () =>
Promise<CallResult<SignOutResult>>
Signs the user out, in every tab of this browser. At a dual site,
logoutUrl is your identity provider’s sign-out page: send the browser
there too, or the user stays signed in to your own system.
Returns
Section titled “Returns”Promise<CallResult<SignOutResult>>
traits
Section titled “traits”traits: () =>
Promise<CallResult<{traits:IdTraitDescriptor[]; }>>
Every trait the tenant collects: the questions a sign-up form asks and a
preferences screen edits, with each list trait’s enabled items and
their labels.
Render forms from this, not from
config().methods[].registrationTraits. That is [] for magic-link,
and methods is empty at a site that offers no EdgeKit sign-in, such as
an external_only site, which still collects traits.
A trait appears here as soon as it is saved, before any deploy. Sent without cookies and never cached by this client.
Returns
Section titled “Returns”Promise<CallResult<{ traits: IdTraitDescriptor[]; }>>
updateUserDetails
Section titled “updateUserDetails”updateUserDetails: (
input) =>Promise<CallResult<UserDetails<T>>>
Updates the signed-in user’s own name and traits, and answers with the
result, as getUserDetails does. null clears firstName or
lastName; name cannot be cleared. Traits merge into the stored ones:
see TraitPatch.
Parameters
Section titled “Parameters”Returns
Section titled “Returns”Promise<CallResult<UserDetails<T>>>
session
Section titled “session”Get Signature
Section titled “Get Signature”get session():
SessionState
The current session state, without subscribing. The first read sets up
the session store and requests the session, so it starts as pending.
Returns
Section titled “Returns”subscribe()
Section titled “subscribe()”subscribe(
listener): () =>void
Calls listener at once with the current session state, then on every
change. Returns a function that unsubscribes.
Parameters
Section titled “Parameters”listener
Section titled “listener”Returns
Section titled “Returns”() => void
Get Signature
Section titled “Get Signature”get org():
object
User organizations: groups of users sharing seats bought together, with their members and invitations. A user organization is not your EdgeKit organization.
Built on first access, like id, and like id it never throws on an
HTTP status: a refusal, including a seat limit, is failed.
Nothing here creates or deletes a user organization. One exists because seats were bought.
Returns
Section titled “Returns”acceptInvitation
Section titled “acceptInvitation”acceptInvitation: (
invitationId) =>Promise<CallResult<OrgMember>>
Parameters
Section titled “Parameters”invitationId
Section titled “invitationId”string
Returns
Section titled “Returns”Promise<CallResult<OrgMember>>
cancelInvitation
Section titled “cancelInvitation”cancelInvitation: (
invitationId) =>Promise<CallResult<OkResult>>
Parameters
Section titled “Parameters”invitationId
Section titled “invitationId”string
Returns
Section titled “Returns”Promise<CallResult<OkResult>>
getActiveMember
Section titled “getActiveMember”getActiveMember: () =>
Promise<CallResult<OrgMember>>
Returns
Section titled “Returns”Promise<CallResult<OrgMember>>
getInvitation
Section titled “getInvitation”getInvitation: (
id) =>Promise<CallResult<OrgInvitation>>
Requires a session. The answer has no user organization name or slug and no inviter email, so an accept screen cannot name the inviter from this call alone.
Parameters
Section titled “Parameters”string
Returns
Section titled “Returns”Promise<CallResult<OrgInvitation>>
inviteMember
Section titled “inviteMember”inviteMember: (
input) =>Promise<CallResult<OrgInvitation>>
A seat or invitation limit answers failed with code forbidden, the
same as a permission refusal. No code tells them apart yet, so do not
show an “out of seats” message based on it.
Parameters
Section titled “Parameters”string
organizationId?
Section titled “organizationId?”string
resend?
Section titled “resend?”boolean
"admin" | "member"
Returns
Section titled “Returns”Promise<CallResult<OrgInvitation>>
leave: (
organizationId) =>Promise<CallResult<OkResult>>
Parameters
Section titled “Parameters”organizationId
Section titled “organizationId”string
Returns
Section titled “Returns”Promise<CallResult<OkResult>>
list: () =>
Promise<CallResult<{organizations:Organization[]; }>>
Returns
Section titled “Returns”Promise<CallResult<{ organizations: Organization[]; }>>
listInvitations
Section titled “listInvitations”listInvitations: (
query?) =>Promise<CallResult<{invitations:OrgInvitation[]; }>>
Parameters
Section titled “Parameters”query?
Section titled “query?”organizationId?
Section titled “organizationId?”string
Returns
Section titled “Returns”Promise<CallResult<{ invitations: OrgInvitation[]; }>>
listMembers
Section titled “listMembers”listMembers: (
query?) =>Promise<CallResult<{members:OrgMember[];total:number; }>>
organizationId defaults to the session’s active user organization,
which is unset until setActive is called, so pass it explicitly.
Parameters
Section titled “Parameters”query?
Section titled “query?”limit?
Section titled “limit?”number
offset?
Section titled “offset?”number
organizationId?
Section titled “organizationId?”string
Returns
Section titled “Returns”Promise<CallResult<{ members: OrgMember[]; total: number; }>>
listSeatRefusals
Section titled “listSeatRefusals”listSeatRefusals: (
organizationId) =>Promise<CallResult<{refusals:OrgSeatRefusal[]; }>>
Owners and admins only; anyone else gets 404.
Parameters
Section titled “Parameters”organizationId
Section titled “organizationId”string
Returns
Section titled “Returns”Promise<CallResult<{ refusals: OrgSeatRefusal[]; }>>
listUserInvitations
Section titled “listUserInvitations”listUserInvitations: () =>
Promise<CallResult<{invitations:OrgInvitation[]; }>>
Returns
Section titled “Returns”Promise<CallResult<{ invitations: OrgInvitation[]; }>>
rejectInvitation
Section titled “rejectInvitation”rejectInvitation: (
invitationId) =>Promise<CallResult<OkResult>>
Parameters
Section titled “Parameters”invitationId
Section titled “invitationId”string
Returns
Section titled “Returns”Promise<CallResult<OkResult>>
removeMember
Section titled “removeMember”removeMember: (
input) =>Promise<CallResult<OkResult>>
Parameters
Section titled “Parameters”memberIdOrEmail
Section titled “memberIdOrEmail”string
organizationId?
Section titled “organizationId?”string
Returns
Section titled “Returns”Promise<CallResult<OkResult>>
setActive
Section titled “setActive”setActive: (
organizationId) =>Promise<CallResult<{organization:Organization|null; }>>
Sets the session’s active user organization. organizationId is
required; null clears it.
Parameters
Section titled “Parameters”organizationId
Section titled “organizationId”string | null
Returns
Section titled “Returns”Promise<CallResult<{ organization: Organization | null; }>>
update
Section titled “update”update: (
input) =>Promise<CallResult<Organization>>
Renames a user organization or sets its logo. logo: null clears it.
Parameters
Section titled “Parameters”string | null
string
organizationId?
Section titled “organizationId?”string
Returns
Section titled “Returns”Promise<CallResult<Organization>>
updateMemberRole
Section titled “updateMemberRole”updateMemberRole: (
input) =>Promise<CallResult<OrgMember>>
owner is not assignable, so an admin cannot hand the user organization away.
Parameters
Section titled “Parameters”memberId
Section titled “memberId”string
organizationId?
Section titled “organizationId?”string
"admin" | "member"
Returns
Section titled “Returns”Promise<CallResult<OrgMember>>
checkout()
Section titled “checkout()”checkout(
input,request?):Promise<CallResult<CheckoutResponseBody>>
Buy an offer, with the offerToken a decision just returned and a card
token from Recurly.js on your page. offerToken is an opaque string,
so a page can check out without deciding first.
ok: the purchase reached the charge. Readdata.status:granted,chargedorpending. Money may have moved in every case, so never render one as a failure; treat a status you do not know aspending.failedwith acodeto branch on: a409(an expired or unknown offer token, a closed offer, an expired card token, no active billing provider) or a422(card declined). After an expired offer token, decide again for a fresh one.failedwithcodethree_d_secure_required: run the Recurly.js 3-D Secure challenge ondetails.threeDSecureActionTokenId, then callcheckoutagain with the same tokens plusthreeDSecureActionResultTokenId.unavailable: a5xx, a network failure or an abort.- Throws EdgeKitConfigError on
400,401,403or404.
Nothing is retried for you, and timeoutMs does not apply. Your page
loads Recurly.js, so its Content Security Policy must allow it:
script-src https://js.recurly.com, plus connect-src and frame-src
for api.recurly.com, or api.eu.recurly.com when your public key
starts with fra-.
Parameters
Section titled “Parameters”request?
Section titled “request?”CheckoutOptions = {}
Returns
Section titled “Returns”Promise<CallResult<CheckoutResponseBody>>
createGiftLink()
Section titled “createGiftLink()”createGiftLink(
context?,request?):Promise<CallResult<CreateGiftLinkResponseBody>>
Create a gift link for this page, from the signed-in user. context is
the page’s facts, as on decide, at the same unverified tier. The link
is bound to the value of the fact your gift links are keyed on; when a
recipient opens it, the site’s rules still decide whether the page
opens. Append the returned token to the share URL as ?_gt=.
A link exists only when token does. A refusal is ok with
status: 'denied' and no token; treat any other status without a
token the same way. Signed out is failed with a 401; a page whose
facts lack the key fact is failed with a 422. Throws
EdgeKitConfigError on 400, 403 or 404, including when the
deployed site has no gift link feature.
Not idempotent: every call counts against the user’s gift quota and creates a new link.
Parameters
Section titled “Parameters”context?
Section titled “context?”Record<string, unknown>
request?
Section titled “request?”GiftLinkOptions = {}
Returns
Section titled “Returns”Promise<CallResult<CreateGiftLinkResponseBody>>
decide()
Section titled “decide()”decide<
F>(features,request?):Promise<DecideResults<S,F>>
Ask which outcome each feature shows on this page. Pass one feature name
or an array of up to 32. The result has an entry for every name, with
status decided, unknown (the deployed site has no such feature) or
unavailable (no answer).
A failed decision never throws, because this runs while the page
renders. A timeout, a network failure, a 429 or a 5xx makes every
feature unavailable; a 429 carries retryAfter, and nothing is
retried for you. It throws EdgeKitConfigError only for a setup
mistake: more than 32 features, or a 400, 401, 403 or 404 (for
example an unknown site or an unregistered origin).
The user is whoever the session cookie names, or an anonymous device.
Two parameters on the page URL are forwarded on every call: ?_gt=, a
gift link token, and ?edgekitPreview=, a preview token kept for the
tab until ?edgekitPreview=off. A token the tenant cannot verify is
ignored.
Type Parameters
Section titled “Type Parameters”F extends string
Parameters
Section titled “Parameters”features
Section titled “features”F | readonly F[]
request?
Section titled “request?”BrowserDecideOptions = {}
Returns
Section titled “Returns”Promise<DecideResults<S, F>>
forwardUrl()
Section titled “forwardUrl()”forwardUrl(
feature):string
The URL for a journey feature. Use it as an <a href> or with
location.assign, never with fetch: the URL answers 302 to the
destination, so fetching it fetches the destination page. Builds a
string and sends no request. The site id is in the query string, since a
navigation sends no custom header.
Parameters
Section titled “Parameters”feature
Section titled “feature”S["journeys"]
Returns
Section titled “Returns”string
grants()
Section titled “grants()”grants(
request?):Promise<CallResult<GrantsResponseBody>>
What the signed-in user holds: every grant that reaches this site,
their own and their user organizations’, newest term first. A purchase
through checkout, a comp, a gift and an import all arrive as the same
grant shape; label them in your UI however your site does.
Requires a session. Without one it answers failed with code
unauthorized, never a throw, including through an absolute baseUrl.
Expired, suspended and revoked grants are included, each with its
state; filter on state for the live ones.
Throws EdgeKitConfigError on 400, 403 or 404.
Parameters
Section titled “Parameters”request?
Section titled “request?”GrantsOptions = {}
Returns
Section titled “Returns”Promise<CallResult<GrantsResponseBody>>