Iterable is a growth marketing platform that helps you to create better experiences for—and deeper relationships with—your customers. Use it to send customized email, SMS, push notification, in-app message, web push notification campaigns to your customers.
This SDK helps you integrate your Web apps with Iterable.
To install this SDK through NPM:
$ npm install @iterable/web-sdk
with yarn:
$ yarn add @iterable/web-sdk
or with a CDN:
<script src="https://unpkg.com/@iterable/web-sdk/index.js"></script>
If your Iterable project is hosted on Iterable's European data center (EUDC), you'll need to configure Iterable's Web SDK to interact with Iterable's EU-based API endpoints.
To do this, on the web server that hosts your site, set the IS_EU_ITERABLE_SERVICE
environment variable to true
.
Below are the methods this SDK exposes. See Iterable's API Docs for information on what data to pass and what payload to receive from the HTTP requests.
Method Name | Description |
---|---|
getInAppMessages |
Either fetch and return in-app messages as a Promise or (if the options argument is provided) return methods to fetch, pause/resume, and/or display in-app messages. |
initialize |
Return methods for identifying users and setting a JWT |
refreshJwtToken |
Fetch a new JWT token for the specified user and configure the SDK to use it for future requests. Only for manual token refresh. |
track |
Track custom events |
trackInAppClick 🚨 |
Track when a user clicks on a button or link within a message |
trackInAppClose |
Track when an in-app message is closed |
trackInAppConsume |
Track when a message has been consumed. Deletes the in-app message from the server so it won't be returned anymore |
trackInAppDelivery |
Track when a message has been delivered to a user's device |
trackInAppOpen |
Track when a message is opened and marks it as read |
trackPurchase |
Track purchase events |
updateCart |
Update shoppingCartItems field on user profile |
updateSubscriptions |
Updates user's subscriptions |
updateUser |
Change data on a user's profile or create a user if none exists |
updateUserEmail |
Change a user's email and reauthenticate user with the new email address (in other words, the SDK will call setEmail for you) |
The SDK does not track inAppDelete
events.
🚨 Due to a limitation in WebKit (which affects iOS web browsers, Safari included) web in-app messages displayed in an iOS web browser browser can't automatically fire trackInAppClick events or handle custom CTAs. This will impact analytics for all Safari and mobile iOS users.
API (see required API payload here):
getInAppMessages: (payload: InAppMessagesRequestParams, options?: { display: 'deferred' | 'immediate' }) => Promise<TrackConsumeData> | PaintInAppMessageData
🚨 Notice: v1.0.0 of this SDK deprecates support for showMessagesAutomatically?: boolean
. If needed, please update your getInAppMessages requests to use options: { display: 'deferred' | 'immediate' }
instead.
SDK Specific Options:
Along with the API parameters, you can pass these options to the SDK method to have in-app messages behave differently.
Property Name | Description | Value | Default |
---|---|---|---|
animationDuration | How long (in ms) it should take messages to animate in and out | number |
400 |
bottomOffset | How much space (px or %) to create between the bottom of the screen and a message. Not applicable for center, top, or full-screen messages. | string |
undefined |
displayInterval | How long (in ms) to wait before showing the next in-app message after closing the currently opened one | number |
30000 |
handleLinks | How to open links. If undefined , use browser-default behavior. open-all-new-tab opens all in new tab, open-all-same-tab opens all in same tab, external-new-tab opens only off-site links in new tab, otherwise same tab. Overrides the target attribute defined on link elements. |
'open-all-new-tab' | 'open-all-same-tab' | 'external-new-tab' |
undefined |
onOpenScreenReaderMessage | The text a screen reader should read when opening the message. | string |
undefined |
onOpenNodeToTakeFocus | The DOM element that should receive keyboard focus when the in-app message opens. Any query selector is valid. If not specified, the first interactive element receives focus. | string |
undefined |
rightOffset | The amount of space (px or %) to create between the right of the screen and the message. Not applicable for center or full-screen messages. | string |
undefined |
topOffset | How much space (px or %) to create between the top of the screen and a message. Not applicable for center, bottom, or full-screen messages. | string |
undefined |
closeButton | Properties that define a custom close button to display on a message. | CloseButtonOptions (see below) |
undefined |
Close Button Options:
Property Name | Description | Value | Default |
---|---|---|---|
color | The button's color (does not affect custom icons) | string |
undefined |
iconPath | Custom pathname to an image or SVG to use (instead of the default "X") | string |
undefined |
position | Where the button should display on an in-app message | 'top-right | 'top-left' |
'top-right' |
isRequiredToDismissMessage | If true , users cannot dismiss in-app messages by clicking outside of them. |
boolean |
undefined |
sideOffset | How much space to leave between the button and side of the container | string |
'4%' |
size | How large to set the width, height, and font-size | string | number |
24 |
topOffset | How much space to leave between the button and the top of the container | string |
'4%' |
Example:
Calling getInAppMessages
with options
not set returns a JSON response from Iterable. This response includes an inAppMessages
field, and each item in the list has a content.html
field that's an iframe
with an embedded in-app message. The iframe
's sandbox
attribute is set, isolating its render and preventing any malicious JavaScript execution.
import { getInAppMessages } from '@iterable/web-sdk';
getInAppMessages({ count: 20, packageName: 'mySite1' })
.then((resp) => {
/* This will be an iframe element that can be attached to the DOM */
const messageIframe = resp.data.inAppMessages[0].content.html;
document.body.appendChild(messageIframe);
/* Additional styling logic can be done here to customly render the message */
})
.catch();
This code places an in-app on the page, but it won't be visible. To render it, you'll need to modify the page's CSS, setting up whatever styles you'd like. You'll also need to set up click handlers to handle closing the message and tracking events (in-app click, etc.).
Or, to show messages automatically:
import { getInAppMessages } from '@iterable/web-sdk';
const { request, pauseMessageStream, resumeMessageStream } = getInAppMessages(
{
count: 20,
packageName: 'my-website',
displayInterval: 5000,
onOpenScreenReaderMessage:
'hey screen reader here telling you something just popped up on your screen!',
onOpenNodeToTakeFocus: 'input',
closeButton: {
color: 'red',
size: '16px',
topOffset: '20px'
}
},
{ display: 'immediate' }
);
request().then().catch();
or if you want to show messages with your own custom filtering/sorting and choose to display later:
import {
getInAppMessages,
sortInAppMessages,
filterHiddenInAppMessages
} from '@iterable/web-sdk';
const {
request,
pauseMessageStream,
resumeMessageStream,
triggerDisplayMessages
} = getInAppMessages(
{
count: 20,
packageName: 'my-website',
displayInterval: 5000,
onOpenScreenReaderMessage:
'hey screen reader here telling you something just popped up on your screen!',
onOpenNodeToTakeFocus: 'input',
closeButton: {
color: 'red',
size: '16px',
topOffset: '20px'
}
},
{ display: 'deferred' }
);
request()
.then((response) => {
/* do your own manipulation here */
const filteredMessages = doStuffToMessages(response.data.inAppMessages);
/* also feel free to take advantage of the sorting/filtering methods used internally */
const furtherManipulatedMessages = sortInAppMessages(
filterHiddenInAppMessages(response.data.inAppMessages)
) as InAppMessage[];
/* then display them whenever you want */
triggerDisplayMessages(furtherManipulatedMessages);
})
.catch();
🚨 PLEASE NOTE: If you choose the deferred
option, the SDK will not do any filtering or sorting on the messages internally. You will get the messages exactly as they come down from the API, untouched. This means you may (for example) show in-app messages marked read
or show the messages in the wrong order based on priority
.
If you want to keep the default sorting and filtering, please take advantage of the sortInAppMessages
and filterHiddenInAppMessages
methods the SDK provides. Also see filterOnlyReadAndNeverTriggerMessages
, which is similar to filterHiddenInAppMessages
but does not filter out JSON-only messages.
API:
initialize: (authToken: string, generateJWT: ({ email?: string, userID?: string }) => Promise<string>) => {
clearRefresh: () => void;
setEmail: (email: string) => Promise<string>;
setUserID: (userId: string) => Promise<string>;
logout: () => void;
}
Example:
import { initialize } from '@iterable/web-sdk';
const { clearRefresh, setEmail, setUserID, logout } = initialize(
'my-API-key',
/*
_email_ will be defined if you call _setEmail_
_userID_ will be defined if you call _setUserID_
*/
({ email, userID }) =>
yourAsyncJWTGeneratorMethod({ email, userID }).then(
({ jwt_token }) => jwt_token
)
);
API:
refreshJwtToken: (authTypes: string) => Promise<string>
Example:
import { initialize } from '@iterable/web-sdk';
refreshJwtToken("user@example.com").then().catch();
API (see required API payload here):
track: (payload: InAppTrackRequestParams) => Promise<TrackData>
Example:
import { track } from '@iterable/web-sdk';
track({ eventName: 'my-event' }).then().catch();
API (see required API payload here):
trackInAppClick: (payload: InAppEventRequestParams) => Promise<TrackClickData>
Example:
import { trackInAppClick } from '@iterable/web-sdk';
trackInAppClick({
messageId: '123',
deviceInfo: { appPackageName: 'my-website' }
})
.then()
.catch();
API (see required API payload here):
trackInAppClose: (payload: InAppEventRequestParams) => Promise<TrackCloseData>
Example:
import { trackInAppClose } from '@iterable/web-sdk';
trackInAppClose({
messageId: '123',
deviceInfo: { appPackageName: 'my-website' }
})
.then()
.catch();
API (see required API payload here):
trackInAppConsume: (payload: InAppEventRequestParams) => Promise<TrackConsumeData>
Example:
import { trackInAppConsume } from '@iterable/web-sdk';
trackInAppConsume({
messageId: '123',
deviceInfo: { appPackageName: 'my-website' }
})
.then()
.catch();
API (see required API payload here):
trackInAppDelivery: (payload: InAppEventRequestParams) => Promise<TrackDeliveryData>
Example:
import { trackInAppDelivery } from '@iterable/web-sdk';
trackInAppDelivery({
messageId: '123',
deviceInfo: { appPackageName: 'my-website' }
})
.then()
.catch();
API (see required API payload here):
trackInAppOpen: (payload: InAppEventRequestParams) => Promise<TrackOpenData>
Example:
import { trackInAppOpen } from '@iterable/web-sdk';
trackInAppOpen({
messageId: '123',
deviceInfo: { appPackageName: 'my-website' }
})
.then()
.catch();
API (see required API payload here):
trackPurchase: (payload: TrackPurchaseRequestParams) => Promise<TrackPurchaseData>
Example:
import { trackPurchase } from '@iterable/web-sdk';
trackPurchase({
items: [{ id: '123', name: 'keyboard', price: 100, quantity: 2 }],
total: 200
})
.then()
.catch();
API (see required API payload here):
updateCart: (payload: UpdateCartRequestParams) => Promise<UpdateCartData>
Example:
import { updateCart } from '@iterable/web-sdk';
updateCart({
items: [{ id: '123', price: 100, name: 'keyboard', quantity: 1 }]
})
.then()
.catch();
API (see required API payload here):
updateSubscriptions: (payload?: UpdateSubscriptionParams) => Promise<UpdateSubsData>
Example:
import { updateSubscriptions } from '@iterable/web-sdk';
updateSubscriptions({ emailListIds: [1, 2, 3] })
.then()
.catch();
API (see required API payload here):
updateUser: (payload?: UpdateUserParams) => Promise<UpdateUserData>
Example:
import { updateUser } from '@iterable/web-sdk';
updateUser({ dataFields: {} }).then().catch();
API (see required API payload here):
updateUserEmail: (newEmail: string) => Promise<UpdateEmailData>
Example:
import { updateUserEmail } from '@iterable/web-sdk';
updateUserEmail('user@example.com').then().catch();
First thing you need to do is generate an API key on the Iterable app. Make sure this key is JWT-enabled and is of the Web key type. This will ensure the SDK has access to all the necessary endpoints when communicating with the Iterable API. After you generate your key, save both the API Key and JWT Secret somewhere handy. You'll need both of them.
First, you'll deal with the JWT Secret. Typically, you need some backend service that is going to use that JWT Secret to sign a JWT and return it to your client app. For the purposes of this explanation, this can be demonstrated this with a site like jwt.io. See the documentation on the Iterable website for instructions on how to generate a JWT from your JWT secret.
Once you have a JWT or a service that can generate a JWT automatically, you're ready to start making requests in the SDK. The syntax for that looks like this:
import { initialize } from '@iterable/web-sdk';
(() => {
initialize('YOUR_API_KEY_HERE', ({ email, userID }) =>
yourAsyncJWTGeneratorMethod({ email, userID }).then(
({ jwt_token }) => jwt_token
)
);
})();
Now that we've set our authorization logic within our app, it's time to set the user. You can identify a user by either the email or user ID. User ID is preferred because the SDK will automatically create a user in your Iterable instance. If you identify by email, the user will remain "anonymous" with no user ID attached to it. See Iterable's updateUser endpoint for more information about how users are created.
The syntax for identifying a user by user ID looks like this:
import { initialize } from '@iterable/web-sdk';
(() => {
const { setUserID, logout } = initialize(
'YOUR_API_KEY_HERE',
({ email, userID }) =>
yourAsyncJWTGeneratorMethod({ email, userID }).then(
({ jwt_token }) => jwt_token
)
);
yourAsyncLoginMethod().then((response) => {
/* this code assumes you have some backend endpoint that will return a user's ID */
setUserID(response.user_id).then(() => {
/* now your user is set and you can begin hitting the Iterable API */
});
});
/* optionally logout the user when you don't need to hit the Iterable API anymore */
logout();
})();
Doing this with an email is similar:
import { initialize } from '@iterable/web-sdk';
(() => {
const { setEmail, logout } = initialize(
'YOUR_API_KEY_HERE',
({ email, userID }) =>
yourAsyncJWTGeneratorMethod({ email, userID }).then(
({ jwt_token }) => jwt_token
)
);
yourAsyncLoginMethod().then((response) => {
/*
this code assumes you have some backend
endpoint that will return a user's email address
*/
setEmail(response.email).then(() => {
/* now your user is set and you can begin hitting the Iterable API */
});
});
/* optionally logout the user when you don't need to hit the Iterable API anymore */
logout();
})();
Now let's put it altogether with an Iterable API method:
import { initialize, track } from '@iterable/web-sdk';
(() => {
const { setUserID, logout } = initialize(
'YOUR_API_KEY_HERE',
({ email, userID }) =>
yourAsyncJWTGeneratorMethod({ email, userID }).then(
({ jwt_token }) => jwt_token
)
);
yourAsyncLoginMethod().then((response) => {
/* this code assumes you have some backend endpoint that will return a user's ID */
setUserID(response.user_id).then(() => {
document.getElementById('my-button').addEventListener('click', () => {
/*
no need to pass a user ID to this endpoint.
_setUserID_ takes care of this for you
*/
track({ eventName: 'button-clicked' });
});
});
});
})();
This SDK relies on a library called Axios. For all outgoing XHR requests, the SDK utilizes Axios interceptors to add your user information to the requests.
You can do that! This SDK exposes the base Axios request instance so you can do whatever you like with it and build upon that. You can import the Axios request like so and anything in the Axios documentation is fair game to use:
import { baseAxiosRequest } from '@iterable/web-sdk';
For example, if you want to set an email
query param on every outgoing request, you would just implement the way Axios advises like so:
import { baseAxiosRequest } from '@iterable/web-sdk';
(() => {
baseAxiosRequest.interceptors.request.use((config) => {
return {
...config,
params: {
...config.params,
email: 'user@example.com'
}
};
});
})();
🚨 Please note, you won't likely need access to this Axios instance. This is reserved for advanced use cases only.
This SDK allows that. Simply call the getInAppMessages
method but pass { display: 'immediate' }
as the second parameter. This will expose some methods used to make the request to show the messages and pause and resume the queue.
Normally to request a list of in-app messages, you'd make a request like this:
import { initialize, getInAppMessages } from '@iterable/web-sdk';
(() => {
const { setUserID } = initialize('YOUR_API_KEY_HERE', ({ email, userID }) =>
yourAsyncJWTGeneratorMethod({ email, userID }).then(
({ jwt_token }) => jwt_token
)
);
yourAsyncLoginMethod().then((response) => {
setUserID(response.user_id).then(() => {
getInAppMessages({
count: 20,
packageName: 'my-website'
})
.then()
.catch();
});
});
})();
In order to take advantage of the SDK showing them automatically, you would implement the same method in this way:
import { initialize, getInAppMessages } from '@iterable/web-sdk';
(() => {
const { setUserID } = initialize('YOUR_API_KEY_HERE', ({ email, userID }) =>
yourAsyncJWTGeneratorMethod({ email, userID }).then(
({ jwt_token }) => jwt_token
)
);
yourAsyncLoginMethod().then((response) => {
setUserID(response.user_id).then(() => {
const { request } = getInAppMessages(
{
count: 20,
packageName: 'my-website'
},
{ display: 'immediate' }
);
/* trigger the start of message presentation */
request().then().catch();
});
});
})();
Optionally, you can pass arguments to fine-tune how you want the messages to appear. See the usage section to see all available options and what they do.
import { initialize, getInAppMessages } from '@iterable/web-sdk';
(() => {
const { setUserID } = initialize('YOUR_API_KEY_HERE', ({ email, userID }) =>
yourAsyncJWTGeneratorMethod({ email, userID }).then(
({ jwt_token }) => jwt_token
)
);
yourAsyncLoginMethod().then((response) => {
setUserID(response.user_id).then(() => {
const { request } = getInAppMessages(
{
count: 20,
packageName: 'my-website',
displayInterval: 5000,
onOpenScreenReaderMessage:
'hey screen reader here telling you something just popped up on your screen!',
onOpenNodeToTakeFocus: 'input',
topOffset: '20px',
bottomOffset: '20px',
rightOffset: '20px',
animationDuration: 400,
handleLinks: 'external-new-tab'
},
{ display: 'immediate' }
);
/* trigger the start of message presentation */
request().then().catch();
});
});
})();
You can also pause and resume the messages stream if you like
import { initialize, getInAppMessages } from '@iterable/web-sdk';
(() => {
const { setUserID } = initialize('YOUR_API_KEY_HERE', ({ email, userID }) =>
yourAsyncJWTGeneratorMethod({ email, userID }).then(
({ jwt_token }) => jwt_token
)
);
yourAsyncLoginMethod().then((response) => {
setUserID(response.user_id).then(() => {
const { request, pauseMessageStream, resumeMessageStream } =
getInAppMessages(
{
count: 20,
packageName: 'my-website'
},
{ display: 'immediate' }
);
/* trigger the start of message presentation */
request().then().catch();
/* pause any more in-app messages from appearing for a little while */
pauseMessageStream();
/*
pick up where you left off and show the next message in the queue.
And start the timer again.
*/
resumeMessageStream();
});
});
})();
Finally, you can also choose to do your own manipulation to the messages before choosing to display them:
import {
initialize,
getInAppMessages,
sortInAppMessages,
filterHiddenInAppMessages
} from '@iterable/web-sdk';
(() => {
const { setUserID } = initialize('YOUR_API_KEY_HERE', ({ email, userID }) =>
yourAsyncJWTGeneratorMethod({ email, userID }).then(
({ jwt_token }) => jwt_token
)
);
yourAsyncLoginMethod().then((response) => {
setUserID(response.user_id).then(() => {
const { request, pauseMessageStream, resumeMessageStream } =
getInAppMessages(
{
count: 20,
packageName: 'my-website'
},
{ display: 'deferred' }
);
/* trigger the start of message presentation */
request()
.then((response) => {
/* do your own manipulation here */
const filteredMessages = doStuffToMessages(
response.data.inAppMessages
);
/*
also feel free to take advantage of the sorting/filtering
methods used internally
*/
const furtherManipulatedMessages = sortInAppMessages(
filterHiddenInAppMessages(response.data.inAppMessages)
) as InAppMessage[];
/* then display them whenever you want */
triggerDisplayMessages(furtherManipulatedMessages);
})
.catch();
});
});
})();
This SDK already handles that for you. The rules for the in-app message presentation varies based on which display type you've selected. Here's a table to explain how it works:
Message Position → Browser Size ↓ |
Center | Full | Top-Right | Bottom-Right |
---|---|---|---|---|
0px - 850px | 100% | 100% | 100% | 100% |
851px - 975px | 50% | 100% | 45% | 45% |
976px - 1300px | 50% | 100% | 33% | 33% |
1300px+ | 50% | 100% | 25% | 25% |
Looking at this table, you can see the browser sizes on the left, and the display positions on top. For example, if your in-app message is positioned in the top-right of the screen and your browser window is at 1000px, then your in-app message will take up 33% of the screen.
Another example: If your in-app is positioned in the center and your browser if at 700px, your in-app message will grow to take up 100% of the screen.
This chart also implies that your in-app message is taking 100% of its container. Your results may vary if you add, for example, a max-width: 200px
CSS rule to your message HTML. Regardless of how you write your CSS, these rules will take effect, so it is recommended that you stick to percentage-based CSS widths when possible when creating your message
Clicking links breaks the experience of my single-page app (or how you add a custom callback to link clicks)
No problem! Please see the link handling section for more information on how to create callback methods on link clicks. There, you'll find information on how to create a seamless link-clicking experience if you're using a library such as React Router.
JWT expiration is handled for you automatically by the SDK. There are 3 points where the SDK will generate a new JWT token for you, apart from the initial call when invoking setEmail
or setUserID
:
- The JWT is within 1 minute of expiration
- An Iterable API request has failed with a 401 response
- Your code invoked the
updateUserEmail
method
As previously explained, when initializing the SDK you need to pass a function that returns a Promise with the JWT, which looks something like this:
import { initialize } from '@iterable/web-sdk';
initialize('API_KEY_HERE', ({ email, userID }) =>
yourAsyncJWTGenerationMethod({ email, userID }).then(
(response) => response.jwt_token
)
);
When the previous 3 listed events occur, the SDK will invoke the method passed as the second argument, and when the Promise resolves, attach the new JWT to any future Iterable API requests.
Finally, if the request to regenerate the JWT fails however, the SDK will not attempt to generate the JWT again so requests will start failing at that point.
To perform a manual JWT token refresh, call refreshJwtToken
.
This library exposes UMD modules and a single-file build for you to import from. In other words, this means that you'll be able to import methods in these ways:
import { getInAppMessages, initialize, updateUser } from '@iterable/web-sdk';
import { initialize, getInAppMessages, updateUser } from '@iterable/web-sdk';
For those using Webpack/Rollup/Some Other Build Tool, it is recommended to import methods with the later approach for smaller final bundles. Importing with the second method ensures your bundle will only include the code you're using and not the code you're not.
Since the Web SDK renders in-app messages in an iframe element on your website if you choose to render the messages automatically, the event handler that is responsible for clicking links is highjacked by the SDK code internally. To the user, this doesn't really change the experience. As expected, <a />
tags will open the link in the same browser tab unless given the target="_blank"
property.
But there are few features which the SDK adds so that you can customize how you'd like links to behave:
First, the handleLinks
option provided by getInAppMessages
allows you to specify how the SDK opens links: in the current tab, in a new tab, or a combination (external links in a new tab, internal links in the current tab). For example, this code:
import { getInAppMessages } from '@iterable/web-sdk';
getInAppMessages({
count: 5,
packageName: 'my-website',
handleLinks: 'external-new-tab'
});
will ensure the following links open in the same tab if your domain is mydomain.com
, for example:
/about
https://mydomain.com
https://mydomain.com/about
and these will open in a new tab
https://google.com
https://hello.com
Iterable reserves the iterable://
and action://
URL schemas to define custom link click actions:
-
iterable://dismiss
- Removes the in-app message from the screen, grabs the next one to display, and invokes both trackInAppClose and trackInAppClick. -
action://{anything}
- Makes aWindow.prototype.postMessage
call with payload{ type: 'iterable-action-link', data: '{anything}' }
, to be consumed by the parent website as needed. These links also dismiss the message and invoke trackInAppClose and trackInAppClick.
The SDK may reserve more keywords in the future.
🚨 iterable://
and action://
links are not supported with WebKit (which affects iOS web browsers, Safari included). In these browsers, users can close an in-app message by clicking away from the message.
Knowing now the custom link schemas available, let's explain how you can leverage them to add custom routing or callback functions. If for example you want to hook into a link click and send the user to your /about
page with a client-side routing solution, you'd do something like this if you're using React Router:
/*
assuming you're clicking this link in your in-app message:
<a href="action://about">go to about page</a>
*/
import { useHistory } from 'react-router-dom';
const SomeComponent = () => {
const history = useHistory();
React.useEffect(() => {
global.addEventListener('message', (event) => {
if (event.data.type && event.data.type === 'iterable-action-link') {
/* route us to the content that comes after "action://" */
history.push(`/${event.data.data}`);
}
});
}, []);
return <></>;
};
The Iterable Web SDK includes TypeScript definitions out of the box. All SDK methods
should be typed for you already but if you need to import specific typings, you can
parse through each types.d.ts
file inside of the ./dist
directory to find what you need.
Request and response payloads should all be available.
If you feel something is missing, feel free to open an issue!
Looking to contribute? Please see the contributing instructions here for more details.
This SDK is released under the MIT License. See LICENSE for more information.