# Get Merchant Accounts Source: https://docs.knotapi.com/api-reference/accounts/get-accounts GET /accounts/get Get a user's merchant accounts linked to Knot. This endpoint allows you to retrieve an array of merchant accounts for a given user (specified in the request with `external_user_id`) that are currently linked to Knot. If a user unlinked a merchant account with [Unlink Merchant Account](https://docs.knotapi.com/api-reference/accounts/unlink-account), it will not be returned. You can choose to retrieve all merchant accounts for a user or a single merchant account by passing a specific `merchant_id` in the request. # Unlink Merchant Account Source: https://docs.knotapi.com/api-reference/accounts/unlink-account POST /accounts/unlink Unlink a merchant account from the Knot platform. # Get Audit Logs Source: https://docs.knotapi.com/api-reference/audit-logs/get-audit-logs GET /audit_logs Retrieve audit logs for employee usage of the Knot Dashboard. #### Logged actions * **Authentication**: Login attempts, 2FA changes, password resets * **Users**: Adding, updating, and removing Knot Dashboard users * **Merchants**: Activating, hiding, and exporting merchants * **Webhooks**: Creating, updating, and deleting webhook configurations * **Access grants**: Creating and revoking privileged access grants * **Secrets**: Viewing and rotating API secrets * **SSO**: Adding, updating, enabling, and disabling SAML configurations * **Domains**: Adding and removing allowlisted domains * **Account**: Account activation and app logo updates # Authenticate Knot API requests with HTTP basic auth Source: https://docs.knotapi.com/api-reference/authentication Authenticate Knot API requests using HTTP basic auth with your client_id and secret, base64-encoded, plus the development and production base URLs. ## Overview Authentication to the API is performed via HTTP basic authentication. Requests made over plain HTTP or without authentication will fail. In each request, pass a base64-encoded string `username:password` as an authorization header. In the [Knot Dashboard](https://dashboard.knotapi.com/developers/keys), you will find your `client_id` and `secret` for the development environment, which you can use as the basic auth `username` and `password` respectively. When you are ready to go to production, use your production `client_id` and generated `secret` from the [Knot Dashboard](https://dashboard.knotapi.com/developers/keys) for the authentication header. #### URLs | Environment | URL | | ----------- | --------------------------------- | | Development | `https://development.knotapi.com` | | Production | `https://production.knotapi.com` | Knot sends and receives all API traffic from the following IP address in all environments (Production & Development): `35.232.249.218/32`. This is the same IP address Knot uses to send [webhook](/webhooks) payloads. This IP address is subject to change and Knot will notify you in advance of any changes. ## Base64 Encoding To create the basic authorization header, base64 encode your `client_id` and `secret` in the format `client_id:secret` like below. ```typescript TypeScript icon=node-js theme={"system"} const clientId = "your_client_id"; const secret = "your_secret"; // Combine client_id and secret with a colon const credentials = `${clientId}:${secret}`; // Base64 encode the credentials const encodedCredentials = Buffer.from(credentials).toString('base64'); // Use in Authorization header const authHeader = `Basic ${encodedCredentials}`; console.log(authHeader); ``` ```python Python icon=python theme={"system"} import base64 client_id = "your_client_id" secret = "your_secret" # Combine client_id and secret with a colon credentials = f"{client_id}:{secret}" # Base64 encode the credentials encoded_credentials = base64.b64encode(credentials.encode()).decode() # Use in Authorization header auth_header = f"Basic {encoded_credentials}" print(auth_header) ``` ```go Go icon=golang theme={"system"} package main import ( "encoding/base64" "fmt" ) func main() { clientId := "your_client_id" secret := "your_secret" // Combine client_id and secret with a colon credentials := clientId + ":" + secret // Base64 encode the credentials encodedCredentials := base64.StdEncoding.EncodeToString([]byte(credentials)) // Use in Authorization header authHeader := "Basic " + encodedCredentials fmt.Println(authHeader) } ``` ```java Java icon=java theme={"system"} import java.util.Base64; import java.nio.charset.StandardCharsets; public class BasicAuth { public static void main(String[] args) { String clientId = "your_client_id"; String secret = "your_secret"; // Combine client_id and secret with a colon String credentials = clientId + ":" + secret; // Base64 encode the credentials String encodedCredentials = Base64.getEncoder() .encodeToString(credentials.getBytes(StandardCharsets.UTF_8)); // Use in Authorization header String authHeader = "Basic " + encodedCredentials; System.out.println(authHeader); } } ``` ```php PHP icon=php theme={"system"} ``` # Link Account Source: https://docs.knotapi.com/api-reference/development/link-account POST /development/accounts/link Link a merchant account in development. Before using this endpoint, we recommend setting up a webhook for the development environment in the [Knot Dashboard](https://dashboard.knotapi.com/developers/webhooks) if you have not done so already. #### Overview Use this endpoint to manually link a user's merchant account in the development environment for testing purposes without the need to install or invoke the client-side SDK. Calling this endpoint will generate a linked merchant account on the Knot platform for a specific user & merchant. It can optionally simulate the card switcher flow (by passing `card_switcher: true`) or generate new transactions (by passing the `transactions` object in the request). #### Usage You can test all of the following without use of the client-side SDK: 1. Link a merchant account to the Knot platform. 2. Receive notification of the link via the [`AUTHENTICATED`](/link/webhook-events/authenticated) event to your webhook, then POST card data to the [`Send Card (JWE)`](/api-reference/products/card-switcher/switch-card-jwe) or [`Send Card`](/api-reference/products/card-switcher/switch-card) endpoint. 3. Receive notification of the result via the [`CARD_UPDATED`](/card-switcher/webhook-events/card-updated) or [`CARD_FAILED`](/card-switcher/webhook-events/card-failed) event. The `card_id` parameter is required when `card_switcher` is `true`. The `card_switcher` parameter is mutually exclusive with the `transactions` parameter. You cannot use both in the same request. You can test all of the following without use of the client-side SDK: 1. Link a merchant account to the Knot platform. 2. Receive notification of the link via the [`AUTHENTICATED`](/link/webhook-events/authenticated) event to your webhook. 3. Generate sample transactions. 4. Receive notification of the transactions via the [`NEW_TRANSACTIONS_AVAILABLE`](/transaction-link/webhook-events/new-transactions-available) event to your webhook. You can test all of the following without use of the client-side SDK: 1. Link a merchant account to the Knot platform. 2. Receive notification of the link via the [`AUTHENTICATED`](/link/webhook-events/authenticated) event to your webhook. 3. Begin making requests to [Sync Cart](/api-reference/products/shopping/sync-cart) and [Checkout](/api-reference/products/shopping/checkout). # mTLS Source: https://docs.knotapi.com/api-reference/mTLS Configure mutual TLS (mTLS) authentication for enhanced API security using client certificates. ## Overview All API requests made over mTLS use the following **unique domains** for each of Knot's environments: | Environment | Domain | | ----------- | -------------------------------------- | | Development | `https://mtls.development.knotapi.com` | | Production | `https://mtls.production.knotapi.com` | ## Enabling mTLS To enable mTLS, follow the steps below. Use your preferred method to generate a private key and corresponding CSR that meets the following requirements: 1. The CSR uses RSA 2048 as the key algorithm. 2. The CSR uses SHA2-256 as the hash algorithm. 3. The Common Name (CN) attribute is assigned to your `client_id`. Below is an example using OpenSSL: ``` openssl req \ -new -sha256 -newkey rsa:2048 -nodes \ -subj '/CN=[client_id]' \ -keyout client.key -out client.req ``` Ensure you keep the private key secure as it will be used later in API requests. Forward the CSR file to Knot and request that it be activated for your `client_id`. You'll promptly receive a client certificate signed by Knot. This certificate, in conjunction with your private key, will serve as the authentication mechanism for interacting with the API. Receive the client certificate signed by Knot and confirm that mTLS is enabled for a given environment. Make all requests to the API with your `client_id` over mTLS. Attach the `client.cert` and `client.key` in your HTTP client. | Environment | Domain | | ----------- | -------------------------------------- | | Development | `https://mtls.development.knotapi.com` | | Production | `https://mtls.production.knotapi.com` | # List Merchants Source: https://docs.knotapi.com/api-reference/merchants/list-merchants POST /merchant/list Retrieve a list of available merchants. # Retrieve JWK Source: https://docs.knotapi.com/api-reference/products/card-switcher/retrieve-jwk GET /jwe/key Retrieve a public key in JWK format. ### Building the JWE See code samples [here](/card-switcher/sending-card-data#code-samples) for how to structure and encrypt the JWE in various programming languages. You can encrypt the payload you'll provide to the [Switch Card (JWE)](https://docs.knotapi.com/api-reference/products/card-switcher/switch-card-jwe) endpoint using your JWE public key. The JWE specifications are the following: 1. RSA 2048 certificate in JWK format 2. RSA-OAEP-256 as key encryption algorithm 3. A256GCM as content encryption algorithm The JWE value should be a JSON string with the structure below. Additionally, in the development environment, the below values are sufficient to pass validation when building the JWE. ```json JSON icon="file-brackets-curly" theme={"system"} { "user": { "name": { "first_name": "Ada", // Max length: 255 "last_name": "Lovelace" // Max length: 255 }, "address": { "street": "100 Main Street", // Max length: 46 "street2": "#100", // Max length: 46 "city": "NEW YORK", // Max length: 32 "region": "NY", // Must be an ISO 3166-2 sub-division code "postal_code": "12345", // Min length: 5, Max length: 10 "country": "US" // Must be an ISO 3166-1 alpha-2 code }, "phone_number": "+11234567890" // Must be in E.164 format }, "card": { "number": "4242424242424242", "expiration": "08/2030", // MM/YYYY or MM/YY format "cvv": "012" // Max length: 4 } } ``` # Switch Card Source: https://docs.knotapi.com/api-reference/products/card-switcher/switch-card api-reference/openapi_secure.json POST /card Switch a card in a user's merchant account. # Switch Card JWE Source: https://docs.knotapi.com/api-reference/products/card-switcher/switch-card-jwe api-reference/openapi.json POST /card Switch a card in a user's merchant account. See code samples [here](/card-switcher/sending-card-data#code-samples) for how to structure and encrypt the JWE in various programming languages. Receiving a successful response from this endpoint means your request has passed validations, including for the `jwe`. # Detect Accounts Source: https://docs.knotapi.com/api-reference/products/detect/detect-accounts POST /detect Detect merchant accounts with a user's email. After calling this endpoint, listen for the [`NEW_DETECTED_ACCOUNTS_AVAILABLE`](/detect/webhook-events/new-detected-accounts-available) webhook event which will fire if any detected accounts are found. This may take up to 1 minute. # List Detected Accounts Source: https://docs.knotapi.com/api-reference/products/detect/list-detected-accounts POST /detected-accounts/list List a user's detected accounts using cursor-based pagination. #### Overview This endpoint allows you to list **detected** accounts for a given user using cursor-based pagination. Primarily, these detected accounts are delivered in the [`NEW_DETECTED_ACCOUNTS_AVAILABLE`](/detect/webhook-events/new-detected-accounts-available) webhook payload. # Search Detected Accounts Source: https://docs.knotapi.com/api-reference/products/detect/search-detected-accounts POST /detected-accounts/search Search for a user's merchant accounts online. #### Overview This endpoint allows you to search for detected accounts by providing a list of merchant or company `names`. For each name, the response indicates whether the user has a detected account. This is useful for developers with a predefined list of companies or offers who want to determine whether they apply to a given user. Call this endpoint upon receiving the [`NEW_DETECTED_ACCOUNTS_AVAILABLE`](/detect/webhook-events/new-detected-accounts-available) webhook. #### Usage You can use detected accounts retrieved from this endpoint to closely personalize the merchants you present to users in your app or through lifecycle marketing campaigns. For example, if a detected account at Uber is present for a user, you can more prominently display Uber to that user in your app or as a push notification/email. Moreover, aggregated detected account information can be useful in designing a rewards program or other product features throughout your app. # Sync Detected Accounts Source: https://docs.knotapi.com/api-reference/products/detect/sync-detected-accounts POST /detected-accounts/sync Sync a user's detected accounts using cursor-based pagination. This endpoint is deprecated. Listen to the [`NEW_DETECTED_ACCOUNTS_AVAILABLE`](/detect/webhook-events/new-detected-accounts-available) webhook event to receive detected accounts for a user or call [List Detected Accounts](/api-reference/products/detect/list-detected-accounts). # Checkout Source: https://docs.knotapi.com/api-reference/products/shopping/checkout POST /cart/checkout Checkout a user's merchant cart. # Sync Cart Source: https://docs.knotapi.com/api-reference/products/shopping/sync-cart POST /cart Add one or more products to a user's merchant cart. # Cancel Subscription Source: https://docs.knotapi.com/api-reference/products/subscriptions/cancel POST /subscriptions/{id}/cancel Cancel a subscription or bill associated with a linked merchant account. # Get Subscription By ID Source: https://docs.knotapi.com/api-reference/products/subscriptions/get-by-id GET /subscriptions/{id} Get a specific subscription by ID. # List Subscriptions Source: https://docs.knotapi.com/api-reference/products/subscriptions/list POST /subscriptions/list List a user's subscriptions using cursor-based pagination. # Refresh Subscriptions Source: https://docs.knotapi.com/api-reference/products/subscriptions/refresh POST /subscriptions/refresh Initiate an on-demand refresh of subscription data for a user's merchant account. If new or updated subscriptions are found, you will receive the [`NEW_SUBSCRIPTIONS_AVAILABLE`](/subscription-manager/webhook-events/new-subscriptions-available) or [`UPDATED_SUBSCRIPTIONS_AVAILABLE`](/subscription-manager/webhook-events/updated-subscriptions-available) webhooks respectively. # The Subscription Object Source: https://docs.knotapi.com/api-reference/products/subscriptions/subscription-object # Get Transaction By ID Source: https://docs.knotapi.com/api-reference/products/transaction-link/get-by-id GET /transactions/{id} Get a specific transaction by ID. # Refresh Transactions Source: https://docs.knotapi.com/api-reference/products/transaction-link/refresh POST /transactions/refresh Initiate an on-demand refresh of transaction data for a user's merchant account. # Sync Transactions Source: https://docs.knotapi.com/api-reference/products/transaction-link/sync POST /transactions/sync Sync a user's transactions for a merchant account using cursor-based pagination. # The Transaction Object Source: https://docs.knotapi.com/api-reference/products/transaction-link/transaction-object # Create Session Source: https://docs.knotapi.com/api-reference/sessions/create-session POST /session/create Create a session and use it to initialize the SDK. Subsequently pass the value you receive in this endpoint into the `KnotConfiguration` class of the SDK during initialization. Sessions last **30 minutes**. It is best practice to create a new session each time you initialize the SDK and not log the session in any internal or 3rd party tooling. # Extend Session Source: https://docs.knotapi.com/api-reference/sessions/extend-session POST /session/extend Extend a session. This endpoint allows you to extend an existing session (`session_id`) for another **30 minutes** while a user has the SDK open. Call this endpoint when you receive the `refresh session request` event in the `onEvent` callback. If the SDK is closed, it is best practice to create a new session before re-initializing the SDK using [Create Session](https://docs.knotapi.com/api-reference/sessions/create-session). # Delete User & Data Source: https://docs.knotapi.com/api-reference/users/delete-user-and-data POST /user/delete Delete a user and their data. # Versioning Source: https://docs.knotapi.com/api-reference/versioning Understand how API versioning works and how to specify API versions using the Knot-Version header. ## Overview When Knot makes backwards-incompatible updates to non-beta products, a new API version is released to avoid breaking changes for developers. When a new version is released, you can choose whether to continue using an existing API version or update your application to the newer version. Updating lets you take advantage of new features, improvements, and fixes. To specify the API version of a request, use the `Knot-Version` header. If the `Knot-Version` header is not provided in a request, it will default to major version `2.0` of the API. We recommend including the `Knot-Version` header, like below. ``` curl -X POST 'https://development.knotapi.com/session/create' \ -u 'bd271e95-14e6-47ab-9f4f-225898f69183:cf819749c0574616ba93b5935b8cf108' \ -H 'Content-Type: application/json' \ -H 'Knot-Version: 2.0' ``` ## Backwards Compatible Changes Knot considers the following changes to be backwards compatible (i.e. non-breaking): * Adding new API endpoints * Adding new optional parameters to existing API endpoints * Adding new properties to existing API response schemas * Adding new values to enum fields in API responses * Adding new webhook events * Adding new properties to webhook event payloads. Ensure your webhook logic can gracefully handle unfamiliar event types. * Changing the length, format, or content of human-readable strings (error messages, etc.) # Building with AI Source: https://docs.knotapi.com/building-with-ai Use AI-powered tools to integrate Knot's products faster by searching docs from your IDE with the MCP server or installing skills to guide your agent through specific integrations. ## MCP server Knot provides an MCP server that lets you search and interact with this documentation via natural language from your IDE, terminal, or any MCP-compatible tool. The MCP server searches Knot's documentation only and does not call Knot's API. ### Setup Run the following command in your terminal to install the Knot MCP server as a project-level MCP locally. It auto-detects your AI tools (Cursor, Claude Code, VS Code, etc.) and configures them automatically. ```bash theme={"system"} npx add-mcp https://docs.knotapi.com/mcp --name knot-docs ``` For the Claude Code Desktop App, you may also follow Claude's docs on installing an MCP server [here](https://support.claude.com/en/articles/10949351-getting-started-with-local-mcp-servers-on-claude-desktop). Ask questions and the agent will search Knot's documentation to retrieve answers directly in your development environment. **Example prompts:** * "How do I integrate the Knot iOS SDK?" * "How can I get the available merchant list for TransactionLink?" * "Tell me how to create a session and what do I do after that?" ## Skills Knot provides product-specific skills that guide AI agents through implementing specific integrations, including the right API calls, webhook handling, data schemas, and testing steps. Installing a skill gives your agent the procedural knowledge to build an integration on your behalf. The MCP server helps you *search* Knot's docs. Skills teach your agent *how to build* specific integrations step-by-step. ### Install skills Run the following command in your terminal to view and select which skills to install into your agent's context so it can take actions on your behalf: ```bash theme={"system"} npx skills add https://docs.knotapi.com ``` You can also select and copy & paste the skill.md files for relevant skill(s) below to your agent to add skills. ### Available skills The full list of available skills can be found at [`docs.knotapi.com/.well-known/skills/index.json`](https://docs.knotapi.com/.well-known/skills/index.json). #### Integration skills | Name | Description | | ------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [`knot-sdk`](https://docs.knotapi.com/.well-known/skills/knot-sdk/skill.md) | Install and initialize the Knot SDK across platforms (iOS, Android, React Native, Flutter, Web). | | [`knot-transaction-link`](https://docs.knotapi.com/.well-known/skills/knot-transaction-link/skill.md) | Implement the full transaction data integration from scratch, including session creation, merchant account linking via the SDK, and syncing SKU-level transaction data. | | [`knot-sync-transactions`](https://docs.knotapi.com/.well-known/skills/knot-sync-transactions/skill.md) | Sync and store SKU-level transaction data from Knot's API when card switching is already implemented or another use case that links merchant accounts is in place. | | [`knot-subscriptions`](https://docs.knotapi.com/.well-known/skills/knot-subscriptions/skill.md) | Retrieve and store subscription data from Knot's API when card switching is already implemented. | #### Prototyping skills | Name | Description | | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | [`knot-prototype-transactions`](https://docs.knotapi.com/.well-known/skills/knot-prototype-transactions/skill.md) | Generate sample transaction data from the development API and use it immediately for prototyping — no SDK, no webhooks, no production setup required. | | [`knot-prototype-shopping`](https://docs.knotapi.com/.well-known/skills/knot-prototype-shopping/skill.md) | Prototype a shopping experience — link a merchant account, add products to a cart, checkout, and retrieve order confirmation data from the development API. | # Server-Side Updates Source: https://docs.knotapi.com/card-switcher/card-updater Automatically provision new cards to merchant accounts without user interaction. ## Overview This functionality (CardUpdater™) enables you to provision a user's new card to their merchant accounts entirely server-side, without requiring them to re-authenticate through the SDK. This is ideal for scenarios where a user already has linked merchant accounts and you want to seamlessly update their payment method when a new card is issued. ## Use Cases * **A user's card is reissued** (e.g., expiration or replacement) and you want to update their payment method across merchants where they've previously authenticated. * **A user is issued a different card** that you want to provision to merchants, such as when you have a business incentive to capture higher interchange spend (e.g., moving from debit to credit). This functionality requires users to have previously authenticated with merchants through the Knot SDK. It updates cards at existing merchant account connections—it does not create new ones. ## Integration Steps When a new card is issued for a user, call [Get Merchant Accounts](/api-reference/accounts/get-accounts) to retrieve the merchant accounts where the card can be provisioned. Whether the card can be updated at each merchant is indicated by the presence of `update_card` in `connection.scopes.type`: ```json theme={"system"} [ { "merchant": { "id": 19, "name": "DoorDash", "logo": "https://knot.imgix.net/merchants/KBQ5j6cN010PPpwbO7RpKGyDrCpsZ91FRhwnZp5u.png" }, "connection": { "status": "connected", "scopes": [ { "type": "update_card" } ] }, "lifecycle": { "status": null }, "last_user_action": { "attempted_at": null, "status": null } } ] ``` For each merchant account that supports updating the card and where updating is desired, make a request to [Switch Card](/api-reference/products/card-switcher/switch-card#cardupdater) or [Switch Card (JWE)](/api-reference/products/card-switcher/switch-card-jwe#cardupdater) to provision the new card. Listen for the [`CARD_UPDATED`](/card-switcher/webhook-events/card-updated) and [`CARD_FAILED`](/card-switcher/webhook-events/card-failed) webhooks to track the status of each card update, just as you do for SDK-initiated card switches. # Personalization Source: https://docs.knotapi.com/card-switcher/personalization Learn about automatic personalization features in the CardSwitcher experience using detected merchant accounts. ## Overview The Knot Link SDK includes many features to personalize the cardswitching experience for users. Together, these features (as described below) provide a highly-tailored and frictionless experience to each individual user that seeks to switch their card across various merchant wallets. The set of personalization features operate across a few of Knot's direct merchant integrations: Apple, Google, and Amazon. ## User Experience ### Auto-Detection Via Email & Phone When you pass a user's email address or phone number in the [Create Session](/api-reference/sessions/create-session) endpoint, Knot automatically detects which merchant accounts the user has online. The SDK merchant list is then personalized to surface these detected merchants, allowing users to immediately see the accounts most relevant to them without needing to search or scroll. ### Quick-Switch Merchants with Apple When a user switches their card at Apple, additional merchant accounts they use online are detected across two categories: 1. Merchant accounts that allow for the most frictionless form of login on iOS: **FaceID**. 2. Subscriptions and in-app purchases These detected merchant accounts are surfaced prominently to users. This reduces the friction involved in selecting the right merchants to switch their card, as well as the friction of logging in to those merchant accounts. This is achieved by the SDK supporting **FaceID** for login. Personalization Detected Merchant Accounts Pn ### Quick-Switch Merchants with Google When a user switches their card with Google, additional merchants where they login with Google SSO are detected and surfaced prominently. Subsequently, users can choose to switch their card with these additional merchants **without the need to login to each individually**. Personalization Google SSO ### Quick-Switch Merchants with Amazon When a user switches their card with Amazon, additional merchants linked to their Amazon account are detected and surfaced prominently. Furthermore, if a user has Amazon Prime Video or Amazon Music, these services will be detected and the card automatically switched into these wallets as well. This enables you to capture the spend across all Amazon services, not just the standard Amazon.com wallet. Personalization Detected Amazon Pn ### Popular Merchants When a user first sees the Home screen that displays a list of merchants in the SDK, they are presented with a category of "Popular" merchants. This "Popular" category is dynamic (rather than static) and frequently/automatically updates based on the most popular merchants where users seek to switch their card. Popular Category ## API Not only are detected merchant accounts used to personalize the card switching experience, but you can also retrieve the full set of detected merchant accounts for a user. To do so, listen for the [`NEW_DETECTED_ACCOUNTS_AVAILABLE`](/detect/webhook-events/new-detected-accounts-available) webhook - its payload contains the user's detected merchant accounts. You can also call the [List Detected Accounts](/api-reference/products/detect/list-detected-accounts) endpoint to query them on demand. This can be quite useful in personalizing the set of merchants you encourage users to switch their card with through lifecycle re-engagement campaigns. # Plaid Integration Source: https://docs.knotapi.com/card-switcher/plaid-integration ## Overview Knot has a partnership with Plaid, described in Plaid's documentation [here](https://plaid.com/partner-directory/). This partnership allows Knot to use Plaid's `processor_token` functionality to retrieve transaction data from Plaid for the purpose of detecting merchants where users are spending. Doing so can help improve conversion. By creating a `processor_token` through Plaid's API and providing that to Knot when you create a session to initialize the Knot SDK, Knot will automatically detect merchants where the end user is spending and make them more prominent in the user experience. This will in turn help improve conversion for your product experience. Please note that this process takes a number of seconds, so users will not immediately see a modified list of merchants when the SDK is initialized. Rather, they will see it after a number of seconds while on the merchant list screen, if returning to the screen after authenticating to a merchant, or if returning on subsequent sessions. ## Setup Follow [Plaid's documentation](https://plaid.com/docs/transactions/partnerships/knot/) to create a `processor_token`. Pass the `processor_token` in the request to [Create Session](/api-reference/sessions/create-session). # I2C Source: https://docs.knotapi.com/card-switcher/processor-digital-banking-integrations/i2c Use Knot's direct integration with I2C to securely retrieve and send card information without backend requests. ## Overview Knot has a direct integration with I2C to retrieve end user card information directly on your behalf. You can use this integration to securely send card information to Knot without your backend needing to make any requests. If you do not already have access to end user card information, you can take advantage of the direct Knot and I2C integration to simplify your integration with Knot's CardSwitcher product. # Q2 Source: https://docs.knotapi.com/card-switcher/processor-digital-banking-integrations/q2 Enable cardholders to add their cards to merchants through Knot's direct integration with Q2 mobile banking. ## Overview Knot integrates directly with Q2 mobile banking so your cardholders can securely add their cards to merchants. Knot is listed in the Q2 marketplace, where you can request access directly. A typical implementation takes three to five weeks. You can request third-party applications from the Q2 Partner Catalog on behalf of your organization. ## Setup In the Q2 Partner Catalog, click **Request App** and accept Q2's agreements. Knot is notified of your request. The Knot team reaches out to finalize and submit the partnership agreement. Q2 reviews and approves the onboarding request. You submit your navigation preferences, and Knot submits the technical app configuration. Q2 deploys the app into your environment. Knot helps you test and validate that everything works correctly in production. You enable the app for your full user base. The app goes live immediately in your mobile banking platform. # Unit Source: https://docs.knotapi.com/card-switcher/processor-digital-banking-integrations/unit Use Knot's direct integration with Unit to securely retrieve and send card information without backend requests. ## Overview Knot has a [direct integration with Unit](https://www.unit.co/docs/partnerships/partner-tokens/#unit-customers-requesting-a-partner-token-set-up) and is set up as a `partner user` on Unit's platform in order to retrieve card information. You can use this integration to securely send card information to Knot without your backend needing to make any requests. ## Setup Contact your Customer Success Manager at Unit to request that Knot be allowed to retrieve end-user information from Unit in both **sandbox** and **production**. Once this is complete, notify Knot. Once Unit has confirmed that Knot may retrieve card information on your users, notify the Knot team. Knot will configure your integration to enable Unit as the method of retrieving card information. As a result, you will not be required to provide end-user card information to Knot in the `/card` endpoint. # Quickstart Source: https://docs.knotapi.com/card-switcher/quickstart Get started with the CardSwitcher integration to provision cards to merchant accounts. ## Introduction Integrating Knot is quite simple. You'll need a basic client-side and server-side integration: your backend creates a session, your client invokes the SDK, and once the user authenticates, your backend listens for the webhook and switches the card. ## Set Up Ensure you have access to your [Customer Dashboard](https://dashboard.knotapi.com) and retrieve your `client_id` and `secret`, which you will use as the basic auth username and password for your API key respectively. Note that your `client_id` and `secret` vary between the `development` and `production` environments. Install and import an SDK for your platform: [iOS](/sdk/ios), [Android](/sdk/android), [React Native](/sdk/react-native), [Flutter](/sdk/flutter), or [Web](/sdk/web). Register a webhook endpoint in the [Customer Dashboard](https://dashboard.knotapi.com/developers/webhooks) and subscribe to the [webhook events](/webhooks) your backend needs to be notified of when to send the card details to Knot and further card switching lifecycle events. ## Start the Flow With your `client_id` and `secret` for the `development` environment, call [Create Session](/api-reference/sessions/create-session) with `type: card_switcher` to create a session used when invoking the SDK. You can send the user's `email` and `phone_number` when creating the session to automatically detect their online merchant accounts and personalize the experience in the SDK. [See more here.](/card-switcher/personalization#auto-detection-via-email-&-phone) Initialize the SDK with the `session_id` retrieved from [Create Session](/api-reference/sessions/create-session). The SDK is where users will interact with the Knot UI to authenticate to various merchants. All login flows, including step-up authentication, are handled with the SDK. Users will see real-time feedback as they progress through authenticating with a merchant. Wire up `onSuccess`, `onError`, `onExit`, and `onEvent` to react to client-side events as the user moves through the flow. See further details on callback events for [iOS](/sdk/ios#events), [Android](/sdk/android#events), [React Native](/sdk/react-native#events), [Flutter](/sdk/flutter#events), and [Web](/sdk/web). **Tag your entry points.** To provide better visibility into conversion across different entry points, the Knot SDK supports an `entryPoint` parameter when invoking the SDK. This value is returned in the down-funnel [`AUTHENTICATED`](/link/webhook-events/authenticated) webhook event, allowing you to measure conversion by entry point in your analytics tool of choice. Pass a distinct `entryPoint` value for each entry point location where the Knot SDK is invoked in your app. Common examples: `onboarding`, `home`, `push-notif-X`, `in-app-lifecycle-card-X`. Tagging entry points is strongly recommended to future-proof visibility into your implementation and allow for downstream conversion optimizations. Send card information to [Switch Card](/api-reference/products/card-switcher/switch-card) or [Switch Card (JWE)](/api-reference/products/card-switcher/switch-card-jwe) within 15 seconds of receiving the [`AUTHENTICATED`](/link/webhook-events/authenticated) webhook when `send_card: true`. This event is fired after a user authenticates to a merchant account. You can read more about sending card data to Knot [here](/card-switcher/sending-card-data). In the development environment, you can bypass the client-side SDK entirely to test this flow. Use the [Link Account](/api-reference/development/link-account) endpoint with `card_switcher: true` to simulate a user authentication and fire the `AUTHENTICATED` webhook. See the [testing guide](/card-switcher/testing) for more detailed steps & instructions. ## Testing Validate your integration end-to-end in the `development` environment with the [testing guide](/card-switcher/testing). ## What's Next Auto-detect a user's online merchant accounts for a personalized card switching experience. Receive subscription details back when a card is provisioned to a merchant account. Sync detected merchant accounts to power lifecycle and re-engagement campaigns. Retrieve users' SKU-level transaction history from their connected merchant accounts. # Sending Card Data Source: https://docs.knotapi.com/card-switcher/sending-card-data Learn how to securely send card data to Knot using JWE encryption, vault providers, or processor integrations. ## Overview There are a few options for how you can send card data to Knot when integrating the CardSwitcher product. The first option is to send the card data encrypted in a JWE. The second option is to send the card data in JSON to a secure endpoint controlled by a vault provider, and the third is to rely on one of Knot's direct processor integrations. No option is more or less secure than the other, all maintain strict handling of the card data to comply with PCI guidelines, and all are valid options for sending card data to Knot. ## Send Encrypted Data Directly to Knot We recommend this option. In this option, you can encrypt the JSON payload of card data in a JWE format prior to sending it to Knot. 1. Get a JWE public key from Knot's [Retrieve JWK](/api-reference/products/card-switcher/retrieve-jwk) endpoint. 2. Encrypt the JSON string payload of the card data with the JWE public key (referenced [here](/api-reference/products/card-switcher/retrieve-jwk)). 3. Send the encrypted payload to [Switch Card (JWE)](/api-reference/products/card-switcher/switch-card-jwe). 4. Knot sends the encrypted data (the JWE) to a PCI-compliant vendor's environment. 5. Knot receives an alias associated with the encrypted card data. Card data is never processed or stored outside PCI-compliant vendor environments. After card data is used for a card switch, it is explicitly deleted from the PCI-compliant vendor's vault within milliseconds. ### VGS JWE Encryption If you already have a vault set up with Very Good Security (VGS), you can manipulate the payload and send the JWE directly from your VGS vault via an outbound route to the [Switch Card (JWE)](/api-reference/products/card-switcher/switch-card-jwe) endpoint. Please see the VGS StarLarky code sample below for guidance or reach out to the Knot team. ### Code Samples Below are a number of code samples demonstrating how to structure and encrypt a JWE: ```typescript expandable TypeScript icon="node-js" theme={"system"} import { CompactEncrypt, importJWK, JWK } from 'jose'; const KNOT_BASE = 'https://development.knotapi.com'; function basicAuthHeader(clientId: string, secret: string): string { const creds = Buffer.from(`${clientId}:${secret}`, 'utf8').toString('base64'); return `Basic ${creds}`; } /** * GetKey gets the JWK associated with your client ID from the Knot API. * The implementation follows the steps outlined in the KnotAPI documentation: * https://docs.knotapi.com/api-reference/products/card-switcher/retrieve-jwk */ export async function getKey(): Promise { const clientId = process.env.KNOT_CLIENT_ID || ''; const secret = process.env.KNOT_SECRET || ''; if (!clientId || !secret) { throw new Error('KNOT_CLIENT_ID and KNOT_SECRET must be set in the environment.'); } const resp = await fetch(`${KNOT_BASE}/jwe/key`, { method: 'GET', headers: { Authorization: basicAuthHeader(clientId, secret), Accept: 'application/json', }, }); if (!resp.ok) { const text = await resp.text().catch(() => ''); throw new Error(`Failed to fetch JWK (${resp.status}): ${text}`); } const jwk = (await resp.json()) as JWK; return jwk; } /** * encryptData encrypts the card data using the provided JWK, the JWE must contain the JWK's "kid" and "alg" parameters to be considered valid. * The implementation follows the steps outlined in the KnotAPI documentation: * https://docs.knotapi.com/api-reference/products/card-switcher/retrieve-jwk#building-the-jwe * You can also opt to implement these RFC's manually instead of using a library: * https://datatracker.ietf.org/doc/html/rfc7516 * https://datatracker.ietf.org/doc/html/rfc7517 * https://datatracker.ietf.org/doc/html/rfc7518 * https://datatracker.ietf.org/doc/html/rfc7638 */ export async function encryptData(cardData: unknown, jwk: JWK): Promise { const alg = jwk.alg; if (!alg) { throw new Error('JWK is missing "alg" (required).'); } // Import the public key from JWK for encryption // jose infers the right key type/algorithm from JWK fields const publicKey = await importJWK(jwk, alg); const plaintext = new TextEncoder().encode(JSON.stringify(cardData)); const jwe = await new CompactEncrypt(plaintext) .setProtectedHeader({ alg, enc: 'A256GCM', ...(jwk.kid ? { kid: jwk.kid } : {}), }) .encrypt(publicKey); return jwe; } /** * submitJWE Submits the JWE to the Knot API for an active task. * The implementation follows the steps outlined in the KnotAPI documentation: * https://docs.knotapi.com/api-reference/products/card-switcher/switch-card-jwe */ export async function submitJWE(taskId: string, jwe: string): Promise { const clientId = process.env.KNOT_CLIENT_ID || ''; const secret = process.env.KNOT_SECRET || ''; if (!clientId || !secret) { throw new Error('KNOT_CLIENT_ID and KNOT_SECRET must be set in the environment.'); } console.log(`Submitting JWE: ${jwe}`); const resp = await fetch(`${KNOT_BASE}/card`, { method: 'POST', headers: { Authorization: basicAuthHeader(clientId, secret), 'Content-Type': 'application/json', Accept: 'application/json', }, body: JSON.stringify({ task_id: taskId, jwe }), }); const text = await resp.text().catch(() => ''); // Try parse JSON if possible for error_message let parsed: Record | null = null; try { parsed = text ? JSON.parse(text) : null; } catch {} if (!resp.ok) { const msg = parsed?.error_message ?? (text || `HTTP ${resp.status}`); throw new Error(String(msg)); } const errorMessage = (parsed?.error_message as string | undefined) ?? undefined; if (errorMessage) { throw new Error(errorMessage); } } async function main() { /** * Hardcoded card data for demonstration purposes. * You should use your own card data. * See the KnotAPI documentation for more information: * https://docs.knotapi.com/api-reference/products/card-switcher/retrieve-jwk#building-the-jwe */ const cardData = { user: { name: { first_name: 'Ada', // Max length: 255 last_name: 'Lovelace', // Max length: 255 }, address: { street: '100 Main Street', // Max length: 46 street2: '#100', // Max length: 46 city: 'NEW YORK', // Max length: 32 region: 'NY', // Must be an ISO 3166-2 sub-division code postal_code: '12345', // Min length: 5, Max length: 10 country: 'US', // Must be an ISO 3166-1 alpha-2 code }, phone_number: '+11234567890', // Must be in E.164 format }, card: { number: '4242424242424242', // Card number expiration: '08/2030', // MM/YYYY or MM/YY format cvv: '012', // Max length: 4 }, }; const jwk = await getKey(); const jwe = await encryptData(cardData, jwk); await submitJWE('123456', jwe); } if (require.main === module) { main().catch((err) => { console.error(err); process.exit(1); }); } ``` ```python expandable Python icon="python" theme={"system"} from __future__ import annotations import json import os from typing import Any, Dict import requests from jose import jwe # python-jose # pip install "python-jose[cryptography]" requests KNOT_BASE = "https://development.knotapi.com" def _auth_tuple() -> tuple[str, str]: cid = os.getenv("KNOT_CLIENT_ID") sec = os.getenv("KNOT_SECRET") if not cid or not sec: raise RuntimeError("KNOT_CLIENT_ID or KNOT_SECRET is missing in the environment") return cid, sec def get_key() -> Dict[str, Any]: """ GetKey gets the JWK associated with your client ID from the Knot API. The implementation follows the steps outlined in the KnotAPI documentation: https://docs.knotapi.com/api-reference/products/card-switcher/retrieve-jwk Returns the JWK JSON as a Python dict (suitable for python-jose). """ resp = requests.get(f"{KNOT_BASE}/jwe/key", auth=_auth_tuple(), timeout=30) if not resp.ok: raise RuntimeError(f"GetKey failed: {resp.status_code} {resp.text}") jwk = resp.json() # python-jose accepts a JWK dict as the key parameter return jwk def encrypt_data(card_data: Dict[str, Any], jwk_dict: Dict[str, Any]) -> str: """ encrypt_data encrypts the card data using the provided JWK, the JWE must contain the JWK's "kid" and "alg" parameters to be considered valid. The implementation follows the steps outlined in the KnotAPI documentation: https://docs.knotapi.com/api-reference/products/card-switcher/retrieve-jwk#building-the-jwe You can also opt to implement these RFC's manually instead of using a library: https://datatracker.ietf.org/doc/html/rfc7516 https://datatracker.ietf.org/doc/html/rfc7517 https://datatracker.ietf.org/doc/html/rfc7518 https://datatracker.ietf.org/doc/html/rfc7638 """ plaintext = json.dumps(card_data).encode("utf-8") return jwe.encrypt( plaintext, jwk_dict, # JWK dict (python-jose derives the key) algorithm=jwk_dict.get("alg"), encryption="A256GCM", kid=jwk_dict.get("kid") ).decode("utf-8") def submit_jwe(task_id: str, compact_jwe: str) -> None: """ submit_jwe Submits the JWE to the Knot API for an active task. The implementation follows the steps outlined in the KnotAPI documentation: https://docs.knotapi.com/api-reference/products/card-switcher/switch-card-jwe """ payload = {"task_id": task_id, "jwe": compact_jwe} resp = requests.post( f"{KNOT_BASE}/card", auth=_auth_tuple(), json=payload, headers={"Content-Type": "application/json"}, timeout=30, ) body_text = resp.text or "" try: parsed = resp.json() if body_text else {} except Exception: parsed = {} if not resp.ok: msg = parsed.get("error_message") or f"{resp.status_code} {resp.reason}" raise RuntimeError(f"SubmitJWE failed: {msg}") if parsed.get("error_message"): raise RuntimeError(parsed["error_message"]) def main() -> None: card_data: Dict[str, Any] = { "user": { "name": { "first_name": "Ada", # Max length: 255 "last_name": "Lovelace", # Max length: 255 }, "address": { "street": "100 Main Street", # Max length: 46 "street2": "#100", # Max length: 46 "city": "NEW YORK", # Max length: 32 "region": "NY", # Must be an ISO 3166-2 sub-division code "postal_code": "12345", # Min length: 5, Max length: 10 "country": "US", # Must be an ISO 3166-1 alpha-2 code }, "phone_number": "+11234567890", # Must be in E.164 format }, "card": { "number": "4242424242424242", # Card number "expiration": "08/2030", # MM/YYYY or MM/YY format "cvv": "012", # Max length: 4 }, } jwk_dict = get_key() compact_jwe = encrypt_data(card_data, jwk_dict) submit_jwe("123456", compact_jwe) if __name__ == "__main__": main() ``` ```go expandable Go icon="golang" theme={"system"} package main import ( "bytes" "encoding/json" "fmt" "io" "net/http" "os" "github.com/lestrrat-go/jwx/v3/jwe" "github.com/lestrrat-go/jwx/v3/jwk" ) /* GetKey gets the JWK associated with your client ID from the Knot API. The implementation follows the steps outlined in the KnotAPI documentation: https://docs.knotapi.com/api-reference/products/card-switcher/retrieve-jwk */ func GetKey() (jwk.Key, error) { req, err := http.NewRequest(http.MethodGet, "https://development.knotapi.com/jwe/key", nil) if err != nil { return nil, err } req.SetBasicAuth(os.Getenv("KNOT_CLIENT_ID"), os.Getenv("KNOT_SECRET")) resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return nil, err } parsedKey, err := jwk.ParseKey(body) if err != nil { return nil, err } return parsedKey, nil } /* EncryptData encrypts the card data using the provided JWK, the JWE must contain the JWK's "kid" and "alg" parameters to be considered valid. The implementation follows the steps outlined in the KnotAPI documentation: https://docs.knotapi.com/api-reference/products/card-switcher/retrieve-jwk#building-the-jwe You can also opt to implement these RFC's manually instead of using a library: https://datatracker.ietf.org/doc/html/rfc7516 https://datatracker.ietf.org/doc/html/rfc7517 https://datatracker.ietf.org/doc/html/rfc7518 https://datatracker.ietf.org/doc/html/rfc7638 */ func EncryptData(cardData map[string]any, parsedKey jwk.Key) ([]byte, error) { plaintext, err := json.Marshal(cardData) if err != nil { return nil, err } algo, ok := parsedKey.Algorithm() if !ok { return nil, fmt.Errorf("key does not have an algorithm") } return jwe.Encrypt(plaintext, jwe.WithKey(algo, parsedKey)) } /* SubmitJWE Submits the JWE to the Knot API for an active task. The implementation follows the steps outlined in the KnotAPI documentation: https://docs.knotapi.com/api-reference/products/card-switcher/switch-card-jwe */ func SubmitJWE(taskId string, cipherText []byte) error { payload := map[string]string{ "task_id": taskId, "jwe": string(cipherText), } payloadBytes, err := json.Marshal(&payload) if err != nil { return err } req, err := http.NewRequest(http.MethodPost, "https://development.knotapi.com/card", bytes.NewReader(payloadBytes)) if err != nil { return err } req.SetBasicAuth(os.Getenv("KNOT_CLIENT_ID"), os.Getenv("KNOT_SECRET")) resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return err } parsedBody := map[string]string{} err = json.Unmarshal(body, &parsedBody) if err != nil { return err } errorMessage, ok := parsedBody["error_message"] if ok { return fmt.Errorf(errorMessage) } return nil } func main() { /* Hardcoded card data for demonstration purposes. You should use your own card data. See the KnotAPI documentation for more information: https://docs.knotapi.com/api-reference/products/card-switcher/retrieve-jwk#building-the-jwe */ cardData := map[string]any{ "user": map[string]any{ "name": map[string]string{ "first_name": "Ada", // Max length: 255 "last_name": "Lovelace", // Max length: 255 }, "address": map[string]string{ "street": "100 Main Street", // Max length: 46 "street2": "#100", // Max length: 46 "city": "NEW YORK", // Max length: 32 "region": "NY", // Must be an ISO 3166-2 sub-division code "postal_code": "12345", // Min length: 5, Max length: 10 "country": "US", // Must be an ISO 3166-1 alpha-2 code }, "phone_number": "+11234567890", // Must be in E.164 format }, "card": map[string]string{ "number": "4242424242424242", "expiration": "08/2030", // MM/YYYY or MM/YY format "cvv": "012", // Max length: 4 }, } parsedKey, err := GetKey() if err != nil { panic(err) } cipherText, err := EncryptData(cardData, parsedKey) if err != nil { panic(err) } err = SubmitJWE("123456", cipherText) if err != nil { panic(err) } } ``` ```python expandable VGS StarLarky icon="star" theme={"system"} load('@stdlib//json', 'json') load('@vgs//vault', 'vault') load('@vendor//jose/jwe', 'jwe') def _reveal_card_fields(card): return { "number": vault.reveal(card["number"]), "expiration": vault.reveal(card["expiration"]), "cvv": vault.reveal(card["cvv"]), } def _encrypt_jwe(jwk, plaintext_bytes): """ _encrypt_jwe encrypts the card data using the provided JWK, the JWE must contain the JWK's "kid" and "alg" parameters to be considered valid. """ return jwe.encrypt( plaintext_bytes, jwk, algorithm=jwk["alg"], encryption="A256GCM", kid=jwk["kid"] ) def process(input, ctx): # input.body is a string (JSON). Example: # { # "jwk": { "kty": "...", "alg": "RSA-OAEP-256", "n": "...", "e": "...", "kid": "..." }, (retrieved from https://development.knotapi.com/jwe/key) # "user": { # "name": { # "first_name": "Ada", # Max length: 255 # "last_name": "Lovelace" # Max length: 255 # }, # "address": { # "street": "100 Main Street", # Max length: 46 # "street2": "#100", # Max length: 46 # "city": "NEW YORK", # Max length: 32 # "region": "NY", # ISO 3166-2 sub-division # "postal_code": "12345", # Min 5, Max 10 # "country": "US" # ISO 3166-1 alpha-2 # }, # "phone_number": "+11234567890" # E.164 # }, # "card": { # "number": "tok_sandbox_...", # "expiration": "tok_sandbox_...", # MM/YYYY MM/YY # "cvv": "tok_sandbox_..." # Max length: 4 # } # } body = json.loads(input.body) jwk = body["jwk"] user = body["user"] card = body["card"] revealed_card = _reveal_card_fields(card) card_data = {"user": user, "card": revealed_card} plaintext = json.dumps(card_data).encode("utf-8") encrypted_jwe = _encrypt_jwe(jwk, plaintext) input.body = encrypted_jwe input.headers["Content-Type"] = "text/plain" return input ``` ```java expandable Java icon="java" theme={"system"} package com.knotapi; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.nimbusds.jose.*; import com.nimbusds.jose.crypto.*; import com.nimbusds.jose.jwk.*; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.LinkedHashMap; import java.util.Map; public class Main { private static final String KNOT_BASE = "https://development.knotapi.com"; private static final ObjectMapper MAPPER = new ObjectMapper(); private static final HttpClient HTTP = HttpClient.newHttpClient(); /** Get the JWK associated with your client ID from the Knot API. * The implementation follows the steps outlined in the KnotAPI documentation: * https://docs.knotapi.com/api-reference/products/card-switcher/retrieve-jwk */ public static JWK getKey() throws Exception { String id = System.getenv("KNOT_CLIENT_ID"); String secret = System.getenv("KNOT_SECRET"); if (id == null || secret == null) { throw new IllegalStateException("KNOT_CLIENT_ID or KNOT_SECRET missing"); } String basic = "Basic " + Base64.getEncoder() .encodeToString((id + ":" + secret).getBytes(StandardCharsets.UTF_8)); HttpRequest req = HttpRequest.newBuilder() .uri(URI.create(KNOT_BASE + "/jwe/key")) .header("Authorization", basic) .GET() .build(); HttpResponse resp = HTTP.send(req, HttpResponse.BodyHandlers.ofString()); if (resp.statusCode() / 100 != 2) { throw new RuntimeException("GetKey failed: " + resp.statusCode() + " " + resp.body()); } // Parse JWK directly from JSON string return JWK.parse(resp.body()); } /** Select a JWEEncrypter that matches the provided JWK and alg. */ private static JWEEncrypter encrypterFor(JWK jwk, JWEAlgorithm alg) throws Exception { // Always use a public key for asymmetric encryption if (jwk instanceof RSAKey) { RSAKey rsa = (RSAKey) ((RSAKey) jwk).toPublicJWK(); return new RSAEncrypter(rsa); } if (jwk instanceof ECKey) { ECKey ec = (ECKey) ((ECKey) jwk).toPublicJWK(); return new ECDHEncrypter(ec); } if (jwk instanceof OctetSequenceKey) { OctetSequenceKey oct = (OctetSequenceKey) jwk; byte[] keyBytes = oct.toByteArray(); if (JWEAlgorithm.DIR.equals(alg)) { return new DirectEncrypter(keyBytes); } return new AESEncrypter(keyBytes); } throw new JOSEException("Unsupported JWK type: " + jwk.getKeyType()); } /** encryptData encrypts the card data using the provided JWK, the JWE must contain the JWK's "kid" and "alg" parameters to be considered valid. * The implementation follows the steps outlined in the KnotAPI documentation: * https://docs.knotapi.com/api-reference/products/card-switcher/retrieve-jwk#building-the-jwe * You can also opt to implement these RFC's manually instead of using a library: * https://datatracker.ietf.org/doc/html/rfc7516 * https://datatracker.ietf.org/doc/html/rfc7517 * https://datatracker.ietf.org/doc/html/rfc7518 * https://datatracker.ietf.org/doc/html/rfc7638 */ public static String encryptData(Map cardData, JWK jwk) throws Exception { byte[] plaintext = MAPPER.writeValueAsBytes(cardData); // Use alg from the JWK; enc = A256GCM (adjust if your server requires a different enc) if (jwk.getAlgorithm() == null) throw new IllegalStateException("JWK missing 'alg'"); JWEAlgorithm alg = JWEAlgorithm.parse(jwk.getAlgorithm().getName()); JWEHeader header = new JWEHeader.Builder(alg, EncryptionMethod.A256GCM) .keyID(jwk.getKeyID()) // include kid if present .contentType(null) // no nested JWT .build(); JWEObject jwe = new JWEObject(header, new Payload(plaintext)); JWEEncrypter encrypter = encrypterFor(jwk, alg); jwe.encrypt(encrypter); return jwe.serialize(); } /** submitJWE Submits the JWE to the Knot API for an active task. * The implementation follows the steps outlined in the KnotAPI documentation: * https://docs.knotapi.com/api-reference/products/card-switcher/switch-card-jwe */ public static void submitJWE(String taskId, String compactJWE) throws Exception { String id = System.getenv("KNOT_CLIENT_ID"); String secret = System.getenv("KNOT_SECRET"); if (id == null || secret == null) { throw new IllegalStateException("KNOT_CLIENT_ID or KNOT_SECRET missing"); } String basic = "Basic " + Base64.getEncoder() .encodeToString((id + ":" + secret).getBytes(StandardCharsets.UTF_8)); Map payload = Map.of( "task_id", taskId, "jwe", compactJWE ); String json = MAPPER.writeValueAsString(payload); HttpRequest req = HttpRequest.newBuilder() .uri(URI.create(KNOT_BASE + "/card")) .header("Authorization", basic) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString(json)) .build(); HttpResponse resp = HTTP.send(req, HttpResponse.BodyHandlers.ofString()); String body = resp.body(); // Try to parse JSON body for error_message (even on non-2xx) Map parsed = Map.of(); try { if (body != null && !body.isEmpty()) { parsed = MAPPER.readValue(body, new TypeReference>() {}); } } catch (Exception ignored) {} if (resp.statusCode() / 100 != 2) { Object msg = parsed.getOrDefault("error_message", resp.statusCode() + " " + resp.body()); throw new RuntimeException("SubmitJWE failed: " + msg); } if (parsed.containsKey("error_message")) { throw new RuntimeException(String.valueOf(parsed.get("error_message"))); } } static void main() throws Exception { /** * Hardcoded card data for demonstration purposes. * You should use your own card data. * See the KnotAPI documentation for more information: * https://docs.knotapi.com/api-reference/products/card-switcher/retrieve-jwk#building-the-jwe */ Map name = new LinkedHashMap<>(); name.put("first_name", "Ada"); // Max length: 255 name.put("last_name", "Lovelace"); // Max length: 255 Map address = new LinkedHashMap<>(); address.put("street", "100 Main Street"); // Max length: 46 address.put("street2", "#100"); // Max length: 46 address.put("city", "NEW YORK"); // Max length: 32 address.put("region", "NY"); // ISO 3166-2 sub-division address.put("postal_code", "12345"); // Min 5, Max 10 address.put("country", "US"); // ISO 3166-1 alpha-2 Map user = new LinkedHashMap<>(); user.put("name", name); user.put("address", address); user.put("phone_number", "+11234567890"); // E.164 format Map card = new LinkedHashMap<>(); card.put("number", "4242424242424242"); card.put("expiration", "08/2030"); // MM/YYYY or MM/YY card.put("cvv", "012"); // Max length: 4 Map cardData = new LinkedHashMap<>(); cardData.put("user", user); cardData.put("card", card); JWK jwk = getKey(); String jwe = encryptData(cardData, jwk); submitJWE("123456", jwe); } } ``` ## Send Data to Secure Vault Provider This option is often chosen by those integrating with Knot that already have a vault set up with a PCI-compliant vendor such as Very Good Security (VGS) or Basis Theory. 1. Set up a route to Knot's [Switch Card](/api-reference/products/card-switcher/switch-card) endpoint from the vault that stores card data at your PCI-compliant vendor. 2. When you receive the `AUTHENTICATED` webhook, make a request to the vault. 3. The vault provider will send the necessary card data to Knot's [Switch Card](/api-reference/products/card-switcher/switch-card) endpoint in JSON. 4. Knot receives the card data to the aforementioned endpoint. **This endpoint is controlled by Knot's PCI-compliant vendor which stores the data and proxies it to Knot via an alias.** Card data is never processed or stored outside PCI-compliant vendor environments. After card data is used for a card switch, it is explicitly deleted from the PCI-compliant vendor's vault within milliseconds. You can also request to enable [Mutual Transport Layer Security (mTLS)](/api-reference/mTLS) for the [Switch Card](/api-reference/products/card-switcher/switch-card) endpoint as an additional security measure if desired. ### VGS 1-Click Route Setup Knot partners with [VGS](https://www.verygoodsecurity.com/) (a PCI-compliant vendor) to streamline the process of sending card data in a PCI-compliant manner as part of your integration with Knot. Within your VGS account online, you can set up an outbound route to Knot's [Switch Card](/api-reference/products/card-switcher/switch-card) endpoint. VGS specifies how to set up an outbound connection to a 3rd party (in this case Knot) [here](https://www.verygoodsecurity.com/docs/guides/outbound-connection#outbound-connection). Doing so will allow you to automatically route card data stored in your VGS vault to Knot. To make this process even easier, in the "Addons" section of your vault in your VGS account online, you will find a set of "route templates." You can search for and select the "KnotAPI" route template to get started. ## Direct Processor Integrations ### Unit With this option, you can allow Knot to retrieve the card data directly from Unit if you use their software as your issuer processor. More on this integration [here](/card-switcher/processor-digital-banking-integrations/unit). ### I2C With this option, you can allow Knot to retrieve the card data directly from I2C if you use their software as your issuer processor. More on this integration [here](/card-switcher/processor-digital-banking-integrations/i2c). # Testing Source: https://docs.knotapi.com/card-switcher/testing Use test credentials and best practices to test CardSwitcher functionality in development and production environments. ## Development In the development environment, you can test the full flow - including webhooks and card data submission - without installing or invoking the SDK. This is useful for backend-only development & testing. In your [Knot Dashboard](https://dashboard.knotapi.com/developers/webhooks), add a webhook endpoint for the development environment. This is where Knot will deliver the [`AUTHENTICATED`](/link/webhook-events/authenticated) and subsequent events. Call the [Link Account](/api-reference/development/link-account) endpoint with `card_switcher: true`. This links a test merchant account to the Knot platform and fires an [`AUTHENTICATED`](/link/webhook-events/authenticated) webhook to your registered endpoint. ```bash theme={"system"} curl --request POST \ --url https://development.knotapi.com/development/accounts/link \ --header 'Authorization: Basic ' \ --header 'Content-Type: application/json' \ --data ' { "external_user_id": "abc123", "merchant_id": 19, "card_switcher": true, "card_id": "81n9al10a0ayn13" } ' ``` When your server receives the [`AUTHENTICATED`](/link/webhook-events/authenticated) event, extract the `task_id` from the payload. You have 15 seconds to submit user & card data. Retrieve the JWK from [Retrieve JWK](/api-reference/products/card-switcher/retrieve-jwk), encrypt the payload below, then POST to [Switch Card (JWE)](/api-reference/products/card-switcher/switch-card-jwe). See [Sending Card Data](/card-switcher/sending-card-data) for full code samples. It is best practice to cache the JWK public key for an extended period (e.g. 1 day), rather than retrieve it on every merchant account authentication. ```json theme={"system"} { "user": { "name": { "first_name": "Ada", "last_name": "Lovelace" }, "address": { "street": "100 Main Street", "street2": "#100", "city": "NEW YORK", "region": "NY", "postal_code": "12345", "country": "US" }, "phone_number": "+11234567890" }, "card": { "number": "4242424242424242", "expiration": "08/2030", "cvv": "012" } } ``` Knot will fire a [`CARD_UPDATED`](/card-switcher/webhook-events/card-updated) or [`CARD_FAILED`](/card-switcher/webhook-events/card-failed) webhook with the result. With your `client_id` and `secret` for the `development` environment, call [Create Session](https://docs.knotapi.com/api-reference/sessions/create-session) with `type: card_switcher` and a dummy `external_user_id` to create a session used when invoking the SDK. Use the `session_id` from the previous step to initialize the SDK. Use one of the sets of credentials below to simulate various authentication scenarios. | Scenario | Description | Username | Password | | :------------------------ | :---------------------------------------------------------------------------------------------------------- | :------------------- | :---------- | | Successful authentication | Simulates a successful authentication to a merchant account. | `user_good` | `pass_good` | | One-time password (OTP) | Simulates an authentication that requires an OTP. `1234` for a valid OTP and `0000` for an invalid OTP. | `user_good` | `pass_otp` | | Invalid credentials | Simulates a failed authentication due to invalid credentials. | `credentials` | `failed` | | Account failure | Simulates a failed authentication due to an issue with the user's merchant account. | `account` | `failed` | | Merchant failure | Simulates a failed authentication due to an issue with the merchant. | `merchant` | `failed` | | Too many attempts | Simulates a failed authentication due to too many consecutive, failed login attempts. | `too many attempts` | `failed` | | Card not supported | Simulates a failed card switch due to the card not being supported by the merchant. | `card not supported` | `failed` | | Card expired | Simulates a failed card switch due to the card having insufficient funds (for debit & prepaid cards). | `insufficient funds` | `failed` | | No subscription | Simulates a failed card switch due to the user's lack of a paid subscription with the merchant. | `subscription` | `failed` | | Subscription admin | Simulates a failed card switch due to the user's account lacking the proper permissions to update the card. | `subscription admin` | `failed` | When your server receives the [`AUTHENTICATED`](/link/webhook-events/authenticated) event, extract the `task_id` from the payload. You have 15 seconds to submit user & card data. Retrieve the JWK from [Retrieve JWK](/api-reference/products/card-switcher/retrieve-jwk), encrypt the payload below, then POST to [Switch Card (JWE)](/api-reference/products/card-switcher/switch-card-jwe). See [Sending Card Data](/card-switcher/sending-card-data) for full code samples. It is best practice to cache the JWK public key for an extended period (e.g. 1 day), rather than retrieve it on every merchant account authentication. ```json theme={"system"} { "user": { "name": { "first_name": "Ada", "last_name": "Lovelace" }, "address": { "street": "100 Main Street", "street2": "#100", "city": "NEW YORK", "region": "NY", "postal_code": "12345", "country": "US" }, "phone_number": "+11234567890" }, "card": { "number": "4242424242424242", "expiration": "08/2030", "cvv": "012" } } ``` Knot will fire a [`CARD_UPDATED`](/card-switcher/webhook-events/card-updated) or [`CARD_FAILED`](/card-switcher/webhook-events/card-failed) webhook with the result. ## Production Below are a set of best practices when testing Knot in production. 1. Ensure testing occurs from devices in the U.S. and with merchant accounts based in the U.S. International devices and accounts are not enabled. 2. Replicate real-life behavior: 1. Do not attempt to provision multiple cards to the same merchant account multiple consecutive times in a short period. The merchant's fraud rules are likely to prevent this behavior. 2. Do not attempt to provision the same card to multiple different accounts with the same merchant. Similar to the above, the merchant's fraud rules are likely to prevent this behavior. 3. Do not attempt to log in to the same merchant multiple consecutive times in a short time frame on the same device. 4. Do not attempt to log into a merchant account while on a company VPN. 3. Ensure the proper personal information (beyond the card information) is being provided to Knot (typically in the call to [Switch Card](/api-reference/products/card-switcher/switch-card)). Many merchants require first name, last name, billing address, and/or phone number to update a card-on-file. The billing address may need to pass Address Verification Service (AVS) checks by the merchant. This information can come from a number of different places depending on your integration with Knot or your server's storage/retrieval of this information from other third parties (e.g. bank partner, processor, etc.). 4. Ensure the card that is being sent to Knot is active (i.e. not locked/frozen) and has sufficient funds (if a debit card). Many merchants attempt a small authorization hold of `$0.01` or `$1.00` on debit or prepaid cards. 5. If you are testing Knot's web SDK, ensure you are not logged in to the merchant in another browser tab at the same time as when logging in via the SDK. 6. If you choose to check if your card is actually provisioned to the merchant account after completing the flow in the Knot SDK: 1. Allow a bit of time for the merchant account to update. Certain merchants can take a few minutes for the new card to be reflected in the account. 2. Hard refresh the merchant account page and/or log out and log in again to see the newly provisioned card. # CARD_FAILED Source: https://docs.knotapi.com/card-switcher/webhook-events/card-failed api-reference/openapi.json webhook card_failed Fired when a card failed to be updated in a merchant account. Fired when a card failed to be updated in a merchant account. The reason is specified in the `reason` field. ### Failure Reasons | Reason | **Description** | | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | `account` | The user's merchant account has an issue (e.g. a foreign account). | | `card` | The user's card information has an issue (e.g. incorrect phone #, incorrect billing address, unsupported card type, etc.). | | `card expired` | The user's card is expired. | | `card in use` | The card is already in use in another account. | | `insufficient funds` | The user's depository account associated with their card does not have sufficient funds to cover the pre-authorization hold from the merchant. | | `subscription` | The user's merchant account does not have an active subscription. | | `subscription admin` | The user does not have the necessary authorization to update the payment method in the merchant account. | | `third-party payment method on subscription` | The user pays for the merchant's service through a 3rd party merchant account (e.g. Spotify through Hulu). | | `too close to end of billing cycle` | The user's billing cycle date is too close. | | `too many attempts` | The user attempted to enter their login credentials (e.g. username, password, OTP) to the merchant account too many times. | | `credentials` | The user entered incorrect credentials when logging in to the merchant. | | `otp` | The user entered an incorrect OTP code when logging in to the merchant. | | `credentials timeout` | The user did not enter their login credentials to the merchant account in a certain period of time. | | `otp timeout` | The user did not enter their otp code to the merchant account in a certain period of time. | | `questions timeout` | The user did not enter the answers to the security questions for the merchant account in a certain period of time. | | `zip timeout` | The user did not enter their zip code associated with their merchant account in a certain period of time. | | `session not authenticated` | Knot could not authenticate to the user's merchant account. | | `did not receive payment method information` | Knot did not receive any card information. | | `could not handle payment method information` | Knot encountered an error handling the user's card information once received. | | `could not retrieve payment method information` | Knot was unsuccessful in retrieving payment method information from a direct processor integration. | | `other` | An unknown issue was encountered. | # CARD_UPDATED Source: https://docs.knotapi.com/card-switcher/webhook-events/card-updated api-reference/openapi.json webhook card_updated Fired when a card is updated in a merchant account. Fired when a card is updated in a merchant account. # Product Updates Source: https://docs.knotapi.com/changelog/product-updates Product updates automatically publish to an RSS feed [here](https://docs.knotapi.com/changelog/product-updates/rss.xml), so you can subscribe to updates as desired. [Here](https://slack.com/help/articles/218688467-Add-RSS-feeds-to-Slack) are instructions on sending the RSS feed of changes to a Slack workspace, as well as simple instructions for an email feed via Zapier [here](https://zapier.com/apps/email/integrations/rss/1441/send-new-rss-feed-entries-via-email).\ \ This is the RSS feed URL: [https://docs.knotapi.com/changelog/product-updates/rss.xml](https://docs.knotapi.com/changelog/product-updates/rss.xml) ## Merchant Account Detection in CardSwitcher [Detect](/detect/quickstart) now runs automatically during card switching sessions. When new merchant accounts are discovered, the `NEW_DETECTED_ACCOUNTS_AVAILABLE` webhook fires with a full array of detected merchant accounts. A new [List Detected Accounts](/api-reference/products/detect/list-detected-accounts) endpoint provides a streamlined way to retrieve all detected merchant accounts for a user. Use this to build real-time personalization experiences that surface relevant merchants immediately after a user links an account. ## Link by Stripe Support for Card Switching [CardSwitcher](/card-switcher/quickstart) now supports Link by Stripe as a merchant integration, enabling users to update their card on file for Stripe's one-click checkout service. This is useful for issuers looking to maximize card-on-file placement across popular checkout and payment platforms. ## French Canadian Localization The Knot SDK now supports Canadian French (`fr-CA`) and Canadian English [localization](/localization) across all platforms, joining the existing U.S. English (`en-US`) and U.S. Spanish (`es-US`) language options. Use this to present the merchant linking experience in French or English for your Canadian users. ## Webhook Payload in Dashboard Logs The [Logs](https://dashboard.knotapi.com/developers/logs) page in the [Knot Dashboard](https://dashboard.knotapi.com) now displays the full webhook request body payload in the side panel when viewing a webhook log entry. Use this to inspect the exact data delivered to your webhook endpoint without needing to check your own server logs. ## Try Detect from the Dashboard A new interactive experience on the [Detect](https://dashboard.knotapi.com/detect) page in the [Knot Dashboard](https://dashboard.knotapi.com/detect) lets you try out the [Detect](/detect/quickstart) product directly. Enter an email address to automatically detect which online merchant accounts are associated with it. Use this to see Detect in action before integrating. To integrate, pass the user's email when calling [Create Session](/api-reference/sessions/create-session). ## Loyalty Membership in Transactions Transactions now include a `loyalty_membership` object, returned on the [Sync Transactions](/api-reference/products/transaction-link/sync) and [Get Transaction By ID](/api-reference/products/transaction-link/get-by-id) endpoints. Use this to personalize reward offers and affiliate promotions based on a user's actual membership status. ## Refresh Transactions on Demand A new [Refresh Transactions](/api-reference/products/transaction-link/refresh) endpoint lets you trigger an on-demand transaction sync for a specific user at a merchant. Use this when you need fresh transaction data immediately, rather than waiting for the next scheduled daily sync. ## Custom Webhook Authentication Headers Webhook deliveries now support custom authentication headers, providing an additional layer of security on top of [HMAC signature verification](/webhooks#verifying-webhook-signatures). This is useful for customers whose webhook receivers require an API key or token header on incoming requests. Reach out to your account manager to configure a custom header name and value for your webhook endpoints. ## Link Account Testing Improvements The [Link Account](/api-reference/development/link-account) endpoint now supports a `metadata` parameter, so webhook events from test sessions include the same metadata your production integration sends. [Link Account](/api-reference/development/link-account) API calls are also now visible on the [Logs](https://dashboard.knotapi.com/developers/logs) page in the [Knot Dashboard](https://dashboard.knotapi.com), making it easier to trace and debug server-side test sessions end-to-end. ## Extended Merchant Account Detection You can now detect which merchant accounts a user has by providing their email and phone number when calling [Create Session](/api-reference/sessions/create-session#body-email-one-of-0). There is no further integration necessary to take full advantage of this functionality. With this expanded [Personalization](/card-switcher/personalization#auto-detection-via-email-&-phone) functionality, the merchant selection experience for users is highly relevant and drives high conversion for card switching. ## Canadian Support for Card Switching CardSwitcher now supports Canadian billing addresses and Canadian-issued cards. Issuers can pass `country: CA` for a user's billing address when calling the [Switch Card](/api-reference/products/card-switcher/switch-card#body-one-of-0-user-address-country) endpoint to provision a Canadian-issued card to a merchant account. ## Improved SDK Load Performance We've significantly reduced the time it takes for the Knot SDK to display its initial UI on all platforms by nearly 20%. The changes applied improve conversion by getting users to the merchant selection experience faster. There is no need to adopt a new SDK version for users to experience the improvements in your app. ## AI Skills for Integration Development Knot now offers product-specific [skills](/building-with-ai#skills) that guide AI agents through implementing integrations step-by-step, including the right API calls, webhook handling, data schemas, and testing steps. Browse the [available skills](https://docs.knotapi.com/.well-known/skills/index.json) or install via `npx skills add https://docs.knotapi.com`. Use these to let your AI coding agent build Knot integrations on your behalf. ## Multi-Webhook Configuration You can now configure up to 10 webhook URLs in both development and production environments from the [Knot Dashboard](https://dashboard.knotapi.com/developers/webhooks). This allows you to route webhook events to multiple endpoints simultaneously, useful for sending events to both your primary backend and auxiliary systems like logging, analytics, or staging environments. Learn more in the [Webhooks](/webhooks) guide. ## Transaction Exploration Agent An AI-powered exploration agent is now available on the [Transactions](https://dashboard.knotapi.com/transactions) page in the [Knot Dashboard](https://dashboard.knotapi.com/transactions). Use it to ask natural-language questions about your users' SKU-level transaction and subscription data, such as top product categories, spending trends, basket analysis, and designing a rewards offer strategy, and get instant insights directly from the dashboard. ## Expanded Subscription Data Coverage [SubscriptionManager](/subscription-manager/quickstart) now supports additional merchants for subscription and bill data retrieval. New additions include Amazon (Prime, Prime Video, Music), Uber / Uber Eats, DoorDash, and AT\&T. Subscription details can be retrieved via the [Get Subscription By ID](/api-reference/products/subscriptions/get-by-id) endpoint. Use this to surface comprehensive billing and subscription data for your users across all their major subscriptions and bills. ## Expanded Subscription Cancellation Coverage The [Cancel Subscription](/api-reference/products/subscriptions/cancel) endpoint now supports five additional merchants: **Hulu, Disney+, HBO Max, DoorDash DashPass,** and **Uber One**. This builds on the initial launch (Walmart+, Instacart+, Netflix, Spotify, and ClassPass) to cover more of your users' most common recurring charges. Use this to let your users cancel unwanted subscriptions without ever leaving your app. ## Amazon Business Account Support for TransactionLink [TransactionLink](/transaction-link/quickstart) now supports Amazon Business accounts. Transaction history for Amazon Business orders is available via the [Sync Transactions](/api-reference/products/transaction-link/sync) and [Get Transaction By ID](/api-reference/products/transaction-link/get-by-id) endpoints. ## Subscription Cancellation You can now cancel subscriptions on behalf of your users via the new [Cancel Subscription](/api-reference/products/subscriptions/cancel) endpoint. When a cancellation completes, you'll receive the [`CANCELLATION_SUCCEEDED`](/subscription-manager/webhook-events/cancellation-succeeded) webhook event. This is available for Walmart+, Instacart+, Netflix, Spotify, and ClassPass, with more merchants coming soon. Additionally, [SubscriptionManager](/subscription-manager/quickstart) now supports retrieving subscription data for ClassPass. Use this to help your users manage recurring charges directly from your app. ## GoPuff Integration for TransactionLink [TransactionLink](/transaction-link/quickstart) now supports GoPuff as a merchant integration, enabling you to retrieve SKU-level transaction data for GoPuff orders. Transaction data is available via the [Sync Transactions](/api-reference/products/transaction-link/sync) and [Get Transaction By ID](/api-reference/products/transaction-link/get-by-id) endpoints. ## Server-Side CardSwitcher Testing The [Link Account](/api-reference/development/link-account) endpoint now supports the full CardSwitcher testing flow, including `card_id` for specifying which card to switch. This allows you to test the complete CardSwitcher flow — from linking to card switching — entirely server-side without any client-side SDK integration. ## SDK Localization The Knot SDK now supports [localization](/localization), allowing you to present the merchant linking experience in your users' preferred language. Pass a `locale` parameter when initializing the SDK to display content in Spanish (`es-US`), with English (`en-US`) remaining the default. Localization is available across all SDK platforms — iOS, Android, React Native, Flutter, and Web. ## New Subscription Merchant Integrations SubscriptionManager now supports retrieving subscription data across additional merchant coverage (outlined below). Subscription details for these merchants can be retrieved via the [Get Subscription By ID](/api-reference/products/subscriptions/get-by-id) endpoint. * Subscriptions: HBO Max, Netflix, Disney, Hulu, Spotify, Walmart+, Google (YouTube, YouTube Premium, YouTube TV, etc.), Apple TV+ * Bills: Xfinity Mobile & Internet, T-Mobile, Metro by T-Mobile, Verizon, Boost Mobile, Spectrum, Straight Talk ## Detect API You can now programmatically access detected merchant accounts via two new endpoints. [Sync Detected Accounts](/api-reference/products/detect/sync-detected-accounts) retrieves all detected accounts for a user, while [Search Detected Accounts](/api-reference/products/detect/search-detected-accounts) lets you search by merchant or company name to check if a user has an account at specific merchants. The [`NEW_DETECTED_ACCOUNTS_AVAILABLE`](/detect/webhook-events/new-detected-accounts-available) webhook notifies you when new accounts have been detected. Use this to personalize the reward offers you present in your app to users or re-engagement lifecycle marketing campaigns based on which merchants your users have accounts with. ## Shop Pay Integration for TransactionLink TransactionLink now supports Shop Pay as a merchant integration. Because Shop Pay is used across millions of Shopify-powered merchants, this single integration unlocks SKU-level transaction data for a significantly expanded merchant footprint. Transaction data is available via the [Sync Transactions](/api-reference/products/transaction-link/sync) and [Get Transaction By ID](/api-reference/products/transaction-link/get-by-id) endpoints. ## New Subscription & Bill Fields The [Subscription object](/api-reference/products/subscriptions/subscription-object) now includes additional fields for richer subscription & bill data: `description`, `start_date`, `next_renewal_date`, `expiration_date`, `cancel_instructions`, `is_paid`, and `is_family_plan`. These fields provide deeper insight into subscription details, renewal timelines, and plan characteristics. Subscription data can be retrieved via the [Get Subscription By ID](/api-reference/products/subscriptions/get-by-id) endpoint. ## New Rental Merchant Coverage CardSwitcher now supports two additional rental payment merchants: RentCafe (Yardi) and ClickPay. Your users can now update their card on file for rent payments at properties managed through these platforms. Learn more in the [CardSwitcher quickstart](/card-switcher/quickstart). ## SubscriptionManager Introducing [SubscriptionManager](/subscription-manager/quickstart), a new capability that surfaces subscription and bill data after a card is provisioned to a user's merchant account. Subscription and bill information can be retrieved via the [Get Subscription By ID](/api-reference/products/subscriptions/get-by-id) endpoint. All Apple account & app store subscriptions are supported with additional merchants rolling out each week in Q1 2026. ## Dashboard Logs Search The Logs page in the [Knot Dashboard](https://dashboard.knotapi.com/developers/logs) now supports searching by session ID and task ID, making it faster to trace and debug specific user flows without scrolling through the full log history. ## Shipping Information in Transactions Transactions now include a `shipping` field containing the recipient's name and delivery address, allowing you to reconcile orders with fulfillment data in a single API call. The shipping field is available on the [Sync Transactions](/api-reference/products/transaction-link/sync) and [Get Transaction By ID](/api-reference/products/transaction-link/get-by-id) endpoints. ## Session Metadata in Webhooks You can now attach custom metadata (arbitrary key/value pairs) to SDK sessions via the [Create Session](/api-reference/sessions/create-session) endpoint, which will be included in your [webhook](/webhooks) payloads. This is useful for passing reference tokens, internal correlation IDs, or any data you'd like echoed back without maintaining a separate mapping. ## Seller Data in Transactions Transactions now include a `seller` object on products, identifying whether an item was sold by the marketplace or a third-party seller — helpful for distinguishing first-party vs third-party purchases in your transaction analysis. Seller data is available on the [Sync Transactions](/api-reference/products/transaction-link/sync) and [Get Transaction By ID](/api-reference/products/transaction-link/get-by-id) endpoints. ## Detected Merchant Accounts in Dashboard A new Detected Accounts page is now available in the [Knot Dashboard](https://dashboard.knotapi.com), showing which merchant accounts have been detected for your users via [Detect](/detect/quickstart). This makes it easy to monitor detection coverage and verify that accounts are being surfaced correctly. ## CardUpdater on Card Endpoints The [Switch Card](/api-reference/products/card-switcher/switch-card) and [Switch Card (JWE)](/api-reference/products/card-switcher/switch-card-jwe) endpoints now support [CardUpdater](/card-switcher/card-updater) flows. This allows you to initiate a card update by passing `external_user_id` and `merchant_id` directly, without requiring an active SDK session. ## Products Page in Dashboard A new Products page is now available in the [Knot Dashboard](https://dashboard.knotapi.com/products), providing an overview of all Knot products — CardSwitcher, Detect, TransactionLink, CardUpdater, Shopping, and Vaulting — with descriptions and quick-start links to help you get up and running faster. ## Domain Allowlisting Now Optional Web SDK integrations no longer require domain allowlisting by default, removing a common friction point during [integration setup](/sdk/web). If you'd like the additional security of domain restrictions, you can still enable it in your [Knot Dashboard](https://dashboard.knotapi.com) settings. ## Transaction Enrichment TransactionLink data now includes `products.imageUrl`, `products.url`, and `products.description` fields on the [Sync Transactions](/api-reference/products/transaction-link/sync) and [Get Transaction By ID](/api-reference/products/transaction-link/get-by-id) endpoints. Product images are stored durably and served from Knot's infrastructure, so you never have to worry about broken merchant image links. ## Multi-Currency Transaction Support TransactionLink now supports purchases made in non-USD currencies, capturing the correct currency for international transactions. Currency data is returned on the [Sync Transactions](/api-reference/products/transaction-link/sync) and [Get Transaction By ID](/api-reference/products/transaction-link/get-by-id) endpoints. ## Audit Logs A new [Get Audit Logs](/api-reference/audit-logs/get-audit-logs) endpoint is now available, allowing you to programmatically retrieve a log of actions taken in the [Knot Dashboard](https://dashboard.knotapi.com). This is particularly useful for compliance workflows and tracking configuration changes across your team. ## Vaulting Introducing [Vaulting](/vaulting/quickstart), a new Knot product that enables wallet providers to vault their digital wallet as the default payment method at merchant accounts — instead of a payment card. Vaulting uses the same SDK-driven experience as Card Switcher, so your users can authenticate and set up their wallet without leaving your app. ## Merchant Flow Improvements We have made significant improvements to select merchant authentication flows, nearly doubling conversion rates for [CardSwitcher](/card-switcher/quickstart) users. This update is available starting with: * [iOS SDK](/sdk/ios) version 1.0.14+ * [React Native SDK](/sdk/react-native) version 1.0.13+ ## MCP Server An MCP server for the Knot API is now available, enabling interaction with the API and docs via natural language prompting. Learn how to set it up in the [Building with AI](/building-with-ai) guide. ## Customization for Multi-Product Card Portfolios Versions `1.0.5` on iOS, `2.0.5` on Android, `1.0.2` on Flutter, and `1.0.2` on React Native (with JS to follow) include new features allowing multi-product card issuers to integrate Knot's SDK. For those that issue multiple, differently-named card programs or may issue cards under multiple brands, you can now override and customize the name of your company and card inside the Knot SDK using the [`CustomerConfiguration class`](/sdk/ios#customer-configuration). ## New Guides & API Reference We've launched new [guides and API reference documentation](/) to help you integrate Knot's SDK and APIs. The updated docs cover quickstart walkthroughs, endpoint references, and webhook event details for all Knot products. ## Updates to Existing Transactions Once a user's merchant account is linked to Knot, you can receive updates to existing transactions you've already received. The [`UPDATED_TRANSACTIONS_AVAILABLE`](/transaction-link/webhook-events/updated-transactions-available) webhook notifies you when changes occur, and the [Get Transaction By ID](/api-reference/products/transaction-link/get-by-id) endpoint provides access to the updated data. ## TransactionLink Introducing [TransactionLink](/transaction-link/quickstart), a new Knot product that allows end users to link their merchant accounts to your app and retrieve item-level transaction data on an ongoing basis. ## I2C Processor Integration Issuers looking to integrate our CardSwitcher product that use I2C as an issuer processor can now rely on our [direct integration with I2C](/card-switcher/processor-digital-banking-integrations/i2c) to retrieve and provide card information to Knot in a PCI-compliant manner. Along with our existing integration with Unit, this integration builds upon our dedication toward handling card information seamlessly and with strict PCI-compliance. # SDK version updates Source: https://docs.knotapi.com/changelog/sdk-updates Release notes for the Knot Link iOS, Android, React Native, Flutter, and Web SDKs, with an RSS feed you can subscribe to for new version alerts. SDK version updates automatically publish to an RSS feed [here](https://docs.knotapi.com/changelog/sdk-updates/rss.xml), so you can subscribe to updates as desired. [Here](https://slack.com/help/articles/218688467-Add-RSS-feeds-to-Slack) are instructions on sending the RSS feed of changes to a Slack workspace, as well as simple instructions for an email feed via Zapier [here](https://zapier.com/apps/email/integrations/rss/1441/send-new-rss-feed-entries-via-email).\ \ This is the RSS feed URL: [https://docs.knotapi.com/changelog/sdk-updates/rss.xml](https://docs.knotapi.com/changelog/sdk-updates/rss.xml) ## React Native SDK Release Fixes and improvements. ## iOS SDK Release Fixes and improvements. ## Android SDK Release Reduced external dependencies. ## React Native SDK Release Fixes and improvements. ## iOS SDK Release Improved safe area handling when the SDK is presented modally, full-screen, or above another sheet. ## Android SDK Release Fixes and improvements. ## iOS SDK Release Fixes and improvements. ## Android SDK Release Fixes and improvements. ## React Native SDK Release Fixes and improvements. ## Android SDK Release Fixes and improvements. ## React Native SDK Release Added [localization](/localization) support for Spanish, Canadian English, and Canadian French. Updated iOS podspec for React Native 0.84 compatibility. ## Flutter SDK Release Added Spanish, Canadian English, and Canadian French [localization](/localization) support. ## iOS SDK Release Added [localization](/localization) support for Spanish, Canadian English, and Canadian French. ## Android SDK Release Added Canadian English and Canadian French [localization](/localization) support. ## Web SDK Release Fixes and improvements. ## iOS SDK Release Fixes and improvements. ## Android SDK Release Improved SDK startup performance. ## React Native SDK Release Optimized memory usage and improved architecture for SDK managed web views. ## iOS SDK Release Optimized memory usage and improved architecture for SDK managed web views. ## iOS SDK Release Fixes and improvements. ## Android SDK Release Fixes and improvements. ## Android SDK Release Added Spanish [localization](/localization) support. ## Android SDK Release The Knot SDK now runs in a separate Android process. Android re-runs Application.onCreate() for every new process. Add this guard at the top of your Application.onCreate() to skip it for ours: ```kotlin lines wrap theme={"system"} override fun onCreate() { if (getProcessName()?.endsWith(":knotapi") == true) return super.onCreate() // ... } ``` Without this guard, SDK startup latency increases by however long your onCreate() takes to execute. ## iOS SDK Release Fixes and improvements. ## Web SDK Release Added [localization](/localization) support. Added support for `metadata` to include custom key-value pairs in [webhook](/webhooks#session-metadata) payloads. ## Android SDK Release Fixes and improvements. ## Android SDK Release Fixes and improvements. ## Web SDK Release Fixes and improvements. ## iOS SDK Release Added support for vaulting digital wallets. You can learn more about this new functionality [here](/vaulting/quickstart). ## Android SDK Release Fixes and improvements. ## Android SDK Release Added support for vaulting digital wallets. You can learn more about this new functionality [here](/vaulting/quickstart). ## iOS SDK Release Fixes and improvements. ## React Native SDK Release Fixes and improvements. ## iOS SDK Release Fixes and improvements. ## Android SDK Release Fixes and improvements. ## Web SDK Release Fixes and improvements. ## Web SDK Release Fixes and improvements. ## Android SDK Release Fixes and improvements. ## React Native SDK Release Fixes a memory leak that could impact app stability for a small number of users. ## iOS SDK Release Fixes a memory leak that could impact app stability for a small number of users. ## iOS SDK Release Fixes an edge case where the SDK could be interrupted during launch. ## iOS SDK Release Fixes a crash caused by a race condition affecting a very small percentage of users. ## iOS SDK Release Fixes a crash caused by memory allocation affecting a very small percentage of users. ## React Native SDK Release Fixes and improvements. ## Flutter SDK Release You can now retrieve the current SDK version programmatically via the SDK. More information [here](/sdk/flutter#get-current-sdk-version). ## Android SDK Release Fixes and improvements. ## iOS SDK Release Fixes and improvements. ## React Native SDK Release Fixes and improvements. ## iOS SDK Release Fixes and improvements. ## React Native SDK Release Fixes and improvements. ## iOS SDK Release You can now retrieve the current SDK version programmatically via the SDK. More information [here](/sdk/ios#get-current-sdk-version). ## React Native SDK Release Fixes and improvements. ## React Native SDK Release Fixes and improvements. ## iOS SDK Release Fixes and improvements. ## iOS SDK Release Fixes and improvements. ## React Native SDK Release Fixes and improvements. ## React Native SDK Release Fixes and improvements. ## React Native SDK Release You can now retrieve the current SDK version programmatically via the SDK. More information [here](/sdk/react-native#get-current-sdk-version). ## React Native SDK Release Fixes and improvements. ## iOS SDK Release Fixes and improvements. ## Android SDK Release Added the ability to retrieve the current SDK version programmatically via the SDK. More information [here](/sdk/android). ## iOS SDK Release Fixes and improvements. ## React Native SDK Release Fixes and improvements. ## Android SDK Release Fixes and improvements. ## Android SDK Release Fixes and improvements. ## iOS SDK Release Fixes and improvements. ## Simplification of SDK Initialization Versions `1.0.11` on iOS, `2.0.12` on Android, and `1.0.7` on React Native (with Flutter & JS to follow) remove the need to specify a `product` in `KnotConfiguration` when configuring the session to initialize the SDK. Now, simply choose a `type` for your session when calling [Create Session](/api-reference/sessions/create-session) and initialize the SDK with that session. ## SDK Major Version & Improvements New major versions of the Knot SDK on all platforms have been released with improvements to initialization and event handling. For companies already integrated with earlier versions of Knot, a migration guide is provided to make the transition smooth. **Migration Guides:** * [iOS](/sdk/migration-guides/ios/ios-1-0) * [Android](/sdk/migration-guides/android/android-2-0) * [React Native](/sdk/migration-guides/react-native/react-native-1-0) * [Flutter](/sdk/migration-guides/flutter/flutter-1-0) * [Web](/sdk/migration-guides/web/web-1-0) # Overview Source: https://docs.knotapi.com/dashboard/overview Learn about the Knot Dashboard where you can manage API keys, webhooks, and team permissions. The Knot Dashboard is where you can manage various aspects of your integration with Knot, in particular where you can retrieve API keys (`client_id` and `secret`) and subscribe to webhooks. If you are currently a Knot customer and have been sent access to your Knot Dashboard, you can login [here](https://dashboard.knotapi.com/). If you are having difficulties logging in, please reach out to your account’s administrator who can provide you with access or contact the Knot team to get set up. ## Permissions | Role | Team Management | SAML Settings | Merchant Visibility | SDK Settings | API Keys | API Requests | Webhook URLs | Support Requests | | ---------- | --------------- | :------------ | :------------------ | :----------- | :------- | :----------- | :----------- | :--------------- | | **Owner** | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | | **Admin** | Yes | No | Yes | Yes | Yes | Yes | Yes | Yes | | **Member** | No | No | Yes | Yes | Yes | Yes | Yes | Yes | ## Audit Logs You can retrieve audit logs of employee usage of the Knot Dashboard via [Get Audit Logs](/api-reference/audit-logs/get-audit-logs). # Single Sign-On Source: https://docs.knotapi.com/dashboard/single-sign-on Configure single sign-on (SSO) for your team using SAML 2.0 with identity providers like Okta. ## Overview Single sign-on (SSO) services enable you to manage your team’s identity across all your SaaS products and you can use this functionality to manage authentication to the [Dashboard](https://docs.knotapi.com/dashboard/overview). With an SSO service, a user can access multiple applications using one set of credentials (for example, an email address and password). The SSO service authenticates the user once for all the applications the user has been given rights to and eliminates further prompts when the user switches applications during the same session. An example of SSO is Google's sign-in implementation for products like Gmail, YouTube, and Google Drive. Any user signed in to one of Google's products is also automatically signed in to their other products. ## SSO setup for SAML The identity provider (IdP) must support the SAML 2.0 standard. Today, [Okta](/dashboard/single-sign-on#okta) is the only supported IdP. Most SAML 2.0 compliant identity providers require the same information about the service provider for setup - Knot being the service provider in this case. While configuring your IdP, make sure to set your user's email address in SAML attributes and claims. Knot expects to receive an email address from your IdP to identify the user. Even if you configure SSO, email/password and Google oAuth 2.0 authentication methods remain enabled for your team. ## Okta To configure **Single Sign-On (SSO)** with **Okta**, you need to create a custom SAML application. Additionally, you must be an administrator in **Okta** and have an `Owner` role in your [Knot Dashboard](https://dashboard.knotapi.com) to set up SSO for your team. ### Configuring SSO using a custom SAML app To continue configuring your custom SAML application, do the following: Open your Okta admin console in a new tab. Go to "Applications" and then select "Applications." Click "Create App Integration." In the "Create a new app integration" screen, select "SAML 2.0" and then click "Next." In the "General Settings" tab, enter an app name you'll recognize later, and then click "Next." In the "Configure SAML" tab, specify [https://dashboard.knotapi.com](https://dashboard.knotapi.com/) as the "Single Sign-On URL" and use it as your "Audience URI (SP Entity ID)." Select "Email Address" as the "name ID" format. Keep in mind that later on, you will need to modify the "Single Sign-On URL" and the "Audience URI (SP Entity ID)" according to the configuration generated when setting up the SAML/SSO in the [Knot Dashboard](https://dashboard.knotapi.com). In the "Feedback" tab, select "I'm an Okta customer adding an internal app." Select "This is an internal app that we have created" as the app type, and then click "Finish." Click the "Sign On" tab, and then click "View SAML setup instructions" to display the "IdP details." Login to the [Knot Dashboard](https://dashboard.knotapi.com) with an `Owner` role and navigate to the "Account" page. Based on the IdP details you got in Step 8, fill in the form with the following details: 1. Entity ID -> Identity Provider Issuer 2. Login URL and Logout URL -> Identity Provider Single Sign-On URL 3. X509 Certificate -> X509 Certificate Click "Submit" to generate the configuration. Navigate to the custom SAML application in Okta, click "Edit," and update the SAML settings using the configuration generated in the prior step. The "Single Sign-On" is the "Reply URL" and the "Audience URI (SP Entity ID)" is the "Entity ID." # Quickstart Source: https://docs.knotapi.com/detect/quickstart ## Introduction **Detect** enables you to identify a user's online merchant accounts and optionally retrieve them from the Knot platform, allowing you to then personalize your user experience. ## Getting started This option is for those who've already implemented the CardSwitcher product. When calling [Create Session](/api-reference/sessions/create-session) to create a session for the SDK, simply add the user's email address and phone number to the request. ```curl theme={"system"} curl --request POST \ --url https://development.knotapi.com/session/create \ --header 'Authorization: Basic ' \ --header 'Content-Type: application/json' \ --data ' { "type": "card_switcher", "external_user_id": "123abc", "card_id": "81n9al10a0ayn13", "email": "ada.lovelace@gmail.com", "phone_number": "+11234567890" } ' ``` When the merchant selection experience is rendered in the SDK, it will be automatically personalized to take into account a user's detected online merchant accounts. Listen for the [`NEW_DETECTED_ACCOUNTS_AVAILABLE`](/detect/webhook-events/new-detected-accounts-available) webhook event to be notified when new detected accounts have been found for a given user. The webhook payload includes a `data.detected_accounts` array with the user's detected merchant accounts. To test receiving the `NEW_DETECTED_ACCOUNTS_AVAILABLE` webhook in the development environment, pass the user's email into the `email` field when calling [Create Session](https://docs.knotapi.com/api-reference/sessions/create-session). Ensure you have access to the [Knot Dashboard](https://dashboard.knotapi.com) and retrieve your `client_id` and `secret` to create an API key. Learn more about creating an API key and authentication to the API [here](/api-reference/authentication). Call [Detect Accounts](/api-reference/products/detect/detect-accounts) with the user's `email` and `phone_number` to detect their online merchant accounts. Listen for the [`NEW_DETECTED_ACCOUNTS_AVAILABLE`](/detect/webhook-events/new-detected-accounts-available) webhook event to be notified when new detected accounts have been found for a given user. The webhook payload includes a `data.detected_accounts` array with the user's detected merchant accounts. ### Start the flow Ensure you have access to the [Knot Dashboard](https://dashboard.knotapi.com) and retrieve your `client_id` and `secret` to create an API key. Learn more about creating an API key and authentication to the API [here](/api-reference/authentication). Call [List Merchants](/api-reference/merchants/list-merchants) to retrieve a list of merchants that are available for merchant account detection by passing `type = detect` in the request. These are merchants you can allow users to link in your app and subsequently detect merchant accounts. You will be notified via the [`MERCHANT_STATUS_UPDATE`](/link/webhook-events/merchant-status-update) webhook when/if the available merchant list changes, even if temporarily. With your API key for the `development` environment, call [Create Session](https://docs.knotapi.com/api-reference/sessions/create-session) with `type: link` to create a session used when invoking the SDK. More [here](/api-reference/authentication) on how to create an API key. Install an SDK of your choosing, for example on iOS [here](https://docs.knotapi.com/sdk/ios) and Android [here](/sdk/android). Initialize the SDK with the `session_id` retrieved from [Create Session](https://docs.knotapi.com/api-reference/sessions/create-session) and a merchant `Id` retrieved from [List Merchants](https://docs.knotapi.com/api-reference/merchants/list-merchants) in `KnotConfiguration`. Alternatively, you can use `merchant_id: 60` for Apple to get started quickly. The SDK is where users will interact with the Knot UI to authenticate to various merchants. All login flows, including step-up authentication, are handled within the SDK. Users will see real-time feedback as they progress through authenticating with a merchant. ### Link a merchant account **In the development environment,** login to a merchant account using `user_good` / `pass_good` credentials to link your user's merchant account. Subscribe to webhooks in the [Knot Dashboard](https://dashboard.knotapi.com/developers/webhooks) so your backend can be notified about user-generated, server-side events. Listen for the following events: * [`AUTHENTICATED`](/link/webhook-events/authenticated): fired when the authentication to a merchant is successful and the merchant account is therefore successfully linked to Knot. Similarly and as applicable, listen to the client-side `onEvent` callback in the SDK to receive the `authenticated` event. * [`NEW_DETECTED_ACCOUNTS_AVAILABLE`](/detect/webhook-events/new-detected-accounts-available): fired when new detected accounts are available. ### Search for detected accounts When you receive the [`NEW_DETECTED_ACCOUNTS_AVAILABLE`](/detect/webhook-events/new-detected-accounts-available) webhook, call [Search Detected Accounts](/api-reference/products/detect/search-detected-accounts) with a list of merchant or company names that are relevant to your use case. The endpoint will return whether a detected merchant account was found for the user at each one. This is useful when you have a specific set of merchants or companies you care about and want to know if a user has an account at any of them. **In the development environment,** this endpoint accepts any list of `names` you provide and returns `detected: true` for 6 of them at random. ### Receive detected accounts The [`NEW_DETECTED_ACCOUNTS_AVAILABLE`](/detect/webhook-events/new-detected-accounts-available) webhook payload includes a `data.detected_accounts` array with the user's detected merchant accounts. The array includes only merchants that Knot supports on its platform (i.e. those with a `merchant_id`), making it useful for engaging or re-engaging users to provision a payment method to the merchant wallet. # NEW_DETECTED_ACCOUNTS_AVAILABLE Source: https://docs.knotapi.com/detect/webhook-events/new-detected-accounts-available api-reference/openapi.json webhook new_detected_accounts_available Fired when new detected accounts are available. #### Usage You can use detected accounts sent in this webhook event to closely personalize the merchants you present to users in your app or through lifecycle marketing campaigns. For example, if a detected account at Uber is present for a user, you can more prominently display Uber to that user in your app or as a push notification/email. Moreover, aggregated detected account information can be useful in designing a rewards program or other product features throughout your app. #### Testing To test receiving the `NEW_DETECTED_ACCOUNTS_AVAILABLE` webhook in the development environment, either call the [Detect Accounts](/api-reference/products/detect/detect-accounts) endpoint or pass the user's email into the `email` field when calling [Create Session](/api-reference/sessions/create-session). Note that the **Detect** product must be enabled for your account to receive the webhook event. Please reach out to the Knot team to enable **Detect**. # Introduction Source: https://docs.knotapi.com/introduction Overview of Knot's merchant connectivity platform. Knot Flow Knot is a merchant connectivity platform that financial institutions and other businesses of all sizes use to connect their applications to the online merchant accounts of their end users. Knot allows your application to read and write data to online merchant accounts for various use cases. [CardSwitcher](/card-switcher/quickstart), for example, allows users to quickly and securely update their credit and debit cards on-file with merchants. This allows card issuers to improve and maintain top-of-wallet status across merchants. All integrations to Knot's platform include two aspects: 1. A client-side SDK integration which handles credential validation, multi-factor authentication, and error handling. 2. A server-side integration to read data from & write data to Knot, as well as receive webhook events. Provision cards to merchant wallets like Amazon, Netflix, and Uber. Retrieve SKU-level transaction data from merchant accounts. Retrieve and manage subscription data from merchant accounts. Explore and integrate with API endpoints Seamlessly embed Knot in your app LLM? [Read llms.txt](https://docs.knotapi.com/llms.txt). # Launch Checklist Source: https://docs.knotapi.com/launch-checklist Essential checklist of tasks to complete before launching your Knot integration in production. Ensure you complete all of the below tasks before launching your integration with Knot in production. ## Basic Setup * Contact the Knot team to request production access and align on your go-live timeline * Retrieve your production `client_id` and generate a `secret` to create your API key for the production environment * Do not store your production credentials in source control or client-accessible code * Configure a webhook for the production environment in the [Knot Dashboard](https://dashboard.knotapi.com/developers/webhooks) to receive critical server-side events webhooks ## SDK * Install and use the latest version of the Knot SDK, including bumping to any patch versions * Call [Create Session](/api-reference/sessions/create-session) to generate a new session **every time** you initialize the SDK * Do not log session IDs internally or in 3rd party tooling * Only invoke the Knot SDK with sessions generated with your production API key in your production environment * Pass your production `client_id` and `environment: production` when invoking the Knot SDK in your production environment * Handle client-side callbacks (`onSuccess`, `onError`, `onExit`, `onEvent`) * Pass a value for the [`entry_point`](/sdk/ios#configure-the-session) parameter at SDK initialization for analytics & funnel tracking * In case users use the Knot SDK for >30 minutes when the session will expire, handle the `onEvent` callback with `event: REFRESH_SESSION_REQUEST` and call [Extend Session](/api-reference/sessions/extend-session) when you receive the event * If using the Web SDK, you can optionally enable domain allowlisting by reaching out to the Knot team, then allowlisting domains in the [Knot Dashboard](https://dashboard.knotapi.com/developers/domains) for additional security. ## Other * Remove any test/development/staging credentials (e.g., `user_good`) from client-side or server-side code for production * Contact Knot team to request production access and align go-live timeline ## Product-Specific ### CardSwitcher * Always call [Switch Card (JWE)](/api-reference/products/card-switcher/switch-card-jwe) or [Switch Card](/api-reference/products/card-switcher/switch-card) within 15 seconds of receiving the `AUTHENTICATED` webhook * If you display merchants natively in your app, handle the [`MERCHANT_STATUS_UPDATE`](/link/webhook-events/merchant-status-update) webhook event to gracefully handle changes in merchant availability by product `type`, `platform`, and `min_sdk_version` (more [here](/link/retrieving-and-listing-merchants#retrieving%2C-listing%2C-and-searching)) # Retrieving and Listing Merchants Source: https://docs.knotapi.com/link/retrieving-and-listing-merchants Retrieve and display available merchants in your app's UI using the List Merchants API. ## Overview In your app, you may choose to list **available** merchants for users to view, select from, and ultimately link. Knot allows you to retrieve a list of available merchants via API based on a number of parameters. This allows you to completely control and customize the UX & UI for how you display merchants to your users. This optional functionality serves as an alternative to the merchant list and search experience within the Knot SDK. In addition, choosing to list merchants directly in your own app's UI goes hand-in-hand with providing a specific merchant (that the user selected) when you invoke the SDK. ## Retrieving, Listing, and Searching You can retrieve merchants from the [List Merchants](/api-reference/merchants/list-merchants) endpoint. In doing so, there are a number of body parameters that will modify the list of merchants returned in the response. The value you provide in this parameter ensures that the merchant list you receive includes available merchants for a specific product. This is important as not all product use cases are supported for every merchant on Knot's platform. Knot supports merchants across `iOS`, `android`, and `web`, however, not every merchant is supported on each. You can use this parameter to retrieve a list of available merchants on a given platform, so that only those available merchants may be displayed to users on each platform. You may choose to offer your users a search experience in your app where they can search for different merchants by keyword. If so, you can provide a user's search keyword to this parameter and retrieve a merchant (or list of merchants) that match that keyword. For example, if you provide a user's search keyword as `hub` to this parameter, you may receive a list of merchants including `Grubhub`, `Stubhub`, and `Github`. # ACCOUNT_LOGIN_REQUIRED Source: https://docs.knotapi.com/link/webhook-events/account-login-required api-reference/openapi.json webhook account_login_required Fired when a user's merchant account is disconnected and requires the user to login again to reconnect the account. Fired when a user's merchant account is disconnected and requires the user to login again to reconnect the account. # AUTHENTICATED Source: https://docs.knotapi.com/link/webhook-events/authenticated api-reference/openapi.json webhook authenticated Fired when the authentication to a merchant is successful. Fired when the authentication to a merchant is successful. # CREDENTIALS_FAILED Source: https://docs.knotapi.com/link/webhook-events/credentials-failed api-reference/openapi.json webhook credentials_failed Fired when the login credentials used to login to a merchant are incorrect and the user needs to re-enter them (if a `CARD_FAILED` webhook is not received). Fired when the login credentials used to login to a merchant are incorrect and the user needs to re-enter them (if a `CARD_FAILED` webhook is not received). # DOB_FAILED Source: https://docs.knotapi.com/link/webhook-events/dob-failed api-reference/openapi.json webhook dob_failed Fired when the date of birth provided is incorrect and the user needs to re-enter it (if a `CARD_FAILED` webhook is not received). Fired when the date of birth provided is incorrect and the user needs to re-enter it (if a `CARD_FAILED` webhook is not received). # DOB_REQUIRED Source: https://docs.knotapi.com/link/webhook-events/dob-required api-reference/openapi.json webhook dob_required Fired when a user needs to verify their date of birth to login to a merchant for the 1st time. Fired when a user needs to verify their date of birth to login to a merchant for the 1st time. # LICENSE_FAILED Source: https://docs.knotapi.com/link/webhook-events/license-failed api-reference/openapi.json webhook license_failed Fired when an incorrect driver license number is provided and the user needs to re-enter the driver license number. Fired when an incorrect driver license number is provided and the user needs to re-enter the driver license number. # LICENSE_REQUIRED Source: https://docs.knotapi.com/link/webhook-events/license-required api-reference/openapi.json webhook license_required Fired when a user needs to provide their driver license number to login to a merchant for the 1st time. Fired when a user needs to provide their driver license number to login to a merchant for the 1st time. # LOGIN_APPROVAL Source: https://docs.knotapi.com/link/webhook-events/login-approval api-reference/openapi.json webhook login_approval Fired when a user needs to approve the login to a merchant via a push notification, text message, or similar from a merchant. Fired when a user needs to approve the login to a merchant via a push notification, text message, or similar from a merchant. # MERCHANT_STATUS_UPDATE Source: https://docs.knotapi.com/link/webhook-events/merchant-status-update api-reference/openapi.json webhook merchant_status_update Fired when a merchant becomes available or unavailable. Listening to this event is only necessary if you intend to display and allow users to select various merchants natively inside your app. Fired when the availability of a merchant changes on the Knot platform (even if temporarily), including when a brand new merchant is made available for the first time. Availability is unique to product types, platforms, and minimum versions of the SDK. As such, the event is emitted independently for each product `type` and `platform`. Particularly if you are implementing multiple of Knot's products, you should consider the `type` property to determine whether a merchant is available for a given product, as availability can differ. This event does not include a `session_id`, which is relevant when generating a hash map for webhook verification, as described in [Webhook Verification](/webhooks#webhook-verification). # OTP_FAILED Source: https://docs.knotapi.com/link/webhook-events/otp-failed api-reference/openapi.json webhook otp_failed Fired when the OTP code used to login to a merchant is incorrect and the user needs to re-enter it (if a `CARD_FAILED` webhook is not received). Fired when the OTP code used to login to a merchant is incorrect and the user needs to re-enter it (if a [`CARD_FAILED`](/card-switcher/webhook-events/card-failed) webhook is not received). # OTP_REQUIRED Source: https://docs.knotapi.com/link/webhook-events/otp-required api-reference/openapi.json webhook otp_required Fired when a user needs to enter an OTP code to login to a merchant for the 1st time. Fired when a user needs to enter an OTP code to login to a merchant for the 1st time. # QUESTIONS_FAILED Source: https://docs.knotapi.com/link/webhook-events/questions-failed api-reference/openapi.json webhook questions_failed Fired when the answers to security questions are incorrect and the user needs to re-enter the answers (if a `CARD_FAILED` webhook is not received). Fired when the answers to security questions are incorrect and the user needs to re-enter the answers (if a [`CARD_FAILED`](/card-switcher/webhook-events/card-failed) webhook is not received). # QUESTIONS_REQUIRED Source: https://docs.knotapi.com/link/webhook-events/questions-required api-reference/openapi.json webhook questions_required Fired when a user needs to provide answers to security questions for the 1st time. Fired when a user needs to provide answers to security questions for the 1st time. # Localization Source: https://docs.knotapi.com/localization Localize the Knot SDK to display content in your users' preferred language. ## Overview The Knot SDK supports localization, allowing you to present the merchant linking experience in your users' preferred language. Localization is controlled via the `locale` parameter, a [BCP-47](https://developer.mozilla.org/en-US/docs/Glossary/BCP_47_language_tag) language tag passed when configuring the SDK. The `locale` you pass controls both the SDK's display language and which regional version of the merchant site loads where applicable. For example, passing `en-CA` will load the Canadian version of a merchant's site, while `en-US` will load the U.S. version. If your app supports users in multiple regions, we recommend always passing an explicit `locale` to match the user's region. ## Supported Locales | Locale | Language | Region | | ------- | -------- | ------ | | `en-US` | English | U.S. | | `en-CA` | English | Canada | | `es-US` | Spanish | U.S. | | `fr-CA` | French | Canada | If no `locale` is provided, the SDK defaults to `en-US`, displaying in English and loading the U.S. version of the merchant site where applicable. ## Usage Pass the `locale` parameter when configuring the SDK to localize the experience. For example, to display the SDK in Spanish: ```javascript Javascript icon=js theme={"system"} knotapi.open({ sessionId: "Your Session ID", clientId: "Your Client ID", environment: "production", locale: "es-US", // ... other parameters }); ``` The `locale` parameter is available across all mobile SDKs. See the [iOS](/sdk/ios#configure-the-session), [Android](/sdk/android#configure-the-session), [React Native](/sdk/react-native#configure-the-session), and [Flutter](/sdk/flutter#configure-the-session) SDK guides for platform-specific examples. ## What is Localized When a supported locale is set, all text in the Knot SDK is localized. Merchant login flows are also localized, and the SDK loads the regional version of the merchant's site where the merchant supports the provided locale. # Android SDK Source: https://docs.knotapi.com/sdk/android Install and initialize the Knot Link Android SDK via Maven Central, configure a session, and handle events to link users' merchant accounts. ## Overview **SDK Updates**\ New versions of the SDK are released frequently, not only to add new features and address issues in the SDK, but also to continuously improve the conversion of merchant login flows. As a result, we strongly recommend that you frequently update your SDK version across any platforms where you invoke the SDK. The Knot Link SDK provides a seamless way for end users to link their merchant accounts to your Android app, serving as the foundation for Knot's merchant connectivity platform. It is a client-side integration, consisting of initializing & configuring the SDK and handling events. ## Installation Follow the best-practice installation approach outlined below, which works seamlessly for both Java and Kotlin projects. Adding Maven Central to your root-level `build.gradle` file ensures that the Android Gradle plugin (and all other dependencies) are properly resolved and kept up-to-date. ```groovy Groovy theme={"system"} allprojects { repositories { mavenCentral() } } ``` In your module-level Gradle file (typically `app/build.gradle`), configure your Android settings-such as the minimum SDK version and Java 8 support-to ensure compatibility with the SDK. The Knot SDK requires Android `API level 21` or greater. ```groovy Groovy theme={"system"} android { defaultConfig { minSdkVersion 21 // or greater } // Enable Java 8 support for KnotAPI SDK compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } ``` Include the Knot SDK dependency so that Gradle can fetch the SDK from [Maven Central](https://central.sonatype.com/artifact/com.knotapi.knot/knotapi-android-sdk). ```groovy Gradle theme={"system"} implementation group: 'com.knotapi.knot', name: 'knotapi-android-sdk', version: '' ``` ```groovy Gradle (short) theme={"system"} implementation 'com.knotapi.knot:knotapi-android-sdk:' ``` Replace `` with the current version provided by Knot. ```java Java icon=java theme={"system"} import com.knotapi.knot.*; ``` ```kotlin Kotlin icon=k theme={"system"} import com.knotapi.knot.* ``` ## Initialization Your backend will create a session by calling [Create Session](/api-reference/sessions/create-session) and provide it to your frontend. To initialize the Knot SDK, you must first configure the session with a `KnotConfiguration` class. The configuration allows you to set the environment, entry point, and other user experience configurations. It's expected that your integration with Knot will retrieve and pass a new session into the SDK on each initialization. ### Configure the session Use the `KnotConfiguration` and `CustomerConfiguration` classes to initialize the SDK with specific parameters. #### `KnotConfiguration` | Name | Type | Description | | :------------ | :------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | sessionId | String | The session created by calling `/session/create` in your backend. | | clientId | String | Your organization's client ID. Note that this varies between `development` and `production` environments. | | environment | Environment | The desired environment (`development` or `production`). | | entryPoint | String | **Optional.** The specific entry point from within your app where you are initializing the Knot SDK (e.g. `onboarding`). Only alphanumeric characters (a-z, A-Z, 0-9), spaces, ampersands (&), periods (.), and hyphens (-) are allowed. | | merchantIds | int\[] | **Optional.** A list of merchant ID(s) to display. We recommend providing 0 or 1 merchant IDs depending on your desired user experience. | | useCategories | Bool | **Optional.** Whether to display merchant categories and therefore group merchants into categories for discoverability. Default: `true`. | | useSearch | Bool | **Optional.** Whether to display the search bar, enabling users to search for merchants. Default: `true`. | | domainUrls | String\[] | **Optional. Deprecated in version 3.0.0+.** A set of domains for which Knot should explicitly not clear cookies. | | metadata | Map\ | **Optional.** Custom key-value pairs to include in [webhook](/webhooks#session-metadata) payloads. Maximum 10 keys with string values up to 500 characters each. | | locale | String | **Optional.** A [BCP-47](https://developer.mozilla.org/en-US/docs/Glossary/BCP_47_language_tag) language tag to set the locale for the SDK. Currently supported: `en-US`, `es-US`, `en-CA`, and `fr-CA`. If not provided, defaults to `en-US`. | | product | Product | **Optional. Ignored in version 2.0.12+.** The Knot product the session will inherit — the same as the type of session created (e.g. `card_switcher`, `transaction_link`). | #### `CustomerConfiguration` This class is entirely optional and infrequently used, typically only when you offer Knot for multiple, differently-named card programs in the same app. The Knot team will set pre-defined values of your choosing for each parameter that you can then subsequently pass into the SDK. Passing a value that is not pre-defined will result in an `onError` callback. To take advantage of this functionality, please contact the Knot team who will be happy to assist you. | Name | Type | Description | | :----------- | :----- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | cardName | String | **Optional.** The differentiated display name for the card product, used inside the Knot SDK (e.g. `Debit Card`, `Credit Card`). This value will override the default value `card`. | | customerName | String | **Optional.** The differentiated display name for the company, used both as a standalone and prepended to the `cardName` (e.g. `Smart Bank`, `Payment Corp`). This value will override your default customer name value. Only recommended if you issue cards under multiple brands. | | logoId | String | **Optional.** The differentiated logo for the company. This value will override your default logo. | See the following examples for how the parameters are used together in text inside the Knot SDK: `"Your [customerName] [cardName | card] was added."` ```java java icon=java theme={"system"} CustomerConfiguration customerConfig = new CustomerConfiguration( "Card Name", // cardName "Customer Name", // customerName "LogoId" // logoId ); Map metadata = new HashMap<>(); metadata.put("reference_token", "your-token"); metadata.put("trace_id", "your-trace-id"); KnotConfiguration config = new KnotConfiguration( "session_12345", // sessionId "client_67890", // clientId Environment.production, // environment new int[]{101}, // merchantIds true, // useCategories true, // useSearch new String[]{"https://example.com"}, // domainUrls (deprecated in 3.0.0+) "onboarding", // entryPoint metadata, // metadata for webhooks customerConfig, "es-US" // locale (optional BCP-47 language tag) ); ``` ```kotlin kotlin icon=k theme={"system"} val customerConfig = CustomerConfiguration( "Card Name", // cardName "Customer Name", // customerName "LogoId" // logoId ) val metadata = mapOf( "reference_token" to "your-token", "trace_id" to "your-trace-id" ) val config = KnotConfiguration( "session_12345", // sessionId "client_67890", // clientId Environment.PRODUCTION, // environment Knot.Product.card_switcher, // product intArrayOf(101, 102, 103), // merchantIds true, // useCategories true, // useSearch arrayOf("https://example.com"), // domainUrls (deprecated in 3.0.0+) "onboarding", // entryPoint metadata, // metadata for webhooks customerConfig, "es-US" // locale (optional BCP-47 language tag) ) ``` ### Open the session To begin the flow, use the `open` method with a `context`, a `KnotConfiguration` instance, and an optional `KnotEventDelegate`. ```java java icon=java theme={"system"} Knot.open(context, knotConfiguration, knotEventDelegate); ``` ```kotlin kotlin icon=k theme={"system"} Knot.open(context, knotConfiguration, knotEventDelegate) ``` ```java java icon=java theme={"system"} CustomerConfiguration customerConfig = new CustomerConfiguration( "Card Name", // cardName "Customer Name", // customerName "LogoId" // logoId ); Map metadata = new HashMap<>(); metadata.put("reference_token", "your-token"); KnotConfiguration config = new KnotConfiguration( "session_12345", // sessionId "client_67890", // clientId Environment.production, // environment new int[]{101}, // merchantIds true, // useCategories true, // useSearch new String[]{"https://example.com"}, // domainUrls (deprecated in 3.0.0+) "onboarding", // entryPoint metadata, // metadata for webhooks customerConfig, "es-US" // locale (optional BCP-47 language tag) ); Knot.open(context, config, knotEventDelegate); ``` ```kotlin kotlin icon=k theme={"system"} val customerConfig = CustomerConfiguration( "Card Name", // cardName "Customer Name", // customerName "LogoId" // logoId ) val metadata = mapOf("reference_token" to "your-token") val config = KnotConfiguration( "session_12345", // sessionId "client_67890", // clientId Environment.PRODUCTION, // environment Knot.Product.card_switcher, // product intArrayOf(101, 102, 103), // merchantIds true, // useCategories true, // useSearch arrayOf("https://example.com"), // domainUrls (deprecated in 3.0.0+) "onboarding", // entryPoint metadata, // metadata for webhooks customerConfig, "es-US" // locale (optional BCP-47 language tag) ) Knot.open(context, config, knotEventDelegate) ``` To test logging in to a merchant in the SDK, please reference a set of available test credentials for the CardSwitcher product [here](/card-switcher/testing) and the TransactionLink product [here](/transaction-link/testing). ## Single merchant flow You may decide to use [List Merchants](/api-reference/merchants/list-merchants) to retrieve a list of merchants, list them in your app, and then open the SDK with a single merchant. To do so, pass a merchant ID when configuring the session in the `KnotConfiguration`. If a single merchant ID is provided, the user will be sent directly to that merchant's login experience in the SDK. More in [Retrieving & Listing Merchants](/link/retrieving-and-listing-merchants). The merchant ID is the same across all environments. Although available, we do not recommend that you provide a long list of merchants in order to remove a few, but rather "hide" certain merchants that you desire from your [Customer Dashboard](https://dashboard.knotapi.com/). ## Entry points In your app's user experience, you may choose to integrate Knot in one or multiple places (e.g. from different tabs or screens). How users behave when interacting with Knot from each of these "entry points" may vary. It will be useful for you to be able to differentiate these groups of users by entry point in order to assess the value of each entry point. You can provide a value for the entry point in `KnotConfiguration.entryPoint` when [configuring the session](/sdk/android#configure-the-session). This value will be returned in the `AUTHENTICATED` webhook. ## Categories & search Users are presented with a list of merchants in the SDK (unless you provide a single merchant as described above). Accompanying the list is a set of categories and a search experience. Each of these components is visible to users by default (as set in Knot's backend). You can choose to remove either of them by setting `useCategories: false` and `useSearch: false` in `KnotConfiguration`. **This is not recommended**. ## Events To receive updates from the SDK, implement `KnotEventDelegate` in your class. ```java java icon=java theme={"system"} // Initialize your event delegate KnotEventDelegate eventDelegate = new KnotEventDelegate() { @Override public void onSuccess(String merchant) { // Handle successful operation } @Override public void onError(KnotError knotError) { // Handle error operation using knotError } @Override public void onExit() { // Handle SDK exit event } @Override public void onEvent(KnotEvent knotEvent) { // Extract and process event details from KnotEvent } }; // Initialize Knot SDK and set the event delegate instance (assuming you have a KnotConfiguration object) Knot.open(context, knotConfiguration, eventDelegate) ``` ```kotlin kotlin icon=k theme={"system"} // Initialize your event delegate val eventDelegate = object : KnotEventDelegate { override fun onSuccess(merchant: String) { // Handle successful operation } override fun onError(knotError: KnotError) { // Handle error operation using knotError } override fun onExit() { // Handle SDK exit event } override fun onEvent(knotEvent: KnotEvent) { // Extract and process event details from KnotEvent } } // Initialize Knot SDK and set the event delegate instance (assuming you have a KnotConfiguration object) Knot.open(context, knotConfiguration, eventDelegate) ``` ### `onSuccess` This event is called when a user successfully logged in to the merchant and their card was switched. This event takes a single string argument representing the merchant's name. ### `onError` This event is called when an error occurs during SDK initialization. It emits a `KnotError` enum as described below. | Error Case | Description | Debugging Steps | | ------------------------ | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | INVALID\_SESSION | The session is invalid. | Ensure `KnotConfiguration.environment` matches the environment the `sessionId` was created for (`development` or `production`). | | EXPIRED\_SESSION | The session has expired. | Sessions are valid for 30 minutes. It is best practice to ensure that you create a new session **every time** a user invokes the SDK using [Create Session](/api-reference/sessions/create-session). | | INVALID\_CLIENT\_ID | The client ID is invalid. | Verify that the value you are providing for `KnotConfiguration.clientId` is for the environment matching the value you are providing for `KnotConfiguration.environment` (i.e. `development` or `production`). If you provide your production `clientId` but set `environment: development`, you will experience this error. | | INTERNAL\_ERROR | An internal error occurred. | Simply retry invoking the SDK with a new `sessionId`. | | MERCHANT\_ID\_NOT\_FOUND | The merchant ID is required. | The [`type`](/api-reference/sessions/create-session#body-type) of `sessionId` you are providing on invocation of the SDK requires that you also provide a value in `KnotConfiguration.merchantIds` to ensure the user is directed to a specific merchant's login flow in the SDK. You can retrieve a list of merchant IDs (the same in all environments) in [List Merchants](/api-reference/merchants/list-merchants). | | INVALID\_CARD\_NAME | The card name is invalid. | The value you are providing for `customerConfiguration.cardName` must exactly match an allowlisted value for your organization. Please reach out to the Knot team for access to this allowlist. | | INVALID\_CUSTOMER\_NAME | The customer name is invalid. | The value you are providing for `customerConfiguration.customerName` must exactly match an allowlisted value for your organization. Please reach out to the Knot team for access to this allowlist. | | INVALID\_LOGO\_ID | The logo ID is invalid. | The value you are providing for `customerConfiguration.logoId` must exactly match an allowlisted value for your organization. Please reach out to the Knot team for access to this allowlist. | | INVALID\_LOCALE | The locale is invalid. | The value you are providing for `KnotConfiguration.locale` must be a valid [BCP-47](https://developer.mozilla.org/en-US/docs/Glossary/BCP_47_language_tag) language tag. Currently supported: `en-US`, `es-US`, `en-CA`, and `fr-CA`. | Sessions are valid for 30 minutes. If a session expires while the SDK is open, the SDK will emit an expired session error via `onError` and automatically close. To provide a seamless experience, handle the `REFRESH_SESSION_REQUEST` event via the `onEvent` callback to proactively extend the session using [Extend Session](/api-reference/sessions/extend-session) before expiration occurs. ```java java icon=java theme={"system"} public void onError(KnotError error) { switch (error) { case INVALID_SESSION: Log.e("KnotError", "Error: INVALID_SESSION - " + error.getErrorDescription()); break; case EXPIRED_SESSION: Log.e("KnotError", "Error: EXPIRED_SESSION - " + error.getErrorDescription()); break; case INVALID_CLIENT_ID: Log.e("KnotError", "Error: INVALID_CLIENT_ID - " + error.getErrorDescription()); break; case INTERNAL_ERROR: Log.e("KnotError", "Error: INTERNAL_ERROR - " + error.getErrorDescription()); break; case MERCHANT_ID_NOT_FOUND: Log.e("KnotError", "Error: MERCHANT_ID_NOT_FOUND - " +error.getErrorDescription()); break; case INVALID_CARD_NAME: Log.e("KnotError", "Error: INVALID_CARD_NAME - " +error.getErrorDescription()); break; case INVALID_CUSTOMER_NAME: Log.e("KnotError", "Error: INVALID_CUSTOMER_NAME - " +error.getErrorDescription()); break; case INVALID_LOGO_ID: Log.e("KnotError", "Error: INVALID_LOGO_ID - " +error.getErrorDescription()); break; case INVALID_LOCALE: Log.e("KnotError", "Error: INVALID_LOCALE - " +error.getErrorDescription()); break; } } ``` ```kotlin kotlin icon=k theme={"system"} fun onError(error: KnotError) { when (error) { KnotError.INVALID_SESSION -> Log.e("KnotError", "Error: INVALID_SESSION - ${error.errorDescription}") KnotError.EXPIRED_SESSION -> Log.e("KnotError", "Error: EXPIRED_SESSION - ${error.errorDescription}") KnotError.INVALID_CLIENT_ID -> Log.e("KnotError", "Error: INVALID_CLIENT_ID - ${error.errorDescription}") KnotError.INTERNAL_ERROR -> Log.e("KnotError", "Error: INTERNAL_ERROR - ${error.errorDescription}") KnotError.MERCHANT_ID_NOT_FOUND -> Log.e("KnotError", "Error: MERCHANT_ID_NOT_FOUND - ${error.errorDescription}") KnotError.INVALID_CARD_NAME -> Log.e("KnotError", "Error: INVALID_CARD_NAME - The card name is invalid.") KnotError.INVALID_CUSTOMER_NAME -> Log.e("KnotError", "Error: INVALID_CUSTOMER_NAME - The customer name is invalid.") KnotError.INVALID_LOGO_ID -> Log.e("KnotError", "Error: INVALID_LOGO_ID - ${error.errorDescription}") KnotError.INVALID_LOCALE -> Log.e("KnotError", "Error: INVALID_LOCALE - ${error.errorDescription}") } } ``` ### `onExit` This event is called when a user closes the SDK. ### `onEvent` This event is called when certain events occur in the SDK. With this callback, you will be able to understand how a user is progressing through their lifecycle of authenticating to a merchant. It emits a `KnotEvent` class as described below. ```java java icon=java theme={"system"} public void onEvent(KnotEvent knotEvent) { // Extract event details from the KnotEvent object // The type of event that occurred (e.g., "MERCHANT_CLICKED", "AUTHENTICATED") String eventType = knotEvent.getEvent(); // The product the event emitted from String product = knotEvent.getProduct(); // The environment the event emitted from String environment = knotEvent.getEnvironment(); // The merchant associated with the event (if applicable) String merchant = knotEvent.getMerchantName(); // The merchant Id associated with the event (if applicable) String merchantId = knotEvent.getMerchantId(); // The unique identifier for this specific event instance String taskId = knotEvent.getTaskId(); // Additional metadata a key-value map containing extra details related to the event Map metadata = knotEvent.getMetaData(); //Additional processing if applicable }; ``` ```kotlin kotlin icon=k theme={"system"} fun onEvent(knotEvent: KnotEvent) { // Extract event details from the KnotEvent object // The type of event that occurred (e.g., "MERCHANT_CLICKED", "AUTHENTICATED") val eventType: String = knotEvent.event // The product the event emitted from val product = knotEvent.getProduct() // The environment the event emitted from val environment = knotEvent.getEnvironment() // The merchant associated with the event val merchant: String = knotEvent.merchantName // The unique identifier for this specific event instance val taskId: String = knotEvent.taskId // Additional metadata - a key-value map containing extra details related to the event val metadata: Map = knotEvent.metaData // Additional processing if applicable } ``` Below is a list of all possible events emitted via the `KnotEvent.event` property. | Name | Description | | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | REFRESH\_SESSION\_REQUEST | Emitted when the session used to initialize the SDK needs to be refreshed. Use [Extend Session](/api-reference/sessions/extend-session) to extend the session before expiration occurs. | | MERCHANT\_CLICKED | Emitted when a user clicks on a merchant from the merchant list. | | LOGIN\_STARTED | Emitted when a user submits their credentials to login to the merchant. | | AUTHENTICATED | Emitted when a user successfully logs in to the merchant. | | OTP\_REQUIRED | Emitted when a user needs to enter an OTP code to login to the merchant. | | SECURITY\_QUESTIONS\_REQUIRED | Emitted when a user needs to enter answers to security questions to login to the merchant. | | APPROVAL\_REQUIRED | Emitted when a user needs to approve the login - often via a push notification or directly in the merchant's mobile app - to login to the merchant. | | ZIPCODE\_REQUIRED | Emitted when a user needs to enter their zip code to login to the merchant. | | DOB\_REQUIRED | Emitted when a user needs to verify their date of birth to login to the merchant. | | LICENSE\_REQUIRED | Emitted when a user needs to enter their drivers license to login to the merchant. | ## Other options ### Get current SDK version If you need to retrieve the current SDK version for your own use case, implement the following: ```java Java icon=java theme={"system"} String sdkVersion = Knot.getSdkVersion(); Log.d("Knot SDK Version", sdkVersion != null ? sdkVersion : "Unknown"); ``` ```kotlin Kotlin icon=k theme={"system"} val sdkVersion = Knot.getSdkVersion() Log.d("Knot SDK Version", sdkVersion ?: "Unknown") ``` ### Close the SDK If you need to explicitly close the SDK, use the below method, otherwise end users will naturally close the SDK as they interact with the interface. ```java java icon=java theme={"system"} Knot.close(); ``` ```kotlin kotlin icon=k theme={"system"} Knot.close() ``` ### Maintain cookies `domainUrls` is deprecated in version 3.0.0+ and will be removed in a future release. Knot clears cookies for security purposes. If your app relies on cookies, you can allowlist specific domains using the `domainUrls` configuration in `KnotConfiguration`. **This is uncommon.** ```java java icon=java theme={"system"} // The allowed domains for cookies to add as a parameter in KnotConfiguration String[] domainUrls = new String[]{ "https://example.com", "https://secure.example.com", "https://auth.example.com" }; ``` ```kotlin kotlin icon=k theme={"system"} // The allowed domains for cookies to add as a parameter in KnotConfiguration val domainUrls = arrayOf( "https://example.com", "https://secure.example.com", "https://auth.example.com" ) ``` # Flutter SDK Source: https://docs.knotapi.com/sdk/flutter Add the Knot Link Flutter SDK to your Dart project with pub, initialize a session, and handle events to link users' merchant accounts. ## Overview **SDK Updates**\ New versions of the SDK are released frequently, not only to add new features and address issues in the SDK, but also to continuously improve the conversion of merchant login flows. As a result, we strongly recommend that you frequently update your SDK version across any platforms where you invoke the SDK. The Knot Link SDK provides a seamless way for end users to link their merchant accounts to your app, serving as the foundation for Knot's merchant connectivity platform. It is a client-side integration, consisting of initializing & configuring the SDK and handling events. ## Installation You can install the Knot SDK using **pub package manager**. ```shell icon="terminal" Shell theme={"system"} flutter pub add knotapi_flutter ``` ### Import the SDK ```dart icon="dart-lang" Dart theme={"system"} import 'package:knotapi_flutter/knotapi_flutter.dart'; import 'package:knotapi_flutter/knotapi_configuration.dart'; ``` ## Initialization Your backend will create a session by calling [Create Session](/api-reference/sessions/create-session) and provide it to your frontend. To start a Knot session, you must first configure the session with a `KnotConfiguration` class. The configuration allows you to set the environment, product type, entry point, and other user experience configurations. It's expected that your integration with Knot will retrieve and pass a new session into the SDK on each initialization. ### Configure the session Use the `KnotConfiguration` and `CustomerConfiguration` classes to initialize the SDK with specific parameters. #### `KnotConfiguration` | Name | Type | Description | | ------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | sessionId | String | The session created by calling `/session/create` in your backend. | | clientId | String | Your organization's client ID. Note that this varies between `development` and `production` environments. | | environment | Environment | The desired environment (`development` or `production`). | | entryPoint | String? | **Optional.** The specific entry point from within the app where you are initializing the Knot SDK (e.g. `onboarding`). | | merchantIds | List\[int]? | **Optional.** A list of merchant ID(s) to display. We recommend providing 0 or 1 merchant ID depending on your desired user experience. | | useCategories | Bool? | **Optional.** Whether to display merchant categories and therefore group merchants into categories for discoverability. Default: `true`. | | useSearch | Bool? | **Optional.** Whether to display the search bar, enabling users to search for merchants. Default: `true`. | | domainUrls | List\[String]? | **Optional. Android only.** A set of domains for which Knot should explicitly not clear cookies. | | metadata | Map\ | **Optional.** Custom key-value pairs to include in [webhook](/webhooks#session-metadata) payloads. Maximum 10 keys with string values up to 500 characters each. | | locale | String? | **Optional.** A [BCP-47](https://developer.mozilla.org/en-US/docs/Glossary/BCP_47_language_tag) language tag to set the locale for the SDK. Currently supported: `en-US`, `es-US`, `en-CA`, and `fr-CA`. If not provided, defaults to `en-US`. | | product | Product? | **Optional. Ignored in version 2.0.0+.** The Knot product the session will inherit — the same as the type of session created (e.g. `card_switcher`, `transaction_link`). | #### `CustomerConfiguration` This class is entirely optional and infrequently used, typically only when you offer Knot for multiple, differently-named card programs in the same app. The Knot team will set pre-defined values of your choosing for each parameter that you can then subsequently pass into the SDK. Passing a value that is not pre-defined will result in an `onError` callback. To take advantage of this functionality, please contact the Knot team who will be happy to assist you. | Name | Type | Description | | :----------- | :----- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | cardName | String | **Optional.** The differentiated display name for the card product, used inside the Knot SDK (e.g. `Debit Card`, `Credit Card`). This value will override the default value `card`. | | customerName | String | **Optional.** The differentiated display name for the company, used both as a standalone and prepended to the `cardName` (e.g. `Smart Bank`, `Payment Corp`). This value will override your default customer name value. Only recommended if you issue cards under multiple brands. | | logoId | String | **Optional.** The differentiated logo for the company. This value will override your default logo. | See the following examples for how the parameters are used together in text inside the Knot SDK: `"Your [customerName] [cardName | card] was added."` ```dart icon="dart-lang" Dart theme={"system"} CustomerConfiguration customerConfiguration = CustomerConfiguration( cardName: "Card Name", // Your cardName customerName: "Customer Name", // Your customerName logoId: "LogoId" // Your logoID ); KnotConfiguration( sessionId: "session_12345", // Current session ID clientId: "client_67890", // Your client ID environment: Environment.production, // 'development' | 'production' merchantIds: [52], // Recommend 0 or 1 merchant IDs domainUrls: null, // Explicitly passing null useCategories: true, // Recommend true entryPoint: "onboarding", // Defined by you useSearch: true, // Recommend true customerConfiguration: customerConfiguration, // Optional metadata: { // Optional metadata for webhooks "reference_token": "your-token", "trace_id": "your-trace-id" }, locale: "es-US" // Optional BCP-47 language tag ) ``` ### Open the session To begin the flow, use the `open` method with a `KnotConfiguration` instance. ```dart icon="dart-lang" Dart theme={"system"} final _knot = KnotapiFlutter(); _knot.open(KnotConfiguration( sessionId: "INSERT_SESSION_ID", clientId: "INSERT_CLIENT_ID", environment: Environment.development, // or Environment.production )); ``` To test logging in to a merchant in the SDK, please reference a set of available test credentials for the CardSwitcher product [here](/card-switcher/testing) and the TransactionLink product [here](/transaction-link/testing). ## Single merchant flow You may decide to use [List Merchants](/api-reference/merchants/list-merchants) to retrieve a list of merchants, list them in your app, and then open the SDK with a single merchant. To do so, pass a merchant ID when [configuring the session](/sdk/flutter#configure-the-session) in the `KnotConfiguration`. More in [Retrieving & Listing Merchants](/link/retrieving-and-listing-merchants). The merchant ID is the same across all environments. Although available, we do not recommend that you provide a long list of merchants in order to remove a few, but rather "hide" certain merchants that you desire from your [Customer Dashboard](https://dashboard.knotapi.com). ## Entry points In your app's user experience, you may choose to integrate Knot in one or multiple places (e.g. from different tabs or screens). How users behave when interacting with Knot from each of these "entry points" may vary. It will be useful for you to be able to differentiate these groups of users by entry point in order to assess the value of each entry point. You can provide a value for the entry point in `KnotConfiguration.entryPoint` when [configuring the session](/sdk/flutter#configure-the-session). This value will be returned in the `AUTHENTICATED` webhook. ## Categories & search Users are presented with a list of merchants in the SDK (unless you provide a single merchant as described above). Accompanying the list is a set of categories and a search experience. Each of these components is visible to users by default (as set in Knot's backend). You can choose to remove either of them by setting `useCategories: false` and `useSearch: false` in `KnotConfiguration`. **This is not recommended**. ## Events To receive updates from the SDK, import the following: ```dart icon="dart-lang" Dart theme={"system"} import 'dart:async'; import 'package:knotapi_flutter/events.dart'; ``` And then implement the example code found below. ### `onSuccess` This event is called when a user successfully logged in to the merchant and their card was switched. It takes a single argument `KnotSuccess`. ```dart icon="dart-lang" Dart theme={"system"} ... StreamSubscription? _streamSuccess; void initState() { super.initState(); _streamSuccess = KnotapiFlutter.onSuccess.listen(_onSuccess); } void _onSuccess (KnotSuccess event) { String merchant = event.merchant; print("onSuccess - merchant: $merchant"); } ... ``` ### `onError` This event is called when an error occurs during SDK initialization and it takes a single argument `KnotError`. | errorCode | errorDescription | Debugging Steps | | ------------------------ | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | INVALID\_SESSION | The session is invalid. | Ensure `KnotConfiguration.environment` matches the environment the `sessionId` was created for (`development` or `production`). | | EXPIRED\_SESSION | The session has expired. | Sessions are valid for 30 minutes. It is best practice to ensure that you create a new session **every time** a user invokes the SDK using [Create Session](/api-reference/sessions/create-session). | | INVALID\_CLIENT\_ID | The client ID is invalid. | Verify that the value you are providing for `KnotConfiguration.clientId` is for the environment matching the value you are providing for `KnotConfiguration.environment` (i.e. `development` or `production`). If you provide your production `clientId` but set `environment: development`, you will experience this error. | | INTERNAL\_ERROR | An internal error occurred. | Simply retry invoking the SDK with a new `sessionId`. | | MERCHANT\_ID\_NOT\_FOUND | The merchant ID is required. | The `type` of `sessionId` you are providing on invocation of the SDK requires that you also provide a value in `KnotConfiguration.merchantIds` to ensure the user is directed to a specific merchant’s login flow in the SDK. You can retrieve a list of merchant IDs (the same in all environments) in [List Merchants](/api-reference/merchants/list-merchants). | | INVALID\_CARD\_NAME | The card name is invalid. | The value you are providing for `customerConfiguration.cardName` must exactly match an allowlisted value for your organization. Please reach out to the Knot team for access to this allowlist. | | INVALID\_CUSTOMER\_NAME | The customer name is invalid. | The value you are providing for `customerConfiguration.customerName` must exactly match an allowlisted value for your organization. Please reach out to the Knot team for access to this allowlist. | | INVALID\_LOGO\_ID | The logo ID is invalid. | The value you are providing for `customerConfiguration.logoId` must exactly match an allowlisted value for your organization. Please reach out to the Knot team for access to this allowlist. | | INVALID\_LOCALE | The locale is invalid. | The value you are providing for `KnotConfiguration.locale` must be a valid [BCP-47](https://developer.mozilla.org/en-US/docs/Glossary/BCP_47_language_tag) language tag. Currently supported: `en-US`, `es-US`, `en-CA`, and `fr-CA`. | Sessions are valid for 30 minutes. If a session expires while the SDK is open, the SDK will emit an expired session error via `onError` and automatically close. To provide a seamless experience, handle the `REFRESH_SESSION_REQUEST` event via the `onEvent` callback to proactively extend the session using [Extend Session](/api-reference/sessions/extend-session) before expiration occurs. ```dart icon="dart-lang" Dart theme={"system"} ... StreamSubscription? _streamError; void initState() { super.initState(); _streamError = KnotapiFlutter.onError.listen(_onError); } void _onError (KnotError event) { String errorDescription = event.errorDescription; String errorCode = event.errorCode; print("onError - errorDescription: $errorDescription, errorCode: $errorCode"); } ... ``` ### `onExit` This event is called when a user closes the SDK. It takes a single argument `KnotExit`. ```dart icon="dart-lang" Dart theme={"system"} ... StreamSubscription? _streamExit; void initState() { super.initState(); _streamExit = KnotapiFlutter.onExit.listen(_onExit); } void _onExit (KnotExit event) { // User exited the SDK } ... ``` ### `onEvent` This event is called when certain events occur in the SDK. With this callback, you will be able to understand how a user is progressing through their lifecycle of authenticating to a merchant. It takes a single argument `KnotEvent`. ```dart icon="dart-lang" Dart theme={"system"} ... StreamSubscription? _streamEvent; void initState() { super.initState(); _streamEvent = KnotapiFlutter.onEvent.listen(_onEvent); } void _onEvent (KnotEvent event) { String environment = event.environment; String event = event.event; String? merchant = event.merchant; String? merchantId = event.merchantId; String? taskId = event.taskId; Map metaData = event.metaData; print("onEvent - environment: $environment, event: $event, merchant: $merchant, merchantId: $merchantId metaData: $metaData"); } ... ``` The following list contains all possible events emitted in the `KnotEvent.event` property. | Name | Description | | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | REFRESH\_SESSION\_REQUEST | Emitted when the session used to initialize the SDK needs to be refreshed. Use [Extend Session](/api-reference/sessions/extend-session) to extend the session before expiration occurs. | | MERCHANT\_CLICKED | Emitted when a user clicks on a merchant from the merchant list. | | LOGIN\_STARTED | Emitted when a user submits their credentials to login to the merchant. | | AUTHENTICATED | Emitted when a user successfully logs in to the merchant. | | OTP\_REQUIRED | Emitted when a user needs to enter an OTP code to login to the merchant. | | SECURITY\_QUESTIONS\_REQUIRED | Emitted when a user needs to enter answers to security questions to login to the merchant. | | APPROVAL\_REQUIRED | Emitted when a user needs to approve the login - often via a push notification or directly in the merchant's mobile app - to login to the merchant. | | ZIPCODE\_REQUIRED | Emitted when a user needs to enter their zip code to login to the merchant. | | DOB\_REQUIRED | Emitted when a user needs to verify their date of birth to login to the merchant. | | LICENSE\_REQUIRED | Emitted when a user needs to enter their drivers license to login to the merchant. | ## Other options ### Get current SDK version If you need to retrieve the current SDK version for your own use case, implement the following: ```dart icon="dart-lang" Dart theme={"system"} final _knotapiFlutterPlugin = KnotapiFlutter(); String sdkVersion = await _knotapiFlutterPlugin.getSdkVersion(); print('Knot SDK Version: ${sdkVersion ?? "Unknown"}'); ``` ### Close the SDK If you need to explicitly close the SDK, use the below method, otherwise end users will naturally close the SDK as they interact with the interface. ```dart icon="dart-lang" Dart theme={"system"} _knot.close(); ``` ### Maintain cookies Knot clears cookies for security purposes. If your app relies on cookies, you can allowlist specific domains using the `domainUrls` configuration in `KnotConfiguration`. **This is uncommon.** ```dart icon="dart-lang" Dart theme={"system"} // The allowed domains for cookies to add as a parameter in KnotConfiguration KnotConfiguration( sessionId: "695ce724-7f61-40f1-a410-cb0c0fcf9b7f", clientId: "3f4acb6b-a8c9-47bc-820c-b0eaf24ee771", environment: Environment.development, merchantIds: [17], useCategories: false, useSearch: false, domainUrls: ["https://example.com", "https://secure.example.com"], locale: "es-US" // Optional BCP-47 language tag ) ``` # Introduction Source: https://docs.knotapi.com/sdk/introduction Overview of Knot SDKs for iOS, Android, React Native, Flutter, and Web platforms. **SDK Updates**\ New versions of the SDK are released frequently, not only to add new features and address issues in the SDK, but also to continuously improve the conversion of merchant login flows. As a result, we strongly recommend that you frequently update your SDK version across any platforms where you invoke the SDK. ## Refreshing Sessions To maintain a continuous and secure connection to the SDK while a user is interacting with it, it's important to maintain a fresh session. Sessions expire after 30 minutes, therefore in order to maintain an active session, call [Extend Session](/api-reference/sessions/extend-session) upon receiving the `REFRESH_SESSION_REQUEST` event in the `onEvent` callback. This event is sent 5 seconds prior to the session expiration. Note that it is best practice to create a new session using [Create Session](/api-reference/sessions/create-session) each time the SDK is invoked. # iOS SDK Source: https://docs.knotapi.com/sdk/ios Install the Knot Link iOS SDK with CocoaPods or Swift Package Manager, initialize a session, and handle events to link users' merchant accounts. ## Overview **SDK Updates**\ New versions of the SDK are released frequently, not only to add new features and address issues in the SDK, but also to continuously improve the conversion of merchant login flows. As a result, we strongly recommend that you frequently update your SDK version across any platforms where you invoke the SDK. The Knot Link SDK provides a seamless way for end users to link their merchant accounts to your iOS app, serving as the foundation for Knot's merchant connectivity platform. It is a client-side integration, consisting of initializing & configuring the SDK and handling events. ## Installation You can install the Knot SDK using **CocoaPods** or **Swift Package Manager (SPM)**. ### Using CocoaPods If you haven't already, install the latest version of CocoaPods. If you don't have an existing Podfile, run the following command to create one: ```ruby theme={"system"} pod init ``` Add the below line to your Podfile in your iOS project directory: ``` pod 'KnotAPI' ``` ### Using Swift Package Manager (SPM) To install the Knot SDK using Swift Package Manager, ensure you're using Swift version 5.3 or later. In your Xcode project, go to File, Add Packages. In the top right corner of the dialog box, you'll see a search bar. Enter the Knot package URL: [https://github.com/millionscard/knot-api-ios](https://github.com/millionscard/knot-api-ios). From the results, choose the `knot-api-ios` package. We recommend opting for Up to Next Major Version. Choose the project you want to integrate with KnotAPI and click on Add Package. Confirm that the KnotAPI Swift package was added as a package dependency to your project successfully. ## Initialization Your backend will create a session by calling [Create Session](/api-reference/sessions/create-session) and provide it to your frontend. To start a Knot session, you must first configure the session with a `KnotConfiguration` class. The configuration allows you to set the environment, entry point, and other user experience configurations. It's expected that your integration with Knot will retrieve and pass a new session into the SDK on each initialization. ### Configure the session Use the `KnotConfiguration` and `CustomerConfiguration` classes to initialize the SDK with specific parameters. #### `KnotConfiguration` | Name | Type | Description | | :------------ | :----------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | sessionId | String | The session created by calling `/session/create` in your backend. | | clientId | String | Your organization's client ID. Note that this varies between `development` and `production` environments. | | environment | Environment | The desired environment (`development` or `production`). | | entryPoint | String? | **Optional.** The specific entry point from within your app where you are initializing the Knot SDK (e.g. `onboarding`). | | merchantIds | \[Int]? | **Optional.** A list of merchant ID(s) to display. We recommend providing 0 or 1 merchant ID depending on your desired user experience. | | useCategories | Bool | **Optional.** Whether to display merchant categories and therefore group merchants into categories for discoverability. Default: `true`. | | useSearch | Bool | **Optional.** Whether to display the search bar, enabling users to search for merchants. Default: `true`. | | metadata | \[String: String]? | **Optional.** Custom key-value pairs to include in [webhook](/webhooks#session-metadata) payloads. Maximum 10 keys with string values up to 500 characters each. | | locale | String? | **Optional.** A [BCP-47](https://developer.mozilla.org/en-US/docs/Glossary/BCP_47_language_tag) language tag to set the locale for the SDK. Currently supported: `en-US`, `es-US`, `en-CA`, and `fr-CA`. If not provided, defaults to `en-US`. | | product | Product | **Optional. Ignored in version 1.0.11+.** The Knot product the session will inherit — the same as the type of session created (e.g. `card_switcher`, `transaction_link`). | #### `CustomerConfiguration` This class is entirely optional and infrequently used, typically only when you offer Knot for multiple, differently-named card programs in the same app. The Knot team will set pre-defined values of your choosing for each parameter that you can then subsequently pass into the SDK. Passing a value that is not pre-defined will result in an `onError` callback. To take advantage of this functionality, please contact the Knot team who will be happy to assist you. | Name | Type | Description | | :----------- | :----- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | cardName | String | **Optional.** The differentiated display name for the card product, used inside the Knot SDK (e.g. `Debit Card`, `Credit Card`). This value will override the default value `card`. | | customerName | String | **Optional.** The differentiated display name for the company, used both as a standalone and prepended to the `cardName` (e.g. `Smart Bank`, `Payment Corp`). This value will override your default customer name value. Only recommended if you issue cards under multiple brands. | | logoId | String | **Optional.** The differentiated logo for the company. This value will override your default logo. | See the following examples for how the parameters are used together in text inside the Knot SDK: `"Your [customerName] [cardName | card] was added."` ```swift Swift icon=swift theme={"system"} let customerConfiguration = CustomerConfiguration( cardName: "Card Name", // Your cardName customerName: "Customer Name", // Your customerName logoId: "LogoId" // Your logoID ) let config = KnotConfiguration( sessionId: "session_12345", // Current session ID clientId: "client_67890", // Your client ID environment: .production, // 'development' | 'production' merchantIds: [52], // Recommend 0 or 1 merchant IDs useCategories: true, // Recommend true useSearch: true, // Recommend true domainUrls: nil, // Explicitly passing nil entryPoint: "onboarding", // Defined by you metadata: [ // Optional metadata for webhooks "reference_token": "your-token", "trace_id": "your-trace-id" ], customerConfiguration: customerConfiguration, locale: "es-US" // Optional BCP-47 language tag ) ``` ```Objective-C Objective-C icon=c theme={"system"} CustomerConfiguration *customerConfiguration = [[CustomerConfiguration alloc] initWithCardName:@"Card Name" customerName:@"Customer Name" logoId:@"LogoId"]; NSDictionary *metadata = @{ @"reference_token": @"your-token", @"trace_id": @"your-trace-id" }; KnotConfiguration *config = [[KnotConfiguration alloc] initWithSessionId:@"session_12345" clientId:@"client_67890" environment:EnvironmentDevelopment // enum case entryPoint:@"onboarding" product:ProductCard_switcher // enum case useCategories:YES useSearch:YES merchantIds:@[@52] metadata:metadata customerConfiguration:customerConfiguration locale:@"es-US"]; // Optional BCP-47 language tag ``` ### Open the session To begin the flow, use the `open` method with a `KnotConfiguration` instance and an optional `KnotEventDelegate`. ```swift Swift icon=swift theme={"system"} Knot.open(configuration: myConfiguration, delegate: self) ``` ```Objective-C Objective-C icon=c theme={"system"} [Knot openWithConfiguration:config delegate:self]; ``` ```swift Swift icon=swift theme={"system"} let customerConfiguration = CustomerConfiguration( cardName: "Card Name", customerName: "Customer Name", logoId: "LogoId" ) let config = KnotConfiguration( sessionId: "session_12345", clientId: "client_67890", environment: .development, entryPoint: "onboarding", useCategories: true, useSearch: true, merchantIds: [52], metadata: ["reference_token": "your-token"], customerConfiguration: customerConfiguration, locale: "es-US" // Optional BCP-47 language tag ) Knot.open(configuration: config, delegate: self) ``` To ensure seamless integration and avoid any UI issues when embedding the view controller into your UI hierarchy, it's crucial to properly position the view controller within the safe area of your application's interface. This practice helps maintain the accessibility and visibility of the controller across different devices and screen sizes. This is especially important considering the presence of notches, status bars, and navigation elements that might obscure the content. ```swift Swift icon=swift theme={"system"} class MyViewController : UIViewController { func configureKnot() { let controller = Knot.createKnotViewController(configuration: session, delegate: delegate) view.addSubview(controller.view) addChild(controller) } ... } ``` ```Objective-C Objective-C icon=c theme={"system"} - (void)configureKnot { // 2) If self implements KnotEventDelegate, you can pass `self`; otherwise pass another object conforming to the protocol UIViewController *knotViewController = [Knot createKnotViewControllerWithConfiguration:config delegate:self]; // 3) Add knotViewController as a child [self addChildViewController:knotViewController]; [self.view addSubview:knotViewController.view]; knotViewController.view.frame = self.view.bounds; // optionally size it [knotViewController didMoveToParentViewController:self]; } ``` To test logging in to a merchant in the SDK, please reference a set of available test credentials for the CardSwitcher product [here](/card-switcher/testing) and Transaction Link product [here](/transaction-link/testing). ## Single merchant flow You may decide to use [List Merchants](/api-reference/merchants/list-merchants) to retrieve a list of merchants, list them in your app, and then open the SDK with a single merchant. To do so, pass a merchant ID when [configuring the session](/sdk/ios#configure-the-session) in the `KnotConfiguration`. More in [Retrieving & Listing Merchants](/link/retrieving-and-listing-merchants). The merchant ID is the same across all environments. Although available, we do not recommend that you provide a long list of merchants in order to remove a few, but rather "hide" certain merchants that you desire from your [Customer Dashboard](https://dashboard.knotapi.com). ## Entry points In your app's user experience, you may choose to integrate Knot in one or multiple places (e.g. from different tabs or screens). How users behave when interacting with Knot from each of these "entry points" may vary. It will be useful for you to be able to differentiate these groups of users by entry point in order to assess the conversion and value of each entry point. You can provide a value for the entry point in `KnotConfiguration.entryPoint` when [configuring the session](/sdk/ios#configure-the-session). This value will be returned in the `AUTHENTICATED` webhook. ## Categories & search Users are presented with a list of merchants in the SDK (unless you provide a single merchant as described above). Accompanying the list is a set of categories and a search experience. Each of these components is visible to users by default (as set in Knot's backend). You can choose to remove either of them by setting `useCategories: false` and `useSearch: false` in `KnotConfiguration`. **This is not recommended**. ## Events To receive updates from the SDK, implement `KnotEventDelegate` in your class. ```swift Swift icon=swift theme={"system"} class MyViewController: UIViewController, KnotEventDelegate { func onSuccess(merchant: String) { print("Merchant \(merchant) successfully authenticated.") } func onError(error: KnotError) { print("Error occurred: \(error.errorDescription)") } func onEvent(event: KnotEvent) { print("Received event: \(event.event)") } func onExit() { print("Knot session exited.") } } ``` ```Objective-C Objective-C icon=c theme={"system"} // Make sure to import the generated Swift header, e.g.: #import // or @import KnotAPI; @interface MyViewController () @end @implementation MyViewController - (void)viewDidLoad { [super viewDidLoad]; // Your setup code here... } // MARK: - KnotEventDelegate Methods - (void)onSuccessWithMerchant:(NSString *)merchant { NSLog(@"Merchant %@ successfully authenticated.", merchant); } - (void)onErrorWithError:(KnotError)error { // Because KnotError is an NS_ENUM (NSInteger), it does not expose the Swift-only // `errorDescription` property. You can switch on the enum or do something similar: NSString *errorDesc; switch (error) { case KnotErrorInvalidSession: errorDesc = @"Invalid session."; break; case KnotErrorExpiredSession: errorDesc = @"Session has expired."; break; case KnotErrorInvalidClientId: errorDesc = @"Invalid client ID."; break; case KnotErrorInternalError: errorDesc = @"An internal error occurred."; break; } NSLog(@"Error occurred: %@", errorDesc); } - (void)onEventWithEvent:(KnotEvent *)event { // event.event is a public NSString property on KnotEvent NSLog(@"Received event: %@", event.event); } - (void)onExit { NSLog(@"Knot session exited."); } ``` ### `onSuccess` This event is called when a user successfully logged in to the merchant and their card was switched. It takes a single string argument containing the name of the merchant. ### `onError` This event is called when an error occurs during SDK initialization and emits a `KnotError` enum as described below. | Error Case | Description | Debugging Steps | | -------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | .invalidSession | The session is invalid. | Ensure `KnotConfiguration.environment` matches the environment the `sessionId` was created for (`development` or `production`). | | .expiredSession | The session has expired. | Sessions are valid for 30 minutes. It is best practice to ensure that you create a new session **every time** a user invokes the SDK using [Create Session](/api-reference/sessions/create-session). | | .invalidClientId | The client ID is invalid. | Verify that the value you are providing for `KnotConfiguration.clientId` is for the environment matching the value you are providing for `KnotConfiguration.environment` (i.e. `development` or `production`). If you provide your production `clientId` but set `environment: development`, you will experience this error. | | .internalError | An internal error occurred. | Simply retry invoking the SDK with a new `sessionId`. | | .merchantIdNotFound | The merchant ID is required. | The `type` of `sessionId` you are providing on invocation of the SDK requires that you also provide a value in `KnotConfiguration.merchantIds` to ensure the user is directed to a specific merchant’s login flow in the SDK. You can retrieve a list of merchant IDs (the same in all environments) in [List Merchants](/api-reference/merchants/list-merchants). | | .invalidCardName | The card name is invalid. | The value you are providing for `customerConfiguration.cardName` must exactly match an allowlisted value for your organization. Please reach out to the Knot team for access to this allowlist. | | .invalidCustomerName | The customer name is invalid. | The value you are providing for `customerConfiguration.customerName` must exactly match an allowlisted value for your organization. Please reach out to the Knot team for access to this allowlist. | | .invalidLogoId | The logo ID is invalid. | The value you are providing for `customerConfiguration.logoId` must exactly match an allowlisted value for your organization. Please reach out to the Knot team for access to this allowlist. | | .invalidLocale | The locale is invalid. | The value you are providing for `KnotConfiguration.locale` must be a valid [BCP-47](https://developer.mozilla.org/en-US/docs/Glossary/BCP_47_language_tag) language tag. Currently supported: `en-US`, `es-US`, `en-CA`, and `fr-CA`. | Sessions are valid for 30 minutes. If a session expires while the SDK is open, the SDK will emit an expired session error via `onError` and automatically close. To provide a seamless experience, handle the `REFRESH_SESSION_REQUEST` event via the `onEvent` callback to proactively extend the session using [Extend Session](/api-reference/sessions/extend-session) before expiration occurs. ```swift Swift icon=swift theme={"system"} func onError(error: KnotError) { switch error { case .invalidSession: print("Error: \(error.errorDescription)") case .expiredSession: print("Error: \(error.errorDescription)") case .invalidClientId: print("Error: \(error.errorDescription)") case .internalError: print("Error: \(error.errorDescription)") case .merchantIdNotFound: print("Error: \(error.errorDescription)") case .invalidCardName: print("Error: \(error.errorDescription)") case .invalidCustomerName: print("Error: \(error.errorDescription)") case .invalidLogoId: print("Error: \(error.errorDescription)") case .invalidLocale: print("Error: \(error.errorDescription)") } } ``` ```Objective-C Objective-C icon=c theme={"system"} - (void)onErrorWithError:(KnotError)error { // Because KnotError is an NS_ENUM (NSInteger), it does not expose the Swift-only // `errorDescription` property. You can switch on the enum or do something similar: NSString *errorDesc; switch (error) { case KnotErrorInvalidSession: errorDesc = @"The session is invalid."; break; case KnotErrorExpiredSession: errorDesc = @"The session has expired."; break; case KnotErrorInvalidClientId: errorDesc = @"The client ID is invalid."; break; case KnotErrorInternalError: errorDesc = @"An internal error occurred."; break; case KnotErrorMerchantIdNotFound: errorDesc = @"The merchant ID is required when product type = transaction_link."; break; case KnotErrorInvalidCardName: errorDesc = @"The card name is invalid."; break; case KnotErrorInvalidCustomerName: errorDesc = @"The customer name is invalid."; break; case KnotErrorInvalidLogoId: errorDesc = @"The logo ID is invalid."; break; case KnotErrorInvalidLocale: errorDesc = @"The locale is invalid."; break; } NSLog(@"Error occurred: %@", errorDesc); } ``` ### `onExit` This event is called when a user closes the SDK. ### `onEvent` This event is called when certain events occur in the SDK. With this callback, you will be able to understand how a user is progressing through their lifecycle of authenticating to a merchant. It emits a `KnotEvent` class as described below. ```swift Swift icon=swift theme={"system"} /// Represents a Knot event received during user interaction within the Knot SDK. @objc public class KnotEvent { /// The environment the event emitted from @objc public let environment: Environment /// The product the event emitted from @objc public let product: Product /// The primary event code. @objc public let event: String /// The merchant associated with the event (if applicable). @objc public let merchant: String? /// The merchant ID associated with the event (if applicable). @objc public let merchantId: String? /// Additional metadata related to the event, stored as an Objective-C compatible dictionary. @objc public let metaData: NSDictionary /// The identifier associated with this event. @objc public let taskId: String? ... } ``` The following list contains all possible events emitted in the `KnotEvent.event` property. | Name | Description | | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | REFRESH\_SESSION\_REQUEST | Emitted when the session used to initialize the SDK needs to be refreshed. Use [Extend Session](/api-reference/sessions/extend-session) to extend the session before expiration occurs. | | MERCHANT\_CLICKED | Emitted when a user clicks on a merchant from the merchant list. | | LOGIN\_STARTED | Emitted when a user submits their credentials to login to the merchant. | | AUTHENTICATED | Emitted when a user successfully logs in to the merchant. | | OTP\_REQUIRED | Emitted when a user needs to enter an OTP code to login to the merchant. | | SECURITY\_QUESTIONS\_REQUIRED | Emitted when a user needs to enter answers to security questions to login to the merchant. | | APPROVAL\_REQUIRED | Emitted when a user needs to approve the login - often via a push notification or directly in the merchant's mobile app - to login to the merchant. | | ZIPCODE\_REQUIRED | Emitted when a user needs to enter their zip code to login to the merchant. | | DOB\_REQUIRED | Emitted when a user needs to verify their date of birth to login to the merchant. | | LICENSE\_REQUIRED | Emitted when a user needs to enter their drivers license to login to the merchant. | ## Other options ### Get current SDK version If you need to retrieve the current SDK version for your own use case, implement the following: ```swift Swift icon=swift theme={"system"} let sdkVersion = Knot.SDKVersion() print("Knot SDK Version: \(sdkVersion ?? "Unknown")") ``` ```Objective-C Objective-C icon=c theme={"system"} NSString *sdkVersion = [Knot SDKVersion]; NSLog(@"Knot SDK Version: %@", sdkVersion ?: @"Unknown"); ``` ### Close the SDK If you need to explicitly close the SDK, use the below method, otherwise end users will naturally close the SDK as they interact with the interface. ```swift Swift icon=swift theme={"system"} Knot.close() ``` ```Objective-C Objective-C icon=c theme={"system"} [Knot close]; ``` # Android Version 2.0+ Source: https://docs.knotapi.com/sdk/migration-guides/android/android-2-0 ## Overview To simplify your application logic and integration with the Knot Android SDK, Version 2.0 includes a number of breaking changes that may affect the way your integration works or behaves. The new version includes a number of significant improvements: 1. Enhanced speed and stability in loading merchant flows. 2. More streamlined initialization of the SDK. 3. Simplified event handling, more informative event messaging, and uniform naming conventions for easier debugging. 4. Improved maintainability and foundations for new feature compatibility. ## Breaking Changes Configuring and opening the Knot SDK has changed significantly in Android Version 2.0 and requires some refactoring in order to initialize the SDK with a session. Errors are now encapsulated in a `KnotError` object which provides an enumerated value to debug with. ### Session Initialization **Changes** * `CardOnFileSwitcher.getInstance(); `and `Configuration(environment, clientId, sessionId);` are replaced with a more flexible `KnotConfiguration`. * The new interface allows configuration of additional properties such as `useCategories`, `useSearch`, and `merchantIds`. * The `open` action now requires a `KnotConfiguration` and an optional `KnotEventDelegate` which is now `Knot.open(context, knotConfiguration, knotEventDelegate)`. **Before** ```java java theme={"system"} CardOnFileSwitcher cardOnFileSwitcher = CardOnFileSwitcher.getInstance(); Configuration switcherConfig = new Configuration(environment, clientId, sessionId); cardOnFileSwitcher.setMerchantIds(new int[]{44}); cardOnFileSwitcher.setUseCategories(true); cardOnFileSwitcher.setUseSearch(true); Options options = new Options(); String[] domainUrls = {"https://domain1.com", "https://domain2.com", ....}; options.setDomainUrls(domainUrls); cardOnFileSwitcher.init(context, switcherConfig, options, onSessionEventListener); cardOnFileSwitcher.openCardOnFileSwitcher("Onboarding"); ``` ``` console.log("Hello World"); ``` ```kotlin kotlin theme={"system"} val cardOnFileSwitcher = CardOnFileSwitcher.getInstance() val switcherConfig = Configuration(environment, clientId, sessionId) cardOnFileSwitcher.setMerchantIds(intArrayOf(44)) cardOnFileSwitcher.setUseCategories(true) cardOnFileSwitcher.setUseSearch(true) val options = Options() val domainUrls = arrayOf("https://domain1.com", "https://domain2.com") options.setDomainUrls(domainUrls) cardOnFileSwitcher.init(context, switcherConfig, options, onSessionEventListener) cardOnFileSwitcher.openCardOnFileSwitcher("Onboarding") ``` **After** ```java java theme={"system"} KnotConfiguration config = new KnotConfiguration( "session_12345", // sessionId "client_67890", // clientId Environment.production, // environment Knot.Product.card_switcher, // product new int[]{101, 102, 103}, // merchantIds true, // useCategories true, // useSearch new String[]{"https://example.com"}, // domainUrls "onboarding" // entryPoint ); KnotEventDelegate knotEventDelegate = new KnotEventDelegate() { @Override public void onSuccess(String merchant) { // Handle successful operation } @Override public void onError(KnotError knotError) { // Handle error operation using knotError } @Override public void onExit() { // Handle SDK exit event } @Override public void onEvent(KnotEvent knotEvent) { // Extract and process event details from KnotEvent } }; Knot.open(context, config, knotEventDelegate); ``` ```kotlin kotlin theme={"system"} val config = KnotConfiguration( "session_12345", // sessionId "client_67890", // clientId Environment.PRODUCTION, // environment Knot.Product.card_switcher, // product intArrayOf(101, 102, 103), // merchantIds true, // useCategories true, // useSearch arrayOf("https://example.com"), // domainUrls "onboarding" // entryPoint ) KnotEventDelegate knotEventDelegate = new KnotEventDelegate() { @Override public void onSuccess(String merchant) { // Handle successful operation } @Override public void onError(KnotError knotError) { // Handle error operation using knotError } @Override public void onExit() { // Handle SDK exit event } @Override public void onEvent(KnotEvent knotEvent) { // Extract and process event details from KnotEvent } }; Knot.open(context, config, knotEventDelegate) ``` ### Event Handling **Changes** * Event handling is now managed through `KnotEventDelegate` instead of closures. * Events like `onSuccess`, `onError`, and `onExit` are now **explicit methods** inside a delegate. * The `onEvent` method introduces the `KnotEvent` object to better handle Knot emitted events. * The `KnotError` type provides improved error descriptions. * The `sendCard` parameter is deprecated and its functionality incorporated into the `metaData` dictionary within `KnotEvent` when `KnotEvent.event` equals `AUTHENTICATED`. **Before** ```java java theme={"system"} cardOnFileSwitcher.setOnSessionEventListener(new OnSessionEventListener() { @Override public void onSuccess(String merchant) { Log.d("onSuccess", merchant); } @Override public void onError(String errorCode, String errorMessage) { Log.d("onError", errorCode + " " + errorMessage); } @Override public void onExit() { Log.d("onExit", "exit"); } @Override public void onEvent(String eventName, String merchantName, String taskId) { Log.d("onEvent", eventName + " " + merchantName + " " + taskId); } }); ``` ``` cardOnFileSwitcher.setOnSessionEventListener(object : OnSessionEventListener { override fun onSuccess(merchant: String) { Log.d("onSuccess", merchant) } override fun onError(errorCode: String, errorMessage: String) { Log.d("onError", "$errorCode $errorMessage") } override fun onExit() { Log.d("onExit", "exit") } override fun onEvent(eventName: String, merchantName: String, taskId: String) { Log.d("onEvent", "$eventName $merchantName $taskId") } }) ``` **After** ```java java theme={"system"} KnotEventDelegate knotEventDelegate = new KnotEventDelegate() { @Override public void onSuccess(String merchant) { // Handle successful operation } @Override public void onError(KnotError knotError) { // Handle error operation using knotError } @Override public void onExit() { // Handle SDK exit event } @Override public void onEvent(KnotEvent knotEvent) { // Extract and process event details from KnotEvent } }; ``` ```kotlin kotlin theme={"system"} val eventDelegate = object : KnotEventDelegate { override fun onSuccess(merchant: String) { // Handle successful operation } override fun onError(knotError: KnotError) { // Handle error operation using knotError } override fun onExit() { // Handle SDK exit event } override fun onEvent(knotEvent: KnotEvent) { // Extract and process event details from KnotEvent } } ``` **SendCard** Most apps do not use the explicit `sendCard` method, as it is rarely applicable to the integration with the Knot SDK. The `sendCard` parameter has been deprecated and its functionality is incorporated into the `metaData` object within `KnotEvent` when the `KnotEvent.event` equals `AUTHENTICATED`. This change enhances flexibility by allowing additional contextual data to be included in events without requiring separate parameters. Previously, `sendCard` was accessed as a standalone value, but now you can retrieve it from the `metaData` object in the event callback. This approach ensures better extensibility and consistency across different event types. To access the `sendCard` value, simply extract it from the event’s `metaData` object. ```java java theme={"system"} @Override public void onEvent(KnotEvent knotEvent) { // Extract the metadata map from the KnotEvent Map metaData = knotEvent.getMetaData(); // Check if the metadata contains a sendCard flag if (knotEvent.getEvent().equalsIgnoreCase("AUTHENTICATED") && metaData != null && metaData.containsKey("sendCard")) { Boolean sendCard = (Boolean) metaData.get("sendCard"); Log.d("KnotEvent", "sendCard: " + sendCard); } } ``` ```kotlin kotlin theme={"system"} override fun onEvent(knotEvent: KnotEvent) { val metaData = knotEvent.metaData if (knotEvent.event.equals("AUTHENTICATED", ignoreCase = true) && metaData != null && metaData.containsKey("sendCard")) { val sendCard = metaData["sendCard"] as? Boolean Log.d("KnotEvent", "sendCard: $sendCard") } } ``` **Event Names** The SDK now maps raw event names to standardized event names for easier handling. | Event Name Prior to 2.0 | 2.0 Event Name | | :-------------------------- | :---------------------------- | | refresh session request | REFRESH\_SESSION\_REQUEST | | merchant clicked | MERCHANT\_CLICKED | | login started | LOGIN\_STARTED | | authenticated | AUTHENTICATED | | otp required | OTP\_REQUIRED | | security questions required | SECURITY\_QUESTIONS\_REQUIRED | | approval required | APPROVAL\_REQUIRED | ### Error Handling Error handling has been improved with more structured and meaningful error messages. **Changes** * Errors are now encapsulated in the `KnotError` enum. * Each error has a **human-readable description** (`errorDescription`) and a **unique error code** (`errorCode`). * Improved clarity and consistency across error messages. **Before** ```java java theme={"system"} @Override public void onError(String errorCode, String errorMessage) { Log.d("onError", errorCode + " " + errorMessage); } ``` ```kotlin kotlin theme={"system"} override fun onError(error: String?, errorMessage: String?) { Log.d("onError", "$error $errorMessage") } ``` **After** ```swift java theme={"system"} public void onError(KnotError error) { switch (error) { case INVALID_SESSION: Log.e("KnotError", "Error: INVALID_SESSION - " + error.getErrorDescription()); break; case EXPIRED_SESSION: Log.e("KnotError", "Error: EXPIRED_SESSION - " + error.getErrorDescription()); break; case INVALID_CLIENT_ID: Log.e("KnotError", "Error: INVALID_CLIENT_ID - " + error.getErrorDescription()); break; case INTERNAL_ERROR: Log.e("KnotError", "Error: INTERNAL_ERROR - " + error.getErrorDescription()); break; } } ``` ```kotlin kotlin theme={"system"} fun onError(error: KnotError) { when (error) { KnotError.INVALID_SESSION -> Log.e("KnotError", "Error: INVALID_SESSION - ${error.errorDescription}") KnotError.EXPIRED_SESSION -> Log.e("KnotError", "Error: EXPIRED_SESSION - ${error.errorDescription}") KnotError.INVALID_CLIENT_ID -> Log.e("KnotError", "Error: INVALID_CLIENT_ID - ${error.errorDescription}") KnotError.INTERNAL_ERROR -> Log.e("KnotError", "Error: INTERNAL_ERROR - ${error.errorDescription}") } } ``` **Error Types** The Knot SDK provides predefined error cases for you to handle based on your own needs. | Error Case | Description | | :-------------------- | :-------------------------- | | *INVALID\_SESSION* | The session is invalid. | | *EXPIRED\_SESSION* | The session has expired. | | *INVALID\_CLIENT\_ID* | The client ID is invalid. | | *INTERNAL\_ERROR* | An internal error occurred. | ### Closing the SDK Most apps do not use the explicit `close` method, as it is infrequently applicable to the integration with the Knot SDK. **Changes** * Closing the SDK is now statically accessed via `Knot.close()` as opposed to being bound to the session object. **Before** ```java java theme={"system"} cardOnFileSwitcher.closeCardOnFileSwitcher(); ``` ```kotlin kotlin theme={"system"} cardOnFileSwitcher.closeCardOnFileSwitcher() ``` **After** ```java java theme={"system"} Knot.close(); ``` ```kotlin kotlin theme={"system"} Knot.close() ``` # Flutter Version 1.0+ Source: https://docs.knotapi.com/sdk/migration-guides/flutter/flutter-1-0 ## Overview To simplify your application logic and integration with the Knot Flutter SDK, Version 1.0 includes a number of breaking changes that may affect the way your integration works or behaves. The new version includes a number of significant improvements: 1. Enhanced speed and stability in loading merchant flows. 2. More streamlined initialization of the SDK. 3. Simplified event handling, more informative event messaging, and uniform naming conventions for easier debugging. 4. Improved maintainability and foundations for new feature compatibility. ## Breaking Changes Configuring and opening the Knot SDK has changed significantly in Flutter Version 1.0 and requires some refactoring in order to initialize the SDK with a session. Events and Errors now provide additional values to debug with. ### Session Initialization **Changes** * `.openCardOnFileSwitcher` is replaced with a more flexible `.open`. * The `open` action now requires a `KnotConfiguration` which is now `Knot.open(knotConfiguration)`. **Before** ```dart Dart theme={"system"} _knotapiFlutterPlugin.openCardOnFileSwitcher(KnotConfiguration( sessionId: SESSION_ID, clientId: CLIENT_ID, environment: ENVIRONMENT, )); ``` **After** ```dart Dart theme={"system"} _knotapiFlutterPlugin.open(KnotConfiguration( sessionId: "INSERT_SESSION_ID", clientId: "INSERT_CLIENT_ID", environment: Environment.development, // or Environment.production product: Product.cardSwitcher // or Product.cardSwitcher )); ``` ### Event Handling **Changes** * The `onEvent` type now provides the environment and product the event originated from. * The `KnotError` type provides improved error descriptions and product type. * The `KnotSuccess` type now provides a product type and a metaData map. **Before** ```dart Dart theme={"system"} import 'package:knotapi_flutter/knotapi_flutter.dart'; import 'dart:async'; import 'package:knotapi_flutter/events.dart'; ... class _MyAppState extends State { final _knotapiFlutterPlugin = KnotapiFlutter(); StreamSubscription? _streamEvent; StreamSubscription? _streamSuccess; StreamSubscription? _streamError; StreamSubscription? _streamExit; @override void initState() { super.initState(); _streamError = KnotapiFlutter.onError.listen(_onError); _streamEvent = KnotapiFlutter.onEvent.listen(_onEvent); _streamExit = KnotapiFlutter.onExit.listen(_onExit); _streamSuccess = KnotapiFlutter.onSuccess.listen(_onSuccess); } ... void _onSuccess (KnotSuccess event) { String eventName = event.eventName; String type = event.type; String merchant = event.merchant; print("eventName: $eventName, type: $type, merchant: $merchant"); } void _onError (KnotError event) { String type = event.type; String errorMessage = event.errorMessage; print("type: $type, errorMessage: $errorMessage"); } void _onError (KnotError event) { String type = event.type; String name = event.event; String taskId = event.taskId; String merchantName = event.merchant; print("eventName: $eventName, type: $type, event: $name, taskId: $taskId, merchant: $merchantName"); } void _onExit (KnotExit event) { String type = event.type; print("eventName: _onExit, type: $type"); } ``` **After** ```dart Dart theme={"system"} import 'package:knotapi_flutter/knotapi_flutter.dart'; import 'dart:async'; import 'package:knotapi_flutter/events.dart'; ... class _MyAppState extends State { final _knotapiFlutterPlugin = KnotapiFlutter(); StreamSubscription? _streamEvent; StreamSubscription? _streamSuccess; StreamSubscription? _streamError; StreamSubscription? _streamExit; @override void initState() { super.initState(); _streamError = KnotapiFlutter.onError.listen(_onError); _streamEvent = KnotapiFlutter.onEvent.listen(_onEvent); _streamExit = KnotapiFlutter.onExit.listen(_onExit); _streamSuccess = KnotapiFlutter.onSuccess.listen(_onSuccess); } ... void _onSuccess (KnotSuccess event) { Product type = event.product; String merchant = event.merchant; print("eventName: _onSuccess, type: $type, merchant: $merchant"); } void _onError (KnotError event) { String type = event.product; String message = event.message; print("eventName: _onError, type: $type, message: $message"); } void _onEvent (KnotEvent event) { Product type = event.product; String name = event.event; String? taskId = event.taskId; Map metaData = event.metaData; print("eventName: _onEvent, type: $type, event: $name, taskId: $taskId, metaData: $metaData"); } void _onExit (KnotExit event) { String type = event.type; print("eventName: _onExit, type: $type"); } ``` **Event Names** The SDK now maps raw event names to standardized event names for easier handling. | Event Name Prior to 1.0 | 1.0 Event Name | | --------------------------- | ----------------------------- | | refresh session request | REFRESH\_SESSION\_REQUEST | | merchant clicked | MERCHANT\_CLICKED | | login started | LOGIN\_STARTED | | authenticated | AUTHENTICATED | | otp required | OTP\_REQUIRED | | security questions required | SECURITY\_QUESTIONS\_REQUIRED | | approval required | APPROVAL\_REQUIRED | ### Error Handling Error handling has been improved with more structured and meaningful error messages. **Changes** * Errors are now encapsulated in the `KnotError` enum. * Each error has a **human-readable description** (`errorDescription`) and a **unique error code** (`errorCode`). * Improved clarity and consistency across error messages. **Before** ```dart Dart theme={"system"} void _onError (KnotError event) { String eventName = event.eventName; String type = event.type; String errorMessage = event.errorMessage; String errorCode = event.errorCode; print("eventName: $eventName, type: $type, errorCode: $errorCode, errorMessage: $errorMessage"); } ``` **After** ```dart Dart theme={"system"} void onError(KnotError error) { switch (error) { case KnotError.INVALID_SESSION: print("Error: INVALID_SESSION - ${error.message}"); break; case KnotError.EXPIRED_SESSION: print("Error: EXPIRED_SESSION - ${error.message}"); break; case KnotError.INVALID_CLIENT_ID: print("Error: INVALID_CLIENT_ID - ${error.message}"); break; case KnotError.INTERNAL_ERROR: print("Error: INTERNAL_ERROR - ${error.message}"); break; case KnotError.MERCHANT_ID_NOT_FOUND: print("Error: MERCHANT_ID_NOT_FOUND - ${error.message}"); break; } } ``` **Error Types** The Knot SDK provides predefined error cases for you to handle based on your own needs. | Error Case | Description | | ------------------- | ------------------------------------------------------------------ | | .invalidSession | The session is invalid. | | .expiredSession | The session has expired. | | .invalidClientId | The client ID is invalid. | | .internalError | An internal error occurred. | | .merchantIdNotFound | The merchant ID is required when product type = transaction\_link. | ### Closing the SDK Most apps do not use the explicit `close` method, as it is infrequently applicable to the integration with the Knot SDK. **Changes** * Closing the SDK is now statically accessed via `_knotapiFlutterPlugin.close()` as opposed to `_knotapiFlutterPlugin.closeKnotSDK()`. **Before** ```dart Dart theme={"system"} _knotapiFlutterPlugin.closeKnotSDK(); ``` **After** ```dart Dart theme={"system"} _knotapiFlutterPlugin.close(); ``` # iOS Version 1.0+ Source: https://docs.knotapi.com/sdk/migration-guides/ios/ios-1-0 ## Overview To simplify your application logic and integration with the Knot iOS SDK, Version 1.0 includes a number of breaking changes that may affect the way your integration works or behaves. The new version includes a number of significant improvements: 1. Enhanced speed and stability in loading merchant flows. 2. More streamlined initialization of the SDK. 3. Simplified event handling, more informative event messaging, and uniform naming conventions for easier debugging. 4. Improved maintainability and foundations for new feature compatibility. ## Breaking Changes Configuring and opening the Knot SDK has changed significantly in iOS Version 1.0 and requires some refactoring in order to initialize the SDK with a session. Errors are now encapsulated in a `KnotError` object which provides an enumerated value to debug with. ### Session Initialization **Changes** * `Knot.createCardSwitcherSession` and `Knot.createTransactionsLinkSession ` are replaced with a more flexible `KnotConfiguration`. * The new interface allows configuration of additional properties such as `useCategories`, `useSearch`, and `merchantIds`. * The `open` action now requires a `KnotConfiguration` and an optional `KnotEventDelegate` which is now `Knot.open(configuration:delegate:)`. **Before** ```swift Swift theme={"system"} let session = CardOnFileSwitcherSession(sessionId: "SESSION_ID", clientId: "CLIENT_ID", environment: .sandbox) session.entryPoint = "Onboarding" // Optionally, set an entry point. session.merchantIds = [44] // Optionally, a specific merchant Id Knot.open(session: session) ``` ```objectivec Objective-C theme={"system"} #import "CardOnFileSwitcherSession.h" #import "Knot.h" // ... CardOnFileSwitcherSession *session = [[CardOnFileSwitcherSession alloc] initWithSessionId:@"SESSION_ID" clientId:@"CLIENT_ID" environment:CardOnFileSwitcherEnvironmentSandbox]; session.entryPoint = @"Onboarding"; // Optionally, set an entry point. session.merchantIds = @[@44]; // Optionally, a specific merchant ID [Knot openWithSession:session]; ``` **After** ```swift Swift theme={"system"} let configuration = KnotConfiguration( sessionId: "SESSION_ID", clientId: "CLIENT_ID", environment: .development, entryPoint: nil, product: .card_switcher, useCategories: true, useSearch: false, merchantIds: nil ) Knot.open(configuration: configuration, delegate: self) ``` ```objectivec Objective-C theme={"system"} KnotConfiguration *configuration = [[KnotConfiguration alloc] initWithSessionId:@"SESSION_ID" clientId:@"CLIENT_ID" environment:KnotEnvironmentDevelopment entryPoint:nil product:KnotProductCardSwitcher useCategories:YES useSearch:NO merchantIds:nil]; [Knot openWithConfiguration:configuration delegate:self]; ``` ### Event Handling **Changes** * Event handling is now managed through `KnotEventDelegate` instead of closures. * Events like `onSuccess`, `onError`, and `onExit` are now **explicit methods** inside a delegate. * The `onEvent` method introduces the `KnotEvent` object to better handle Knot emitted events. * The `KnotError` type provides improved error descriptions. * The `sendCard` parameter is deprecated and its functionality incorporated into the `metaData` dictionary within `KnotEvent` when `KnotEvent.event` equals `AUTHENTICATED`. **Before** ```swift Swift theme={"system"} session.onSuccess = { merchant in print("Successfully updated card for \(merchant)") } session.onError = { errorCode, errorMessage in print("Error: \(errorCode) - \(errorMessage)") } session.onExit = { print("User exited the SDK") } session.onEvent = { event, merchant, taskId, sendCard in print("event code: \(event), merchant: \(merchant), taskId: \(taskId), sendCard: \(sendCard)") } ``` ``` session.onSuccess = ^(Merchant *merchant) { NSLog(@"Successfully updated card for %@", merchant); }; session.onError = ^(NSInteger errorCode, NSString *errorMessage) { NSLog(@"Error: %ld - %@", (long)errorCode, errorMessage); }; session.onExit = ^{ NSLog(@"User exited the SDK"); }; session.onEvent = ^(NSInteger event, Merchant *merchant, NSString *taskId, BOOL sendCard) { NSLog(@"event code: %ld, merchant: %@, taskId: %@, sendCard: %d", (long)event, merchant, taskId, sendCard); }; ``` **After** ```swift Swift theme={"system"} class MyViewController: UIViewController, KnotEventDelegate { func onSuccess(merchant: String) { print("Successfully updated card for \(merchant)") } func onError(error: KnotError) { print("Error: \(error.errorDescription)") } func onEvent(event: KnotEvent) { print("Event: \(event.event)") } func onExit() { print("User exited the SDK") } } ``` ``` // MyViewController.m #import "MyViewController.h" @interface MyViewController : UIViewController @end // MyViewController.h @implementation MyViewController - (void)onSuccessWithMerchant:(NSString *)merchant { NSLog(@"Successfully updated card for %@", merchant); } - (void)onErrorWithError:(KnotError *)error { // Assuming `KnotError` has an `errorDescription` property NSLog(@"Error: %@", error.errorDescription); } - (void)onEventWithEvent:(KnotEvent *)event { // Assuming `KnotEvent` has an `event` property or method NSLog(@"Event: %@", event.event); } - (void)onExit { NSLog(@"User exited the SDK"); } @end ``` **SendCard** Most apps do not use the explicit `sendCard` method, as it is rarely applicable to the integration with the Knot SDK. The `sendCard` parameter has been deprecated and its functionality is incorporated into the `metaData` dictionary within `KnotEvent` when the `KnotEvent.event` equals `AUTHENTICATED`. This change enhances flexibility by allowing additional contextual data to be included in events without requiring separate parameters. Previously, `sendCard` was accessed as a standalone value, but now you can retrieve it from the `metaData` dictionary in the event callback. This approach ensures better extensibility and consistency across different event types. To access the `sendCard` value, simply extract it from the event’s `metaData` dictionary. ```swift Swift theme={"system"} class MyViewController: UIViewController, KnotEventDelegate { func onSuccess(merchant: String) { print("Successfully updated card for \(merchant)") } func onError(error: KnotError) { print("Error: \(error.errorDescription)") } func onEvent(event: KnotEvent) { if event.event == "AUTHENTICATED" { print("Event.AUTHENTICATED sendCard: \(event.metaData["sendCard"] as? Bool)") } } func onExit() { print("User exited the SDK") } }} ``` ``` // MyViewController.h @interface MyViewController : UIViewController @end // MyViewController.m #import "MyViewController.h" @implementation MyViewController - (void)onSuccessWithMerchant:(NSString *)merchant { NSLog(@"Successfully updated card for %@", merchant); } - (void)onErrorWithError:(KnotError *)error { // Assuming KnotError has a property named errorDescription NSLog(@"Error: %@", error.errorDescription); } - (void)onEventWithEvent:(KnotEvent *)event { // Assuming KnotEvent has an 'event' property (NSString *) and 'metaData' (NSDictionary *) if ([event.event isEqualToString:@"AUTHENTICATED"]) { // Safely extract the "sendCard" value from event.metaData NSNumber *sendCardNumber = event.metaData[@"sendCard"]; BOOL sendCard = [sendCardNumber boolValue]; NSLog(@"Event.AUTHENTICATED sendCard: %d", sendCard); } } - (void)onExit { NSLog(@"User exited the SDK"); } @end ``` **Event Names** The SDK now maps raw event names to standardized event names for easier handling. | Event Name Prior to 1.0 | 1.0 Event Name | | :-------------------------- | :---------------------------- | | refresh session request | REFRESH\_SESSION\_REQUEST | | merchant clicked | MERCHANT\_CLICKED | | login started | LOGIN\_STARTED | | authenticated | AUTHENTICATED | | otp required | OTP\_REQUIRED | | security questions required | SECURITY\_QUESTIONS\_REQUIRED | | approval required | APPROVAL\_REQUIRED | ### Error Handling Error handling has been improved with more structured and meaningful error messages. **Changes** * Errors are now encapsulated in the `KnotError` enum. * Each error has a **human-readable description** (`errorDescription`) and a **unique error code** (`errorCode`). * Improved clarity and consistency across error messages. **Before** ```swift Swift theme={"system"} session.onError = { errorCode, errorMessage in print("Error: \(errorCode) - \(errorMessage)") } ``` ```objectivec Objective-C theme={"system"} session.onError = ^(NSInteger errorCode, NSString *errorMessage) { NSLog(@"Error: %ld - %@", (long)errorCode, errorMessage); }; ``` **After** ```swift Swift theme={"system"} func onError(error: KnotError) { switch error { case .invalidSession: print("Error: Session is invalid. Please check your session ID.") case .expiredSession: print("Error: Session has expired. Please start a new session.") case .invalidClientId: print("Error: Invalid client ID. Ensure your credentials are correct.") case .internalError: print("Error: An internal error occurred. Try again later.") } } ``` ```objectivec Objective-C theme={"system"} - (void)onErrorWithError:(KnotError)error { switch (error) { case KnotErrorInvalidSession: NSLog(@"Error: Session is invalid. Please check your session ID."); break; case KnotErrorExpiredSession: NSLog(@"Error: Session has expired. Please start a new session."); break; case KnotErrorInvalidClientId: NSLog(@"Error: Invalid client ID. Ensure your credentials are correct."); break; case KnotErrorInternalError: NSLog(@"Error: An internal error occurred. Try again later."); break; } // If you also need the string description or error code: NSString *description = KnotErrorDescription(error); NSString *code = KnotErrorCode(error); NSLog(@"Additional info => Description: %@, Code: %@", description, code); } ``` **Error Types** The Knot SDK provides predefined error cases for you to handle based on your own needs. | Error Case | Description | | :--------------- | :-------------------------- | | .invalidSession | The session is invalid. | | .expiredSession | The session has expired. | | .invalidClientId | The client ID is invalid. | | .internalError | An internal error occurred. | ### Closing the SDK Most apps do not use the explicit `close` method, as it is infrequently applicable to the integration with the Knot SDK. **Changes** * Closing the SDK is now statically accessed via `Knot.close()` as opposed to being bound to the session object. **Before** ```swift Swift theme={"system"} session.close() ``` ```objectivec Objective-C theme={"system"} [session close]; ``` **After** ```swift Swift theme={"system"} Knot.close() ``` ```objectivec Objective-C theme={"system"} [Knot close]; ``` # React Native Version 1.0+ Source: https://docs.knotapi.com/sdk/migration-guides/react-native/react-native-1-0 ## Overview To simplify your application logic and integration with the Knot React Native SDK, Version 1.0 includes a number of breaking changes that may affect the way your integration works or behaves. The new version includes a number of significant improvements: 1. Enhanced speed and stability in loading merchant flows. 2. More streamlined initialization of the SDK. 3. Simplified event handling, more informative event messaging, and uniform naming conventions for easier debugging. 4. Improved maintainability and foundations for new feature compatibility. ## Breaking Changes Configuring and opening a Knot SDK has changed significantly in React Native Version 1.0 and requires some refactoring in order to initialize the SDK with a session, receive (`KnotEvent`) events, and handle errors. Errors are now encapsulated in a `KnotError` object which provides an enumerated value to debug with. ### Session Initialization **Changes** * `openCardOnFileSwitcher`, `addCardSwitcherListener`, and `eventNames` from "react-native-knotapi" are replaced with `Knot` and `addKnotListener`. * The new interface allows configuration of additional properties such as `useCategories`, `useSearch`, and `merchantIds`. * The `open` action now requires configuration values that are required to initialize with a session. **Before** ```javascript JavaScript theme={"system"} import { openCardOnFileSwitcher, addCardSwitcherListener, eventNames } from "react-native-knotapi"; openCardOnFileSwitcher({ sessionId: SESSION_ID, clientId: CLIENT_ID, environment: "development", }) ``` **After** ```javascript JavaScript theme={"system"} import { Knot, addKnotListener, } from "react-native-knotapi"; Knot.open({ sessionId: 'session_12345', // Current session ID clientId: 'client_67890', // Your client ID environment: 'development', // 'development' | 'production' entryPoint: 'onboarding', // Defined by you useCategories: true, // Recommend true useSearch: true, // Recommend true merchantIds: [52], // It is not recommended that you provide a long list of merchants domainUrls: ["https://domain1.com", "https://domain2.com"], // Only applicable for Android. customerConfig: { cardName: 'Card Name', customerName: 'Customer Name', logoId: 'LogoId' }, }); ``` ```typescript TypeScript theme={"system"} import { Knot, addKnotListener, type KnotError, type KnotEvent, type KnotExit, type KnotSuccess, } from "react-native-knotapi"; Knot.open({ sessionId: 'session_12345', // Current session ID clientId: 'client_67890', // Your client ID environment: 'development', // 'development' | 'production' entryPoint: 'onboarding', // Defined by you useCategories: true, // Recommend true useSearch: true, // Recommend true merchantIds: [52], // It is not recommended that you provide a long list of merchants domainUrls: ["https://domain1.com", "https://domain2.com"], // Only applicable for Android. customerConfig: { cardName: 'card name', customerName: 'customer name', logoId: 'logo id' }, }); ``` ### Event Handling **Changes** * Event handling is now managed through `addKnotListener` instead of closures. * Events like `onSuccess`, `onError`, and `onExit` are now product agnostic. * The `onEvent` method introduces the `KnotEvent` object to better handle Knot emitted events. * The `KnotError` type provides improved error descriptions. * The `sendCard` parameter is deprecated and its functionality incorporated into the `metaData` dictionary within `KnotEvent` when `KnotEvent.event` equals `AUTHENTICATED`. **Before** ```javascript JavaScript theme={"system"} let listener = addCardSwitcherListener(eventNames.onSuccess, merchant => { console.log(merchant); }); let listener = addCardSwitcherListener(eventNames.onError, (errorCode, errorMessage) => { console.log(`Error ${errorCode}: ${errorMessage}`); }); let listener = addCardSwitcherListener(eventNames.onExit, () => { console.log("onExit"); }); let listener = addCardSwitcherListener(eventNames.onEvent, (e) => { console.log('Event: ', e.event); console.log('taskId: ', e.taskId); console.log('merchant:', e.merchant); }); ``` **After** ```javascript JavaScript theme={"system"} const onKnotSuccess = addKnotListener('onSuccess', (event) => { console.log('onSuccess', 'event', event); }); const onKnotEvent = addKnotListener('onEvent', (event) => { console.log('onEvent', 'event', event); }); const onKnotError = addKnotListener('onError', (event) => { console.log('onError', 'event', event); }); const onKnotExit = addKnotListener('onExit', (event) => { console.log('onExit', 'event', event); }); ``` ```typescript TypeScript theme={"system"} const onKnotSuccess = addKnotListener('onSuccess', (event: KnotSuccess) => { console.log('onSuccess', 'event', event); }); const onKnotEvent = addKnotListener('onEvent', (event: KnotEvent) => { console.log('onEvent', 'event', event); }); const onKnotError = addKnotListener('onError', (event: KnotError) => { console.log('onError', 'event', event); }); const onKnotExit = addKnotListener('onExit', (event: KnotExit) => { console.log('onExit', 'event', event); }); ``` **SendCard** Most apps do not use the `sendCard` property, as it is rarely applicable to the integration with the Knot SDK. The `sendCard` parameter has been deprecated and its functionality has been incorporated into the `metaData` dictionary within `KnotEvent` when the `KnotEvent.event` equals `AUTHENTICATED`. This change enhances flexibility by allowing additional contextual data to be included in events without requiring separate parameters. Previously, `sendCard` was accessed as a standalone value, but now you can retrieve it from the `metaData` dictionary in the event callback. This approach ensures better extensibility and consistency across different event types. To access the `sendCard` value, simply extract it from the event’s `metaData` dictionary. ```javascript theme={"system"} ... const onKnotEvent = addKnotListener('onEvent', (event: KnotEvent) => { console.log('onEvent', 'sendCard', event.metaData.sendCard); }); ``` **Event Names** The Knot SDK now maps raw event names to standardized event names for easier handling. | Event Name Prior to 1.0 | 1.0 Event Name | | :-------------------------- | :---------------------------- | | refresh session request | REFRESH\_SESSION\_REQUEST | | merchant clicked | MERCHANT\_CLICKED | | login started | LOGIN\_STARTED | | authenticated | AUTHENTICATED | | otp required | OTP\_REQUIRED | | security questions required | SECURITY\_QUESTIONS\_REQUIRED | | approval required | APPROVAL\_REQUIRED | ### Error Handling Error handling has been improved with more structured and meaningful error messages. **Changes** * Errors are now encapsulated in the `KnotError` enum. * Each error has a **human-readable description** (`errorDescription`) and a **unique error code** (`errorCode`). * Improved clarity and consistency across error messages. **Before** ```swift JavaScript theme={"system"} let listener = addCardSwitcherListener(eventNames.onError, (errorCode, errorMessage) => { console.log(`Error ${errorCode}: ${errorMessage}`); }); ``` **After** ```javascript JavaScript theme={"system"} const onError = (error) => { switch (error) { case "invalidSession": console.warn("Error: Session is invalid. Please check your session ID."); break; case "expiredSession": console.warn("Error: Session has expired. Please start a new session."); break; case "invalidClientId": console.warn("Error: Invalid client ID. Ensure your credentials are correct."); break; case "internalError": console.warn("Error: An internal error occurred. Try again later."); break; default: console.warn("Error: An unknown error occurred."); } }; ``` **Error Types** The Knot SDK provides predefined error cases for you to handle based on your own needs. | Error Case | Description | | :--------------- | :-------------------------- | | .invalidSession | The session is invalid. | | .expiredSession | The session has expired. | | .invalidClientId | The client ID is invalid. | | .internalError | An internal error occurred. | ### Closing the SDK Most apps do not use the explicit \`close\` method, as it is infrequently applicable to the integration with the Knot SDK. **Changes** * Closing the SDK is now statically accessed via `Knot.close()` as opposed to being bound to the session object. **Before** ```javascript JavaScript theme={"system"} import { closeKnotSDK } from "react-native-knotapi"; closeKnotSDK(); ``` **After** ```swift JavaScript theme={"system"} Knot.close() ``` # Web Version 1.0+ Source: https://docs.knotapi.com/sdk/migration-guides/web/web-1-0 ## Overview To simplify your application logic and integration with the Knot JS SDK, Version 1.0 includes a number of breaking changes that may affect the way your integration works or behaves. The new version includes a number of significant improvements: 1. Enhanced speed and stability in loading merchant flows. 2. More streamlined initialization of the SDK. 3. Simplified event handling, more informative event messaging, and uniform naming conventions for easier debugging. 4. Improved maintainability and foundations for new feature compatibility. ## Breaking Changes Configuring and opening the Knot SDK has changed significantly in JS Version 1.0 and requires some refactoring in order to initialize the SDK with a session. Errors are now encapsulated in a `KnotError` object which provides an enumerated value to debug with. ### Session Initialization **Changes** * `Knot.openCardOnFileSwitcher` is replaced with a more flexible `open`. **Before** ```javascript JavaScript theme={"system"} import KnotapiJS from "knotapi-js"; const knotapi = new KnotapiJS(); // Invoke the openCardOnFileSwitcher method with required parameters knotapi.openCardOnFileSwitcher({ sessionId: "Your Session ID", clientId: "Your Client ID", environment: "development" // or "production" }); ``` **After** ```javascript theme={"system"} import KnotapiJS from "knotapi-js"; const knotapi = new KnotapiJS(); // Invoke the open method with required parameters knotapi.open({ sessionId: "Your Session ID", clientId: "Your Client ID", environment: "development", // or "production" product: "card_switcher" }); ``` ### Event Handling **Changes** * All events introduce a `product` argument to help distinguish events in your code. **Before** ```javascript JavaScript theme={"system"} onSuccess: (merchant) => { console.log("onSuccess", merchant); } onError: (errorCode, errorMessage) => { console.log("onError", errorCode, errorMessage); } onExit: () => { console.log("onExit"); } onEvent: (event, merchant, taskId) => { console.log("onEvent", event, merchant, taskId); } ``` **After** ```javascript JavaScript theme={"system"} onSuccess: (product, merchant) => { console.log("onSuccess", merchant); } onError: (product, errorCode, errorMessage) => { console.log("onError", errorCode, errorMessage); } onExit: (product) => { console.log("onExit"); } onEvent: (product, event, merchant, taskId) => { console.log("onEvent", event, merchant, taskId); } ``` # React Native SDK Source: https://docs.knotapi.com/sdk/react-native Install the Knot Link React Native SDK with npm or yarn, initialize a session, and handle events to link users' merchant accounts in your app. ## Overview **SDK Updates**\ New versions of the SDK are released frequently, not only to add new features and address issues in the SDK, but also to continuously improve the conversion of merchant login flows. As a result, we strongly recommend that you frequently update your SDK version across any platforms where you invoke the SDK. The Knot Link SDK provides a seamless way for end users to link their merchant accounts to your React Native app, serving as the foundation for Knot's merchant connectivity platform. It is a client-side integration, consisting of initializing & configuring the SDK and handling events. ## Installation You can install the Knot SDK using **npm** or **yarn** in your react-native project directory, like below: ```javascript npm icon=npm theme={"system"} npm install react-native-knotapi --save ``` ```bash yarn icon=yarn theme={"system"} yarn add react-native-knotapi ``` From React Native 0.60 and higher, linking is automatic. If you need to manually install the SDK, run the below: ```bash Shell icon=terminal theme={"system"} react-native link react-native-knotapi ``` ## Usage Import methods like the below: ```javascript JavaScript icon=js theme={"system"} import { Knot, addKnotListener, } from "react-native-knotapi"; ``` ```typescript TypeScript icon=t theme={"system"} import { Knot, addKnotListener, type KnotError, type KnotEvent, type KnotExit, type KnotSuccess, } from "react-native-knotapi"; ``` ## Initialization Your backend will create a session by calling [Create Session](/api-reference/sessions/create-session) and provide it to your frontend. To start a Knot session, you must first configure the session with a `KnotConfiguration` class. The configuration allows you to set the `environment`, `entry point`, and other user experience configurations. It's expected that your integration with Knot will retrieve and pass a new session into the SDK on each initialization. ### Configure the session The SDK is configured using the following parameters inside the `open` method: | Name | Type | Description | | ------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | sessionId | string | The session created by calling `/session/create` in your backend. | | clientId | string | Your organization's client ID. Note that this varies between `development` and `production` environments. | | environment | string | The desired environment (`development` or `production`). | | entryPoint | string | **Optional.** The specific entry point from within your app where you are initializing the Knot SDK (e.g. `onboarding`). | | merchantIds | number\[] | **Optional.** A list of merchant ID(s) to display. We recommend providing 0 or 1 merchant ID depending on your desired user experience. | | useCategories | boolean | **Optional.** Whether to display merchant categories and therefore group merchants into categories for discoverability. Default: `true`. | | useSearch | boolean | **Optional.** Whether to display the search bar, enabling users to search for merchants. Default: `true`. | | domainUrls | string\[] | **Optional. Android only.** A set of domains for which Knot should explicitly not clear cookies. | | metadata | Record\ | **Optional.** Custom key-value pairs to include in [webhook](/webhooks#session-metadata) payloads. Maximum 10 keys with string values up to 500 characters each. | | locale | string | **Optional.** A [BCP-47](https://developer.mozilla.org/en-US/docs/Glossary/BCP_47_language_tag) language tag to set the locale for the SDK. Currently supported: `en-US`, `es-US`, `en-CA`, and `fr-CA`. If not provided, defaults to `en-US`. | | product | string | **Optional. Ignored in version 1.0.7+.** The Knot product the session will inherit — the same as the type of session created (e.g. `card_switcher`, `transaction_link`). | #### `CustomerConfiguration` This class is entirely optional and infrequently used, typically only when you offer Knot for multiple, differently-named card programs in the same app. The Knot team will set pre-defined values of your choosing for each parameter that you can then subsequently pass into the SDK. Passing a value that is not pre-defined will result in an `onError` callback. To take advantage of this functionality, please contact the Knot team who will be happy to assist you. | Name | Type | Description | | :----------- | :----- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | cardName | String | **Optional.** The differentiated display name for the card product, used inside the Knot SDK (e.g. `Debit Card`, `Credit Card`). This value will override the default value `card`. | | customerName | String | **Optional.** The differentiated display name for the company, used both as a standalone and prepended to the `cardName` (e.g. `Smart Bank`, `Payment Corp`). This value will override your default customer name value. Only recommended if you issue cards under multiple brands. | | logoId | String | **Optional.** The differentiated logo for the company. This value will override your default logo. | See the following examples for how the parameters are used together in text inside the Knot SDK: `"Your [customerName] [cardName | card] was added."` ### Open the session To begin the flow, use the `open` method like below: ```javascript JavaScript icon=js theme={"system"} Knot.open({ sessionId: 'session_12345', // Current session ID clientId: 'client_67890', // Your client ID environment: 'development', // 'development' || 'production' entryPoint: 'onboarding', // Defined by you useCategories: true, // Recommend true useSearch: true, // Recommend true merchantIds: [52], // Recommend 0 or 1 merchant IDs domainUrls: ["https://domain1.com", "https://domain2.com"], // Uncommon metadata: { // Optional metadata for webhooks reference_token: 'your-token', trace_id: 'your-trace-id' }, customerConfig: { cardName: 'Card Name', customerName: 'Customer Name', logoId: 'LogoId' }, locale: 'es-US', // Optional BCP-47 language tag }); ``` To test logging in to a merchant in the SDK, please reference a set of available test credentials for the CardSwitcher product [here](/card-switcher/testing) and the TransactionLink product [here](/transaction-link/testing). ## Single merchant flow You may decide to use [List Merchants](/api-reference/merchants/list-merchants) to retrieve a list of merchants, list them in your app, and then open the SDK with a single merchant. To do so, pass a merchant ID when [configuring the session](/sdk/react-native#configure-the-session) in the `KnotConfiguration`. More in [Retrieving & Listing Merchants](/link/retrieving-and-listing-merchants). The merchant ID is the same across all environments. Although available, we do not recommend that you provide a long list of merchants in order to remove a few, but rather "hide" certain merchants that you desire from your [Knot Dashboard](https://dashboard.knotapi.com). ## Entry points In your app's user experience, you may choose to integrate Knot in one or multiple places (e.g. from different tabs or screens). How users behave when interacting with Knot from each of these "entry points" may vary. It will be useful for you to be able to differentiate these groups of users by entry point in order to assess the value of each entry point. You can provide a value for the entry point when [configuring the session](/sdk/react-native#configure-the-session) in `Knot.open`. This value will be returned in the `AUTHENTICATED` webhook. ## Categories & search Users are presented with a list of merchants in the SDK (unless you provide a single merchant as described above). Accompanying the list is a set of categories and a search experience. Each of these components is visible to users by default (as set in Knot's backend). You can choose to remove either of them by setting `useCategories: false` and `useSearch: false` in `Knot.open`. **This is not recommended**. ## Events To receive updates from the SDK, implement the `addKnotListener` method. ```javascript JavaScript icon=js theme={"system"} const onKnotSuccess = addKnotListener('knot:onSuccess', (event) => { console.log('onSuccess', 'event', event); }); const onKnotEvent = addKnotListener('knot:onEvent', (event) => { console.log('onEvent', 'event', event); }); const onKnotError = addKnotListener('knot:onError', (event) => { console.log('onError', 'event', event); }); const onKnotExit = addKnotListener('knot:onExit', (event) => { console.log('onExit', 'event', event); }); ``` ```typescript TypeScript icon=t theme={"system"} const onKnotSuccess = addKnotListener('knot:onSuccess', (event: KnotSuccess) => { console.log('onSuccess', 'event', event); }); const onKnotEvent = addKnotListener('knot:onEvent', (event: KnotEvent) => { console.log('onEvent', 'event', event); }); const onKnotError = addKnotListener('knot:onError', (event: KnotError) => { console.log('onError', 'event', event); }); const onKnotExit = addKnotListener('knot:onExit', (event: KnotExit) => { console.log('onExit', 'event', event); }); ``` The provided Typescript types can be found below: ```typescript theme={"system"} export type KnotError = { errorCode: string; errorDescription: string; product?: 'card_switcher' | 'transaction_link'; }; export type KnotEvent = { event: string; environment: string; product?: string; merchantName: string; merchantId?: string; metaData?: Record; product?: 'card_switcher' | 'transaction_link' | 'link'; taskId?: string; }; export type KnotSuccess = { merchant: string; product?: 'card_switcher' | 'transaction_link' | 'link'; }; export type KnotExit = { product?: 'card_switcher' | 'transaction_link' | 'link'; } | void; ``` ### `onSuccess` This event is called when a user successfully logged in to the merchant and their card was switched. It takes a single string argument containing the name of the merchant. ### `onError` This event is called when an error occurs during SDK initialization and emits a `KnotError` with the following errors: | errorCode | errorDescription | Debugging Steps | | ------------------------ | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Invalid\_Session | The session is invalid. | Ensure `KnotConfiguration.environment` matches the environment the `sessionId` was created for (`development` or `production`). | | Expired\_Session | The session has expired. | Sessions are valid for 30 minutes. It is best practice to ensure that you create a new session **every time** a user invokes the SDK using [Create Session](/api-reference/sessions/create-session). | | Invalid\_Client\_Id | The client ID is invalid. | Verify that the value you are providing for `KnotConfiguration.clientId` is for the environment matching the value you are providing for `KnotConfiguration.environment` (i.e. `development` or `production`). If you provide your production `clientId` but set `environment: development`, you will experience this error. | | Internal\_Error | An internal error occurred. | Simply retry invoking the SDK with a new `sessionId`. | | Merchant\_Id\_Not\_Found | The merchant ID is required. | The `type` of `sessionId` you are providing on invocation of the SDK requires that you also provide a value in `KnotConfiguration.merchantIds` to ensure the user is directed to a specific merchant’s login flow in the SDK. You can retrieve a list of merchant IDs (the same in all environments) in [List Merchants](/api-reference/merchants/list-merchants). | | Invalid\_Card\_Name | The card name is invalid. | The value you are providing for `customerConfiguration.cardName` must exactly match an allowlisted value for your organization. Please reach out to the Knot team for access to this allowlist. | | Invalid\_Customer\_Name | The customer name is invalid. | The value you are providing for `customerConfiguration.customerName` must exactly match an allowlisted value for your organization. Please reach out to the Knot team for access to this allowlist. | | Invalid\_Logo\_Id | The logo ID is invalid. | The value you are providing for `customerConfiguration.logoId` must exactly match an allowlisted value for your organization. Please reach out to the Knot team for access to this allowlist. | | Invalid\_Locale | The locale is invalid. | The value you are providing for `locale` must be a valid [BCP-47](https://developer.mozilla.org/en-US/docs/Glossary/BCP_47_language_tag) language tag. Currently supported: `en-US`, `es-US`, `en-CA`, and `fr-CA`. | Sessions are valid for 30 minutes. If a session expires while the SDK is open, the SDK will emit an expired session error via `onError` and automatically close. To provide a seamless experience, handle the `REFRESH_SESSION_REQUEST` event via the `onEvent` callback to proactively extend the session using [Extend Session](/api-reference/sessions/extend-session) before expiration occurs. ### `onExit` This event is called when a user closes the SDK. ### `onEvent` This event is called when certain events occur in the SDK. With this callback, you will be able to understand how a user is progressing through their lifecycle of authenticating to a merchant. The following list contains all possible events emitted in the `KnotEvent.event` property: | Name | Purpose | | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | REFRESH\_SESSION\_REQUEST | Emitted when the session used to initialize the SDK needs to be refreshed. Use [Extend Session](/api-reference/sessions/extend-session) to extend the session before expiration occurs. | | MERCHANT\_CLICKED | Emitted when a user clicks on a merchant from the merchant list. | | LOGIN\_STARTED | Emitted when a user submits their credentials to login to the merchant. | | AUTHENTICATED | Emitted when a user successfully logs in to the merchant. | | OTP\_REQUIRED | Emitted when a user needs to enter an OTP code to login to the merchant. | | SECURITY\_QUESTIONS\_REQUIRED | Emitted when a user needs to enter answers to security questions to login to the merchant. | | APPROVAL\_REQUIRED | Emitted when a user needs to approve the login - often via a push notification or directly in the merchant's mobile app - to login to the merchant. | | ZIPCODE\_REQUIRED | Emitted when a user needs to enter their zip code to login to the merchant. | | DOB\_REQUIRED | Emitted when a user needs to verify their date of birth to login to the merchant. | | LICENSE\_REQUIRED | Emitted when a user needs to enter their drivers license to login to the merchant. | ## Other options ### Get current SDK version If you need to retrieve the current SDK version for your own use case, implement the following: ```javascript JavaScript icon=js theme={"system"} const sdkVersion = Knot.getSdkVersion(); console.log('Knot SDK Version:', sdkVersion ?? 'Unknown'); ``` ### Close the SDK If you need to explicitly close the SDK, use the below method, otherwise end users will naturally close the SDK as they interact with the interface. ```javascript JavaScript icon=js theme={"system"} Knot.close(); ``` ### Maintain cookies Android only. Knot clears cookies for security purposes. If your app relies on cookies, you can allowlist specific domains using the `domainUrls` configuration in `Knot.open`. **This is uncommon.** ```javascript JavaScript icon=js theme={"system"} Knot.open({ sessionId: 'session_12345', // Current session ID clientId: 'client_67890', // Your client ID environment: 'development', // 'development' || 'production' entryPoint: 'onboarding', // Defined by you useCategories: true, // Recommend true useSearch: true, // Recommend true merchantIds: [52], // Recommend 0 or 1 merchant IDs domainUrls: ["https://domain1.com", "https://domain2.com", .....], customerConfig: { cardName: 'Card Name', customerName: 'Customer Name', logoId: 'LogoId' }, locale: 'es-US', // Optional BCP-47 language tag }) ``` # Web SDK Source: https://docs.knotapi.com/sdk/web Load the Knot Link JS SDK from unpkg or npm, initialize a session in the browser, and handle events to link users' merchant accounts on the web. ## Overview **SDK Updates**\ New versions of the SDK are released frequently, not only to add new features and address issues in the SDK, but also to continuously improve the conversion of merchant login flows. As a result, we strongly recommend that you frequently update your SDK version across any platforms where you invoke the SDK. The Knot Link SDK provides a seamless way for end users to link their merchant accounts to your web app, serving as the foundation for Knot's merchant connectivity platform. It is a client-side integration, consisting of initializing & configuring the SDK and handling events. ## Installation The Knot JS SDK is hosted on [unpkg.com](http://unpkg.com), a popular CDN for everything on npm. You can also host the SDK on your servers if preferred. The `next` tag is applied in version `1.0.0`+, which is not automatically fetched by npm when running `npm install knotapi-js`. ### Via npm For Node.js environments, use npm to install the KnotapiJS SDK like below: ```bash npm icon=npm theme={"system"} npm install knotapi-js@next --save ``` ### Via CDN For browser-based projects, you can use the KnotapiJS SDK via a CDN: ```html html icon=html5 theme={"system"} ``` ## Initialization Your backend will create a session by calling [Create Session](/api-reference/sessions/create-session) and provide it to your frontend. To start a Knot session, you must first configure the SDK. The configuration allows you to set the environment, entry point, and other user experience configurations. It's expected that your integration with Knot will retrieve and pass a new session into the SDK on each initialization. ### Configure the SDK The SDK is configured using the following parameters when using the `open` method: | Name | Type | Description | | ------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | sessionId | String | The session created by calling `/session/create` in your backend. | | clientId | String | Your organization's client ID. Note that this varies between `development` and `production` environments. | | environment | Environment | The desired environment (`development` or `production`). | | entryPoint | String? | **Optional.** The specific entry point from within your app where you are initializing the Knot SDK (e.g. `onboarding`). | | merchantIds | \[int]? | **Optional.** A list of merchant ID(s) to display. We recommend providing 0 or 1 merchant IDs depending on your desired user experience. | | useCategories | Boolean | **Optional.** Whether to display merchant categories and therefore group merchants into categories for discoverability. Default: `true`. | | useSearch | Boolean | **Optional.** Whether to display the search bar, enabling users to search for merchants. Default: `true`. | | metadata | Object | **Optional.** Custom key-value pairs to include in [webhook](/webhooks#session-metadata) payloads. Maximum 10 keys with string values up to 500 characters each. | | locale | String | **Optional.** A [BCP-47](https://developer.mozilla.org/en-US/docs/Glossary/BCP_47_language_tag) language tag to set the locale for the SDK. Currently supported: `en-US` and `es-US`. If not provided, defaults to `en-US`. | The below parameters are entirely optional and infrequently used, typically only when you offer Knot for multiple, differently-named card programs in the same app. The Knot team will set pre-defined values for each parameter that you can then subsequently pass into the SDK. Passing a value that is not pre-defined will result in an `onError` callback. To take advantage of this functionality, please contact the Knot team who will be happy to assist you. | Name | Type | Description | | :----------- | :----- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | cardName | String | **Optional.** The differentiated display name for the card product, used inside the Knot SDK (e.g. `Debit Card`, `Credit Card`). This value will override the default value `Card`. | | customerName | String | **Optional.** The differentiated display name for the company, used both as a standalone and prepended to the `cardName` (e.g. `Smart Bank`, `Payment Corp`). This value will override your default customer name value. Only recommended if you issue cards under multiple brands. | | logoId | String | **Optional.** The differentiated logo for the company. This value will override your default logo. | See the following real example for how the `cardName` and `customerName` parameters are used together in text inside the Knot SDK: `"Your [customerName] [cardName | Card] was added."` ### Open the SDK Invoke the `open` method with the parameters like below: #### Node.js ```javascript Javascript icon=js theme={"system"} import KnotapiJS from "knotapi-js"; const knotapi = new KnotapiJS(); // Invoke the open method with parameters knotapi.open({ sessionId: "Your Session ID", clientId: "Your Client ID", environment: "development", // or "production" merchantIds: [17], // Recommend 0 or 1 merchant IDs entryPoint: "onboarding", // Defined by you useCategories: true, // Recommend true useSearch: true, // Recommend true metadata: { // Optional metadata for webhooks reference_token: "your-token", trace_id: "your-trace-id" }, customerName: "Company name", // Optional customer configuration cardName: "Card Name", // Optional customer configuration logoId: 1234, // Optional customer configuration locale: "es-US" // Optional BCP-47 language tag }); ``` #### Browser ```javascript Javascript icon=js theme={"system"} const KnotapiJS = window.KnotapiJS.default; const knotapi = new KnotapiJS(); // Invoke the open method with parameters knotapi.open({ sessionId: "Your Session ID", clientId: "Your Client ID", environment: "development", // or "production" merchantIds: [17], // Recommend 0 or 1 merchant IDs entryPoint: "onboarding", // Defined by you useCategories: true, // Recommend true useSearch: true, // Recommend true metadata: { // Optional metadata for webhooks reference_token: "your-token", trace_id: "your-trace-id" }, customerName: "Company name", // Optional customer configuration cardName: "Card Name", // Optional customer configuration logoId: 1234, // Optional customer configuration locale: "es-US" // Optional BCP-47 language tag }); ``` To test logging in to a merchant in the SDK, please reference a set of available test credentials for the CardSwitcher product [here](/card-switcher/testing) and Transaction Link product [here](/transaction-link/testing). ## Single merchant flow You may decide to use [List Merchants](/api-reference/merchants/list-merchants) to retrieve a list of merchants, list them in your app, and then open the SDK with a single merchant. To do so, pass a merchant ID when [configuring the session](/sdk/web#configure-the-session) in the `KnotConfiguration`. More in [Retrieving & Listing Merchants](/link/retrieving-and-listing-merchants). The merchant ID is the same across all environments. Although available, we do not recommend that you provide a long list of merchants in order to remove a few, but rather “hide” certain merchants that you desire from your [Customer Dashboard](https://dashboard.knotapi.com/). ## Entry points In your app’s user experience, you may choose to integrate Knot in one or multiple places (e.g. from different tabs or screens). How users behave when interacting with Knot from each of these “entry points” may vary. It will be useful for you to be able to differentiate these groups of users by entry point in order to assess the value of each entry point. You can provide a value for the entry point when initializing the SDK in `knotapi.open`. This value will be returned in the `AUTHENTICATED` webhook. ## Categories & search Users are presented with a list of merchants in the SDK (unless you provide a single merchant as described above). Accompanying the list is a set of categories and a search experience. Each of these components is visible to users by default (as set in Knot's backend). You can choose to remove either of them by setting `useCategories: false` and `useSearch: false` when initializing the SDK. **This is not recommended.** ## Events The `open` method provides several callbacks you can use to receive events from the SDK. ```javascript Javascript icon=js theme={"system"} knotapi.open({ sessionId: "Your Session ID", clientId: "Your Client ID", environment: "development", // or "production" merchantIds: [17], entryPoint: "onboarding", locale: "es-US", // Optional BCP-47 language tag onSuccess: (details) => { console.log("onSuccess", details); }, onError: (errorCode, message) => { console.log("onError", errorCode, message); }, onEvent: (event, merchant, payload, taskId) => { console.log("onEvent", event, merchant, payload, taskId); }, onExit: () => { console.log("onExit")} }); ``` ### `onSuccess` This event is called when a user successfully logged in to the merchant and their card was switched. It takes the following argument: `details`, which contains the `merchantName` field, representing the merchant for which the card was updated. ### `onError` This event is called when an error occurs during SDK initialization. It takes the following arguments: `errorCode` and `errorDescription`. | errorCode | errorDescription | Debugging Steps | | ------------------------ | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | INVALID\_SESSION | The session ID is invalid. | Ensure `KnotConfiguration.environment` matches the environment the `sessionId` was created for (`development` or `production`). | | EXPIRED\_SESSION | The session has expired. | Sessions are valid for 30 minutes. It is best practice to ensure that you create a new session **every time** a user invokes the SDK using [Create Session](/api-reference/sessions/create-session). | | INVALID\_CLIENT\_ID | The client ID is invalid. | Verify that the value you are providing for `KnotConfiguration.clientId` is for the environment matching the value you are providing for `KnotConfiguration.environment` (i.e. `development` or `production`). If you provide your production `clientId` but set `environment: development`, you will experience this error. | | INTERNAL\_ERROR | An internal error occurred. | Simply retry invoking the SDK with a new `sessionId`. | | MERCHANT\_ID\_NOT\_FOUND | The merchant ID is required. | The `type` of `sessionId` you are providing on invocation of the SDK requires that you also provide a value in `KnotConfiguration.merchantIds` to ensure the user is directed to a specific merchant’s login flow in the SDK. You can retrieve a list of merchant IDs (the same in all environments) in [List Merchants](/api-reference/merchants/list-merchants). | | INVALID\_CARD\_NAME | The card name is invalid. | The value you are providing for `customerConfiguration.cardName` must exactly match an allowlisted value for your organization. Please reach out to the Knot team for access to this allowlist. | | INVALID\_CUSTOMER\_NAME | The customer name is invalid. | The value you are providing for `customerConfiguration.customerName` must exactly match an allowlisted value for your organization. Please reach out to the Knot team for access to this allowlist. | | INVALID\_LOGO\_ID | The logo ID is invalid. | The value you are providing for `customerConfiguration.logoId` must exactly match an allowlisted value for your organization. Please reach out to the Knot team for access to this allowlist. | | INVALID\_LOCALE | The locale is invalid. | The value you are providing for `locale` must be a valid [BCP-47](https://developer.mozilla.org/en-US/docs/Glossary/BCP_47_language_tag) language tag. Currently supported: `en-US` and `es-US`. | ```javascript Javascript icon=js theme={"system"} onError: (errorCode, errorDescription) => { console.log("onError", errorCode, errorDescription); } ``` ### `onExit` This event is called when a user closes the SDK. ### `onEvent` This event is called when certain events occur in the SDK. With this callback, you will be able to understand how a user is progressing through their lifecycle of authenticating to a merchant. It takes the following arguments: `event`, `merchant`, `merchantId`, `payload`, and `taskId`. ```javascript Javascript icon=js theme={"system"} onEvent: (event, merchant, merchantId, payload, taskId) => { console.log("onEvent", event, merchant, merchantId, payload, taskId); } ``` The following list contains all possible events emitted in the `event` property: | Event | Description | | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | | REFRESH\_SESSION\_REQUEST | Emitted when the session used to initialize the SDK needs to be refreshed. | | MERCHANT\_CLICKED | Emitted when a user clicks on a merchant from the merchant list. | | LOGIN\_STARTED | Emitted when a user submits their credentials to login to the merchant. | | AUTHENTICATED | Emitted when a user successfully logs in to the merchant. | | OTP\_REQUIRED | Emitted when a user needs to enter an OTP code to login to the merchant. | | QUESTIONS\_REQUIRED | Emitted when a user needs to enter answers to security questions to login to the merchant. | | APPROVAL\_REQUIRED | Emitted when a user needs to approve the login - often via a push notification or directly in the merchant's mobile app - to login to the merchant. | | ZIPCODE\_REQUIRED | Emitted when a user needs to enter their zip code to login to the merchant. | | DOB\_REQUIRED | Emitted when a user needs to verify their date of birth to login to the merchant. | | LICENSE\_REQUIRED | Emitted when a user needs to enter their drivers license to login to the merchant. | ## Domain allowlisting As an optional security precaution, you can allowlist certain domains to be used with the Knot SDK. To enable this feature, reach out to the Knot team, after which point you'll be able to allowlist domains from the [Knot Dashboard](https://dashboard.knotapi.com/developers/domains). Once enabled, the Knot Web SDK will only function on allowlisted domains. # Quickstart Source: https://docs.knotapi.com/shopping/quickstart Get started with the Shopping integration to enable cart creation and checkout capabilities in your app. ## Introduction Knot's Shopping product enables you to embed native merchant cart creation and checkout capabilities into your app. With Shopping, users can seamlessly add products to their merchant carts and complete purchases without leaving your application. ## Entry points #### Overview How and where you place entry points to invoke the Knot flow in your app play a crucial role driving engagement and delivering value to end users. Nearly all apps that integrate Knot develop multiple entry points into the Knot flow (e.g. different tabs or screens). As a result and **to provide better visibility into the conversion of the flow across different entry points**, the Knot SDK supports an `entryPoint` parameter when invoking the SDK. This parameter allows you to specify the entry point from which the user came. This value is then returned in the down-funnel [`AUTHENTICATED`](/link/webhook-events/authenticated) webhook event, thereby allowing you to measure the conversion of the flow by entry point in your analytics tool of choice. #### Usage We strongly recommend taking advantage of this functionality, so as to future-proof your visibility into your implementation and allow for future optimizations. To take advantage of this functionality, simply pass a different value to `KnotConfiguration.entryPoint` for each of your entry points when [configuring the session to invoke the SDK](/sdk/ios#configure-the-session). Common entry points include the following: `onboarding`, `home`, `push-notif-X`, `in-app-lifecycle-card-X`, etc. ## Getting started Check out the [Link Account](/api-reference/development/link-account) endpoint to bypass the client-side SDK and quickly link a merchant account in the development environment for testing. ### Start the flow Ensure you have access to your [Customer Dashboard](https://dashboard.knotapi.com) and retrieve your `client_id` and `secret`, which you will use as the basic auth username and password for your API key respectively. Note that your `client_id` and `secret` vary between the `development` and `production` environments. Subscribe to webhooks in your [Customer Dashboard](https://dashboard.knotapi.com) so your backend can be notified about user-generated events, as well as asynchronous processes. You will need to receive these events later on in the flow. Call [List Merchants](/api-reference/merchants/list-merchants) to retrieve a list of merchants that are available for the Shopping product by passing `type = shopping`. These are merchants you can display in your app, allow users to link, and subsequently shop at. To get started quickly, you can use `merchant_id: 45` for Walmart to later pass when initializing the SDK. You will be notified via the [`MERCHANT_STATUS_UPDATE`](/link/webhook-events/merchant-status-update) webhook when/if this list changes. With your API key for the `development` environment, call [Create Session](https://docs.knotapi.com/api-reference/sessions/create-session) with `type: link` to create a session used when invoking the SDK. More [here](/api-reference/authentication) on how to create an API key. Install an SDK of your choosing, for example on iOS [here](/sdk/ios). If you are using the Web SDK, make sure to allowlist your application's domains for the `development` and `production` environments in your [Customer Dashboard](https://dashboard.knotapi.com). Initialize the SDK with the `session_id` retrieved from [Create Session](/api-reference/sessions/create-session). In `KnotConfiguration`, pass a merchant `Id` retrieved from [List Merchants](/api-reference/merchants/list-merchants) or you can use `merchant_id: 45` for Walmart to get started quickly. The SDK is where users will interact with the Knot UI to link various merchant accounts. All login flows, including step-up authentication, are handled within the SDK. Users will see real-time feedback as they link a merchant account. Specifying an exact merchant by passing a merchant `Id` in `KnotConfiguration` is required when initializing the SDK with a session with `type: link`. ### Link a merchant account **In the development environment,** login to a merchant account using `user_good` / `pass_good` credentials to link your user's merchant account. Ingest the [`AUTHENTICATED`](/link/webhook-events/authenticated) webhook to notify your backend that the merchant account is successfully linked to Knot and that the connection status is `connected`. Similarly and as applicable, listen to the client-side `onEvent` callback in the SDK to receive the `authenticated` event. You can also use [Get Merchant Accounts](/api-reference/accounts/get-accounts) to retrieve this and other merchant accounts, as well as their connection status. This can be useful to know that you should display the merchant account to the user in their list of linked merchant accounts with the appropriate connection status (i.e. `connected` or `disconnected`). See more about handling disconnected merchant accounts [here](/shopping/quickstart#handle-disconnected-merchant-accounts). ## Add a product to a cart Call [Sync Cart](/api-reference/products/shopping/sync-cart) to add a product to a cart using the `product.external_id`. To be notified when a product is successfully added to a cart and to receive cart information, listen to the [`SYNC_CART_SUCCEEDED`](/shopping/webhook-events/sync-cart-succeeded) webhook. #### Update delivery address (optional) If a user would like to update their delivery address after it was initially provided in [Sync Cart](/api-reference/products/shopping/sync-cart), simply make the same request again with a new `delivery_location`, like you are patching the cart. You will receive fresh information regarding the cart, including the `price.total`. A delivery address must be present in the user's merchant account prior to checkout if the fulfillment is via any delivery option. Therefore, if a `delivery_location` is not provided in [Sync Cart](/api-reference/products/shopping/sync-cart) and/or a `delivery_location` is not provided back in `SYNC_CART_SUCCEEDED`, then you cannot proceed with [Checkout](/api-reference/products/shopping/checkout). #### Update fulfillment preference (optional) If a user would like to update the fulfillment preference for a given product after the initial [Sync Cart](/api-reference/products/shopping/sync-cart) request, simply make the same request again with a value for `products.fulfillment.id` that you received in the `SYNC_CART_SUCCEEDED` webhook. You will receive fresh information regarding the cart, including the `price.total`. ## Checkout Call [Checkout](/api-reference/products/shopping/checkout) to checkout a cart. To be notified when a checkout process is successful, listen to the [`CHECKOUT_SUCCEEDED`](/shopping/webhook-events/checkout-succeeded) webhook, which will include an array of transactions by `Id` created by the checkout. ## Get order confirmation details When you receive the [`CHECKOUT_SUCCEEDED`](/shopping/webhook-events/checkout-succeeded) webhook, call [Get Transaction By Id](/api-reference/products/transaction-link/get-by-id) for each transaction you receive (using the `transaction.id`) to retrieve transaction information and subsequently enrich an order confirmation. ## Handle disconnected merchant accounts If for example a user changes their password to a merchant account, the `connection.status` in [Get Merchant Accounts](/api-reference/accounts/get-accounts) will be returned as `disconnected`. You will not be able to make any successful requests to [Sync Cart](/api-reference/products/shopping/sync-cart) or [Checkout](/api-reference/products/shopping/checkout) until the user's merchant account is reconnected. If this occurs, you'll be notified via the [`ACCOUNT_LOGIN_REQUIRED`](/link/webhook-events/account-login-required) webhook event. You'll want to display a UX in your app to allow users to reconnect their account. For example, you may choose to display a button that says "Reconnect" or similarly allow the user to invoke the SDK to reconnect their account. To test this behavior in development, use the [Disconnect Account](/api-reference/development/disconnect-account) endpoint. ## Unlink merchant accounts To unlink a user's specific merchant account if they request it, make a request to [Unlink Merchant Account](/api-reference/accounts/unlink-account). # Testing Source: https://docs.knotapi.com/shopping/testing Test the Shopping flow including linking accounts, syncing carts, and checkout using test credentials in development. The below steps and set of credentials allow you to test linking a merchant account, syncing a cart, checking out a cart, and retrieving post-purchase transaction data. Please note that you cannot perform multiple operations on the same merchant account simultaneously, so you must wait to receive a webhook event to confirm the operation is complete before proceeding with any additional requests. \ \ For testing purposes, you can also use a different `external_user_id` when beginning a new round of testing to ensure you link a new merchant account. Call [Create Session](/api-reference/sessions/create-session) with `type: link` and a dummy `external_user_id` that you will use in subsequent API requests. Alternatively, you can use the [Link Account](/api-reference/development/link-account) endpoint to bypass the client-side SDK in the development environment for testing. Use the session to invoke the SDK. In `KnotConfiguration`, pass the session in `sessionId` and a `merchantId` for a merchant of your choosing. In your implementation, merchants are retrieved via [List Merchants](/api-reference/merchants/list-merchants) by passing `type: shopping`. For testing purposes, you can likely hardcode a single `merchantId` for a merchant. Once you've invoked the SDK, you can link a merchant account by logging in to the merchant using the credentials `user_good` / `pass_good`. Once your merchant account is successfully linked, you will receive the `AUTHENTICATED` webhook event. Upon receiving the `AUTHENTICATED` webhook event, make a request to [Sync Cart](/api-reference/products/shopping/sync-cart) with the same `external_user_id` & `merchant_id` as you used in prior steps and a dummy `products.external_id`. Optionally, pass a `delivery_location` as well in the request. Optionally, send `simulate: failed` in the request to simulate receiving a `SYNC_CART_FAILED` webhook event. Receive the `SYNC_CART_SUCCEEDED` webhook event. Optionally, make another request to [Sync Cart](/api-reference/products/shopping/sync-cart) with the `fullfillment.id` of an alternative fulfillment option received in the webhook. Upon receiving the `SYNC_CART_SUCCEEDED` webhook, make a request to [Checkout](/api-reference/products/shopping/checkout) with the same `external_user_id` & `merchant_id` as you used in prior steps and optionally a `payment_method` object. Optionally, send `simulate: failed` in the request to simulate receiving a `CHECKOUT_FAILED` webhook event. Receive the `CHECKOUT_SUCCEEDED` webhook event. Extract the `transactions.id` value(s) from the `CHECKOUT_SUCCEEDED` webhook event and make a request to [Get Transaction By ID](/api-reference/products/transaction-link/get-by-id) for each transaction to retrieve the transaction information. Most merchants generate a single transaction for a purchase, but some generate multiple. # CHECKOUT_FAILED Source: https://docs.knotapi.com/shopping/webhook-events/checkout-failed api-reference/openapi.json webhook checkout_failed Triggered when checkout fails for a user's merchant cart. Fired when checkout fails for a user's merchant cart. # CHECKOUT_SUCCEEDED Source: https://docs.knotapi.com/shopping/webhook-events/checkout-succeeded api-reference/openapi.json webhook checkout_succeeded Fired when checkout succeeds for a user's merchant cart. Fired when checkout succeeds for a user's merchant cart. # SYNC_CART_FAILED Source: https://docs.knotapi.com/shopping/webhook-events/sync-cart-failed api-reference/openapi.json webhook sync_cart_failed Fired when adding products to a user's merchant cart fails. Fired when adding products to a user's merchant cart fails. # SYNC_CART_SUCCEEDED Source: https://docs.knotapi.com/shopping/webhook-events/sync-cart-succeeded api-reference/openapi.json webhook sync_cart_succeeded Fired when adding products to a user's merchant cart is successful. Fired when adding products to a user's merchant cart is successful. # Quickstart Source: https://docs.knotapi.com/subscription-manager/quickstart Get started with SubscriptionManager to retrieve and manage subscription data from merchant accounts. ## Introduction SubscriptionManager allows you to retrieve subscription information from a user's merchant accounts. There are two ways to retrieve subscriptions depending on your use case: 1. After provisioning your card to the merchant account with [CardSwitcher](/card-switcher/quickstart): the user authenticates to the merchant account, their card is provisioned, and their subscription data is returned in the [`CARD_UPDATED`](/card-switcher/webhook-events/card-updated) webhook event. 2. Without provisioning a card: the user authenticates to the merchant account and their subscription data is returned in the [`NEW_SUBSCRIPTIONS_AVAILABLE`](/subscription-manager/webhook-events/new-subscriptions-available) webhook event. SubscriptionManager must be enabled for your account. Reach out to the Knot team to get started. ## Getting started If you have already integrated [CardSwitcher](/card-switcher/quickstart), you can receive subscription data as part of the card switch flow without any additional client-side integration. ### Receive subscription IDs When a card is successfully updated, the [`CARD_UPDATED`](/card-switcher/webhook-events/card-updated) webhook will include subscription IDs in `data.subscriptions`: ```json theme={"system"} { "event": "CARD_UPDATED", ... "data": { "card_id": "123456789", "subscriptions": [ { "id": "ka8sdf0asdfm10as0a0sdfja7ssa8" }, { "id": "8asdh29qjss923kd0d920skd8sjd8" } ] } } ``` Use this approach when you want to retrieve subscription data without provisioning a card to the user's merchant account. For example, you may choose this option when the user's card is already present on the merchant account or you have not issued the user a card at all, but they want to take advantage of subscription management functionality. ### Set up Ensure you have access to your [Customer Dashboard](https://dashboard.knotapi.com) and retrieve your `client_id` and `secret`, which you will use as the basic auth username and password for your API key respectively. Note that your `client_id` and `secret` vary between the `development` and `production` environments. Install and import an SDK for your platform: [iOS](/sdk/ios), [Android](/sdk/android), [React Native](/sdk/react-native), [Flutter](/sdk/flutter), or [Web](/sdk/web). Register a webhook endpoint in the [Customer Dashboard](https://dashboard.knotapi.com/developers/webhooks) and subscribe to the [webhook events](/webhooks) your backend needs to receive. ### Start the flow Call [List Merchants](/api-reference/merchants/list-merchants) to retrieve a list of merchants that are available for the SubscriptionManager product by passing `type = subscription_manager`. These are merchants you can display in your app and allow users to link. More [here](/link/retrieving-and-listing-merchants) on natively displaying available merchants. You will be notified via the [`MERCHANT_STATUS_UPDATE`](/link/webhook-events/merchant-status-update) webhook when the availability of merchants changes by product & platform, so it is not necessary to call [List Merchants](/api-reference/merchants/list-merchants) each time users interact with your app. Merchant IDs are static and remain constant across all environments. With your `client_id` and `secret` for the `development` environment, call [Create Session](/api-reference/sessions/create-session) with `type: link` to create a session used when invoking the SDK. This will allow the user to link their merchant account without provisioning a card. Initialize the SDK with the `session_id` retrieved from [Create Session](/api-reference/sessions/create-session) and a merchant `Id` retrieved from [List Merchants](/api-reference/merchants/list-merchants) in `KnotConfiguration`. The SDK is where users will interact with the Knot UI to authenticate to various merchants. All login flows, including step-up authentication, are handled within the SDK. Wire up `onSuccess`, `onError`, `onExit`, and `onEvent` to react to client-side events as the user moves through the flow. See further details on callback events for [iOS](/sdk/ios#events), [Android](/sdk/android#events), [React Native](/sdk/react-native#events), [Flutter](/sdk/flutter#events), and [Web](/sdk/web). **Tag your entry points.** To provide better visibility into conversion across different entry points, the Knot SDK supports an `entryPoint` parameter when invoking the SDK. This value is returned in the down-funnel [`AUTHENTICATED`](/link/webhook-events/authenticated) webhook event, allowing you to measure conversion by entry point in your analytics tool of choice. Pass a distinct `entryPoint` value for each entry point location where the Knot SDK is invoked in your app. Common examples: `onboarding`, `home`, `push-notif-X`, `in-app-lifecycle-card-X`. Tagging entry points is strongly recommended to future-proof visibility into your implementation and allow for downstream conversion optimizations. **In the development environment,** use testing credentials to login to a merchant account and simulate retrieving subscriptions. Ingest the [`AUTHENTICATED`](/link/webhook-events/authenticated) webhook to notify your backend that the merchant account is successfully linked to Knot and that the connection status is `connected`. You can also use [Get Merchant Accounts](/api-reference/accounts/get-accounts) to retrieve this and other merchant accounts, as well as their connection status. ### Receive subscription IDs After the user authenticates, Knot automatically pulls subscription data from the merchant account. Listen for the [`NEW_SUBSCRIPTIONS_AVAILABLE`](/subscription-manager/webhook-events/new-subscriptions-available) webhook to be notified of subscriptions on the merchant account. ```json theme={"system"} { "event": "NEW_SUBSCRIPTIONS_AVAILABLE", ... "data": { "subscriptions": [ { "id": "ka8sdf0asdfm10as0a0sdfja7ssa8" }, { "id": "8asdh29qjss923kd0d920skd8sjd8" } ] } } ``` ### Retrieve full subscription details Upon receiving the webhook, call [Get Subscription By ID](/api-reference/products/subscriptions/get-by-id) for each ID in `data.subscriptions` to retrieve the full [subscription object](/api-reference/products/subscriptions/subscription-object). ## Get ongoing updates ### New subscriptions To be notified when new subscriptions are detected on a merchant account, listen to the [`NEW_SUBSCRIPTIONS_AVAILABLE`](/subscription-manager/webhook-events/new-subscriptions-available) webhook. You will receive this event each time a new subscription is found on the merchant account. Upon receiving the [`NEW_SUBSCRIPTIONS_AVAILABLE`](/subscription-manager/webhook-events/new-subscriptions-available) webhook, make a request to [Get Subscription By ID](/api-reference/products/subscriptions/get-by-id) for each ID in `data.subscriptions`, passing the ID received in the webhook as a path parameter. ### Updated subscriptions Receiving updated subscription information is entirely optional and may not be relevant for your use case. To be notified about updates to existing subscriptions, listen to the [`UPDATED_SUBSCRIPTIONS_AVAILABLE`](/subscription-manager/webhook-events/updated-subscriptions-available) webhook. You will receive this event for a merchant account each time there are existing subscriptions for which data has changed (e.g. a price change or status update). Upon receiving the [`UPDATED_SUBSCRIPTIONS_AVAILABLE`](/subscription-manager/webhook-events/updated-subscriptions-available) webhook, make a request to [Get Subscription By ID](/api-reference/products/subscriptions/get-by-id) for each ID in `data.subscriptions`, passing the ID received in the webhook as a path parameter. ## Cancel a subscription Once a user has linked a merchant account, they can cancel a subscription or bill associated with that merchant account. Follow the steps below to determine whether cancellation is available and to execute it. Call [Get Merchant Accounts](/api-reference/accounts/get-accounts) with the user's `external_user_id` and `merchant_id` to check whether cancellation is supported for that merchant account. ```bash theme={"system"} curl --request GET \ --url 'https://development.knotapi.com/accounts/get?external_user_id=15d036d8-0ae5-41bb-84fa-88f839465e5e&merchant_id=45' \ --header 'Authorization: Basic Y2xpZW50X2lkOnNlY3JldA==' ``` In the response, check that the merchant account's `connection.scopes` array contains an object with `"type": "cancel"` **and** that the subscription's [`is_cancellable`](/api-reference/products/subscriptions/get-by-id#response-is-cancellable) field is `true`. Only when both conditions are met should you display a cancel button to the user. ```json theme={"system"} // Example connection object from Get Merchant Accounts response { "connection": { "status": "connected", "scopes": [ { "type": "update_card" }, { "type": "cancel" } ] } } ``` When the user taps the cancel button, call [Cancel Subscription](/api-reference/products/subscriptions/cancel) with the subscription ID to cancel the subscription or bill within seconds. Subscribe to the [`CANCELLATION_SUCCEEDED`](/subscription-manager/webhook-events/cancellation-succeeded) and [`CANCELLATION_FAILED`](/subscription-manager/webhook-events/cancellation-failed) webhooks to be notified of the outcome. ## Handle disconnected merchant accounts If for example a user changes their password to a merchant account, the [Get Merchant Accounts](/api-reference/accounts/get-accounts) endpoint will return `connection.status: disconnected` and you'll be notified via the [`ACCOUNT_LOGIN_REQUIRED`](/link/webhook-events/account-login-required) webhook event. When this occurs, subscription data will not be refreshed (e.g. in case there is a plan or status change) and you will not be able to make requests to [Cancel Subscription](/api-reference/products/subscriptions/cancel) without the user relinking their merchant account. You'll want to display a UX in your app to allow users to reconnect their account. For example, you may choose to display a button that says "Reconnect" or "Refresh" depending on the exact UX. When the user clicks the button, call [Create Session](/api-reference/sessions/create-session) with `type: link` and invoke the SDK to allow the user to reconnect their merchant account. To test this behavior in development, use the [Disconnect Account](/api-reference/development/disconnect-account) endpoint. ## Testing To test SubscriptionManager in the `development` environment, use one of the merchants below. The following is a subset of merchants available for testing. It is not the full list of supported merchants. * Verizon * T-Mobile * Spectrum * Xfinity Internet * Xfinity Mobile * Apple * Netflix * Disney+ * Hulu * Spotify **Via CardSwitcher:** Perform a card switch via [CardSwitcher](/card-switcher/quickstart) with one of the merchants above. Testing docs [here](/card-switcher/testing). The resulting `CARD_UPDATED` webhook will include subscription IDs that you can then use to test retrieval of the full subscription details. **Without provisioning a card:** Create a session with `type: link`, initialize the SDK with a test merchant, and authenticate using testing credentials `user_good` / `pass_good`. The [`NEW_SUBSCRIPTIONS_AVAILABLE`](/subscription-manager/webhook-events/new-subscriptions-available) webhook will fire and include subscription IDs that you can then use to test retrieval. # CANCELLATION_FAILED Source: https://docs.knotapi.com/subscription-manager/webhook-events/cancellation-failed api-reference/openapi.json webhook cancellation_failed Fired when a subscription or bill fails to be canceled. Fired when a subscription or bill fails to be canceled. # CANCELLATION_SUCCEEDED Source: https://docs.knotapi.com/subscription-manager/webhook-events/cancellation-succeeded api-reference/openapi.json webhook cancellation_succeeded Fired when a subscription or bill is canceled successfully. Fired when a subscription or bill is canceled successfully. # NEW_SUBSCRIPTIONS_AVAILABLE Source: https://docs.knotapi.com/subscription-manager/webhook-events/new-subscriptions-available api-reference/openapi.json webhook new_subscriptions_available Fired when new subscriptions are available for a user's merchant account. Fired when new subscriptions are available for a user's merchant account. The webhook includes a `data.subscriptions` array of subscription IDs. Call [Get Subscription By ID](/api-reference/products/subscriptions/get-by-id) for each ID when you receive this webhook event. # UPDATED_SUBSCRIPTIONS_AVAILABLE Source: https://docs.knotapi.com/subscription-manager/webhook-events/updated-subscriptions-available api-reference/openapi.json webhook updated_subscriptions_available Fired when updates are available for existing subscriptions for a user's merchant account. Fired when updates are available for existing subscriptions for a user's merchant account. The webhook includes a `data.subscriptions` array of subscription IDs for which data has changed. Call [Get Subscription By ID](/api-reference/products/subscriptions/get-by-id) for each ID when you receive this webhook event. # Quickstart Source: https://docs.knotapi.com/transaction-link/quickstart Get started with the TransactionLink integration to retrieve SKU-level transaction data from merchant accounts. If you have already integrated the CardSwitcher product into your application, you can skip the entire client-side integration for TransactionLink and simply begin [retrieving transactions](/transaction-link/quickstart#receive-transactions) from the API. ## Introduction TransactionLink enables you to retrieve SKU-level transaction data from a user's merchant on a recurring basis. With TransactionLink, you can retrieve both historical transactions and new transactions that will occur in the future. You can reference the exact data you'll receive for each transaction [here](/api-reference/products/transaction-link/transaction-object). ## Entry points #### Overview How and where you place entry points to invoke the Knot flow in your app play a crucial role driving engagement and delivering value to end users. Nearly all apps that integrate Knot develop multiple entry points into the Knot flow (e.g. different tabs or screens). As a result and **to provide better visibility into the conversion of the flow across different entry points**, the Knot SDK supports an `entryPoint` parameter when invoking the SDK. This parameter allows you to specify the entry point from which the user came. This value is then returned in the down-funnel [`AUTHENTICATED`](/link/webhook-events/authenticated) webhook event, thereby allowing you to measure the conversion of the flow by entry point in your analytics tool of choice. #### Usage We strongly recommend taking advantage of this functionality, so as to future-proof your visibility into your implementation and allow for future optimizations. To take advantage of this functionality, simply pass a different value to `KnotConfiguration.entryPoint` for each of your entry points when [configuring the session to invoke the SDK](/sdk/ios#configure-the-session). Common entry points include the following: `onboarding`, `home`, `push-notif-X`, `in-app-lifecycle-card-X`, etc. ## Getting started Check out the [Link Account](/api-reference/development/link-account) endpoint to bypass the client-side SDK and quickly link a merchant account & generate transactions in the development environment for testing. ### Start the flow Ensure you have access to your [Customer Dashboard](https://dashboard.knotapi.com) and retrieve your `client_id` and `secret`, which you will use as the basic auth username and password for your API key respectively. Note that your `client_id` and `secret` vary between the `development` and `production` environments. Subscribe to webhooks in your [Customer Dashboard](https://dashboard.knotapi.com) so your backend can be notified about user-generated events, as well as asynchronous processes. You will need to receive these events later on in the flow. Call [List Merchants](/api-reference/merchants/list-merchants) to retrieve a list of merchants that are available for the TransactionLink product by passing `type = transaction_link`. These are merchants you can display in your app and allow users to link. To get started quickly, you can use `merchant_id: 19` for DoorDash to later pass when initializing the SDK. You will be notified via the [`MERCHANT_STATUS_UPDATE`](/link/webhook-events/merchant-status-update) webhook when/if this list changes. Merchant IDs are static and remain constant across all environments. With your `client_id` and `secret` for the `development` environment, call [Create Session](/api-reference/sessions/create-session) with `type: transaction_link` to create a session used when invoking the SDK. Install an SDK of your choosing, for example on iOS [here](/sdk/ios). If you are using the Web SDK, make sure to allowlist your application's domains for the `development` and `production` environments in your [Customer Dashboard](https://dashboard.knotapi.com). Initialize the SDK with the `session_id` retrieved from [Create Session](/api-reference/sessions/create-session) and a merchant `Id` retrieved from [List Merchants](/api-reference/merchants/list-merchants) in `KnotConfiguration`. Alternatively, you can use `merchant_id: 19` for DoorDash to get started quickly. The SDK is where users will interact with the Knot UI to authenticate to various merchants. All login flows, including step-up authentication, are handled within the SDK. Users will see real-time feedback as they progress through authenticating with a merchant. Specifying an exact merchant by passing a merchant `Id` in `KnotConfiguration` when initializing the SDK is required for the TransactionLink product. ### Link a merchant account **In the development environment,** use [testing credentials](/transaction-link/testing) to login to a merchant account and simulate retrieving transactions. Ingest the [`AUTHENTICATED`](/link/webhook-events/authenticated) webhook to notify your backend that the merchant account is successfully linked to Knot and that the connection status is `connected`. Similarly and as applicable, listen to the client-side `onEvent` callback in the SDK to receive the `authenticated` event. You can also use [Get Merchant Accounts](/api-reference/accounts/get-accounts) to retrieve this and other merchant accounts, as well as their connection status. This can be useful to know that you should display the merchant account to the user in their list of linked merchant accounts with the appropriate connection status (i.e. `connected` or `disconnected`). See more about handling disconnected merchant accounts [here](/transaction-link/quickstart#handle-disconnected-merchant-accounts). ## Receive transactions ### New transactions To be notified about new transactions in a merchant account, listen to the [`NEW_TRANSACTIONS_AVAILABLE`](/transaction-link/webhook-events/new-transactions-available) webhook. You will receive this event shortly after a user authenticates to a merchant account for the first time and on any subsequent instance where new transactions are created in the merchant account. Upon receiving the [`NEW_TRANSACTIONS_AVAILABLE`](/transaction-link/webhook-events/new-transactions-available) webhook, make a request (or multiple) to [Sync Transactions](/api-reference/products/transaction-link/sync) to sync new transactions for a user's specific merchant account. **In the development environment**, you will receive 205 new transactions. ### Updated transactions Receiving updated transaction information is entirely optional and may not be relevant for your use case. To be notified about updates to existing transactions, listen to the [`UPDATED_TRANSACTIONS_AVAILABLE`](/transaction-link/webhook-events/updated-transactions-available) webhook. You will receive this event for a merchant account each time there are existing transactions for which data has changed (e.g. `orderStatus: SHIPPED` -> `orderStatus: DELIVERED`). Upon receiving the [`UPDATED_TRANSACTIONS_AVAILABLE`](/transaction-link/webhook-events/updated-transactions-available) webhook with an array of transaction IDs, make a request to [Get Transaction By Id](/api-reference/products/transaction-link/get-by-id) for each transaction ID, passing the ID received in the webhook as a path parameter. ## Refresh transactions Knot refreshes transactions for a user's linked merchant account once per day. If your use case requires more frequent updates, you can call [Refresh Transactions](/api-reference/products/transaction-link/refresh) to refresh transactions on demand. **To test in the development environment**, call [Link Account](/api-reference/development/link-account) to link a merchant account and generate transactions, sync the transactions via [Sync Transactions](/api-reference/products/transaction-link/sync), then call [Refresh Transactions](/api-reference/products/transaction-link/refresh). The [`NEW_TRANSACTIONS_AVAILABLE`](/transaction-link/webhook-events/new-transactions-available) webhook will be sent again and 2 new transactions will be available. ## Handle disconnected merchant accounts To test this behavior in development, use the [Disconnect Account](/api-reference/development/disconnect-account) endpoint. If for example a user changes their password to a merchant account, the `connection.status` in [Get Merchant Accounts](/api-reference/accounts/get-accounts) will be returned as `disconnected`. You will not receive transaction data for the user's merchant account until it is reconnected. If this occurs, you will be notified via the [`ACCOUNT_LOGIN_REQUIRED`](/link/webhook-events/account-login-required) webhook event. You'll want to display a UX in your app to allow users to reconnect their account. For example, you may choose to display a button that says "Reconnect" or similar. ## Transaction matching This section is only relevant if you are building a use case that requires matching Knot's transaction data to your existing transaction records from a card processor or core banking system, for example to [enrich transaction details](/use-cases/enrich-transaction-details) with SKU-level data. If you are using transaction data independently (e.g. for spending insights or rewards), this section does not apply. Each [transaction](/api-reference/products/transaction-link/transaction-object) from Knot represents an order placed at a merchant. To match it to the corresponding card transaction in your system, match on the following fields: | Knot field | Match against | Matching rule | | ----------------------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | `price.total` | Your transaction amount | Allow a small variance (e.g. within \$0.50) to account for rounding differences, partial authorizations, or post-authorization adjustments like tips. | | `datetime` | Your transaction date | Use a 3-day window. Merchant order dates often differ from card settlement or posting dates by 1-2 days. | | `payment_methods[].last_four` | The last four digits of the user's issued card | Exact match. Use this to confirm the transaction was paid with the card you issued. | | `merchant.name` | Your transaction merchant name (optional) | If your existing transaction record already has a reliable merchant name, use it as an additional matching signal. | ### Filter by order status Before attempting to match, filter transactions from Knot by `order_status`, excluding those with the following values: `CANCELLED`, `REFUNDED`, and `RETURNED`. # Testing Source: https://docs.knotapi.com/transaction-link/testing Test TransactionLink functionality including account linking and transaction generation using test credentials in development. The below steps and sets of credentials allow you to test the end-to-end flow of logging in to merchant accounts and generating transactions. Alternatively, you can use the [Link Account](/api-reference/development/link-account) endpoint to bypass the client-side SDK in the development environment for testing. Call [Create Session](/api-reference/sessions/create-session) with `type: transaction_link` and a dummy `external_user_id`. Please note that if you intend to test generating transactions multiple times consecutively in a short period, we recommend using a different dummy `external_user_id` for each session. Use the `session_id` you receive when creating a session to invoke the SDK. In `KnotConfiguration`, pass a `merchantId` for a merchant of your choosing. In your implementation, merchants are retrieved via [List Merchants](/api-reference/merchants/list-merchants). For testing purposes, you can likely hardcode a single `id` for a merchant. Once you've invoked the SDK, you can login to a merchant account using one of the sets of credentials below, depending on what you would like to test. | Scenario | What will happen | Username | Password | | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | ------------------- | | New transactions | Your user's merchant account will be successfully linked. 205 new transactions will be generated within a few seconds and you will be notified of them via the `NEW_TRANSACTIONS_AVAILABLE` event. The transactions will be retrievable via [Sync Transactions](/api-reference/products/transaction-link/sync). | `user_good_transactions` | `pass_good` | | New **and updated** transactions | Your user's merchant account will be successfully linked. 205 new transactions will be generated within a few seconds and you will be notified of them via the `NEW_TRANSACTIONS_AVAILABLE` event. The transactions will be retrievable via [Sync Transactions](/api-reference/products/transaction-link/sync). Additionally, a few transactions will be updated and you will be notified of them via the `UPDATED_TRANSACTIONS_AVAILABLE` event nearly immediately as well. The transactions will be retrievable via [Get Transaction By ID](/api-reference/products/transaction-link/get-by-id). | `user_good_transactions` | `pass_good_updates` | # NEW_TRANSACTIONS_AVAILABLE Source: https://docs.knotapi.com/transaction-link/webhook-events/new-transactions-available api-reference/openapi.json webhook new_transactions_available Fired when new transactions for a user's merchant account are available. Fired when new transactions for a user's merchant account are available. # UPDATED_TRANSACTIONS_AVAILABLE Source: https://docs.knotapi.com/transaction-link/webhook-events/updated-transactions-available api-reference/openapi.json webhook updated_transactions_available Fired when updated data is available for existing transactions for a user's merchant account. Fired when updated data is available for existing transactions for a user's merchant account. The webhook includes an array of transaction IDs for which there exists updated data. Call [Get Transaction By ID](/api-reference/products/transaction-link/get-by-id) when you receive this webhook event. # Cross-Merchant Savings Source: https://docs.knotapi.com/use-cases/cross-merchant-savings Alert users when a product they bought is available for less at another merchant. Click "Copy Page" to copy this use case as markdown. > This is a product use case page. When implementing, follow the linked quickstarts for canonical API setup steps rather than inferring them from this page. ## Problem Users tend to shop at whichever merchant is most convenient without realizing the same product is available for less at another retailer. Neither large retailers nor delivery platforms are uniformly cheaper - price differences depend on the specific product, and the gap can be significant. Without SKU-level visibility into what was purchased and where, there is no practical way to surface these comparisons. ## Solution Use SKU-level transaction data from Knot's [Sync Transactions](/api-reference/products/transaction-link/sync) endpoint to build a cross-merchant price index across your user base. After each purchase, check whether the same product is available at a lower price at another merchant. Surface the savings opportunity as a proactive tip. iPhone lock screen showing a push notification: Save $4.50 on Tide PODS - You paid $24.99 at Target. The same pack is $20.49 on Amazon. This turns passive spending data into actionable savings guidance, without requiring the user to comparison shop on their own. ## Flow ```mermaid placement="top-right" actions={false} theme={"system"} %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#f5f5f5', 'primaryTextColor': '#000', 'primaryBorderColor': '#000', 'lineColor': '#000', 'secondaryColor': '#f5f5f5', 'tertiaryColor': '#f5f5f5', 'edgeLabelBackground': '#fff', 'fontSize': '16px'}}}%% flowchart LR A[User switches their card at a merchant through Knot or simply links their merchant account to begin syncing SKU-level purchase data] --> B[Sync each user's purchase data daily from Knot's API] B --> C[Your server adds each product to a cross-merchant price index] C --> D[On each new transaction, check for lower prices at other merchants] D --> E{Savings above threshold?} E -->|Yes| F[Surface price comparison to the user] E -->|No| G[No action needed] ``` ## Implementation When a user switches their card at a merchant or links their merchant account, Knot begins collecting transaction data. Listen for the [`NEW_TRANSACTIONS_AVAILABLE`](/transaction-link/webhook-events/new-transactions-available) webhook event, then call [Sync Transactions](/api-reference/products/transaction-link/sync) to pull the user's transactions. **Key fields** | Field | Purpose | | ----------------------------- | ---------------------------------------------------------------- | | `products[].name` | Product display name, used as the cross-merchant matching key. | | `products[].external_id` | Merchant-specific product identifier. | | `products[].price.unit_price` | Price paid at this merchant. | | `products[].category` | Knot-provided category, used to validate matches are comparable. | | `products[].subcategory` | Knot-provided subcategory for narrower match validation. | | `products[].image_url` | Product image for the comparison card. | | `merchant.id` | Identifies which merchant this price is from. | | `merchant.name` | Retailer display name for the UI. | | `datetime` | Purchase timestamp. | | `external_user_id` | Identifies the user in your system. | | `order_status` | Filter to completed orders only. | Only include transactions with `order_status` of `COMPLETED`, `DELIVERED`, `PICKED_UP`, `SHIPPED`, `BILLED`, or `ORDERED`. Filter out `CANCELLED`, `REFUNDED`, and `FAILED`. As products are synced, maintain a shared price index keyed by product name and merchant. Use `products[].name` combined with `products[].subcategory` (or `products[].category` as fallback) to identify the same product at different merchants. ```text theme={"system"} FOR each product in new_transaction.products: upsert into price_index: key: (normalized_name, subcategory, merchant_id) fields: (name, unit_price, image_url, merchant_name) ``` This index is populated by data across all your users. A user who has only connected one merchant can still receive alerts about prices at other merchants, because other users' transactions contribute to the index. On each incoming transaction, check whether any product in the order appears in the price index at a lower price at a different merchant. ```text theme={"system"} FOR each product in new_transaction.products: cheaper_options = query price_index WHERE normalized_name == normalize(product.name) AND subcategory == product.subcategory // fall back to category if null AND merchant_id != new_transaction.merchant.id AND unit_price < product.price.unit_price IF cheaper_options is not empty: best = option with lowest unit_price savings = product.price.unit_price - best.unit_price savings_pct = savings / product.price.unit_price IF savings >= MIN_SAVINGS AND savings_pct >= MIN_SAVINGS_PCT: queue comparison for external_user_id ``` **Configuration decisions for your team:** | Parameter | Suggested Default | Considerations | | ------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------- | | `MIN_SAVINGS` | \$1.00 | Cross-merchant switches have more friction than same-merchant swaps. A higher floor keeps alerts worth acting on. | | `MIN_SAVINGS_PCT` | 10% | Avoids surfacing marginal differences on high-priced items. | | Max comparisons per order | 2 | One or two clear wins is more actionable than a long list. | | Alert frequency | Once per product per user | Re-evaluate when a meaningful price change is detected (e.g., gap widens by \$1+). | | Price freshness | 30 days | Discard index entries older than 30 days. Prices shift and stale data leads to false alerts. | **Trigger timing options:** * **Real-time (preferred):** Run the check on each incoming transaction. Alerts surface while the purchase is fresh in the user's mind. * **Weekly digest:** Aggregate all cross-merchant savings opportunities from the past week and present a cumulative summary. Lower friction for users who prefer fewer notifications. * **Hybrid:** Real-time for savings above \$2 per item; weekly digest for smaller gaps. When a cheaper price is found, notify the user with the product, what they paid, where it is cheaper, and the savings amount. iPhone lock screen showing a push notification: Save $4.50 on Tide PODS - You paid $24.99 at Target. The same pack is $20.49 on Amazon. Deep-link the notification to a screen where the user can view the full comparison and navigate to the product at the cheaper merchant. ## Expansion Path * **Merchant recommendations:** If a user consistently overpays at one merchant relative to another on a set of products they buy regularly, surface a merchant-level recommendation rather than per-product alerts. "Most of what you buy at Walmart is cheaper on Amazon" is a more compelling insight than individual item comparisons. * **Weekly savings digest:** Aggregate all cross-merchant comparisons from the week into a single card showing total potential savings. This keeps the feature visible for users who prefer a lower notification volume. # Dispute Deflection Source: https://docs.knotapi.com/use-cases/dispute-deflection Reduce first-party fraud by showing users exactly what they purchased before they file a dispute. Click "Copy Page" to copy this use case as markdown. > This is a product use case page. When implementing, follow the linked quickstarts for canonical API setup steps rather than inferring them from this page. ## Problem A significant share of transaction disputes filed by cardholders are not true fraud. They are first-party disputes, sometimes called "friendly fraud," where a user disputes a charge they actually made. This happens for a few reasons: the user does not recognize a charge amount, they forgot what they ordered, or they find it easier to dispute than to track down a receipt. Traditional transaction feeds show a merchant name and a dollar amount, but that is often not enough for a user to confidently confirm a purchase was theirs. Every dispute costs the card issuer time and money, whether or not it turns out to be legitimate. Investigations, provisional credits, chargeback fees, and support interactions add up. If you could show users what they actually bought before they escalate, many of those disputes would never be filed. ## Solution Use SKU-level transaction data from Knot's [Sync Transactions](/api-reference/products/transaction-link/sync) endpoint to display the exact items purchased in a transaction, along with fulfillment details like order status and delivery confirmation. Surface this information on the transaction detail screen, and again at the point where a user initiates a dispute. When a user sees the specific products they ordered and that the order was delivered to their address, they can immediately recognize the charge as legitimate. Dispute deflection screen on an iPhone showing a $104.78 Target order with delivery status, address, and itemized products including milk, chicken, paper towels, and a coffee maker, with options to continue with dispute or go back This creates a natural deflection point: users confirm the purchase is theirs and abandon the dispute before it enters your investigation pipeline, saving your team from unnecessary chargeback work. ## Flow ```mermaid placement="top-right" actions={false} theme={"system"} %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#f5f5f5', 'primaryTextColor': '#000', 'primaryBorderColor': '#000', 'lineColor': '#000', 'secondaryColor': '#f5f5f5', 'tertiaryColor': '#f5f5f5', 'edgeLabelBackground': '#fff', 'fontSize': '16px'}}}%% flowchart LR A[User switches their card at a merchant through Knot or links their merchant account] --> B[Sync SKU-level transaction data from Knot] B --> C[User initiates a dispute on a transaction] C --> D[Display itemized products and delivery details] D --> E{User recognizes the purchase?} E -->|Yes| F[User cancels the dispute] E -->|No| G[Proceed with dispute flow] ``` ## Implementation When a user switches their card at a merchant or links their merchant account, Knot begins collecting transaction data for that account. Listen for the [`NEW_TRANSACTIONS_AVAILABLE`](/transaction-link/webhook-events/new-transactions-available) webhook event, then call [Sync Transactions](/api-reference/products/transaction-link/sync) to pull the user's transactions. Store the SKU-level data so it can be retrieved when the user views or disputes a transaction. As transactions are updated (e.g., an order ships or is delivered), listen for the [`UPDATED_TRANSACTIONS_AVAILABLE`](/transaction-link/webhook-events/updated-transactions-available) webhook and re-sync the affected transactions via [Get Transaction By ID](/api-reference/products/transaction-link/get-by-id) to keep fulfillment status updated. Persist the SKU-level transaction data so it can be retrieved when a user views a transaction in your app. Match each SKU-level transaction from Knot to the corresponding card transaction in your system using [this guide](https://docs.knotapi.com/transaction-link/quickstart#transaction-matching) to determine exact fields to match on. When a user taps on a transaction in your app and navigates to the transaction detail screen, display the fully-enriched set of transaction information, including the products, order status, and delivery information. In this way, they are more likely to recognize their purchase. Only display transactions with `order_status` of `COMPLETED`, `DELIVERED`, `PICKED_UP`, `SHIPPED`, `BILLED`, or `ORDERED`. Filter out `CANCELLED`, `REFUNDED`, and `FAILED`. **Additional fields to display:** | Field | Purpose | | --------------------------- | -------------------------------------------------------------------------------------------- | | `products[].name` | Product name for display. | | `products[].description` | Product description for display. | | `products[].image_url` | Product image URL to display the image of each product. | | `products[].url` | Product URL to link out to the product. | | `products[].price.total` | Price paid for each product for display. | | `products[].external_id` | Unique product identifier. | | `order_status` | Current status of the order (e.g. `DELIVERED`, `SHIPPED`). | | `shipping.location.address` | Delivery address including `line1`, `line2`, `city`, `region`, `postal_code`, and `country`. | **If Knot SKU-level txn information is NOT available:** There are a few scenarios where your system may not have enriched, SKU-level transaction data from Knot for a corresponding card transaction in your system. Below are each scenario outlined with their solution if applicable: | Scenario | Solution | | --------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | The corresponding merchant account was never linked to your app or is currently disconnected. | If your system is aware of the merchant for the transaction, display a button on the transaction detail screen requesting the user link (or relink) the appropriate merchant account. If your system is not aware of the merchant, display an identical button that instead navigates to a list of merchants for the user to choose to link. | | The transaction is recent (e.g. occurred within the last 24 hours). | Call Knot's `Refresh Transactions` endpoint to refresh the synced SKU-level transactions or simply do not display the enriched information. | This makes the enriched experience feel like a natural upgrade rather than an inconsistent feature. If a user chooses to initiate a dispute on a transaction where SKU-level transaction data is available, insert a confirmation step before the dispute flow that surfaces the enriched, SKU-level transaction data below: | Field | Purpose | | :-------------------------- | :------------------------------------------------------------------------------------------- | | `products[].name` | Product name for display. | | `products[].description` | Product description for display. | | `products[].image_url` | Product image URL to display the image of each product. | | `products[].url` | Product URL to link out to the product. | | `products[].price.total` | Price paid for each product for display. | | `products[].external_id` | Unique product identifier. | | `order_status` | Current status of the order (e.g. `DELIVERED`, `SHIPPED`). | | `shipping.location.address` | Delivery address including `line1`, `line2`, `city`, `region`, `postal_code`, and `country`. | If the user confirms the purchase is theirs, dismiss the dispute flow and return them to the transaction detail screen. If the user does not recognize the purchase, proceed with your standard dispute flow. The enriched data still has value here: your investigations team can reference the itemized breakdown when reviewing the case, reducing the time spent manually researching the transaction. ## Expansion Path * **Dispute analytics** - Track how often users abandon disputes after seeing the enriched breakdown versus how often they proceed. This gives you a measurable deflection rate to quantify the impact on chargeback volume and operational costs. * **Dispute operations tooling** - Expose the same enriched transaction data to your dispute operations team. When a user calls in about an unrecognized charge, the agent can read back the specific items and delivery details, resolving the inquiry without escalating to a formal dispute. # Enrich transactions with SKU-level purchase details Source: https://docs.knotapi.com/use-cases/enrich-transaction-details Use Knot's Sync Transactions API to enrich bank transactions with item-level purchase data — product names, images, quantities, and prices. Click "Copy Page" to copy this use case as markdown. > This is a product use case page. When implementing, follow the linked quickstarts for canonical API setup steps rather than inferring them from this page. ## Problem When users open their banking or fintech app and scroll through recent transactions, the best they typically see is a cleaned-up merchant name (*sometimes*) and a dollar amount. A \$36.58 charge at DoorDash tells them where they spent money, but not what they actually ordered. This is the L3 transaction data gap. Without item-level detail, users cannot verify that a charge is legitimate, recall what they purchased, or feel confident that their account activity is accurate. This gap is especially problematic for fraud detection. A user who sees an unfamiliar charge amount at a merchant they do use has no way to confirm whether the purchase was theirs without leaving your app and digging through email receipts or merchant order histories. ## Solution Use SKU-level transaction data from Knot's [Sync Transactions](https://docs.knotapi.com/api-reference/products/transaction-link/sync) endpoint to enrich transactions in your app with the individual items purchased, including product names, descriptions, images, quantities, and prices. Further data such as the order status and delivery information is also available. Surface this data on the dedicated transaction detail screen your app already has for each transaction, so users can see exactly what was in each order. Transaction detail screen showing a $104.78 Target order with itemized products including milk, chicken, paper towels, and a coffee maker This turns every transaction from a generic line item into a rich, verifiable record, giving users confidence in their purchase history and making your app the first place they check when something looks off. ## Flow ```mermaid placement="top-right" actions={false} theme={"system"} %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#f5f5f5', 'primaryTextColor': '#000', 'primaryBorderColor': '#000', 'lineColor': '#000', 'secondaryColor': '#f5f5f5', 'tertiaryColor': '#f5f5f5', 'edgeLabelBackground': '#fff', 'fontSize': '16px'}}}%% flowchart LR A[User switches their card at a merchant through Knot or simply links their merchant account to begin syncing SKU-level purchase data] --> B[Sync each user's purchase data daily from Knot's API] B --> C[Store SKU-level transaction data] C --> D[Match SKU-level transactions to existing transactions from processor] D --> E[Display enriched products and other info on transaction detail screen] ``` ## Implementation When a user switches their card at a merchant or links their merchant account, Knot begins collecting transaction data for that account. Listen for the `NEW_TRANSACTIONS_AVAILABLE` webhook event, then call [Sync Transactions](/api-reference/products/transaction-link/sync) to pull the user's transactions. Persist the SKU-level transaction data so it can be retrieved when a user views a transaction in your app. Match each SKU-level transaction from Knot to the corresponding card transaction in your system using [this guide](/transaction-link/quickstart#transaction-matching) to determine exact fields to match on. When a user taps on a transaction in your app and navigates to the transaction detail screen, display the fully-enriched set of transaction information, including the products, order status, and delivery information. Only display transactions with `order_status` of `COMPLETED`, `DELIVERED`, `PICKED_UP`, `SHIPPED`, `BILLED`, or `ORDERED`. Filter out `CANCELLED`, `REFUNDED`, and `FAILED`. **Additional fields to display:** | Field | Purpose | | --------------------------- | -------------------------------------------------------------------------------------------- | | `products[].name` | Product name for display. | | `products[].description` | Product description for display. | | `products[].image_url` | Product image URL to display the image of each product. | | `products[].url` | Product URL to link out to the product. | | `products[].price.total` | Price paid for each product for display. | | `products[].external_id` | Unique product identifier. | | `order_status` | Current status of the order (e.g. `DELIVERED`, `SHIPPED`). | | `shipping.location.address` | Delivery address including `line1`, `line2`, `city`, `region`, `postal_code`, and `country`. | **If Knot SKU-level txn information is NOT available:** There are a few scenarios where your system may not have enriched, SKU-level transaction data from Knot for a corresponding card transaction in your system. Below are each scenario outlined with their solution if applicable: | Scenario | Solution | | --------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | The corresponding merchant account was never linked to your app or is currently disconnected. | If your system is aware of the merchant for the transaction, display a button on the transaction detail screen requesting the user link (or relink) the appropriate merchant account. If your system is not aware of the merchant, display an identical button that instead navigates to a list of merchants for the user to choose to link. | | The transaction is recent (e.g. occurred within the last 24 hours). | Call Knot's `Refresh Transactions` endpoint to refresh the synced SKU-level transactions or simply do not display the enriched information. | This makes the enriched experience feel like a natural upgrade rather than an inconsistent feature. ## Expansion Path * **Handle updated transactions** - Transactions can change after they are first synced, for example when an order ships, a refund is issued, or items are modified. Listen for the [`UPDATED_TRANSACTIONS_AVAILABLE`](/transaction-link/webhook-events/updated-transactions-available) webhook and re-sync the affected transactions to keep your stored data current. * **Fraud verification flow** - When a user disputes or flags a transaction, show the itemized breakdown as a first step before escalating. If the user recognizes the items, they can dismiss the dispute immediately, reducing false dispute rates and support volume. # Knot use case gallery for banking and fintech apps Source: https://docs.knotapi.com/use-cases/gallery Browse product use cases you can build with Knot — card switching, subscription management, transaction enrichment, price adjustments, and more. If you have already integrated the CardSwitcher product into your application, you can skip the entire client-side integration for TransactionLink and simply begin [retrieving transactions](/transaction-link/quickstart#receive-transactions) from the API. ## Transaction Data Each [transaction](/api-reference/products/transaction-link/transaction-object) includes all of the following information: 1. Exact [products](/api-reference/products/transaction-link/transaction-object#schema-products) (SKUs) that were included in the purchase, including their brand name, price, and quantity. This qualifies the transaction data as Level 3 (L3) and provides extreme granularity into a user's spending habits. 2. [Payment method(s)](/api-reference/products/transaction-link/transaction-object#schema-payment-methods) used in the transaction (e.g. card, BNPL, Apple Pay, cash, etc.), whether single-tender or split-tender, providing share-of-wallet and competitive insights. 3. Receipt-level [price](/api-reference/products/transaction-link/transaction-object#schema-price) information, including the subtotal, list of adjustments (e.g. tax, tip, fees), and total. 4. [Status of the order](/api-reference/products/transaction-link/transaction-object#schema-order-status) (e.g. shipped, delivered, picked up, etc.), unlocking fulfillment lifecycle visibility. 5. Much more... The transactions you receive from the Knot platform include the following: 1. Both historical and future transactions. 2. Both online and **in-store** transactions. 3. Transactions made on **all payment methods**, including competitive payment methods to the card(s) you may issue (e.g. other cards, digital wallets, BNPL providers, cash, etc.). ## Featured Use Cases
## Additional Use Cases ### AI-assistant If you have plans to integrate an AI-financial assistant into your product (or already have this concept), such a tool becomes significantly more powerful with access to a user's SKU-level transaction data. The assistant could monitor a user's purchasing behavior, unlocking any of the following "sub-use cases": 1. Smart budgeting suggestions, such as "You’re buying snacks three times a week — switching to bulk orders on Amazon could save you `$25/month`." 2. Anomaly detection, such as "You usually spend `$60–$80` on groceries, but this week’s Walmart order was `$150`. Was that intentional?" 3. Receipt-level contextual responses, such as being able to answer “Show me what I bought at Target last Tuesday”. 4. Cashback recommendations by identifying existing brands purchased and recommending for example "You could earn 5% back at this month on your La Croix purchase if you buy at Target.” 5. Financial coaching, such as mentioning "You spent `$120` on takeout this week — that's 3× your usual average. Cooking twice at home could save `$80/month`." 6. Cross-sell product recommendations, such as "I noticed you use Affirm frequently to pay for products at Walmart. Take a peek at our new XYZ credit product to lower your rate." 7. Personalized shopping assistance, such as offering the following: "You usually buy school snacks from Walmart. Want me to check if Target has them cheaper this week?" 8. Meal planning by offering relevant recipes like "You bought pasta, sauce, and garlic bread today — want me to suggest three 20-minute dinner recipes using those ingredients?" ### Competitive insights #### Share of wallet Each transaction includes the payment method(s) used for the purchase across all payment methods stored in the user's merchant account (the full list found [here](/api-reference/products/transaction-link/transaction-object#schema-payment-methods-type)). Therefore, for a given user or across your entire user base, you can determine the spend going towards competitive payment methods. This can give you a sense for how much spend you could be capturing onto your card, as well as the % of spending you're already capturing across grocery, retail, and other discretionary spend. #### Cross-sell new products For users purchasing with competitive payment methods to your own card or other payment products, you can determine that certain of your payment products may be desirable alternatives to those users. For example, you may identify users purchasing with BNPL products and choose to offer them access to your new credit card that offers better rates and more rewards. ### Rewards #### Design a more tailored rewards program Fundamentally, if you can understand the brands and even specific items or services that users purchase, then you can be more strategic about designing your rewards program. Through the identification of specific brand affinities across your user base, you can seek to develop more tailored merchant partnerships to provide more relevant reward offers to your users. You can even provide CPG-brand or item-level offers (as opposed to merchant-level). #### Brand & SKU-level reward offers With SKU-level transaction data, you can create and offer CPG-brand and item-level reward offers to your users - cashback, points, discounts, or other incentives. Specifically, by monitoring exactly which brands a user purchases, you can offer rewards to that user on their brands-of-choice and different brand-specific offers to other users. This allows for a hyper-tailored rewards program. Additionally, you can easily determine that a user purchased a product from a specific brand (or even a particular product) and subsequently credit that user for their rewards. Examples: * "Earn 6x points on Under Armour socks at Target" * "Earn 10% cash back on Spicy Cumin Hand-Ripped Noodles at Xian Famous Foods on DoorDash until 11/6/25" #### EBT spending rewards If a particular purchase is made with an EBT SNAP or other EBT balance, you will receive this information in each transaction. Therefore, you could offer rewards on EBT card spend for those frequently using government benefits to purchase groceries and other household staples. ### Savings All savings features could be delivered through an [AI-assistant](/use-cases/gallery#ai-assistant) as well. #### FSA/HSA reimbursement Using the [eligibility](/api-reference/products/transaction-link/transaction-object#schema-products-eligibility) of each product in a transaction, you can determine whether a particular product is eligible to be retroactively reimbursed from an FSA/HSA account. If a product is eligible for reimbursement, automatically submit a reimbursement on the user's behalf to save them money. ### Spending insights All spending insight features could be delivered through an [AI-assistant](/use-cases/gallery#ai-assistant) as well. #### Hyper-granular budgeting You can automatically categorize purchases into categories far more granular than those possible with transaction data from open banking platforms or networks. Instead of relying on MCC codes or categories derived from MCC codes (via transaction enrichment providers), identify transaction categories based on precisely what is included in a purchase. Even categorize a single purchase across multiple categories. For example, let's say you see that a user made the following \$114.68 purchase at Walmart: * 2 bags of Smartfood white cheddar popcorn * 1 bag of nacho cheese doritos * 3 family-size bags of Tostitos scoops * 2 cases of la croix sparkling water * 2 packs of organic chicken breast * 3 heads of lettuce * 5 tomatoes, on the vine * 1 bag of dry, basmati rice You can categorize the first 3 items into "snacks", the La Croix into "drinks", the chicken breast into "meat", the lettuce and tomatoes into "produce", and the rice into "dry good staples". In reality, the categories (and levels of categories) are unlimited and can be customized to the user's liking. For other types of purchases, you could imagine categories like "pet care", "baby products", and "home repair". This level of budgeting categorization is only possible with SKU-level (L3) transaction data and provides users with significantly more control over their budget. #### Carbon footprint estimation You can estimate a user's carbon footprint based on their actual purchases rather than generic merchant categories by using exact [products](/api-reference/products/transaction-link/transaction-object#schema-products) in each transaction. Differentiate between sustainable and non-sustainable items, generate per-transaction climate impact insights, and promote ESG-aligned behavior and products. # Personalized Dining Deals Source: https://docs.knotapi.com/use-cases/personalized-dining-deals Match cashback deals to restaurants users already order from on delivery platforms. Click "Copy Page" to copy this use case as markdown. > This is a product use case page. When implementing, follow the linked quickstarts for canonical API setup steps rather than inferring them from this page. ## Problem Cashback deal catalogs often include restaurant offers, but without knowing which restaurants a user actually visits, every deal is a guess. Generic promotions compete for attention and frequently go ignored because they don't feel relevant. Users who regularly order through delivery platforms have strong restaurant preferences, but that signal is not available to the issuer's app. ## Solution Use SKU-level transaction data retrieved from linked delivery platform accounts via the [Sync Transactions](/api-reference/products/transaction-link/sync) endpoint to identify which restaurants a user already frequents. When a cashback deal becomes available at one of those restaurants, surface it proactively to a segment of users, personalized with the user's actual order history. In-app deal card showing a personalized cashback offer at a restaurant the user has ordered from recently, with recent menu items listed This turns a generic deal catalog into a targeted, high-relevance experience, increasing deal activation rates by matching offers to established habits rather than broadcasting to everyone. ## Flow ```mermaid placement="top-right" actions={false} theme={"system"} %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#f5f5f5', 'primaryTextColor': '#000', 'primaryBorderColor': '#000', 'lineColor': '#000', 'secondaryColor': '#f5f5f5', 'tertiaryColor': '#f5f5f5', 'edgeLabelBackground': '#fff', 'fontSize': '16px'}}}%% flowchart LR A[User switches their card at a merchant through Knot or simply links their merchant account to begin syncing SKU-level purchase data] --> B[Sync each user's purchase data daily from Knot's API] B --> C[Extract restaurant preferences from products seller name] C --> D{Deal available at a restaurant the user frequents?} D -->|Yes| E[Surface personalized deal notification] D -->|No| F[No action needed] ``` ## Implementation When a user switches their card at a delivery platform or they simply link their merchant account w/o switching their card, Knot begins collecting transaction data for that account. Listen for the [`NEW_TRANSACTIONS_AVAILABLE`](/transaction-link/webhook-events/new-transactions-available) webhook event, then call [Sync Transactions](/api-reference/products/transaction-link/sync) to sync SKU-level transactions on a daily basis. For each delivery platform transaction, read `products[].seller.name` to identify the restaurant. **Key fields** | Field | Purpose | | ------------------------ | ---------------------------------------------------------------------------- | | `products[].seller.name` | Restaurant name (e.g., "Domino's") extracted from a delivery platform order. | | `products[].seller.url` | Restaurant URL for matching against deal catalog entries. | | `products[].name` | Menu item names for personalizing the deal notification. | | `products[].image_url` | Product image to display on the deal card. | | `merchant.id` | Identifies the delivery platform. | | `merchant.name` | Delivery platform display name (e.g., "DoorDash"). | | `datetime` | Purchase timestamp for recency filtering. | | `external_user_id` | Identifies the user in your system. | | `order_status` | Filter to completed orders only. | Only include transactions with `order_status` of `COMPLETED`, `DELIVERED`, `PICKED_UP`, `SHIPPED`, `BILLED`, or `ORDERED`. Filter out `CANCELLED`, `REFUNDED`, and `FAILED`. Store restaurant-level preference data per user. For each transaction, increment order count and last-order timestamp for the restaurant. ```text theme={"system"} FOR each transaction in sync response: IF merchant is a delivery platform: FOR each product in transaction.products: restaurant = product.seller.name user_preferences[external_user_id][restaurant].order_count += 1 user_preferences[external_user_id][restaurant].last_order = transaction.datetime user_preferences[external_user_id][restaurant].recent_items.append(product.name) ``` When a new deal is added to your deals catalog for a restaurant, query your preference store for users who have ordered from that restaurant recently. ```text theme={"system"} FOR each user with deal.restaurant in their preference data: pref = user_preferences[user_id][deal.restaurant] IF pref.order_count >= MIN_ORDER_COUNT AND pref.last_order >= today - RECENCY_WINDOW_DAYS: queue personalized notification for this user ``` **Configuration decisions for your team:** | Parameter | Suggested Default | Considerations | | ------------------------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------- | | `MIN_ORDER_COUNT` | 2 orders | Lower thresholds may include one-time visitors. 2+ orders signals a genuine preference. | | `RECENCY_WINDOW_DAYS` | 90 days | Orders from over 90 days ago may no longer reflect active habits. Adjust based on your user base's ordering frequency. | | Max items shown in notification | 3 | Enough to feel personalized without overwhelming the message. | Send a push notification and surface a deal card in the issuer's app deals or offers section. Push notification and in-app deal card showing a personalized cashback offer at a restaurant the user has ordered from recently Populate the deal card with the user's most recent `products[].name` values for that restaurant, limited to 3 items. If `products[].image_url` is available, use it as the image for each product in the list. ## Expansion Path * **Historical preference scan:** On initial account link, scan historical transactions to immediately identify restaurants the user already frequents. Users get a relevant deal the first time they open the offers section rather than waiting for new orders to accumulate. * **Grocery and convenience expansion:** Apply the same logic to grocery delivery merchants. `products[].seller.name` identifies the specific store brand or product type, enabling matched deals on grocery staples, household items, and beverages. * **Frequency tiers:** Segment users by order frequency (2-4 orders vs. 5+ orders) and surface different deal messaging. Frequent visitors may respond better to a loyalty-framed offer ("Your go-to Domino's"). Less frequent visitors may respond better to a discovery-framed offer ("You've ordered here before, here's a deal to come back"). # Post-purchase price adjustment refunds with Knot Source: https://docs.knotapi.com/use-cases/post-purchase-price-adjustment Detect price drops on items your users already bought and alert them to claim a refund within the retailer's post-purchase adjustment window. Click "Copy Page" to copy this use case as markdown. > This is a product use case page. When implementing, follow the linked quickstarts for canonical API setup steps rather than inferring them from this page. ## Problem Many retailers offer price adjustment policies that let customers claim a refund if an item's price drops within a set window after purchase. Almost no one takes advantage of these policies because they don't know the price dropped, or the process feels like too much effort. Money is left on the table. ## Solution Use SKU-level transaction data from Knot's [Sync Transactions](/api-reference/products/transaction-link/sync) endpoint to detect when the same product is purchased at a lower price by another user at the same retailer. When a price drop is detected within the retailer's adjustment window, proactively alert the original buyer so they can claim the difference. Push notification alerting a user that a product they purchased recently has dropped in price, with the savings amount and a prompt to claim the refund This surfaces real savings opportunities that users would otherwise miss, driving engagement and reinforcing the value of your app. ## Flow ```mermaid placement="top-right" actions={false} theme={"system"} %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#f5f5f5', 'primaryTextColor': '#000', 'primaryBorderColor': '#000', 'lineColor': '#000', 'secondaryColor': '#f5f5f5', 'tertiaryColor': '#f5f5f5', 'edgeLabelBackground': '#fff', 'fontSize': '16px'}}}%% flowchart LR A[User switches their card at a merchant through Knot or simply links their merchant account to begin syncing SKU-level purchase data] --> B[Sync each user's purchase data daily from Knot's API] B --> C[Compare product prices across users at the same merchant] C --> D{Same product at a lower price within the policy window?} D -->|Yes| E[Alert the original buyer with savings amount] D -->|No| F[No action needed] ``` ## Implementation When a user switches their card at a merchant or they simply link their merchant account w/o switching their card, Knot begins collecting transaction data for that account. Listen for the [`NEW_TRANSACTIONS_AVAILABLE`](/transaction-link/webhook-events/new-transactions-available) webhook event, then call [Sync Transactions](/api-reference/products/transaction-link/sync) to sync SKU-level transactions on a daily basis. Each transaction includes an array of products with prices and unique identifiers. **Key fields** | Field | Purpose | | ----------------------------- | --------------------------------------------------------- | | `products[].external_id` | Unique product identifier for cross-user price matching. | | `products[].name` | Product display name for the alert. | | `products[].price.unit_price` | Per-unit price paid. | | `datetime` | Purchase timestamp for policy window calculation. | | `merchant.id` | Identifies the retailer for scoping comparisons. | | `merchant.name` | Retailer display name for the alert. | | `external_user_id` | Identifies the user who made the purchase in your system. | | `external_id` | Order number, useful for pre-filling claim forms. | | `order_status` | Filter to completed orders only. | Only include transactions with `order_status` of `COMPLETED`, `DELIVERED`, `PICKED_UP`, `SHIPPED`, `BILLED`, or `ORDERED`. Filter out `CANCELLED`, `REFUNDED`, and `FAILED`. On each incoming transaction, check whether any previously purchased product (by any user, at the same merchant) was bought at a higher price within the retailer's adjustment window. ```text theme={"system"} FOR each product in new_transaction.products: matches = find all prior purchases WHERE prior.products[].external_id == product.external_id AND prior.merchant.id == new_transaction.merchant.id AND prior.datetime > (today - POLICY_WINDOW_DAYS) AND prior.products[].price.unit_price > product.price.unit_price FOR each match: savings = match.unit_price - product.unit_price IF savings >= MINIMUM_SAVINGS: trigger price drop alert for match.external_user_id ``` This works because Knot aggregates transaction data across all your users. If user B buys the same product at a lower price than user A did last week, user A can claim the difference. The crowd-sourced transaction data provides the price signal. **Configuration decisions for your team:** | Parameter | Suggested Default | Considerations | | -------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------- | | `POLICY_WINDOW_DAYS` | Varies by retailer | Apply the correct window per merchant. Common examples: 14 days for general retailers, 30 days for warehouse clubs. | | `MINIMUM_SAVINGS` | \$5.00 | Keeps alerts meaningful and avoids notification fatigue for small amounts. | | Alert frequency | Once per opportunity | Don't re-alert if the price drops further, or optionally send a follow-up with updated savings. | **Trigger timing options:** * **Real-time:** Run the check on each incoming transaction. Alerts surface within minutes of a price drop being detected. * **Batch:** Nightly scan of all purchases in the trailing policy window. Simpler to implement but delays alerts by up to 24 hours. * **Hybrid:** Real-time for high-value drops (>\$10), batch for smaller amounts. When a price drop opportunity is detected, notify the user with the product name, original price, current price, savings amount, and days remaining in the adjustment window. Push notification alerting a user that a product they purchased recently has dropped in price, with the savings amount and a prompt to claim the refund Deeplink the user to a screen displaying the full details of the price drop, including the order number (`external_id`) and a link to the retailer's price adjustment form to claim the refund. Better yet, use Knot's API to submit the refund request through the merchant prior to notifying the user. ## Expansion Path * **Automated claim submission:** Instead of alerting the user, submit the price adjustment claim on their behalf automatically through Knot's API. Once the refund is processed, notify the user that money is already on its way, no action required. This eliminates friction entirely and maximizes claim rates. * **Price tracking alerts:** Even for retailers without formal price adjustment policies, alert users when a product they bought drops in price. This is useful as a general spending awareness feature that reinforces the value of your app. # Shopping List Source: https://docs.knotapi.com/use-cases/shopping-list Generate a pre-populated shopping list from a user's repeat purchase history. Click "Copy Page" to copy this use case as markdown. > This is a product use case page. When implementing, follow the linked quickstarts for canonical API setup steps rather than inferring them from this page. ## Problem Most people buy the same grocery and household products week after week, but every trip to the store starts from scratch: trying to remember what's running low, writing items down in a notes app, or showing up and improvising. With access to SKU-level transaction data, an app can identify exactly which products a user buys repeatedly and turn that into something useful. ## Solution Use SKU-level transaction data from Knot's [Sync Transactions](/api-reference/products/transaction-link/sync) endpoint to identify products a user buys repeatedly at linked grocery and retail accounts, then generate a personalized shopping list pre-populated with those items. Banking app screen showing a personalized shopping list with frequently purchased grocery items, checkboxes, and an estimated total This turns purchase history into a practical, recurring utility that gives users a reason to open the issuer's app before every shopping trip. ## Flow ```mermaid placement="top-right" actions={false} theme={"system"} %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#f5f5f5', 'primaryTextColor': '#000', 'primaryBorderColor': '#000', 'lineColor': '#000', 'secondaryColor': '#f5f5f5', 'tertiaryColor': '#f5f5f5', 'edgeLabelBackground': '#fff', 'fontSize': '16px'}}}%% flowchart LR A[User switches their card at a merchant through Knot or simply links their merchant account to begin syncing SKU-level purchase data] --> B[Sync each user's purchase data daily from Knot's API] B --> C[Index each product by user and merchant, tracking purchase count] C --> D{Product purchased 3+ times?} D -->|Yes| E[Add to personalized shopping list] D -->|No| F[Not yet a repeat item] ``` ## Implementation When a user switches their card at a merchant or they simply link their merchant account w/o switching their card, Knot begins collecting transaction data for that account. Listen for the [`NEW_TRANSACTIONS_AVAILABLE`](/transaction-link/webhook-events/new-transactions-available) webhook event, then call [Sync Transactions](https://docs.knotapi.com/api-reference/products/transaction-link/sync) to sync SKU-level transactions on a daily basis. For each transaction, iterate over the products array and update a per-user, per-merchant purchase index. **Key fields** | Field | Purpose | | ----------------------------- | ---------------------------------------------------------- | | `products[].external_id` | Unique product identifier for deduplication across orders. | | `products[].name` | Product display name for the shopping list. | | `products[].price.unit_price` | Most recent unit price, used to estimate the list total. | | `products[].image_url` | Product image for the list UI. | | `products[].url` | Link to the product page for the list UI. | | `merchant.id` | Identifies the retailer. Scope the list per merchant. | | `merchant.name` | Retailer display name. | | `datetime` | Purchase timestamp for recency scoring. | | `external_user_id` | Identifies the user in your system. | | `order_status` | Filter to completed orders only. | Only include transactions with `order_status` of `COMPLETED`, `DELIVERED`, `PICKED_UP`, `SHIPPED`, `BILLED`, or `ORDERED`. Filter out `CANCELLED`, `REFUNDED`, and `FAILED`. ```text theme={"system"} FOR each transaction in sync response: FOR each product in transaction.products: key = (external_user_id, merchant.id, product.external_id) purchase_index[key].count += 1 purchase_index[key].last_purchased = transaction.datetime purchase_index[key].name = product.name purchase_index[key].last_price = product.price.unit_price purchase_index[key].image_url = product.image_url purchase_index[key].url = product.url ``` When a user's purchase index reaches the minimum threshold, compile their shopping list. Run this on initial sync (to catch historical data) and refresh after each new transaction batch. ```text theme={"system"} candidates = products in purchase_index[user][merchant] WHERE count >= MIN_PURCHASE_COUNT AND last_purchased >= (today - PURCHASE_WINDOW_DAYS) ranked = sort candidates by count descending, then last_purchased descending shopping_list = ranked[:MAX_LIST_SIZE] estimated_total = sum(p.last_price for p in shopping_list) ``` **Configuration decisions for your team:** | Parameter | Suggested Default | Considerations | | ---------------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | | `MIN_PURCHASE_COUNT` | 3 purchases | 2 purchases may include one-time buys. 3+ is a reliable signal of a regular item. | | `PURCHASE_WINDOW_DAYS` | 30 days | Only count purchases within this window. Avoids surfacing products bought 3 times over a year that are not part of a regular routine. | | `MAX_LIST_SIZE` | 30 items | Covers a typical weekly grocery run without feeling overwhelming. | | List refresh frequency | After each new transaction sync | Keeps the list current as buying habits change. | Send a push notification when the list is first generated or materially updated, and surface it as a persistent in-app feature. Banking app screen showing a personalized shopping list with frequently purchased grocery items, checkboxes, and an estimated total Users should be able to check off items as they shop, add one-off items manually, and skip items they don't need that week. ## Expansion Path * **Predictive reorder timing:** For each recurring item, track the average interval between purchases (e.g., milk every 6 days). Surface items on the list when they are likely running low, rather than showing all items at once every week. * **Cross-merchant list:** Combine repeat-purchase data across multiple linked merchants into one unified list. For items that appear at more than one merchant, surface the lower price as a recommendation. * **Store-brand swap suggestions:** For each name-brand item on the list, check whether a comparable store-brand product is available at a lower price and offer an inline swap. This reuses the same purchase history data and adds a savings angle without requiring a separate flow. # Store Brand Alternative Savings Source: https://docs.knotapi.com/use-cases/store-brand-alternative-savings Suggest cheaper store-brand alternatives for name-brand purchases. Click "Copy Page" to copy this use case as markdown. > This is a product use case page. When implementing, follow the linked quickstarts for canonical API setup steps rather than inferring them from this page. ## Problem Users frequently buy name-brand products without realizing equivalent store-brand alternatives exist at the same retailer for significantly less. Store brands are often 25-55% cheaper for comparable items, but most users default to name brands out of habit. Without visibility into what was purchased at the SKU level, there is no practical way to surface these savings at the right moment. ## Solution Retrieve SKU-level transaction data from the [Sync Transactions](/api-reference/products/transaction-link/sync) endpoint. Compare the products users purchase against a catalog of comparable products at the same merchant. When a cheaper alternative exists in the same category, surface it as a proactive savings tip. iPhone lock screen showing a push notification: Grocery savings opportunity found — You can save $2.50 each time you buy ketchup with a different brand at Walmart This gives users a concrete, actionable savings insight tied to their real purchases, without requiring any change to their checkout behavior. ## Flow ```mermaid placement="top-right" actions={false} theme={"system"} %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#f5f5f5', 'primaryTextColor': '#000', 'primaryBorderColor': '#000', 'lineColor': '#000', 'secondaryColor': '#f5f5f5', 'tertiaryColor': '#f5f5f5', 'edgeLabelBackground': '#fff', 'fontSize': '16px'}}}%% flowchart LR A[User switches their card at a merchant through Knot or simply links their merchant account to begin syncing SKU-level purchase data] --> B[Sync each user's purchase data daily from Knot's API] B --> C[Check each product against cheaper alternatives at same merchant] C --> D{Savings above threshold?} D -->|Yes| E[Surface savings suggestion] D -->|No| F[No action needed] ``` ## Implementation When a user switches their card at a merchant or they simply link their merchant account w/o switching their card, Knot begins collecting transaction data for that account. Listen for the [`NEW_TRANSACTIONS_AVAILABLE`](/transaction-link/webhook-events/new-transactions-available) webhook event, then call [Sync Transactions](/api-reference/products/transaction-link/sync) to sync SKU-level transactions on a daily basis. **Key fields** | Field | Purpose | | ----------------------------- | ------------------------------------------------------------------ | | `products[].name` | Product display name for the push or UI. | | `products[].external_id` | Unique product identifier for tracking across orders. | | `products[].price.unit_price` | Price paid, used to calculate potential savings. | | `products[].category` | Knot-provided product category for grouping alternatives. | | `products[].subcategory` | Knot-provided subcategory for narrower matching within a category. | | `products[].image_url` | Product image for the UI. | | `merchant.id` | Identifies the merchant for catalog scoping. | | `merchant.name` | Merchant display name for the UI. | | `datetime` | Purchase timestamp. | | `external_user_id` | Identifies the user in your system. | | `order_status` | Filter to completed orders only. | Only include transactions with `order_status` of `COMPLETED`, `DELIVERED`, `PICKED_UP`, `SHIPPED`, `BILLED`, or `ORDERED`. Filter out `CANCELLED`, `REFUNDED`, and `FAILED`. As transaction data accumulates across your users, maintain a per-merchant product catalog. Knot can provide a `products[].category` and `products[].subcategory` on each product, so no custom categorization logic is required. Use `subcategory` for matching where available - it groups products at the right level of specificity (e.g., "creamy peanut butter" rather than just "condiments"). ```text theme={"system"} FOR each product in new_transaction.products: upsert into product_catalog: (merchant_id, external_id, name, category, subcategory, unit_price, image_url) ``` Cross-user data compounds the value: as more users shop at a merchant, the catalog grows to cover more categories and refine price signals for each product. On each incoming transaction (or on a daily job), check whether any product in the order has a cheaper alternative in the same category at the same merchant. ```text theme={"system"} FOR each product in new_transaction.products: alternatives = query product_catalog WHERE merchant_id == new_transaction.merchant.id AND subcategory == product.subcategory // fall back to category if null AND unit_price < product.price.unit_price AND external_id != product.external_id IF alternatives is not empty: best = alternative with lowest unit_price savings = product.price.unit_price - best.unit_price savings_pct = savings / product.price.unit_price IF savings >= MIN_SAVINGS AND savings_pct >= MIN_SAVINGS_PCT: queue suggestion for external_user_id ``` **Configuration decisions for your team:** | Parameter | Suggested Default | Considerations | | ------------------------- | ------------------------- | --------------------------------------------------------------------------------------------- | | `MIN_SAVINGS` | \$0.50 | Filters out trivial differences that won't feel meaningful to users. | | `MIN_SAVINGS_PCT` | 15% | Prevents surfacing alternatives where the price gap is negligible relative to the item price. | | Max suggestions per order | 3 | Avoids alert fatigue when a user buys many name-brand items in one order. | | Alert frequency | Once per product per user | Don't re-alert on the same product in a future order unless the price gap has grown. | **Trigger timing options:** * **Real-time (preferred):** Run the check on each incoming transaction. Best for high-savings items where the impact is immediate and the user is likely to remember the purchase. * **Weekly digest:** Aggregate all matched alternatives from the week and present a cumulative "you could have saved \$X" summary. Less disruptive for small per-item amounts. * **Hybrid:** Real-time for savings above \$2 per item; weekly digest for smaller gaps. When an alternative is identified, notify the user with the product name, what they paid, the store-brand alternative, and the potential savings. **Push notification:** > **Save \$2.50 on ketchup next time.** You can save \$2.50 each time you buy ketchup with a different brand at Walmart. Deeplink the user to a screen displaying more details about the savings opportunity, including a direct link to the alternative product, so they can check it out more deeply. ## Expansion Path * **Broader merchant coverage:** Extend the catalog to additional retailers with established store brands. Each new merchant multiplies the addressable product categories and addressable users. * **Personalized suggestions:** Use a user's purchase history to prioritize alternatives they are most likely to try. Users who already buy some store-brand products in a category are significantly more likely to switch in adjacent categories. Surface those first. * **Weekly savings digest:** Aggregate all suggested swaps from the past week and send a single in-app card showing total potential savings. This keeps the feature visible without generating a notification per purchase. # Upcoming subscription charge funding alerts Source: https://docs.knotapi.com/use-cases/upcoming-charge-funding-alert Notify debit and prepaid cardholders to fund their account before an upcoming subscription or recurring merchant charge risks declining. Click "Copy Page" to copy this use case as markdown. > This is a product use case page. When implementing, follow the linked quickstarts for canonical API setup steps rather than inferring them from this page. ## Problem When a user adds their debit or prepaid card to an online merchant account that has an active subscription or recurring bill, there's a risk that the next charge will fail if the account doesn't have sufficient funds. Unlike credit cards, debit and prepaid cards draw directly from the account balance. A declined charge can result in a missed payment, service interruption from the merchant, overdraft fees from the issuing bank, and a poor user experience for the new cardholder. Today, users have no visibility into upcoming charges for bills or subscriptions where they've just provisioned their card through Knot. They switch their card, move on, and find out days later when a charge fails. ## Solution Immediately after a card is switched, check whether the merchant account has an upcoming subscription charge that exceeds the user's available balance. If it does, proactively notify the user to fund their account before the charge hits. iPhone lock screen showing a push notification: Upcoming charge for Netflix — Add money to cover your upcoming Netflix subscription charge for $22.99 on Mar 14 This turns a potential failed payment into a proactive funding moment, reducing declines, improving the user experience in your app, and reinforcing trust. ## Flow ```mermaid placement="top-right" actions={false} theme={"system"} %%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#f5f5f5', 'primaryTextColor': '#000', 'primaryBorderColor': '#000', 'lineColor': '#000', 'secondaryColor': '#f5f5f5', 'tertiaryColor': '#f5f5f5', 'edgeLabelBackground': '#fff', 'fontSize': '16px'}}}%% flowchart LR A[User switches card at merchant] --> B[Knot fires CARD_UPDATED webhook] B --> C[Fetch each subscription via GET /subscriptions/id] C --> D{Upcoming charge exceeds balance?} D -->|Yes| E[Send funding alert] D -->|No| F[No action needed] ``` ## Implementation When a user successfully switches their card at a merchant, Knot emits the [`CARD_UPDATED`](/card-switcher/webhook-events/card-updated) event to your webhook. With [SubscriptionManager](/subscription-manager/quickstart) enabled, the payload includes subscription IDs for the subscription(s) or bill(s) on the user's merchant account. **Key fields** | Field | Purpose | | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | `external_user_id` | Used to identify the user in your system. | | `merchant.name` | Merchant display name for the notification. | | `data.subscriptions[].id` | Subscription IDs used to fetch full subscription details from [Get Subscription By ID](/api-reference/products/subscriptions/get-by-id). | If `data.subscriptions` is empty, the merchant account does not have a subscription or bill and no further action is needed. For each subscription ID in the webhook payload, call [Get Subscription By ID](/api-reference/products/subscriptions/get-by-id) to get the subscription or bill details, including the name, status, next billing date, price, etc. The full subscription object is documented [here](/api-reference/products/subscriptions/subscription-object). **Key fields** | Field | Purpose | | ------------------- | --------------------------------------------------------------------- | | `name` | Subscription display name (e.g., "Netflix Standard"). | | `status` | Only act on `ACTIVE` subscriptions. | | `next_billing_date` | When the next charge will occur. | | `price.total` | The amount that will be charged (e.g. `22.99`). | | `price.currency` | Currency code (e.g. `USD`). | | `billing_cycle` | `RECURRING_MONTHLY`, `RECURRING_ANNUALLY`, etc. Useful for messaging. | Once you have the subscription details and the user's account balance, apply two checks: ```text theme={"system"} upcoming_charge = parseFloat(subscription.price.total) days_until_charge = subscription.next_billing_date - today IF subscription.status == "ACTIVE" AND days_until_charge <= THRESHOLD_DAYS AND upcoming_charge > user.account_balance THEN trigger funding alert ``` **Configuration decisions for your team:** | Parameter | Suggested Default | Considerations | | --------------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------- | | `THRESHOLD_DAYS` | 7 days | Too short and the user may not have time to fund. Too long and the alert feels premature. 5-10 days is a reasonable range. | | Minimum charge amount | \$0 (no minimum) | You may want to skip alerts for very small charges (e.g. certain Apple subscriptions \<\$1) to avoid notification fatigue. | If the conditions are met, send a push notification (and in-app message) to the user: > **Upcoming \$22.99 charge for Netflix.** > > Deposit money to cover your upcoming Netflix subscription charge for \$22.99 on Mar 14th. Deeplink the user to a screen where they can fund their account to cover the upcoming charge. ## Expansion Path When the user's balance is short, offer access to a short-term earned-wage advance to cover the upcoming charge. Instead of just telling the user to add funds, the notification can include an option to access wages they've already earned but haven't yet been paid. This turns a potential declined payment into a seamless funding moment. The user taps to advance just enough to cover the charge, the subscription pays on time, and the advance is repaid on their next payday. # Quickstart Source: https://docs.knotapi.com/vaulting/quickstart Get started with the Vaulting integration to enable users to vault digital wallets to their merchant accounts. ## Introduction Knot's Vaulting product enables you to vault a digital wallet to a user's merchant account through the Knot SDK (and without leaving your application). This allows users to securely vault their digital wallet as the default payment method of choice in their merchant accounts. With the Unified Flow, a single `vault` SDK session also supports card switching. Knot automatically determines whether to vault a digital wallet or switch a card based on each merchant's capabilities. ## Getting started The unified flow supports both digital wallet vaulting and card switching within a single SDK session. Knot automatically determines the appropriate action for each merchant — vaulting a digital wallet where supported, and switching a card where only card switching is available. ### Start the flow Ensure you have access to the [Knot Dashboard](https://dashboard.knotapi.com) and retrieve your `client_id` and `secret` to create an API key. Learn more about creating an API key and authentication to the API [here](/api-reference/authentication). With your API key for the `development` environment, call [Create Session](https://docs.knotapi.com/api-reference/sessions/create-session) with `type: vault` to create a session used when invoking the SDK. More [here](/api-reference/authentication) on how to create an API key. Install an SDK of your choosing, for example on iOS [here](https://docs.knotapi.com/sdk/ios). If you are using the Web SDK, make sure to allowlist your application's domains for the `development` and `production` environments in the [Knot Dashboard](https://dashboard.knotapi.com/domains). Initialize the SDK with the `session_id` retrieved from [Create Session](https://docs.knotapi.com/api-reference/sessions/create-session) and a merchant `Id` retrieved from [List Merchants](https://docs.knotapi.com/api-reference/merchants/list-merchants) in `KnotConfiguration`. Alternatively, you can use `merchant_id: 19` for DoorDash to get started quickly. The SDK is where users will interact with the Knot UI to authenticate to various merchants. All login flows, including step-up authentication, are handled within the SDK. Users will see real-time feedback as they progress through authenticating with a merchant. **In the development environment,** use [testing credentials](https://docs.knotapi.com/vaulting/testing) to login to a merchant account and simulate vaulting a digital wallet. ### Handle events Handle `onError`, `onExit`, and `onEvent` SDK callbacks to be notified of client-side events. Subscribe to webhooks in the [Knot Dashboard](https://dashboard.knotapi.com/developers/webhooks) so your backend can be notified about user-generated, server-side events. Listen for the following events: * [`AUTHENTICATED`](/link/webhook-events/authenticated): fired when the authentication to a merchant is successful. * [`VAULTING_SUCCEEDED`](/vaulting/webhook-events/vaulting-succeeded): fired when a digital wallet is successfully vaulted to a user's merchant account. * [`VAULTING_FAILED`](/vaulting/webhook-events/vaulting-failed): fired when a digital wallet fails to be vaulted to a user's merchant account. * [`CARD_UPDATED`](/card-switcher/webhook-events/card-updated): fired when a card is successfully switched at a user's merchant account. * [`CARD_FAILED`](/card-switcher/webhook-events/card-failed): fired when a card fails to be switched at a user's merchant account. ### Start the flow Ensure you have access to the [Knot Dashboard](https://dashboard.knotapi.com) and retrieve your `client_id` and `secret` to create an API key. Learn more about creating an API key and authentication to the API [here](/api-reference/authentication). Call [List Merchants](/api-reference/merchants/list-merchants) to retrieve a list of merchants that are available for vaulting a digital wallet by passing `type = vault` in the request. These are merchants you can display in your app and allow users to vault PwV. You will be notified via the `MERCHANT_STATUS_UPDATE` webhook when/if the available merchant list changes. With your API key for the `development` environment, call [Create Session](https://docs.knotapi.com/api-reference/sessions/create-session) with `type: vault` to create a session used when invoking the SDK. More [here](/api-reference/authentication) on how to create an API key. Install an SDK of your choosing, for example on iOS [here](https://docs.knotapi.com/sdk/ios). If you are using the Web SDK, make sure to allowlist your application's domains for the `development` and `production` environments in the [Knot Dashboard](https://dashboard.knotapi.com/domains). Initialize the SDK with the `session_id` retrieved from [Create Session](https://docs.knotapi.com/api-reference/sessions/create-session) and a merchant `Id` retrieved from [List Merchants](https://docs.knotapi.com/api-reference/merchants/list-merchants) in `KnotConfiguration`. Alternatively, you can use `merchant_id: 19` for DoorDash to get started quickly. The SDK is where users will interact with the Knot UI to authenticate to various merchants. All login flows, including step-up authentication, are handled within the SDK. Users will see real-time feedback as they progress through authenticating with a merchant. **In the development environment,** use [testing credentials](https://docs.knotapi.com/vaulting/testing) to login to a merchant account and simulate vaulting a digital wallet. ### Handle events Handle `onError`, `onExit`, and `onEvent` SDK callbacks to be notified of client-side events. Subscribe to webhooks in the [Knot Dashboard](https://dashboard.knotapi.com/developers/webhooks) so your backend can be notified about user-generated, server-side events. Listen for the following events: * [`AUTHENTICATED`](/link/webhook-events/authenticated): fired when the authentication to a merchant is successful. * [`VAULTING_SUCCEEDED`](/vaulting/webhook-events/vaulting-succeeded): fired when a digital wallet is successfully vaulted to a user's merchant account. * [`VAULTING_FAILED`](/vaulting/webhook-events/vaulting-failed): fired when a digital wallet fails to be vaulted to a user's merchant account. # Testing Source: https://docs.knotapi.com/vaulting/testing Test the Vaulting flow including account linking and digital wallet vaulting using test credentials in development. The below steps and sets of credentials allow you to test the end-to-end flow of logging in to merchant accounts and vaulting a digital wallet. Call [Create Session](/api-reference/sessions/create-session) with `type: vault` and a dummy `external_user_id`. Use the `session_id` you receive when creating a session to invoke the SDK. In `KnotConfiguration`, pass a `merchantId` for a merchant of your choosing. In your implementation, merchants are retrieved via [List Merchants](/api-reference/merchants/list-merchants). For testing purposes, you can likely hardcode a single `id` for a merchant. Once you've invoked the SDK, you can login to a merchant account using one of the sets of credentials below, depending on what you would like to test. | Scenario | Description | Username | Password | | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | ----------- | | Successful vault | Simulates successfully vaulting a digital wallet to a merchant account. After authentication to the merchant account, you will receive the [`VAULTING_SUCCEEDED`](/vaulting/webhook-events/vaulting-succeeded) webhook event. | `user_good_vault` | `pass_good` | | Failed vault | Simulates a failed digital wallet vaulting attempt to a merchant account. After authentication to the merchant account, you will receive the [`VAULTING_FAILED`](/vaulting/webhook-events/vaulting-failed) webhook event. | `user_good_vault` | `failed` | # VAULTING_FAILED Source: https://docs.knotapi.com/vaulting/webhook-events/vaulting-failed api-reference/openapi.json webhook vaulting_failed Fired when a digital wallet fails to be vaulted to a user's merchant account. Fired when a digital wallet fails to be vaulted to a user's merchant account. # VAULTING_SUCCEEDED Source: https://docs.knotapi.com/vaulting/webhook-events/vaulting-succeeded api-reference/openapi.json webhook vaulting_succeeded Fired when a digital wallet is successfully vaulted to a user's merchant account. Fired when a digital wallet is successfully vaulted to a user's merchant account. # Webhooks Source: https://docs.knotapi.com/webhooks Configure webhook endpoints to receive HTTP POST requests from Knot. ## Introduction A webhook is an HTTP request used to provide various events. Knot provides webhooks for updates related to a user's lifecycle in the Knot user experience as well as various asynchronous processes. To receive webhooks from Knot, set up dedicated endpoints on your server as webhook listeners that can receive POST requests from Knot's `development` and `production` environments respectively. ## Configuring Webhooks Once you have added endpoints on your server, add these endpoint URLs in your Knot Dashboard [here](https://dashboard.knotapi.com/developers/webhooks). You can configure up to 10 webhook URLs per environment. The URLs of your dedicated endpoints must be in the standard format of `http(s)://(www.)domain.com/` and must have a valid SSL certificate if https. Knot sends POST payloads with raw JSON to your webhook URL from the following IP address in all environments (Production & Development): `35.232.249.218/32`. This IP address is subject to change and Knot will notify you in advance of any changes. ## Webhook Verification Knot signs all outgoing webhooks so that you can verify the authenticity of any incoming webhooks to your application. This verification process is optional and is not required for your application to handle webhooks from Knot. Extract the Hash-based Message Authentication Code (HMAC) signature included in the `Knot-Signature` header of the webhook. You will later compare this against your computed signature. Collect the following headers and body fields into a hash map. `Content-Length` is the byte length of the entire raw JSON request body. Any tampering with the body (beyond the signed `event` and `session_id` fields) will therefore change this value and invalidate the signature. ```javascript theme={"system"} const data = { "Content-Length": "178", "Content-Type": "application/json", "Encryption-Type": "HMAC-SHA256", "event": "CARD_UPDATED", "session_id": "fb5aa994-ed1c-4c3e-b29a-b2a53222e584" } ``` Not all webhooks will have a `session_id` in the request body (such as the `MERCHANT_STATUS_UPDATE` webhook). In those scenarios, do not include the `session_id` in the hash map. Build the following string from the hash map, concatenating key-value pairs with `|` ``` Content-Length|178|Content-Type|application/json|Encryption-Type|HMAC-SHA256|event|CARD_UPDATED|session_id|fb5aa994-ed1c-4c3e-b29a-b2a53222e584 ``` Using your client secret from the [Knot Dashboard](https://dashboard.knotapi.com/), compute an HMAC signature using SHA256 and base64 encode the result. Compare the signature extracted from the `Knot-Signature` header in the webhook to the signature you computed in the above step and ensure they're the same. ## Retries If there is a non-200 response or no response within 10 seconds from your webhook listener endpoint, Knot will retry sending the webhook up to two times with a few minutes in between each request. ## Session Metadata You can attach custom key-value pairs (metadata) to a session, and this metadata will be included in all webhook payloads for that session. This is useful for: * **Conditional webhook acceptance**: Pass a JWE token, the contents of which can be used to determine whether to accept or reject the webhook payload * **Request correlation**: Include internal reference IDs for tracking * **Custom data**: Any string key-value pairs you need echoed back ### How to Attach Metadata You can attach metadata in two ways: **Server-side**: Include metadata when calling [Create Session](/api-reference/sessions/create-session): ```bash theme={"system"} POST /session/create { "type": "card_switcher", "external_user_id": "user-123", "card_id": "card-456", "metadata": { "reference_token": "your-jwe-token", "internal_ref": "order-789" } } ``` **Client-side**: Pass metadata when opening the SDK. See the [SDK documentation](/sdk/introduction) for platform-specific implementation details. Client-side metadata is merged with any server-side metadata, with client-side values taking precedence for duplicate keys. ### Webhook Payload When metadata is attached to a session, it appears in the `data.metadata` field of webhook payloads: ```json theme={"system"} { "event": "CARD_UPDATED", "session_id": "fb5aa994-ed1c-4c3e-b29a-b2a53222e584", "task_id": 25605, "external_user_id": "user-123", "merchant": { "id": 11, "name": "Uber" }, "data": { "card_id": "card-456", "metadata": { "reference_token": "your-jwe-token", "internal_ref": "order-789" } }, "timestamp": 1710864923198 } ``` Metadata is only included when you provide it. If no metadata is attached to the session, the `metadata` field will not appear in the webhook payload. ### Constraints * Maximum **10 keys** per session * Maximum **500 characters** per value * Keys and values must be **strings**