Hi, I want to send magic link in the mail using different website domain. So is there any way in bac...
a
Hi, I want to send magic link in the mail using different website domain. So is there any way in backend to fetch any website domain and send magic link using that particular domain? And also any way to send additional values from frontend to backend middleware API's.
n
Hi @alen_george , Just so I understand correctly, you want to dynamically change the domain of the magic link (based on where the request is coming in I assume?) and also want to send extra params for the APIs that SuperTokens calls internally
a
Yes you are right.
n
What SDKs are you using on the frontend and backend?
a
Frontend Javascript , Backend Node JS and recipelist - Passwordless
n
supertokens-web-js?
a
yeah
Also sending mail from multiple custom domains possible ?
n
Right so on the frontend when you call the functions exposed by the recipes (
Passwordless.createCode
for example) you can use the
preApiHook
feature to pass additional params It would look similar to this
Copy code
Passwordless.createCode({
  email: "", // or phone
  options: {
    preAPIHook: async (input) => {
      // Modify input.requestInit
      return {
        url: input.url,
        requestInit: input.requestInit
      };
    },
  },
})
This way you can customise the payload, url etc of the network request
On the backend you can use our api override feature to consume the additional params https://supertokens.com/docs/passwordless/advanced-customizations/apis-override/usage You can also use the user context feature (https://supertokens.com/docs/passwordless/advanced-customizations/user-context) to pass custom information to future function calls. For example you could send your website domain as a custom request param to the API, then use userContext to get access to that domain in the the email delivery functions (https://supertokens.com/docs/passwordless/email-delivery/about) that let you modify the content of the email however you like
If you just want to modify the link and not the rest of the content, you can use the same flow as above but refer to this page: https://supertokens.com/docs/passwordless/common-customizations/change-magic-link-url for the email modification
a
Thanks a lot, I will try this out.
n
Happy to help
r
Hey. A simpler method would be to just read the userContext object in the sendEmail override and from there, access the request object using userContext._default.req. And from there, you can read the origin
See this link about the default object in userContext https://supertokens.com/docs/passwordless/advanced-customizations/user-context
a
Thanks for your suggestion. I will try this.
Hi, I'm using custom way to send an email. So according to this how should I read user context so that I get the origin ?
Copy code
emailDelivery: {
                override: (originalImplementation) => {
                    return {
                        ...originalImplementation,
                        sendEmail: async function ({
                            codeLifetime, // amount of time the code is alive for (in MS)
                            email,
                            urlWithLinkCode, // magic link
                            userInputCode, // OTP
                        }) {}
r
One of the inputs to sendEmail function has userContext
See the function signature from your code editor
a
Sure thanks
Hi, I'm using server side rendering in my angular application. On building and while serving the application I'm getting this error
Error: Please provide a valid sessionScope
Could you please help me with this issue.
r
You should call supertokens.init on the frontend only if
typeof window !== "undefined"
a
If I have used Session anywhere else in the code, for eg.
await Session.getUserId()
, Should this also be run in frontend only ?
r
Yea. Depends on which SDK you are using. If the frontend SDK, then yea, frontend only
a
Thanks. Is there any way to identify whether the session has expired or not apart from the doesSessionExist() method.
r
When you make an API call, it will result in a 401.
There is also an event that’s fired which you can listen to. See https://supertokens.com/docs/session/advanced-customizations/frontend-hooks/handle-event
a
Thanks a lot.
Will supertokens API's work for safari browser too?
r
Yea. As long as the websiteDomain and apiDomain share the same top level domain
a
Ok thanks.
Is this only for safari or all other browsers as well ?
r
only safari
a
Got it. Will you be providing any updates for safari in future so that it supports other domains as well ?
r
yes. We will roll out header based sessions as well
a
Thanks a lot.
Hi I was trying with Safari browser, My other API calls are being blocked and Im getting this error. Once if comment out the Supertokens.init it will work normally. Do you guys have any solution for this ?
r
does it give this error in other browsers as well?
a
Not for other browsers
r
let me check
a
Only for Safari
And my other API calls are not being triggered
r
what do you mean by that?
a
My website has several other API's which are not part of Supertokens. When I have supertoken.init function inside my codebase, it gives this error and my other backend functions are not being called. When I comment out the supertokens.init it works normally
r
so the other APIs aren't called at all when you call them from the frontend?
a
yeah
r
i see. Can you please open an issue about this? We will fix it ASAP
a
it shows the above error
Sure where should I create this issue ?
r
the above error doens't actually cause any problems. But we will check it out nonetheless
you can make an issue on supertokens-website repo
a
Ok Got it
r
can we get on a quick call as well so i can see what's happening please?
a
I will add you to a call with my team mate
We can discuss the issue
r
alright1
when do you want to have it?
a
now is it possible ?
r
yea sure
I'll send a link
a
sure thanks
a
Hi, I have opened an issue. Please do let me know once you have fixed it. Atleast if other API's can work it would be nice.
r
Yea. We will fix it ASAP.
a
Thanks
Sorry to disturb, just wanted to know if you could provide me any date before which we could see the fix. I wanted to inform my team members about timeline.
r
well, 3-4 days
we are trying to see what the issue is now
if we figure it out now, then we can release a fix today
but if not, then it may take 3-4 days
a
Sure thanks a lot. Let me know if you need any other information. Avaliable on Discord as well.
r
thanks
hey @alen_george . We have released the fix. Please see my comment here: https://github.com/supertokens/supertokens-website/issues/168#issuecomment-1289376778
a
Hi @rp_st, thanks a lot. I will try this out and let know if the error persists.
Also I was facing one more issue, while doing
await Session.signout()
it's not clearing the cookies properly. Due to which when I check if Session exist or not it returns true and takes me to dashboard.
Do I have to do something else apart from Session.signout to clear the cookies.
r
Nothing else should be required. Try updating the SDK and seeing if it fixes that issue or not
a
I have tried out the fix which you guys gave. Its not throwing the error. Thanks a lot.
r
Great
a
sure I will try it.
I tried signing out. For both Safari browser and incognito mode Sign out doesn't work for me. Still the cookies are present.
r
Hmm I see. Can you send me the request and response headers of the sign out api call?
a
So I'm directly calling it using
await Session.signout()
function from frontend.
In this way only we have to signout right ?
r
You can call the api yourself too
But calling the function should work
What’s the api call being made?
a
/auth/signout
r
Yes
When you call the function, does it not make an API call?
a
The API is being made, getting a success message. But cookies still present.
r
So the request doesn’t have any cookies attached to them
a
So this in safari
r
Do the cookies get sent for regular api calls?
a
regular API calls means supertoken's middleware or our own API ?
r
Supertokens middleware. For example the refresh api call
And also, what are the set-cookies response header in the login api?
a
No cookies being attached in signinup/code API
r
so that API just generates a code
see the API call for the one that consumes the code
a
yeah correct
Ok
Its sending the cookies
Here we are getting the cookies right
But while signing out we aren't sending the cookies in Safari browser or incognito mode
you guys know any way to resolve this issue ?
n
Can you share what configuration you use when calling SuperTokens.init on the frontend and backend?
a
Ok just one mic
min*
Front end :
Copy code
SuperTokens.init({
        appInfo: {
          apiDomain: Constants.SUPERTOKENS_SVC_URL,
          apiBasePath: "/auth",
          appName: "blocksurvey",
        },
        recipeList: [
          Session.init(),
          Passwordless.init()
        ],
      });
Backend :
Copy code
supertokens.init({
    framework: 'express',
    supertokens: {
        // These are the connection details of the app you created on supertokens.com
        connectionURI: config.supertokensConfig.connection_url,
        apiKey: config.supertokensConfig.api_key,
    },
    appInfo: {
        // learn more about this on https://supertokens.com/docs/session/appinfo
        appName: 'blocksurvey',
        apiDomain: apiDomain,
        websiteDomain: websiteDomain,
        apiBasePath: '/auth',
        websiteBasePath: '/signin',
    },
    recipeList: [
        Passwordless.init({
            flowType: 'USER_INPUT_CODE_AND_MAGIC_LINK',
            contactMethod: 'EMAIL',
Are these details enough?
n
What are the values for
Constants.SUPERTOKENS_SVC_URL
on the frontend and
websiteDomain
on the backend?
a
Hi so you guys have any fix for this ?
Or am I doing something wrong ?
r
So the cookies are being set
im not sure why the browser is not sending the cookies in the sign out API
Is it working on chrome?
a
yeah its working on chrome and other browsers apart from safari and incognito mode
r
i see.
I think the issue here is cross site requests
safari disables sending cookies in this case
whats ur website domain going to be in production?
a
it will be varying , we have some clients with custom domain as well
how to forcefully clean the supertokens cookies then ?
r
well, you can't. Cause safari doesn't allow that cross domain. So the best thing you should do is to make the apiDomain a sub domain of the actual website domain
so if websiteDomain is going to be example.com, make the API domain api.example.com or something
and the api domain would reverse proxy to the onrender link you are using now
a
Can we clear the cookies on our own forcefully? Will it create any issues ?
r
you can't. Safari just makes that impossible.
And also you need to have cookies sent over cause things like email verification won't work
You can either do that, or you can switch to non cookie based auth, which is possible, but would require a few customisations on your end. See this example app -> https://github.com/supertokens/supertokens-auth-react/tree/master/examples/with-localstorage
note that if you switch to non cookie based auth, it opens your app to vulnerabilities like token theft via XSS. So if I were you, I would just stick to cookie based auth and setup a reverse proxy sub domain for each of the frontends.
And a reverse proxy sub domain is as simple as adding a DNS record.
a
Actually we have clients who will be using their own domains for hosting our services.
It will not be possible if we go with reverse proxy
So you are saying forceful clearing of cookies in Safari is not possible ?
r
correct. Safari is very strict about cross domain cookies
So then you should enable non cookie based auth
checkout the example app above.
a
Ok thanks.
r
the example above uses axios / fetch and not httpclient. For httpclient, you will need to add an interceptor just like how axios interceptor is added in the above example: https://angular.io/guide/http#write-an-interceptor
a
We are not going with non cookie method as you suggested it will be vulnerable.
Will you be providing any fix for this in future ?
r
well, there is no fix for this other than what i suggested: - Setup a reverse proxy / CNAME for the api domain - Or use non cookie based auth
it's just how Safari chooses to work
a
Ok got it, thanks a lot.
Hi, I want to send a query param from frontend to backend and attach that query param with the magic link send in the mail. Could you please suggest how I could achieve this ?
r
When the create code API is called, you can use the pre API hook to add your own custom property to the request body
then in the backend, you override the sendEmail function in the email delivery config and in that, you can get the request object from the userContext._default property
then from the request object, you can read the custom property and add it to the magic link however you like.
a
Any code refer you have for pre API hook ?
r
you are using passwordless recipe right?
a
Yeah
a
Thanks
I dont use this npm package
"supertokens-auth-react/recipe/passwordless"
. Instead I use
supertokens-web-js/recipe/passwordless
. Will it create any issue?
r
ah right. In that case, the pre API function is available as an input to the function call itself
a
sorry I didnt get it
r
So like this:
Copy code
ts
Passwordless.createCode({
    email: "...",
    options: {
        preAPIHook: async (context) => {
            // TODO: modify request body in context
            return context;
        }
    }
})
a
where should I put this ?
r
i mean wherever you are calling the
createCode
function
a
Ohh got it. Thanks.
In backend then how can I fetch this?
r
override the sendEmail function first, and then read it from the request object available in the userContext input to the sendEmail function
a
Ok thanks
Hi @rp_st this is my sample magic link
http://localhost:4200/signin?rid=passwordless&preAuthSessionId=WvK4-jhGS2O1zIc13Ba7P1nhu-HuZ_BCLDQqC-djGTY=#yvtA3M0PM2sl3Qhi-3j2CYEZy1OXqE5uNFr3brzU5A4=&redirectTo=true
I have added an extra query param
redirectTo=true
. But while validating the url and consuming it. The backend is throwing an error :
Copy code
Error: SuperTokens core threw an error for a POST request to path: '/recipe/signinup/code/consume' with status code: 400 and message: Input encoding error in linkCode

    at Querier.<anonymous> (C:\supertoken-function\node_modules\supertokens-node\lib\build\querier.js:252:31)
    at Generator.throw (<anonymous>)
    at rejected (C:\supertoken-function\node_modules\supertokens-node\lib\build\querier.js:22:44)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
How can I resolve this ?
r
That’s cause you are adding the query param to the fragment part of the url
The fragment part is the stuff followed by the β€œ#” part in the url
You want to add the query param before that
a
How can I add that ?
The link code is forming automatically right from your side
r
Yes. But you can deconstruct the url and add the query param and construct the url again
String manipulation
Or use a package which allows you to change query params from a url
a
Ok got it
but if I put it above rid also its throwing same error
http://localhost:4200/signin?redirectTo=true&rid=passwordless&preAuthSessionId=WvK4-jhGS2O1zIc13Ba7P1nhu-HuZ_BCLDQqC-djGTY%3D#yvtA3M0PM2sl3Qhi-3j2CYEZy1OXqE5uNFr3brzU5A4&=
something like this
r
Yea this should work
a
But this is not working
same error Im getting
r
Are you sure you are correctly changing the url? As in not messing up the stuff after the # tag
a
No im not
r
Maybe console log the link before you change it and after you change it and see the difference
If it’s correct, then maybe open an issue about this on our GitHub and we will check it out
a
Ok I will look into it anc come back to you
Thanks
r
Thanks πŸ™‚
a
Hi, I was getting this error from backend while verifying session user Id :
Copy code
{
    status: "failed", 
    message: "Access token has expired. Please call the refresh API"
}
How can I refresh the access token ? and what expiration time do you recommend for access token ?
Also wanted to know whether we can use 2 3 recipes together ? and compared to passwordless and social auth recipes which one is more converting in numbers? Which one would you recommend?
Do you have some recipes related to Web 3 in your plan ?
r
Does that reply come from the core or from the backend SDK?
> Also wanted to know whether we can use 2 3 recipes together ? and compared to passwordless and social auth recipes which one is more converting in numbers? Which one would you recommend? You can use multiple recipes together as well. I don't have much info in conversions im afraid.
> Do you have some recipes related to Web 3 in your plan ? Yea.. probably next year
a
Which recipe you would recommend the best to go with ?
I'm getting this from backend
Because my access token got expired in frontend. So its not sending the access token along with cookie
I may have to refresh the access toke non frontend. Any idea how I can do that ?
r
the frontend SDK should do that on its own
a
Its not creating.
So I was verifying the session in my backend in this way. As access token expired , it went to catch flow and throw me the above error.
Copy code
let session = await Session.getSession(req, res);
        let userId = session.getUserId();
For testing purpose I kept access token expiry time 60 seconds.
r
have you added the supertokens error handler to your app?
a
fronend or backend ?
r
backend
a
No
r
thats one of the steps in backend quick setup
please add that (before your app's error handler) and then refresh will start working
a
app.use(errorHandler());
this is the one right ?
I have added this already
r
hmm
have you added that after your api routes?
or before?
a
Before it seems
Should that be last ?
r
yes
after all your apis
and before your own app's error handler (if you have made that)
a
Ok got it. thanks
Will try that and let you know
r
ok
a
I wanted to know how this refresh will work? Like I kep 60 secs for expiry. After 60 seconds what will happen ?
Will it automatically refresh ?
r
yes
backend will send a 401
and then frontend sdk will automatically call the refresh API
see the session management section in our docs
a
Ok actually the api from which I'm getting my error is my own API.
I tried keeping the error handler at last
Its not refreshing the error
I'm getting a 500 error
r
Is the error even hitting our error handler?
Use verifySession function instead of getSession in this case if you can’t fix this
a
Mine is JS file , it wont support the Session request method
r
huh?
verifySession works with JS too
anyway, make sure that the error thrown by getSession reaches our error handler
and that the API ends up returning a 401
a
app.post("/like-comment", verifySession(), (req: SessionRequest, res) => { let userId = req.session!.getUserId(); //.... });
Here getting an error in req: SessionRequest
as my file type is not TS
r
remove the ": SessionRequest" part
a
let userId = req.session!.getUserId(); This also throws not null error right
r
yea
a
Hi I'm trying to use third party auth recipe. I'm getting this error while initializing the providers :
Copy code
providers: [
                           ^

ReferenceError: Google is not defined
r
check how you have imported it
a
const ThirdParty = require('supertokens-node/recipe/thirdparty');
r
So do you do
ThirdParty.Google.init()
?
a
Copy code
ThirdParty.init({
            signInAndUpFeature: {
                providers: [
                    // We have provided you with development keys which you can use for testing.
                    // IMPORTANT: Please replace them with your own OAuth keys for production use.
                    Google({
                        clientId: "1060725074195-kmeum4crr01uirfl2op9kd5acmi9jutn.apps.googleusercontent.com",
                        clientSecret: "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"
                    }),
                    Github({
                        clientId: "467101b197249757c71f",
                        clientSecret: "e97051221f4b6426e8fe8d51486396703012f5bd"
                    }),
                    Apple({
                        clientId: "4398792-io.supertokens.example.service",
                        clientSecret: {
                            keyId: "7M48Y4RYDL",
                            privateKey:
                                "-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----",
                            teamId: "YWQCXGJRJL",
                        },
                    }),
                    // Facebook({
                    //     clientSecret: "FACEBOOK_CLIENT_SECRET",
                    //     clientId: "FACEBOOK_CLIENT_ID"
                    // })
                ]
            }
        }),
I have done this way which was given in docs
r
Change it to ThirdParty.Google instead
And same goes for other providers
see the import statement vs your code
a
Ok
Hi just wanted to know how can I handles this API in backend ? http://localhost:3000/auth/callback/google
r
this is a frontend route
a
Ok got it, thanks
How can I resolve it ?
r
depends on the error.. i will need more info
is this the right path? What are you doing on the path? What error is the network call showing? If it's a 500 error from the API, what is the backend error showing?
a
No network call is being made , Do I have to set some token id in cookies ?
r
please see our docs
a
Copy code
import { signInAndUp } from "supertokens-web-js/recipe/thirdparty";

async function handleGoogleCallback() {
    try {
        const response = await signInAndUp();

        if (response.status === "OK") {
            console.log(response.user)
            if (response.createdNewUser) {
                // sign up successful
            } else {
                // sign in successful
            }
            window.location.assign("/home");
        } else {
            // SuperTokens requires that the third party provider
            // gives an email for the user. If that's not the case, sign up / in
            // will fail.

            // As a hack to solve this, you can override the backend functions to create a fake email for the user.

            window.alert("No email provided by social login. Please use another form of login");
            window.location.assign("/auth"); // redirect back to login page
        }
    } catch (err: any) {
        if (err.isSuperTokensGeneralError === true) {
            // this may be a custom error message sent from the API by you.
            window.alert(err.message);
        } else {
            window.alert("Oops! Something went wrong.");
        }
    }
}
r
signInAndUp
should make a network call
a
I'm using this funct on call of for redirect route
Is there any error in this route url?
r
doesn't seem to be
is your code running ? is the signInAndUp function being called?
a
Its entering the async function but not triggering the signInAndUp() func.
r
hmm. Thats weird
please check your code
Not sure how i can help here. The docs are very clear
a
Copy code
async handleGoogleCallback() {
    console.log("true");
    
    try {
      const response = await signInAndUp();
      console.log(response);

      if (response.status === "OK") {
        console.log(response.user)
        if (response.createdNewUser) {
          // sign up successful
          console.log(response);

        } else {
          console.log(response);
          // sign in successful
        }
        // window.location.assign("/signup");
      } else {
        // SuperTokens requires that the third party provider
        // gives an email for the user. If that's not the case, sign up / in
        // will fail.

        // As a hack to solve this, you can override the backend functions to create a fake email for the user.

        window.alert("No email provided by social login. Please use another form of login");
        window.location.assign("/login"); // redirect back to login page
      }
    } catch (err: any) {
      if (err.isSuperTokensGeneralError === true) {
        // this may be a custom error message sent from the API by you.
        window.alert(err.message);
      } else {
        window.alert("Oops! Something went wrong.");
      }
    }
  }
this is my implementation
r
you need to make sure that this function is called and that the network request is going
otherwise, i can't really help πŸ™‚
hope this makes sense
a
Ok
In session storage this is being stored { "stateForAuthProvider": "07b3c2414befb4c22d8c4", "providerId": "google", "expiresAt": 1669631935907, "authorisationURL": "http://localhost:4200/login-auth/google" }
anything missing in this or something I need to add?
r
Hmmm. The state is different. Other than that, it seems all fine
Can you clear storage and try again?
a
Hi, I'm using ThirdpartyPasswordless recipe. I wanted to know is it possible to stop user from signing up from different provider which has same user email address ?
Eg. I used one email address in passwordless to sign up and then again using google prover I signed up using same email address
Any way to stop this ?
r
yea it is. We are going to write docs for it this week - so wait for that. But you can also see this github issue: https://github.com/supertokens/supertokens-node/issues/436
also, please ask questions on the support-questions channel if they are not related to this thread.
a
Ok Sure, thanks a lot.
Sorry to ask in this thread again, ThirdPartyEmailPassword.init is this same as ThirdPartyPasswordless.init ??
n
ThirdPartyEmailPassword: Adds social login and email password login ThirdPartyPasswordless: Adds social login and passwordless login
They are two different recipes
a
So the github issue thread which you shared with me I can use my recipe right ?
instead of ThirdPartyEmailPassword
n
Ah right, yeah most of the snippets should remain the same
If you face any issues let us know we can help with the specifics
r
you will need to override the right set of APis for your recipe
but yea, the logic is the same
n
Some parts of it like
emailPasswordSignUp
wont apply to you
r
we will be writing docs for this sometime this week. So please stay tuned.
a
Sure thanks
For third party its working for me, just for passwordless email which API should I mention. could you pls help, we wanted to make this live as our users are asking.
n
For thirdpartypasswordless you want to override
consumeCodePOST
a
Is it how this should be written ?
Copy code
functions: (oI) => {
                    return {
                        ...oI,
                        consumeCodePOST: async function (input) {
                            console.log(input);
                            console.log(oI);

                            let existingUser = await ThirdPartyPasswordless.getUsersByEmail(input.email);
                            if (existingUser.length === 0) {
                                return oI.consumeCodePOST(input);
                            }
                            return {
                                status: "EMAIL_ALREADY_EXISTS_ERROR"
                            };
                        },
                        thirdPartySignInUp: async function (input) {
                            let existingUser = await ThirdPartyPasswordless.getUsersByEmail(input.email);
                            if (existingUser.length === 0) {
                                return oI.thirdPartySignInUp(input);
                            }
                            if (existingUser.find(i => i.thirdParty !== undefined && i.thirdParty.id === input.thirdPartyId)) {
                                // this means we are trying to sign in with the same social login now. So we allow it
                                return oI.thirdPartySignInUp(input);
                            }
                            throw new Error("Cannot sign up as email already exists");
                        }
                    }
                }
r
Yea I guess. Again, please wait for us to write docs for this and then you can compare πŸ™‚
a
Its working for third party , but not for passwordless email. I'm able to create account with same email address with passwordless email.
n
consumeCode
is the function
If that doesnt work id recommend just waiting for the docs to be done
a
Getting this error :
Error: SuperTokens core threw an error for a GET request to path: '/recipe/user' with status code: 400 and message: Please provide exactly one of userId, email or phoneNumber
Copy code
return {
                        ...oI,
                        consumeCode: async function (input) {
                            console.log(input);
                            console.log(oI);

                            let existingUser = await ThirdPartyPasswordless.getUsersByEmail(input.email);
                            if (existingUser.length === 0) {
                                return oI.consumeCode(input);
                            }
                            return {
                                status: "EMAIL_ALREADY_EXISTS_ERROR"
                            };
                        },
this is my code
r
hey @alen_george
please be patient since we will write docs for this sometime this week
if you can't figure this out on your own, you will have to wait, or then subscribe to our support plan in which we can really help you
thank you.
a
Thanks.
r
a
Thanks a lot!
r
πŸ‘ lmk if you run into any issues related to this feature.
a
Definately, I see for passwordless email auth, you will trigger the mail with OTP or magic link. Isn't there any way to check this deduplication before triggering the mail itself?
r
we are checking for deduplication before triggering the email. So no email / sms will be sent
a
Oh got it. Thanks
10 Views