Web API Reference
Web API Reference
#Constructor
Configure and construct your Magic SDK instance.
#Arguments
new Magic(apiKey, options?)
Parameter | Type | Definition |
apiKey | String | Your publishable API Key retrieved from the Magic Dashboard. |
options.locale? | String | Customize the language of Magic's modal, email and confirmation screen. See Localization for more. |
options.testMode? | Boolean | Enable testMode to assert the desired behavior through the email address you provide to loginWithMagicLink without having to go through the auth flow. |
options.network? | String | Object | (String): A representation of the connected Ethereum network (one of: mainnet, rinkeby, kovan, or ropsten).
(Object): A custom Ethereum Node configuration with the following shape:
• rpcUrl (String): A URL pointing to your custom Ethereum Node.
• chainId? (Number): Some Node infrastructures require you to pass an explicit chain ID. If you are aware that your Node requires this configuration, pass it here as an integer. |
options.endpoint? | String | A URL pointing to the Magic <iframe> application. |
#Example
01import { Magic } from 'magic-sdk';
02
03let m;
04
05// Construct with an API key:
06m = new Magic('API_KEY');
07
08// Construct with an API key and locale:
09m = new Magic('API_KEY', { locale: 'es' });
10
11// Construct with an API key and testMode enabled:
12m = new Magic('API_KEY', { testMode: true });
13
14// Construct with an API key plus options:
15m = new Magic('API_KEY', { network: 'rinkeby', endpoint: '...' });
👉 Learn more about using Magic SDK with Ethereum!
#Global Methods
Global methods and properties are accessible on the Magic SDK instance itself.
01import { Magic } from 'magic-sdk';
02
03const m = new Magic('API_KEY');
04
05m.preload;
#preload
Starts downloading the static assets required to render the Magic iframe context.
#Arguments
None
#Returns
Promise<void>
: A Promise that resolves to indicate the <iframe>
is ready for requests.
#Example
01import { Magic } from 'magic-sdk';
02
03const m = new Magic('API_KEY');
04
05m.preload().then(() => console.log('Magic <iframe> loaded.'));
#Auth Module
The Auth Module and it's members are accessible on the Magic SDK instance by the auth property.
01import { Magic } from 'magic-sdk';
02
03const m = new Magic('API_KEY');
04
05m.auth;
06m.auth.loginWithMagicLink;
07m.auth.loginWithSMS;
08m.auth.loginWithCredential;
09m.auth.loginWithEmailOTP;
#loginWithMagicLink
Authenticate a user passwordlessly using a "magic link" sent to the specified user's email address.
#Arguments
loginWithMagicLink({ email, showUI? = true, redirectURI? })
Parameter | Type | Definition |
email | String | The user email to log in with. |
showUI? | Boolean | If true , show an out-of-the-box pending UI while the request is in flight. |
redirectURI? | String | You can provide a redirect URI that Magic will point to after the user clicks their email link. Don't forget to call loginWithCredential at the specified redirect location!
Note: If you are securing a resource server and have your own
signup flow after this call resolves, be mindful of where you're calling
signup in your implementation to avoid potential concurrency issues! |
#Returns
PromiEvent<string | null>
: The promise resolves upon authentication request success and rejects with a specific error code if the request fails. The resolved value is a Decentralized ID token with a default 15-minute lifespan.
#Example
01import { Magic } from 'magic-sdk';
02
03const m = new Magic('API_KEY');
04
05// log in a user by their email
06try {
07 await m.auth.loginWithMagicLink({ email: 'hello@example.com' });
08} catch {
09 // Handle errors if required!
10}
11
12// log in a user by their email, without showing an out-of-the box UI.
13try {
14 await m.auth.loginWithMagicLink({ email: 'hello@example.com', showUI: false });
15} catch {
16 // Handle errors if required!
17}
#Error Handling
To achieve a fully white-labeled experience, you will need to implement some custom error handling according to your UI needs. Here's a short example to illustrate how errors can be caught and identified by their code:
01import { Magic, RPCError, RPCErrorCode } from 'magic-sdk';
02
03const m = new Magic('API_KEY');
04
05try {
06 await m.auth.loginWithMagicLink({ email: 'hello@example.com', showUI: false });
07} catch (err) {
08 if (err instanceof RPCError) {
09 switch (err.code) {
10 case RPCErrorCode.MagicLinkFailedVerification:
11 case RPCErrorCode.MagicLinkExpired:
12 case RPCErrorCode.MagicLinkRateLimited:
13 case RPCErrorCode.UserAlreadyLoggedIn:
14 // Handle errors accordingly :)
15 break;
16 }
17 }
18}
#Events
Event Name | Definition |
email-not-deliverable | Dispatched if the magic link email is unable to be delivered. |
email-sent | Dispatched when the magic link email has been successfully sent from the Magic Link server. |
retry | Dispatched when the user restarts the flow. This can only happen if showUI: true . |
#loginWithEmailOTP
Authenticate a user passwordlessly using an email one-time code sent to the specified user's email address.
#Arguments
loginWithEmailOTP({ email, showUI? = true })
Parameter | Type | Definition |
email | String | The user email to log in with. |
| Boolean | If |
PromiEvent<string | null>
: The promise resolves upon authentication request success and rejects with a specific error code if the request fails. The resolved value is a Decentralized ID token with a default 15-minute lifespan.
#Example
01import { Magic } from 'magic-sdk';
02
03const m = new Magic('API_KEY');
04
05// log in a user by their email
06try {
07 await m.auth.loginWithEmailOTP({ email: 'hello@example.com' });
08} catch {
09 // Handle errors if required!
10}
11
12// log in a user by their email, without showing an out-of-the box UI.
13try {
14 await m.auth.loginWithEmailOTP({ email: 'hello@example.com', showUI: false });
15} catch {
16 // Handle errors if required!
17}
#Event Handling
A white-label OTP login flow is available when passing showUI: false
to this login method. Here's a short example to illustrate listening for and emitting events during the login flow:
01import { Magic } from 'magic-sdk';
02
03const magic = new Magic('API_KEY');
04
05try {
06 // Initiate login flow
07 const handle = magic.auth.loginWithEmailOTP({ email: "hello@example.com", showUI: false });
08
09 handle
10 .on('email-otp-sent', () => {
11 // The email has been sent to the user
12
13 // Prompt the user for the OTP
14 const otp = window.prompt('Enter Email OTP');
15
16 // Send the OTP for verification
17 handle.emit('verify-email-otp', otp);
18 })
19 .on('invalid-email-otp', () => {
20 // User entered invalid OTP
21
22 /*
23 Have the user retry entering the OTP.
24 Then emit the "verify-email-otp" event with the OTP.
25 */
26
27 /*
28 You may limit the amount of retries and
29 emit a "cancel" event to cancel the login request.
30 */
31
32 // cancel login request
33 handle.emit('cancel');
34 })
35 .on('done', (result) => {
36 // is called when the Promise resolves
37
38 // convey login success to user
39 alert('Login complete!');
40
41 // DID Token returned in result
42 const didToken = result;
43 })
44 .on('error', (reason) => {
45 // is called if the Promise rejects
46 console.error(reason);
47 })
48 .on('settled', () => {
49 // is called when the Promise either resolves or rejects
50 })
51} catch (err) {
52 // handle errors
53}
#Events
Event Name | Definition |
email-otp-sent | Dispatched when the OTP email has been successfully sent from the Magic server. |
verify-email-otp | Emit along with the OTP to verify the code from user. |
invalid-email-otp | Dispatched when the OTP sent fails verification. |
| Emit to cancel the login request. |
#Error Handling
To achieve a fully white-labeled experience, you will need to implement some custom error handling according to your UI needs. Here's a short example to illustrate how errors can be caught and identified by their code:
01import { Magic, RPCError, RPCErrorCode } from 'magic-sdk';
02
03const m = new Magic('API_KEY');
04
05try {
06 await m.auth.loginWithEmailOTP({ email: 'hello@example.com' });
07} catch (err) {
08 if (err instanceof RPCError) {
09 switch (err.code) {
10 case RPCErrorCode.MagicLinkExpired
11 case RPCErrorCode.UserAlreadyLoggedIn:
12 // Handle errors accordingly :)
13 break;
14 }
15 }
16}
#loginWithSMS
Authenticate a user passwordlessly using a one-time code sent to the specified phone number.
List of Currently Blocked Country Codes
#Arguments
loginWithSMS({ phoneNumber })
Parameter | Type | Definition |
phoneNumber | String | E.164 formatted phone number. |
#Returns
PromiEvent<string | null>
: The promise resolves upon authentication request success and rejects with a specific error code if the request fails. The resolved value is a Decentralized ID token with a default 15-minute lifespan.
#Example
01import { Magic } from 'magic-sdk';
02
03const magicClient = new Magic('API_KEY');
04
05// log in a user by their phone number
06try {
07 await magicClient.auth.loginWithSMS({ '+14151231234' });
08} catch {
09 // Handle errors if required!
10}
#Error Handling
To achieve a fully white-labeled experience, you will need to implement some custom error handling according to your UI needs. Here's a short example to illustrate how errors can be caught and identified by their code:
01import { Magic, RPCError, RPCErrorCode } from 'magic-sdk';
02
03const m = new Magic('API_KEY');
04
05try {
06 await m.auth.loginWithSMS({ phoneNumber: "+14151231234" });
07} catch (err) {
08 if (err instanceof RPCError) {
09 switch (err.code) {
10 case RPCErrorCode.AccessDeniedToUser:
11 case RPCErrorCode.MagicLinkRateLimited:
12 case RPCErrorCode.UserAlreadyLoggedIn:
13 // Handle errors accordingly :)
14 break;
15 }
16 }
17}
#loginWithCredential
Authenticate a user via a "Magic Credential," a special, one-time-use DID Token created by the user to hydrate their authentication between page reloads. For example: when executing the loginWithMagicLink
flow with a redirectURI
specified, you can invoke this method to complete the authentication "callback," similar in principal to OAuth 2.0 flows.
If given no arguments, this method will parse the credential token automatically from window.location.search
.
#Arguments
loginWithCredential(credentialOrQueryString?)
Parameter | Type | Definition |
credentialOrQueryString? | String | A credential token or a valid query string (prefixed with ? or # ). By default, this method will look for the a credential token on the magic_credential key of window.location.search . |
#Returns
PromiEvent<string | null>
: The promise resolves upon authentication request success and rejects with a specific error code if the request fails. The resolved value is a Decentralized ID token with a default 15-minute lifespan.
#Example
From your login page:
01import { Magic } from 'magic-sdk';
02
03const m = new Magic('API_KEY');
04
05try {
06 await m.auth.loginWithMagicLink({
07 email: 'hello@example.com',
08 redirectURI: 'https://yourdomain.com/your/magic/link/callback',
09 });
10
11 // When the user clicks their magic link, they will be logged-in here
12 // and in the "callback" context.
13} catch {
14 // Handle errors if required!
15}
From your authentication callback page:
01import { Magic } from 'magic-sdk';
02
03const m = new Magic('API_KEY');
04
05try {
06 await m.auth.loginWithCredential();
07} catch {
08 // Handle errors if required!
09}
10
11// You can also provide the credential yourself
12try {
13 await m.auth.loginWithCredential('iamacredentialtoken');
14} catch {
15 // Handle errors if required!
16}
17
18// You can also provide the credential as a query string
19try {
20 await m.auth.loginWithCredential(window.location.search);
21} catch {
22 // Handle errors if required!
23}
#User Module
The User Module and it's members are accessible on the Magic SDK instance by the user property.
01import { Magic } from 'magic-sdk';
02
03const m = new Magic('API_KEY');
04
05m.user;
06m.user.updateEmail;
07m.user.updatePhoneNumber;
08m.user.getIdToken;
09m.user.generateIdToken;
10m.user.getMetadata;
11m.user.isLoggedIn;
12m.user.logout;
#updateEmail
Initiates the update email flow that allows a user to change their email address.
#Arguments
updateEmail({ email, showUI? = true })
Parameter | Type | Definition |
email | String | The new email to update to. |
showUI? | Boolean | If true , shows an out-of-the-box pending UI which includes instructions on which step of the confirmation process the user is on. Dismisses automatically when the process is complete. |
#Returns
PromiEvent<boolean>
: The promise resolves with a true boolean value if update email is successful and rejects with a specific error code if the request fails.
#Example
01import { Magic } from 'magic-sdk';
02
03const m = new Magic('API_KEY');
04
05// Initiates the flow to update a user's current email to a new one.
06try {
07 ...
08 /* Assuming user is logged in */
09 await magic.user.updateEmail({ email: 'new_user_email@example.com' });
10} catch {
11 // Handle errors if required!
12}
13
14/**
15 * Initiates the flow to update a user's current email to a new one,
16 * without showing an out-of-the box UI.
17 */
18try {
19 /* Assuming user is logged in */
20 await magic.user.updateEmail({ email: 'new_user_email@example.com', showUI: false });
21} catch {
22 // Handle errors if required!
23}
#Error Handling
To achieve a fully white-labeled experience, you will need to implement some custom error handling according to your UI needs. Here's a short example to illustrate how errors can be caught and identified by their code:
01import { Magic, RPCError, RPCErrorCode } from 'magic-sdk';
02
03const m = new Magic('API_KEY');
04
05try {
06 await m.user.updateEmail({ email: 'hello@example.com', showUI: false });
07} catch (err) {
08 if (err instanceof RPCError) {
09 switch (err.code) {
10 case RPCErrorCode.UpdateEmailFailed:
11 // Handle errors accordingly :)
12 break;
13 }
14 }
15}
#Events
Event Name | Definition |
new-email-confirmed | Dispatched when the magic link has been clicked from the user’s new email address. |
email-sent | Dispatched when the magic link email has been successfully sent from the Magic Link server to the user’s new email address. |
email-not-deliverable | Dispatched if the magic link email is unable to be delivered to the user’s new email address. |
old-email-confirmed | Dispatched when the magic link has been clicked from the user’s previous email address. |
retry | Dispatched when the user restarts the flow. This can only happen if showUI: true . |
#updatePhoneNumber
Initiates the update phone number flow that allows a user to change their phone number.
#Arguments
None
#Returns
PromiEvent<string>
: The promise resolves to a string value of the updated phone number if update is successful and rejects if the request fails.
#Example
01import { Magic } from 'magic-sdk';
02
03const m = new Magic('API_KEY');
04
05// Initiates the flow to update a user's phone number to a new one.
06try {
07 ...
08 /* Assuming user is logged in */
09 await m.user.updatePhoneNumber();
10} catch {
11 // Handle errors if required!
12}
#getIdToken
Generates a Decentralized Id Token which acts as a proof of authentication to resource servers.
#Arguments
getIdToken({ lifespan? = 900 })
Parameter | Type | Definition |
lifespan? | Number | Will set the lifespan of the generated token. Defaults to 900s (15 mins) |
#Returns
PromiEvent<string>
: Base64-encoded string representation of a JSON tuple representing
#Example
01import { Magic } from 'magic-sdk';
02
03const m = new Magic('API_KEY');
04
05// Assumes a user is already logged in
06try {
07 const idToken = await m.user.getIdToken();
08} catch {
09 // Handle errors if required!
10}
#generateIdToken
Generates a Decentralized ID token with optional serialized data.
#Arguments
generateIdToken({ lifespan? = 900, attachment? = 'none' })
Parameter | Type | Definition |
lifespan? | Number | Will set the lifespan of the generated token. Defaults to 900s (15 mins) |
attachment? | String | Will set a signature of serialized data in the generated token. Defaults to "none" |
#Returns
PromiEvent<string>
: Base64-encoded string representation of a JSON tuple representing [proof, claim]
#Example
01import { Magic } from 'magic-sdk';
02
03const m = new Magic('API_KEY');
04
05// Assumes a user is already logged in
06try {
07 const idToken = await m.user.generateIdToken({ attachment: 'SERVER_SECRET' });
08} catch {
09 // Handle errors if required!
10}
#getMetadata
Retrieves information for the authenticated user.
#Arguments
None
#Returns
PromiEvent<{ issuer, email, publicAddress }>
: an object containing the issuer, email and cryptographic public address of the authenticated user.
Value | Type | Definition |
issuer | String | The Decentralized ID of the user. In server-side use-cases, we recommend this value to be used as the user ID in your own tables. |
email | String | Email address of the authenticated user. |
phoneNumber | String | The phone number of the authenticated user. |
publicAddress | String | The authenticated user's public address (a.k.a.: public key). |
#Example
01import { Magic } from 'magic-sdk';
02
03const m = new Magic('API_KEY');
04
05// Assumes a user is already logged in
06try {
07 const { email, publicAddress } = await m.user.getMetadata();
08} catch {
09 // Handle errors if required!
10}
#isLoggedIn
Checks if a user is currently logged in to the Magic SDK.
#Arguments
None
#Returns
PromiEvent<Boolean>
#Example
01import { Magic } from 'magic-sdk';
02
03const m = new Magic('API_KEY');
04
05try {
06 const isLoggedIn = await m.user.isLoggedIn();
07 console.log(isLoggedIn); // => `true` or `false`
08} catch {
09 // Handle errors if required!
10}
#logout
Logs out the currently authenticated Magic user
#Arguments
None
#Returns
PromiEvent<Boolean>
#Example
01import { Magic } from 'magic-sdk';
02
03const m = new Magic('API_KEY');
04
05try {
06 await m.user.logout();
07 console.log(await m.user.isLoggedIn()); // => `false`
08} catch {
09 // Handle errors if required!
10}
#Errors & Warnings
There are three types of error class to be aware of when working with Magic's client-side JavaScript SDK:
SDKError
: Raised by the SDK to indicate missing parameters, communicate deprecation notices, or other internal issues. A notable example would be a MISSING_API_KEY error, which informs the required API key parameter was missing from new Magic(...).RPCError
: Errors associated with specific method calls to the Magic<iframe>
context. These methods are formatted as JSON RPC 2.0 payloads, so they return error codes as integers. This type of error is raised by methods likeAuthModule.loginWithMagicLink
.ExtensionError
: Errors associated with method calls to Magic SDK Extensions. Extensions are an upcoming/experimental feature of Magic SDK. More information will be available once Extensions are officially released.
#SDKError
The SDKError
class is exposed for instanceof
operations.
01import { SDKError } from 'magic-sdk';
02
03try {
04 // Something async...
05catch (err) {
06 if (err instanceof SDKError) {
07 // Handle...
08 }
09}
SDKError
instances expose the code
field which may be used to deterministically identify the error. Additionally, an enumeration of error codes is exposed for convenience and readability:
01import { SDKErrorCode } from 'magic-sdk';
02
03SDKErrorCode.MissingApiKey;
04SDKErrorCode.ModalNotReady;
05SDKErrorCode.MalformedResponse;
06// and so forth...
07// Please reference the `Enum Key` column of the error table below.
#Error Codes
Enum Key | Description |
MissingApiKey | Indicates the required Magic API key is missing or invalid. |
ModalNotReady | Indicates the Magic iframe context is not ready to receive events. This error should be rare and usually indicates an environmental issue or improper async/await usage. |
MalformedResponse | Indicates the response received from the Magic iframe context is malformed. We all make mistakes (even us), but this should still be a rare exception. If you encounter this, please be aware of phishing! |
InvalidArgument | Raised if an SDK method receives an invalid argument. Generally, TypeScript saves us all from simple bugs, but there are validation edge cases it cannot solve—this error type will keep you informed! |
ExtensionNotInitialized | Indicates an extension method was invoked before the Magic SDK instance was initialized. Make sure to access extension methods only from the Magic SDK instance to avoid this error. |
#RPCError
The RPCError
class is exposed for instanceof
operations:
01import { RPCError } from 'magic-sdk';
02
03try {
04 // Something async...
05catch (err) {
06 if (err instanceof RPCError) {
07 // Handle...
08 }
09}
RPCError
instances expose the code
field which may be used to deterministically identify the error. Additionally, an enumeration of error codes is exposed for convenience and readability:
01import { RPCErrorCode } from 'magic-sdk';
02
03RPCErrorCode.MagicLinkExpired;
04RPCErrorCode.UserAlreadyLoggedIn;
05RPCErrorCode.ParseError;
06RPCErrorCode.MethodNotFound;
07RPCErrorCode.InternalError;
08// and so forth...
09// Please reference the `Enum Key` column of the error table below.
#Magic Link Error Codes
Code | Enum Key | Description |
-10000 | MagicLinkFailedVerification | The magic link failed verification, possibly due to an internal service error or a generic network error. |
-10001 | MagicLinkExpired | The user clicked their magic link after it had expired (this can happen if the user takes more than 10 minutes to check their email). |
-10002 | MagicLinkRateLimited | If the showUI parameter is set to false , this error will communicate the email rate limit has been reached. Please debounce your method calls if this occurs. |
-10006 | MagicLinkInvalidRedirectURL | If using the redirectURI parameter for the magic link flow, this error is recevied if the provided value is invalid for some reason. |
-10003 | UserAlreadyLoggedIn | A user is already logged in. If a new user should replace the existing user, make sure to call logout before proceeding. |
-10004 | UpdateEmailFailed | An update email request was unsuccessful, either due to an invalid email being supplied or the user canceled the action. |
-10005 | UserRequestEditEmail | The user has stopped the login request because they want to edit the provided email. |
#Standard JSON RPC 2.0 Error Codes
Code | Enum Key | Description |
-32700 | ParseError | Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text. |
-32600 | InvalidRequest | The JSON sent is not a valid Request object. |
-32601 | MethodNotFound | The method does not exist / is not available. |
-32602 | InvalidParams | Invalid method parameter(s). |
-32603 | InternalError | Internal JSON-RPC error. These can manifest as different generic issues (i.e.: attempting to access a protected endpoint before the user is logged in). |
#ExtensionError
The ExtensionError
class is exposed for instanceof
operations:
01import { ExtensionError } from 'magic-sdk';
02
03try {
04 // Something async...
05catch (err) {
06 if (err instanceof ExtensionError) {
07 // Handle...
08 }
09}
ExtensionError
instances expose the code
field which may be used to deterministically identify the error. Magic SDK does not export a global enumeration of Extension error codes. Instead, Extension authors are responsible for exposing and documenting error codes relevant to the Extension's use-case.
#PromiEvents
Magic SDK provides a flexible interface for handling methods which encompass multiple "stages" of an action. Promises
returned by Magic SDK resolve when a flow has reached finality, but certain methods also contain life-cycle events that dispatch throughout. We refer to this interface as a PromiEvent
. There is prior art to inspire this approach in Ethereum's Web3 standard.
PromiEvent
is a portmanteau of Promise
and EventEmitter
. Browser and React Native SDK methods return this object type, which is a native JavaScript Promise
overloaded with EventEmitter
methods. This value can be awaited
in modern async/await
code, or you may register event listeners to handle method-specific life-cycle hooks. Each PromiEvent
contains the following default event types:
"done"
: Called when thePromise
resolves. This is equivalent toPromise.then
."error"
: Called if thePromise
rejects. This is equivalent toPromise.catch
."settled"
: Called when thePromise
either resolves or rejects. This is equivalent toPromise.finally
.
Look for additional event types documented near the method they relate to. Events are strongly-typed by TypeScript to offer developer hints and conveniant IDE auto-complete.
#Example
It's possible to chain Promise
methods like .then
and .catch
with EventEmitter
methods like .on
and .once
seamlessly. There are no limitations to either chaining interface, they all return an awaitable
PromiEvent
, as expected. The species of the object type is always a native JavaScript Promise
.
01const req = magic.auth.loginWithMagicLink({ email: 'hello@magic.link' });
02
03req
04 .on('email-sent', () => {
05 /* ... */
06 })
07 .then(DIDToken => {
08 /* ... */
09 })
10 .once('email-not-deliverable', () => {
11 /* ... */
12 })
13 .catch(error => {
14 /* ... */
15 })
16 .on('error', error => {
17 /* ... */
18 });
#Examples
#Re-authenticate Users
A user's Magic SDK session persists up to 7 days by default, so re-authentication is usually friction-less.
Note: the session length is customizable by the developer through the Magic dashboard.
0. Prerequisite: Install Magic Client SDK
1. Re-authenticate the user:
01import { Magic } from 'magic-sdk';
02const m = new Magic('API_KEY');
03
04const email = 'example@magic.link';
05
06if (await m.user.isLoggedIn()) {
07 const didToken = await m.user.getIdToken();
08
09 // Do something with the DID token.
10 // For instance, this could be a `fetch` call
11 // to a protected backend endpoint.
12 document.getElementById('your-access-token').innerHTML = didToken;
13} else {
14 // Log in the user
15 const user = await m.auth.loginWithMagicLink({ email });
16}