# Get started with Elements
ShipEngine Elements can be integrated into your application in two ways: as an SDK or as a suite of React components.
Regardless of the integration method you choose, you'll need to generate a ShipEngine Platform token and establish a new ShipEngine Partner account.
To learn more about ShipEngine Platform tokens, refer to the [Platform token documentation](/apis/shipengine/docs/partners/jwt). For more information about ShipEngine Partner accounts, visit the [Partners API documentation](/apis/shipengine/docs/partners/getting-started).
iframe
The code presented in this video is from a previous version of Elements. Use the code samples provided in the [Elements SDK](/apis/shipengine/docs/elements/elements-sdk) or [Elements React](/apis/shipengine/docs/elements/elements-react) sections for the current version.
> **NOTE:**
To assist you in generating Elements JWTs, we've provided a [sample application](https://github.com/ShipEngine/elements-auth-example) as a resource.
## Step 1: Creating a ShipEngine Account
To create a ShipEngine Elements JWT, a ShipEngine Account ID (also known as a ShipEngine Seller ID or tenant in some cases) is required. To make a new ShipEngine Seller, run the following `curl` request. To learn more about creating ShipEngine accounts, check out the [ShipEngine account documentation.](/apis/shipengine/docs/partners/accounts/create)
```bash
curl --location --request POST 'https://api.shipengine.com/v1/partners/accounts' \
--header 'api-key: [my-api-key]' \
--header 'Content-Type: application/json' \
--header 'partner-id: [my-partner-id]' \
--data-raw '{
"first_name": "John",
"last_name": "Doe",
"company_name": "Acme Corp.",
"origin_country_code": "US"
}'
```
You will use the `account_id` field on the ShipEngine account creation response that the above curl request to sign a ShipEngine Elements JWT.
```json
{
"api_key": {
"encrypted_api_key": "[another-api-key]",
...
},
"account_id": 1234567, # seller id needed to sign JWT
"external_account_id": null,
"created_at": "2023-03-12T15:02:26.523Z",
"modified_at": "2023-03-12T15:02:26.523Z",
...
}
```
## Step 2: Signing your JWT
The following pieces of information are required to sign ShipEngine Platform JWT.
1. **ShipEngine Partner ID**: You should have received this after you had completed partner onboarding with your ShipEngine technical contact. This is a unique identifier for your ShipEngine Partner account.
2. **Tenant ID**: The ID of the ShipEngine account that was created. This is also known as the ShipEngine Seller ID you may have been given during partner onboarding.
3. **Public/Private RSA keys**: You should have registered the public key with ShipEngine during partner onboarding. We recommend you store the private RSA key you created in a secure container format, like .pem.
4. **Platform Token Key ID**: You should have received this after you had completed partner onboarding with your ShipEngine technical contact. This will be the name of the platform token key you registered with ShipEngine during partner onboarding.
5. **Scope**: You should have received this after you had completed partner onboarding with your ShipEngine technical contact. The scope enforces the usage of a specific subset of ShipEngine API endpoints.
6. **Platform Token Issuer**: You should have received this after you had completed partner onboarding with your ShipEngine technical contact.
> **WARNING:**
The code to sign a JWT **must be run on the server** in order to prevent any security issues.
The following is an example of one possible way of generating a ShipEngine Platform JWT. Keep in mind, the same [sample JWT application](https://github.com/ShipEngine/elements-auth-example) we mentioned earlier can be used to sign your JWT's.
```js
import fs from 'fs';
import jsonwebtoken from 'jsonwebtoken';
import express from 'express';
import cors from 'cors';
import 'dotenv/config';
const app = express();
app.use(cors());
const generateToken = async () => {
const payload = {
partner: process.env.SHIPENGINE_PARTNER_ID,
tenant: process.env.SHIPENGINE_SELLER_ID, // The ShipEngine Seller ID you created
scope: process.env.SCOPE,
};
const secretKey = fs.readFileSync('certs/private.pem', 'utf-8');
if (!secretKey) throw new Error('Missing secret key');
const token = await jsonwebtoken.sign(payload, secretKey, {
algorithm: 'RS256',
expiresIn: 3600,
issuer: process.env.PLATFORM_TOKEN_ISSUER,
keyid: process.env.PLATFORM_TOKEN_KEY_ID,
});
return token;
};
app.get('/generate-token', async (_req, res) => {
const token = await generateToken();
res.status(200).json({ token });
});
app.listen(3002, () => {
console.log('Server listening on port 3002');
});
```
```ruby
require 'jwt'
class Api::TokenController < ApplicationController
def index
token = generate_token
if token
render json: { token: token, status: 200}
else
render json: { error: "Error generating token", status: 500}
end
end
private
def generate_token
current_time = Time.now.to_i
expiration_time = current_time + 3600
rsa_private_key = OpenSSL::PKey.read File.read 'private_key.pem'
payload = {
partner: ENV['SHIPENGINE_PARTNER_ID'],
tenant: ENV['SHIPENGINE_SELLER_ID'],
scope: ENV['SCOPE'],
iat: current_time,
exp: expiration_time,
iss: ENV['PLATFORM_TOKEN_ISSUER']
}
headers = {
alg: "RS256",
typ: "JWT",
kid: ENV['PLATFORM_TOKEN_KEY_ID']
}
token = JWT.encode payload, rsa_private_key, "RS256", headers
token
end
end
```
```python
from flask import Flask
from time import time
import jwt
import os
import pem
app = Flask(__name__)
@app.route('/generate_token')
class Token():
def get(self):
secret_key = pem.parse_file('private_key.pem')
current_time = int(time())
expiration_time = current_time + 3600
payload = {
"partner": os.environ.get('SHIPENGINE_PARTNER_ID'),
"tenant": os.environ.get('SHIPENGINE_SELLER_ID'),
"scope": os.environ.get('SCOPE'),
"iat": current_time,
"exp": expiration_time,
"iss": os.environ.get('PLATFORM_TOKEN_ISSUER')
}
token = jwt.encode(payload, secret_key, algorithm='RS256', headers = {
"alg": "RS256",
"typ": "JWT",
"kid": os.environ.get('PLATFORM_TOKEN_KEY_ID')
})
return token
```
```php
public function generateToken($account_id){
$payload = [
'partner' => $this->partnerId,
'tenant' => $account_id,
'scope' => $this->scope,
'exp' => $this->currentTime + 3600,
'iat' => $this->currentTime,
'iss' => $this->issuer
];
$header = [
'alg' => 'RS256',
'typ' => 'JWT',
'kid' => $this->keyId
];
$key_path = storage_path('/keys/private.pem');
$secret = file_get_contents($key_path, true);
$token = JWT::encode($payload, $secret, 'RS256', null, $header);
return [
'error' => 0,
'responseMessage' => 'Successful',
'token' => $token
];
}
```
## Step 3: Using Elements
Determine your preferred integration method:
- [Elements SDK](/apis/shipengine/docs/elements/elements-sdk)
- [React Components](/apis/shipengine/docs/elements/elements-react) For React developers who want more flexibility and customizability.
If you're just getting started with ShipEngine Elements, it's helpful to know that by default, a ShipEngine Seller does not have warehouses (ship from locations) or carriers (USPS, UPS, DHL Express) attached to the seller account. These are required to purchase a label.
We recommend starting your users (ShipEngine Sellers) with the onboarding workflow via the [Onboarding](/apis/shipengine/docs/elements/onboarding/onboarding) Element to set up their warehouse and carrier accounts.
Updates to warehouse and carrier accounts created during onboarding can be made through the [Account Settings](/apis/shipengine/docs/elements/account-settings/account-settings) Element.
## Connecting Carrier Accounts
Elements integrates with the leading global shipping carriers. Check out the complete [carriers documentation](/apis/shipengine/docs/carriers/setup) to learn more about ShipEngine carriers.
To enable ShipEngine carrier accounts, you must explicitly list them in the `globalFeatures.enabledShipEngineCarriers` array within the features prop when initializing Elements.
> **DANGER:**
If you exclude the `globalFeatures.enabledShipEngineCarriers` property, or set the value to an empty array without including any supported carrier codes, users will encounter an error that will prevent them from advancing through the onboarding process within the [Onboarding Element](/apis/shipengine/docs/elements/onboarding/onboarding) and will not be able to access the [Carrier Services Element](/apis/shipengine/docs/elements/account-settings/carrier-services).
### Supported ShipEngine Carriers
- US Carrier Codes
- USPS: `stamps_com`
- DHL Express: `dhl_express_worldwide`
- GB Carrier Codes
- Yodel: `yodel_walleted`
- Evri: `hermes`
- DPD: `dpdwallet`
- CA Carrier Codes
- GlobalPost: `globalpost`
- AU Carrier Codes
- Aramex Australia: `aramex_au_walleted`
- Sendle: `sendle_walleted`
- CouriersPlease : `couriersplease_walleted`
### Supported External Carriers
To enable external carrier connections, you must explicitly list them in the `globalFeatures.enabledExternalCarriers` array within the features prop when initializing Elements. To learn more about connecting your external carriers checkout the [Connect External Carrier](/apis/shipengine/docs/elements/connect-external-carrier) Element.
> **DANGER:**
If you exclude the `globalFeatures.enabledExternalCarriers` property, or set the value to an empty array without including any supported carrier codes, users will not be able to view and connect External Carriers in the [Account Settings Element](/apis/shipengine/docs/elements/account-settings/account-settings) or the [Manage External Carriers Element](/apis/shipengine/docs/elements/account-settings/manage-external-carriers).
For an in depth overview of the global `features` object, please review the Configuring Elements Features step in either the [ElementsProvider](/apis/shipengine/docs/elements/elements-react#configuring-elements-features) or the [Elements SDK](/apis/shipengine/docs/elements/elements-sdk#configuring-elements-features) guides.
## Migration Guide to Elements v2
### 1. ShipEngine Alchemy is no longer a required package
To better address our partner's bundle size requirements in their applications, we have removed the need for the `@shipengine/alchemy` package. The core functionality of `@shipengine/alchemy` has been baked into the `@shipengine/elements` package. Similarly rather than wrapping your app in the `` in v2, we have exposed the new ``.
To learn more about getting started with the Elements React components, check out the [Elements React Components](/apis/shipengine/docs/elements/elements-react) documentation.
### 2. Elements name changes
Some of the Elements names have be changed to better align the Elements with proper naming conventions.
| v1 Name | v2 Name |
| --- | --- |
| `ManageWalletWorkflow` | `CarrierServices` |
| `ShipEngineCarriers` | `ManageCarriers` |
| `ConnectCarrier` | `ConnectExternalCarrier` |
| `ExternalCarriers` | `ManageExternalCarriers` |
| `WalletHistory` | `TransactionHistory` |
| `PurchaseLabelWorkflow` | `LabelWorkflow` |
| `ViewShipment` | `ShipmentSummary` |
> **NOTE:**
These changes refer to the names of the Element components, and do not necessarily match the (now deprecated) method names used by the Elements SDK (see below).
### 3. Features added to the Elements Provider and Elements SDK constructor
To improve the developer experience while implementing Elements, we have added a `features` prop to the Elements Provider and a `features` parameter to the Elements SDK constructor. In essence, this new features object rolls all the features throughout the Elements project into one object. For organizational purposes, we nested each Element's features under the Element's respective key within the object.
Features used throughout the Elements project, especially those pertaining to carriers like `enabledShipEngineCarriers` were hoisted up to a global features object shared throughout the project.
Any Element, in v1, whose only features are related to carriers i.e. `enabledShipEngineCarriers` or `enabledExternalCarriers`, have been moved up to the global features context. Below is the list of Elements which were affected by this change.
#### Affected Elements
- `Onboarding`: No longer has a `features` prop.
- `ManageExternalCarriers`: No longer has a `features` prop.
- `ManageCarriers`: No longer has any props.
- `CarrierServices`: No longer has any props.
- `ConnectExternalCarrier`: No longer has a `features` prop.
- `AccountSettings`: No longer requires carriers in its `features` prop.
- `VoidLabel`: No longer has a `features` prop.
> **NOTE:**
Global features are set within the features prop of the Elements Provider or parameter of the Elements SDK class constructor and **cannot** be overridden within specific Element feature props.
We recommend configuring the features you want to use in the Elements Provider or the SDK constructor for the Element. However, if you need to override features for a specific Element, you can use the feature prop for that Element, provided the Element supports it.
Learn more about each elements respective features.
- [Purchase Label Features](/apis/shipengine/docs/elements/label-workflow/purchase-label#purchase-label-features)
- [Shipment Summary Features](/apis/shipengine/docs/elements/label-workflow/shipment-summary#shipment-summary-features)
- [Account Settings Features](/apis/shipengine/docs/elements/account-settings/account-settings#account-settings-features)
```js
const elementsFeatures = {
globalFeatures: {
// Available external carrier codes for external carriers you wish to enable.
// ! NOTE: If this feature is omitted, external carrier accounts will not be available to connect.
enabledExternalCarriers: [
'apc',
'asendia',
'better_trucks',
'courierpost',
'dpd',
'seko',
'ups',
'yodel',
],
// Available ShipEngine carrier codes for carriers you wish to enable.
// ! NOTE: If this feature is omitted, ShipEngine carrier accounts will not be available to connect.
enabledShipEngineCarriers: ['ups', 'stamps_com'],
// Enables the `Powered by ShipEngine` logo in the footer of various Elements.
poweredByShipEngine: false,
},
purchaseLabelFeatures: { ... },
shipmentSummaryFeatures: { ... },
accountSettingsFeatures: { ... },
labelsGridFeatures: { ... }
}
// Create a new instance of the Elements SDK with features.
const elements = new ElementsSDK(
getToken,
{
onError: (err: Error) => console.error(err),
baseURL: 'https://elements.shipengine.com',
themeConfig: themeConfig,
locale: 'en-US',
features: elementsFeatures
}
);
```
### 4. Elements SDK improvements
In a effort to more closely align the Elements SDK with the functionality provided by the Element React components, we have made significant improvement to the Elements SDK.
**Mount Elements anywhere in the DOM**
Using the new `create` method on the Elements SDK class, you can now pass an HTML ID attribute or HTML element to the `mount` method to mount any Elements anywhere in your application HTML document. See [here](/apis/shipengine/docs/elements/elements-sdk#element-methods) to learn more about mounting Elements.
**Rendering in a Side Panel**
Methods like `elements.onboardUser` or `elements.purchaseLabel`, accessible via the Elements SDK class, have been deprecated in Elements v2 and will be removed in future versions. We have replaced these methods with the `renderSidePanel` method, which is available in the returned object via the `create` method. Please follow the recommended workflow in the [Elements Render Methods](/apis/shipengine/docs/elements/elements-sdk#element-render-methods) section to render elements in a Side Panel.
If you wish to temporarily continue using these methods, the parameter signatures have changed from v1, and will need to be updated. Rather than passing three parameters for Element properties, side panel properties, and header properties respectively, the methods take a single object argument with these values as properties:
```js
// Version 1 method invocation...
elements.purchaseLabel(props, panelProps, headerProps);
// ...becomes in version 2
elements.purchaseLabel({ elementProps: props, sidePanelProps: panelProps, sidePanelHeaderProps: headerProps });
```
Furthermore, some of the method names have been updated to correspond with the updated Element names. All others remain the same:
| v1 Name | v2 Name |
| --- | --- |
| `purchaseLabelWorkflow` | `labelWorkflow` |
| `viewShipment` | `shipmentSummary` |
**Destroy method**
In v1 of the Elements SDK, the `unmount` method, available via the SDK class, removed the React root and the HTML element to which the SDK attached the side panel. This method has been renamed `destroy`.