Migrating to the Unified SDK
What's involved
The Unified Enrollment SDK replaces the legacy Card Enrollment SDK with one flow that covers card entry, bank connections, and network bulk feeds. Migrating is client-side work only: swap the script, rename the global, and rework the callbacks. Your backend keeps working as is, with the same token endpoint, the same webhooks, and the same cardId and subscriptionId.
Out of the box the unified SDK behaves like the legacy one: the card form and 3DS verification, full-screen or in your container. Bank linking and bulk feeds stay off until you enable them server-side, so you can migrate first and adopt the new rails later with no further client release. See Choose your rails.
The unified SDK is in beta
What stays the same
- Authentication. The
getAccessTokencontract is unchanged: an async function returning a token minted by your backend, with the same 5000 ms default timeout (getAccessTokenTimeoutMsto raise it). Never ship a client secret to the browser. - Config options.
companyName,subaccountId,terms,statusLabels(same four stages),customerReferenceId,container, andmetadatacarry over unchanged. onConnect. Fires at the same moment in both SDKs: when the cardholder submits the card form and verification begins. An existing handler carries over as is.- Theming. Every legacy
setStylevariable keeps its name and effect. The unified SDK adds more surfaces (headlines, progress bar, chooser tiles, status chips); see Make it yours. - Everything server-side. Webhooks (
cardsubscription.created), REST resources, ids, and the subaccount'sverificationPolicyare untouched. A backend that assigns cards from webhooks needs no changes.
What changes
| Legacy | Unified | |
|---|---|---|
| Script | https://sdk.astrada.co/v1/cardEnrollmentSdk.js | https://sdk.astrada.co/unified/v1/unifiedEnrollmentSdk.js |
Script data-id | card-enrollment-sdk | unified-enrollment-sdk |
| Global | CardEnrollmentSdk | UnifiedEnrollmentSdk |
| Open / close | openForm(config) / closeForm() | open(config) / close() |
| TypeScript types | @astrada.co/card-enrollment-sdk | @astrada.co/card-enrollment-sdk/unified (same package, new entry point) |
onSuccess | One card, at the moment it enrolls | The whole session, once, at close |
onError payload | { type, error: { detail, errorCode } } | { flow, code, message }, and the session stays open |
onCancel | Cardholder closed the form | Session closed with nothing enrolled |
| New options | onEvent, entry, rail preferences, linkAnotherCard, maxWidth |
The renames are mechanical. The three callback rows change behavior, so take them one at a time below.
The swap
<script
type="text/javascript"
src="https://sdk.astrada.co/v1/cardEnrollmentSdk.js"
data-id="card-enrollment-sdk"
></script>
<script>
CardEnrollmentSdk.openForm({
companyName: "Your company",
subaccountId: "<subaccountId>",
getAccessToken: async () => "<access token>",
onSuccess: (data) => assignCard(data.cardId, data.subscriptionId),
onError: (data) => showError(data.error.detail),
onCancel: () => {},
});
</script><script
type="text/javascript"
src="https://sdk.astrada.co/unified/v1/unifiedEnrollmentSdk.js"
data-id="unified-enrollment-sdk"
></script>
<script>
UnifiedEnrollmentSdk.open({
companyName: "Your company",
subaccountId: "<subaccountId>",
getAccessToken: async () => "<access token>",
onSuccess: (result) => {
for (const card of Object.values(result.cards)) {
assignCard(card.cardId, card.subscriptionId);
}
},
onError: (err) => showError(err.message),
onCancel: () => {},
});
</script>
Both scripts can coexistThe two SDKs use separate globals and namespaced messages, so a page that ends up loading both tags during a staged rollout is fine. Don't mount both into the same
container.
Rework the callbacks
onSuccess: one session result, at close
onSuccess: one session result, at closeThe legacy SDK fired onSuccess the moment a card enrolled, with that one card. The unified SDK fires it once, when the session closes with at least one completed action, with a map of everything the session created. Iterate the map instead of reading data.cardId:
onSuccess: (result) => {
for (const card of Object.values(result.cards)) {
assignCard(card.cardId, card.subscriptionId);
}
},Three things to check in your handler:
- Timing. If you acted at the moment of enrollment (advancing your own UI, polling the subscription), read the
card-enrolledevent ononEvent; it still fires per card, as it happens. - Fields.
enrollmentGuidanceandauthenticationFloware not in the map. Capture them from thecard-enrolledevent if you use them. - Multiple cards. The success screen offers Link another card, so one session can produce several. The loop above handles that; pass
linkAnotherCard: falseto keep one-card sessions.
Webhooks stay your system of recordSession callbacks mirror this browser session. The durable record is the
cardsubscription.createdwebhook, which fires for every enrolled card regardless of how the
session ended. See Listen for results.
onError: new payload, and the flow continues
onError: new payload, and the flow continues| Legacy field | Unified field |
|---|---|
error.errorCode | code |
error.detail | message |
type ("client" / "server") | flow ("card" / "bank" / "feed" / "session") |
verificationId, cardId, subscriptionId | Unchanged: present once the objects exist |
Errors no longer end the enrollment. The SDK shows the cardholder the error screen and lets them retry or pick another path, so treat onError as a signal for logging and support, and let onSuccess or onCancel tell you how the session actually ended.
If you intercepted card_subscription.card_must_be_network_bulk_enrolled and called closeForm() to run your own flow, retire that workaround: enable the bulk rail and the SDK routes those cards to the bulk feed wizard itself.
onCancel: means nothing was enrolled
onCancel: means nothing was enrolledIt fires only when the session closes with zero completed actions. A session that enrolled a card and then closed fires onSuccess, never onCancel. If your handler assumed no card was created, that assumption now always holds. If you only need to know the surface closed, listen for the closed event on onEvent; it fires on every close and carries the same maps onSuccess gets.
Optional: switch to onEvent
onEventThe callbacks above are enough for a like-for-like migration. onEvent is the unified SDK's primary callback and streams every action as it happens (card-enrolled, bank-linked, feed-created, error, closed, cancelled). Adopt it when you want per-action reactions, and before you turn on the bank or bulk rails. See Listen for results.
Update the types
Same package, new entry point. The root export keeps typing the legacy SDK, so both imports coexist while you migrate:
npm install --save-dev @astrada.co/card-enrollment-sdk@latest// Before
import type { CardEnrollmentSdkConfig, EnrollmentSuccess } from "@astrada.co/card-enrollment-sdk";
// After
import type {
UnifiedEnrollmentSdkConfig,
EnrollmentSuccess, // now the consolidated session result
SdkEvent, // the onEvent union
} from "@astrada.co/card-enrollment-sdk/unified";EnrollmentSuccess exists at both entry points with different shapes. Import it from the entry point that matches the SDK you're calling.
Migration checklist
- Swap the script URL and its
data-id. - Rename calls:
CardEnrollmentSdk.openForm()toUnifiedEnrollmentSdk.open(),closeForm()toclose(). - Rework
onSuccessto iterateresult.cards; readauthenticationFlowandenrollmentGuidancefrom thecard-enrolledevent if you use them. - Rework
onErrorto readcodeandmessage, and stop treating an error as the end of the session. - Re-check
onCancelhandlers; it now fires only when nothing was enrolled. - Retire any
closeForm()workaround for bulk-eligible cards. - Point TypeScript imports at
@astrada.co/card-enrollment-sdk/unified. - Keep
setStyleas is (same variables); add the new ones when you want them. - Test the full flow in sandbox before switching production traffic: Test Cards & Sandbox Testing.
- When you're ready for bank linking or bulk feeds, set
enrollmentPolicyon the subaccount: Choose your rails. No client release needed.
Related
- Unified Enrollment SDK: the full reference for
open(), events, and rails. - Installation: the legacy SDK reference you are migrating from.
- Test Cards & Sandbox Testing: drive success, challenge, and decline paths deterministically.
- Unified Card & Bank Feeds: the shared card model behind every rail.
Updated about 2 hours ago
