# Enrich a batch of card transactions Source: https://docs.spade.com/api-reference/card-enrichment/enrich-a-batch-of-card-transactions /api-reference/spec.yml post /batches/transactions/cards/enrich Submit a batch of transactions for enrichment. This endpoint will return a `batchId` which can be used in the `/batches/{batchId}` and `/batches/{batchId}/results` endpoints to check the status of the batch job and retrieve the results. You can pass `?synchronous=true` to enrich smaller sets of transactions synchronously. See the [Microbatch enrichment guide](https://docs.spade.com/reference/microbatch-enrichment-guide) for details. Note that we impose a rate limit on the number of batch requests made in a rolling 12 hour window. Please reach out to sales@spade.com with any questions. # Enrich a batch of card transactions with DE43 data Source: https://docs.spade.com/api-reference/card-enrichment/enrich-a-batch-of-card-transactions-with-de43-data /api-reference/spec.yml post /batches/transactions/cards/enrich/parse Submit a batch of card transactions for enrichment where de43 data in the request body takes precedence over the other fields in the request body. Just like the `/transactions/cards/enrich/parse` endpoint, the transactions can omit `city` and `merchantName` fields if the `de43` field is present. This endpoint returns a `batchId` which can be used in the `/batches/{batchId}` and `/batches/{batchId}/results` endpoints to check the status of the batch job and retrieve the results. You can pass `?synchronous=true` to enrich smaller sets of transactions synchronously. See the [Microbatch enrichment guide](https://docs.spade.com/reference/microbatch-enrichment-guide) for details. Note that we impose a rate limit on the number of batch requests made in a rolling 12 hour window. Please reach out to sales@spade.com with any questions. # Enrich a card transaction Source: https://docs.spade.com/api-reference/card-enrichment/enrich-a-card-transaction /api-reference/spec.yml post /transactions/cards/enrich When working with this endpoint, note that several of the input fields are optional (region, acquirerId, etc.). However, we *strongly* recommend including all of the fields that you have available. Some optional fields can substantially improve performance. Take care to ensure you are using unaltered and correct data in each field. Some fields go by multiple names, so if you have any questions regarding integration please feel free to reach out. Note that some response fields may not be included in your product package, please reach out with any questions. # Enrich a card transaction Source: https://docs.spade.com/api-reference/card-enrichment/enrich-a-card-transaction-1 /api-reference/spec.yml post /transactions/enrich Enrich a card transaction. Please use `/transactions/cards/enrich` instead. This endpoint now aliases to `/transactions/cards/enrich`. # Enrich a card transaction with an unparsed de43 Source: https://docs.spade.com/api-reference/card-enrichment/enrich-a-card-transaction-with-an-unparsed-de43 /api-reference/spec.yml post /transactions/cards/enrich/parse Enrich a card transaction where parsed de43 data takes precedence over the other fields in the request body. This otherwise endpoint is identical to `/transactions/cards/enrich` except that `city` and `merchantName` are now optional, but only when `de43` is included. Note: Because DE43 format is not consistent between sources, we recommend parsing your own data and using `/transactions/cards/enrich/` if possible. Note that some response fields may not be included in your product package, please reach out to sales@spade.com with any questions. # Enrich a card transaction with an unparsed de43 Source: https://docs.spade.com/api-reference/card-enrichment/enrich-a-card-transaction-with-an-unparsed-de43-1 /api-reference/spec.yml post /transactions/enrich/parse Enrich a card transaction. Please use `/transactions/cards/enrich/parse` instead. This endpoint now aliases to `/transactions/cards/enrich/parse`. # Get metadata for the batch card enrichment endpoint Source: https://docs.spade.com/api-reference/card-enrichment/get-metadata-for-the-batch-card-enrichment-endpoint /api-reference/spec.yml options /batches/transactions/cards/enrich Returns metadata about this endpoint, including the current `synchronousMax` — the cap on items per request when `?synchronous=true`. Can be used to fetch the current cap dynamically. # Get metadata for the batch card parse enrichment endpoint Source: https://docs.spade.com/api-reference/card-enrichment/get-metadata-for-the-batch-card-parse-enrichment-endpoint /api-reference/spec.yml options /batches/transactions/cards/enrich/parse Returns metadata about this endpoint, including the current `synchronousMax` — the cap on items per request when `?synchronous=true`. Can be used to fetch the current cap dynamically. # Get the results of a batch card enrichment job Source: https://docs.spade.com/api-reference/card-enrichment/get-the-results-of-a-batch-card-enrichment-job /api-reference/spec.yml get /batches/{cardEnrichmentBatchId}/results Get the results of a batch card enrichment job. # Get the status of a batch card enrichment job Source: https://docs.spade.com/api-reference/card-enrichment/get-the-status-of-a-batch-card-enrichment-job /api-reference/spec.yml get /batches/{cardEnrichmentBatchId} Get the status of a batch job. # Clear account-level category triggers Source: https://docs.spade.com/api-reference/category-action-triggers/clear-account-level-category-triggers /api-reference/spec.yml delete /category-action-triggers Clear all category action triggers at the account scope. This creates a new version with an empty trigger list. To learn more about action triggers, please read the [Category Action Triggers Guide](https://docs.spade.com/reference/category-action-triggers-guide). # Clear card-level category triggers Source: https://docs.spade.com/api-reference/category-action-triggers/clear-card-level-category-triggers /api-reference/spec.yml delete /users/{userId}/cards/{cardId}/category-action-triggers Clear all category action triggers at the card scope. This creates a new version with an empty trigger list. To learn more about action triggers, please read the [Category Action Triggers Guide](https://docs.spade.com/reference/category-action-triggers-guide). # Clear program-level category triggers Source: https://docs.spade.com/api-reference/category-action-triggers/clear-program-level-category-triggers /api-reference/spec.yml delete /programs/{programId}/category-action-triggers Clear all category action triggers at the program scope. This creates a new version with an empty trigger list. To learn more about action triggers, please read the [Category Action Triggers Guide](https://docs.spade.com/reference/category-action-triggers-guide). # Clear user-level category triggers Source: https://docs.spade.com/api-reference/category-action-triggers/clear-user-level-category-triggers /api-reference/spec.yml delete /users/{userId}/category-action-triggers Clear all category action triggers at the user scope. This creates a new version with an empty trigger list. To learn more about action triggers, please read the [Category Action Triggers Guide](https://docs.spade.com/reference/category-action-triggers-guide). # Get account-level category trigger status Source: https://docs.spade.com/api-reference/category-action-triggers/get-account-level-category-trigger-status /api-reference/spec.yml get /category-action-triggers Get the status and version of the latest category action trigger registration at the account scope. Account-level triggers apply to all transactions for all users in your account. **Important:** Triggers are only applied to enrichment responses when the status is `succeeded`. To learn more about action triggers, please read the [Category Action Triggers Guide](https://docs.spade.com/reference/category-action-triggers-guide). # Get card-level category trigger status Source: https://docs.spade.com/api-reference/category-action-triggers/get-card-level-category-trigger-status /api-reference/spec.yml get /users/{userId}/cards/{cardId}/category-action-triggers Get the status and version of the latest category action trigger registration at the card scope. Card-level triggers apply only to transactions for the specified card. **Important:** Triggers are only applied to enrichment responses when the status is `succeeded`. To learn more about action triggers, please read the [Category Action Triggers Guide](https://docs.spade.com/reference/category-action-triggers-guide). # Get program-level category trigger status Source: https://docs.spade.com/api-reference/category-action-triggers/get-program-level-category-trigger-status /api-reference/spec.yml get /programs/{programId}/category-action-triggers Get the status and version of the latest category action trigger registration at the program scope. Program-level triggers apply to all enrichment requests where the `programId` field matches this program. **Important:** Triggers are only applied to enrichment responses when the status is `succeeded`. To learn more about action triggers, please read the [Category Action Triggers Guide](https://docs.spade.com/reference/category-action-triggers-guide). # Get user-level category trigger status Source: https://docs.spade.com/api-reference/category-action-triggers/get-user-level-category-trigger-status /api-reference/spec.yml get /users/{userId}/category-action-triggers Get the status and version of the latest category action trigger registration at the user scope. User-level triggers apply to all transactions for all cards belonging to the specified user. **Important:** Triggers are only applied to enrichment responses when the status is `succeeded`. To learn more about action triggers, please read the [Category Action Triggers Guide](https://docs.spade.com/reference/category-action-triggers-guide). # Incremental category trigger operations at account scope Source: https://docs.spade.com/api-reference/category-action-triggers/incremental-category-trigger-operations-at-account-scope /api-reference/spec.yml patch /category-action-triggers Perform incremental operations on category action triggers at the account scope. Supports two operations: - **add**: Register new category triggers or update existing ones. Only the submitted triggers are affected—existing registrations are preserved. If a trigger ID already exists, its registration is replaced (upsert). All registrations are processed synchronously. - **remove**: Remove category triggers by trigger ID. Removed triggers are excluded from enrichment lookups. Trigger IDs that are not currently active are silently ignored (idempotent). Successful `add` operations return `201`; successful `remove` operations return `200`. **Limit:** The total number of active triggers at a scope cannot exceed 300. An `add` operation that would push the total past this limit will receive a `400` error. **Important:** Only one operation can be in progress per scope at a time. Concurrent requests return 409. To learn more about action triggers, please read the [Category Action Triggers Guide](https://docs.spade.com/reference/category-action-triggers-guide). # Incremental category trigger operations at card scope Source: https://docs.spade.com/api-reference/category-action-triggers/incremental-category-trigger-operations-at-card-scope /api-reference/spec.yml patch /users/{userId}/cards/{cardId}/category-action-triggers Perform incremental operations on category action triggers at the card scope. See the account-scope PATCH endpoint for full documentation on supported operations (add, remove). # Incremental category trigger operations at program scope Source: https://docs.spade.com/api-reference/category-action-triggers/incremental-category-trigger-operations-at-program-scope /api-reference/spec.yml patch /programs/{programId}/category-action-triggers Perform incremental operations on category action triggers at the program scope. See the account-scope PATCH endpoint for full documentation on supported operations (add, remove). # Incremental category trigger operations at user scope Source: https://docs.spade.com/api-reference/category-action-triggers/incremental-category-trigger-operations-at-user-scope /api-reference/spec.yml patch /users/{userId}/category-action-triggers Perform incremental operations on category action triggers at the user scope. See the account-scope PATCH endpoint for full documentation on supported operations (add, remove). # Register category triggers at account scope Source: https://docs.spade.com/api-reference/category-action-triggers/register-category-triggers-at-account-scope /api-reference/spec.yml put /category-action-triggers Register category action triggers at the account scope. Account-level triggers apply to all transactions for all users in your account. **Field requirements:** - `id`, `categoryId`, and `action` are required for each trigger - `categoryId` must be a valid Spade category ID (use [`GET /categories`](https://docs.spade.com/api-reference/category-personalization/get-all-default-and-custom-integration-level-categories) to discover valid IDs) **Limit:** A maximum of 300 `categoryTriggers` can be submitted per request. All registrations are processed synchronously. **Important:** Triggers are only applied to enrichment responses when the status is `succeeded`. To learn more about action triggers, please read the [Category Action Triggers Guide](https://docs.spade.com/reference/category-action-triggers-guide). # Register category triggers at card scope Source: https://docs.spade.com/api-reference/category-action-triggers/register-category-triggers-at-card-scope /api-reference/spec.yml put /users/{userId}/cards/{cardId}/category-action-triggers Register category action triggers at the card scope. Card-level triggers apply only to transactions for the specified card. **Field requirements:** - `id`, `categoryId`, and `action` are required for each trigger - `categoryId` must be a valid Spade category ID (use [`GET /categories`](https://docs.spade.com/api-reference/category-personalization/get-all-default-and-custom-integration-level-categories) to discover valid IDs) **Limit:** A maximum of 300 `categoryTriggers` can be submitted per request. All registrations are processed synchronously. **Important:** Triggers are only applied to enrichment responses when the status is `succeeded`. To learn more about action triggers, please read the [Category Action Triggers Guide](https://docs.spade.com/reference/category-action-triggers-guide). # Register category triggers at program scope Source: https://docs.spade.com/api-reference/category-action-triggers/register-category-triggers-at-program-scope /api-reference/spec.yml put /programs/{programId}/category-action-triggers Register category action triggers at the program scope. Program-level triggers apply to all enrichment requests where the `programId` field matches this program. **Field requirements:** - `id`, `categoryId`, and `action` are required for each trigger - `categoryId` must be a valid Spade category ID (use [`GET /categories`](https://docs.spade.com/api-reference/category-personalization/get-all-default-and-custom-integration-level-categories) to discover valid IDs) **Limit:** A maximum of 300 `categoryTriggers` can be submitted per request. All registrations are processed synchronously. **Important:** Triggers are only applied to enrichment responses when the status is `succeeded`. To learn more about action triggers, please read the [Category Action Triggers Guide](https://docs.spade.com/reference/category-action-triggers-guide). # Register category triggers at user scope Source: https://docs.spade.com/api-reference/category-action-triggers/register-category-triggers-at-user-scope /api-reference/spec.yml put /users/{userId}/category-action-triggers Register category action triggers at the user scope. User-level triggers apply to all transactions for all cards belonging to the specified user. **Field requirements:** - `id`, `categoryId`, and `action` are required for each trigger - `categoryId` must be a valid Spade category ID (use [`GET /categories`](https://docs.spade.com/api-reference/category-personalization/get-all-default-and-custom-integration-level-categories) to discover valid IDs) **Limit:** A maximum of 300 `categoryTriggers` can be submitted per request. All registrations are processed synchronously. **Important:** Triggers are only applied to enrichment responses when the status is `succeeded`. To learn more about action triggers, please read the [Category Action Triggers Guide](https://docs.spade.com/reference/category-action-triggers-guide). # Create a custom integration-level category Source: https://docs.spade.com/api-reference/category-personalization/create-a-custom-integration-level-category /api-reference/spec.yml post /categories Create a custom integration-level category. This category will be available to all users of your integration. To learn more about category personalization, please read the [Category Personalization Guide](https://docs.spade.com/reference/category-personalization-guide). # Create a custom user-level category Source: https://docs.spade.com/api-reference/category-personalization/create-a-custom-user-level-category /api-reference/spec.yml post /users/{userId}/categories Create a custom user-level category. To learn more about user category personalization, please read the [User Category Personalization Guide](https://docs.spade.com/reference/user-category-personalization-guide). # Create a user-level counterparty category personalization Source: https://docs.spade.com/api-reference/category-personalization/create-a-user-level-counterparty-category-personalization /api-reference/spec.yml put /users/{userId}/counterparty-category-personalizations Create a user-level counterparty category personalization. This personalization will apply to all enrichments that take place at the given `counterpartyId` for the user with the given `userId`. To learn more about user category personalization, please read the [User Category Personalization Guide](https://docs.spade.com/reference/user-category-personalization-guide). # Create an integration-level counterparty category personalization Source: https://docs.spade.com/api-reference/category-personalization/create-an-integration-level-counterparty-category-personalization /api-reference/spec.yml put /counterparty-category-personalizations Create an integration-level counterparty category personalization. This personalization will apply to all enrichments that take place at the given `counterpartyId` for your integration. To learn more about category personalization, please read the [Category Personalization Guide](https://docs.spade.com/reference/category-personalization-guide). # Delete a counterparty category personalization Source: https://docs.spade.com/api-reference/category-personalization/delete-a-counterparty-category-personalization /api-reference/spec.yml delete /counterparty-category-personalizations/{counterpartyId} Delete a counterparty category personalization. To learn more about category personalization, please read the [Category Personalization Guide](https://docs.spade.com/reference/category-personalization-guide). # Delete a custom integration-level category Source: https://docs.spade.com/api-reference/category-personalization/delete-a-custom-integration-level-category /api-reference/spec.yml delete /categories/{id} Delete a custom integration-level category. To learn more about category personalization, please read the [Category Personalization Guide](https://docs.spade.com/reference/category-personalization-guide). # Delete a custom user-level category Source: https://docs.spade.com/api-reference/category-personalization/delete-a-custom-user-level-category /api-reference/spec.yml delete /users/{userId}/categories/{id} Delete a custom user-level category. To learn more about user category personalization, please read the [User Category Personalization Guide](https://docs.spade.com/reference/user-category-personalization-guide). # Delete a user-level counterparty category personalization Source: https://docs.spade.com/api-reference/category-personalization/delete-a-user-level-counterparty-category-personalization /api-reference/spec.yml delete /users/{userId}/counterparty-category-personalizations/{counterpartyId} Delete a user-level counterparty category personalization. To learn more about user category personalization, please read the [User Category Personalization Guide](https://docs.spade.com/reference/user-category-personalization-guide). # Delete all custom integration-level categories Source: https://docs.spade.com/api-reference/category-personalization/delete-all-custom-integration-level-categories /api-reference/spec.yml delete /categories Delete all custom integration-level categories. To learn more about category personalization, please read the [Category Personalization Guide](https://docs.spade.com/reference/category-personalization-guide). # Delete all custom user-level categories Source: https://docs.spade.com/api-reference/category-personalization/delete-all-custom-user-level-categories /api-reference/spec.yml delete /users/{userId}/categories Delete all custom user-level categories. To learn more about user category personalization, please read the [User Category Personalization Guide](https://docs.spade.com/reference/user-category-personalization-guide). # Delete all integration-level counterparty category personalizations Source: https://docs.spade.com/api-reference/category-personalization/delete-all-integration-level-counterparty-category-personalizations /api-reference/spec.yml delete /counterparty-category-personalizations Delete all integration-level counterparty category personalizations. To learn more about category personalization, please read the [Category Personalization Guide](https://docs.spade.com/reference/category-personalization-guide). # Delete all user-level counterparty category personalizations Source: https://docs.spade.com/api-reference/category-personalization/delete-all-user-level-counterparty-category-personalizations /api-reference/spec.yml delete /users/{userId}/counterparty-category-personalizations Delete all user-level counterparty category personalizations. To learn more about user category personalization, please read the [User Category Personalization Guide](https://docs.spade.com/reference/user-category-personalization-guide). # Get all default and custom integration-level categories Source: https://docs.spade.com/api-reference/category-personalization/get-all-default-and-custom-integration-level-categories /api-reference/spec.yml get /categories Fetch categories from Spade's database. This endpoint returns a list containing Spade's default categories, plus any custom integration-level categories you've created. To learn more about category personalization, please read the [Category Personalization Guide](https://docs.spade.com/reference/category-personalization-guide). # Get all integration-level counterparty category personalizations Source: https://docs.spade.com/api-reference/category-personalization/get-all-integration-level-counterparty-category-personalizations /api-reference/spec.yml get /counterparty-category-personalizations Fetch counterparty category personalizations from Spade's database. This endpoint returns a list containing all integration-level counterparty category personalizations for your integration. To learn more about category personalization, please read the [Category Personalization Guide](https://docs.spade.com/reference/category-personalization-guide). # Get all user-level counterparty category personalizations Source: https://docs.spade.com/api-reference/category-personalization/get-all-user-level-counterparty-category-personalizations /api-reference/spec.yml get /users/{userId}/counterparty-category-personalizations Fetch user-level counterparty category personalizations from Spade's database. This endpoint returns a list containing all user-level counterparty category personalizations for the user with the given `userId`. To learn more about user category personalization, please read the [User Category Personalization Guide](https://docs.spade.com/reference/user-category-personalization-guide). # Get categories Source: https://docs.spade.com/api-reference/category-personalization/get-categories /api-reference/spec.yml get /users/{userId}/categories Fetch custom user-level categories from Spade's database. This endpoint returns a list containing all custom categories that the user with the given `userId` has created. To learn more about user category personalization, please read the [User Category Personalization Guide](https://docs.spade.com/reference/user-category-personalization-guide). # Report a card enrichment as incorrect Source: https://docs.spade.com/api-reference/feedback-and-reporting/report-a-card-enrichment-as-incorrect /api-reference/spec.yml post /transactions/report Endpoint to report a card enrichment as incorrect. An `enrichmentId` and an `errorDescription` are required fields to report an enrichment. Optional request fields such as `incorrectLocation`, `incorrectLocationDescription`, `incorrectCategory`, `incorrectCategoryDescription`, `incorrectChannel`, `incorrectChannelDescription`, `incorrectCounterparty`, and `incorrectCounterpartyDescription`, can be set to indicate what needs to be addressed in the enrichment. Once a report has been addressed, you will receive a corrected enrichment via a callback url you set with Spade. See the callback section to learn more on how we send back a corrected enrichment. # Report fraud on a transaction Source: https://docs.spade.com/api-reference/feedback-and-reporting/report-fraud-on-a-transaction /api-reference/spec.yml post /enrichments/{enrichmentId}/fraud An endpoint for reporting suspected fraud on a transaction which was not related to a chargeback, decline, or refund. If you've already indicated suspected fraud in a chargeback, decline, or refund, it's not necessary to send a request to this endpoint. The `isFraud`, `fraudType`, and `fraudNotes` fields are used to provide information about fraud or suspected fraud on this transaction. Note that this transaction must previously have been enriched to report other fraud information. # Report that a card decline occurred Source: https://docs.spade.com/api-reference/feedback-and-reporting/report-that-a-card-decline-occurred /api-reference/spec.yml post /enrichments/{enrichmentId}/decline An endpoint for reporting that a card decline has occurred on a transaction. The `declineCode`, `declineDescription`, and `declineNotes` properties can be used to provide information about the decline. The `isFraud`, `fraudType`, and `fraudNotes` are used to provide information about fraud or suspected fraud related to this decline. Note that this transaction must previously have been enriched to report the decline. # Report that a chargeback occurred Source: https://docs.spade.com/api-reference/feedback-and-reporting/report-that-a-chargeback-occurred /api-reference/spec.yml post /enrichments/{enrichmentId}/chargeback An endpoint for reporting that a chargeback process has occurred on a transaction. The `state` and `chargebackNotes` properties can be used to provide information about the chargeback. The `isFraud`, `fraudType`, and `fraudNotes` are used to provide information about fraud or suspected fraud related to this chargeback. Note that this transaction must previously have been enriched to report the chargeback. # Report that a refund occurred Source: https://docs.spade.com/api-reference/feedback-and-reporting/report-that-a-refund-occurred /api-reference/spec.yml post /enrichments/{enrichmentId}/refund An endpoint for reporting that a refund has occurred on a transaction associated with an enrichment. The `refundNotes` property can be used to provide information about the refund. The `isFraud`, `fraudType`, and `fraudNotes` are used to provide information about fraud or suspected fraud related to this refund. Note that this transaction must previously have been enriched to report the refund. # Clear account-level triggers Source: https://docs.spade.com/api-reference/merchant-action-triggers/clear-account-level-triggers /api-reference/spec.yml delete /merchant-action-triggers Clear all merchant action triggers at the account scope. This creates a new version with an empty trigger list. To learn more about action triggers, please read the [Merchant Action Triggers Guide](https://docs.spade.com/reference/merchant-action-triggers-guide). # Clear card-level triggers Source: https://docs.spade.com/api-reference/merchant-action-triggers/clear-card-level-triggers /api-reference/spec.yml delete /users/{userId}/cards/{cardId}/merchant-action-triggers Clear all merchant action triggers at the card scope. This creates a new version with an empty trigger list. To learn more about action triggers, please read the [Merchant Action Triggers Guide](https://docs.spade.com/reference/merchant-action-triggers-guide). # Clear program-level triggers Source: https://docs.spade.com/api-reference/merchant-action-triggers/clear-program-level-triggers /api-reference/spec.yml delete /programs/{programId}/merchant-action-triggers Clear all merchant action triggers at the program scope. This creates a new version with an empty trigger list. To learn more about action triggers, please read the [Merchant Action Triggers Guide](https://docs.spade.com/reference/merchant-action-triggers-guide). # Clear user-level triggers Source: https://docs.spade.com/api-reference/merchant-action-triggers/clear-user-level-triggers /api-reference/spec.yml delete /users/{userId}/merchant-action-triggers Clear all merchant action triggers at the user scope. This creates a new version with an empty trigger list. To learn more about action triggers, please read the [Merchant Action Triggers Guide](https://docs.spade.com/reference/merchant-action-triggers-guide). # Get account-level trigger status Source: https://docs.spade.com/api-reference/merchant-action-triggers/get-account-level-trigger-status /api-reference/spec.yml get /merchant-action-triggers Get the status and version of the latest merchant action trigger registration at the account scope. Account-level triggers apply to all transactions for all users in your account. **Important:** Triggers are only applied to enrichment responses when the status is `succeeded`. To learn more about action triggers, please read the [Merchant Action Triggers Guide](https://docs.spade.com/reference/merchant-action-triggers-guide). # Get card-level trigger status Source: https://docs.spade.com/api-reference/merchant-action-triggers/get-card-level-trigger-status /api-reference/spec.yml get /users/{userId}/cards/{cardId}/merchant-action-triggers Get the status and version of the latest merchant action trigger registration at the card scope. Card-level triggers apply only to transactions for the specified card. **Important:** Triggers are only applied to enrichment responses when the status is `succeeded`. To learn more about action triggers, please read the [Merchant Action Triggers Guide](https://docs.spade.com/reference/merchant-action-triggers-guide). # Get program-level trigger status Source: https://docs.spade.com/api-reference/merchant-action-triggers/get-program-level-trigger-status /api-reference/spec.yml get /programs/{programId}/merchant-action-triggers Get the status and version of the latest merchant action trigger registration at the program scope. Program-level triggers apply to all enrichment requests where the `programId` field matches this program. See [What is a program?](/reference/merchant-action-triggers-guide#what-is-a-program) for more details. **Important:** Triggers are only applied to enrichment responses when the status is `succeeded`. To learn more about action triggers, please read the [Merchant Action Triggers Guide](https://docs.spade.com/reference/merchant-action-triggers-guide). # Get user-level trigger status Source: https://docs.spade.com/api-reference/merchant-action-triggers/get-user-level-trigger-status /api-reference/spec.yml get /users/{userId}/merchant-action-triggers Get the status and version of the latest merchant action trigger registration at the user scope. User-level triggers apply to all transactions for all cards belonging to the specified user. **Important:** Triggers are only applied to enrichment responses when the status is `succeeded`. To learn more about action triggers, please read the [Merchant Action Triggers Guide](https://docs.spade.com/reference/merchant-action-triggers-guide). # Incremental trigger operations at account scope Source: https://docs.spade.com/api-reference/merchant-action-triggers/incremental-trigger-operations-at-account-scope /api-reference/spec.yml patch /merchant-action-triggers Perform incremental operations on merchant action triggers at the account scope. Supports two operations: - **add**: Register new merchants or update existing ones. Only the submitted merchants are affected—existing registrations are preserved. If a trigger ID already exists, its registration is replaced (upsert). Registrations with more than 100 triggers are processed asynchronously as a batch job. - **remove**: Remove merchants by trigger ID. Removed merchants are excluded from enrichment lookups. Trigger IDs that are not currently active are silently ignored (idempotent). **Limits:** Up to 100,000 triggers per `add` request, with a total cap of 300,000 active triggers per scope. To register more than 100,000 triggers, split them into ≤100,000 batches and submit them with sequential `add` operations, waiting for each to reach `succeeded` before the next — see [Registering large trigger sets](/reference/merchant-action-triggers-guide#registering-large-trigger-sets). Successful `add` operations return `201`; successful `remove` operations return `200`. **Important:** Only one operation can be in progress per scope at a time. Concurrent requests return 409. To learn more about action triggers, please read the [Merchant Action Triggers Guide](https://docs.spade.com/reference/merchant-action-triggers-guide). # Incremental trigger operations at card scope Source: https://docs.spade.com/api-reference/merchant-action-triggers/incremental-trigger-operations-at-card-scope /api-reference/spec.yml patch /users/{userId}/cards/{cardId}/merchant-action-triggers Perform incremental operations on merchant action triggers at the card scope. See the account-scope PATCH endpoint for full documentation on supported operations (add, remove). # Incremental trigger operations at program scope Source: https://docs.spade.com/api-reference/merchant-action-triggers/incremental-trigger-operations-at-program-scope /api-reference/spec.yml patch /programs/{programId}/merchant-action-triggers Perform incremental operations on merchant action triggers at the program scope. See the account-scope PATCH endpoint for full documentation on supported operations (add, remove). **Limits:** Up to 100,000 triggers per `add` request, with a total cap of 300,000 active triggers per scope. To register more than 100,000 triggers, split them into ≤100,000 batches and submit them with sequential `add` operations, waiting for each to reach `succeeded` before the next — see [Registering large trigger sets](/reference/realtime-action-triggers-guide#registering-large-trigger-sets). # Incremental trigger operations at user scope Source: https://docs.spade.com/api-reference/merchant-action-triggers/incremental-trigger-operations-at-user-scope /api-reference/spec.yml patch /users/{userId}/merchant-action-triggers Perform incremental operations on merchant action triggers at the user scope. See the account-scope PATCH endpoint for full documentation on supported operations (add, remove). # Register merchant triggers at account scope Source: https://docs.spade.com/api-reference/merchant-action-triggers/register-merchant-triggers-at-account-scope /api-reference/spec.yml put /merchant-action-triggers Register merchant action triggers at the account scope. Account-level triggers apply to all transactions for all users in your account. **Field requirements:** - `id`, `merchantName`, and `action` are always required for each trigger - If location fields (address, city) are NOT provided, `website` is required **Batch processing:** Registrations with more than 100 triggers are processed asynchronously as a batch job. Poll the GET endpoint to monitor completion. **Limits:** Up to 100,000 triggers per request, with a total cap of 300,000 active triggers per scope. To register more than 100,000 triggers, split them into ≤100,000 batches sent sequentially via PATCH `add` — see [Registering large trigger sets](/reference/realtime-action-triggers-guide#registering-large-trigger-sets). **Important:** Triggers are only applied to enrichment responses when the status is `succeeded`. To learn more about action triggers, please read the [Merchant Action Triggers Guide](https://docs.spade.com/reference/merchant-action-triggers-guide). # Register merchant triggers at card scope Source: https://docs.spade.com/api-reference/merchant-action-triggers/register-merchant-triggers-at-card-scope /api-reference/spec.yml put /users/{userId}/cards/{cardId}/merchant-action-triggers Register merchant action triggers at the card scope. Card-level triggers apply only to transactions for the specified card. **Field requirements:** - `id`, `merchantName`, and `action` are always required for each trigger - If location fields (address, city) are NOT provided, `website` is required **Limit:** A maximum of 100 `merchantTriggers` can be submitted per request at the card scope. Requests exceeding this limit will receive a `400` error. **Important:** Triggers are only applied to enrichment responses when the status is `succeeded`. To learn more about action triggers, please read the [Merchant Action Triggers Guide](https://docs.spade.com/reference/merchant-action-triggers-guide). # Register merchant triggers at program scope Source: https://docs.spade.com/api-reference/merchant-action-triggers/register-merchant-triggers-at-program-scope /api-reference/spec.yml put /programs/{programId}/merchant-action-triggers Register merchant action triggers at the program scope. Program-level triggers apply to all enrichment requests where the `programId` field matches this program. **Field requirements:** - `id`, `merchantName`, and `action` are always required for each trigger - If location fields (address, city) are NOT provided, `website` is required **Batch processing:** Registrations with more than 100 triggers are processed asynchronously as a batch job. Poll the GET endpoint to monitor completion. **Limits:** Up to 100,000 triggers per request, with a total cap of 300,000 active triggers per scope. To register more than 100,000 triggers, split them into ≤100,000 batches sent sequentially via PATCH `add` — see [Registering large trigger sets](/reference/realtime-action-triggers-guide#registering-large-trigger-sets). **Important:** Triggers are only applied to enrichment responses when the status is `succeeded`. To learn more about action triggers, please read the [Merchant Action Triggers Guide](https://docs.spade.com/reference/merchant-action-triggers-guide). # Register merchant triggers at user scope Source: https://docs.spade.com/api-reference/merchant-action-triggers/register-merchant-triggers-at-user-scope /api-reference/spec.yml put /users/{userId}/merchant-action-triggers Register merchant action triggers at the user scope. User-level triggers apply to all transactions for all cards belonging to the specified user. **Field requirements:** - `id`, `merchantName`, and `action` are always required for each trigger - If location fields (address, city) are NOT provided, `website` is required **Limit:** A maximum of 100 `merchantTriggers` can be submitted per request at the user scope. Requests exceeding this limit will receive a `400` error. **Important:** Triggers are only applied to enrichment responses when the status is `succeeded`. To learn more about action triggers, please read the [Merchant Action Triggers Guide](https://docs.spade.com/reference/merchant-action-triggers-guide). # Search for merchants Source: https://docs.spade.com/api-reference/merchant-search/search-for-merchants /api-reference/spec.yml get /corporations This endpoint is in beta. To request access, contact [sales@spade.com](mailto:sales@spade.com). Search for merchants in Spade's merchant database by name. Designed for powering autocomplete experiences, this endpoint returns up to five matching merchants with their name, logo, and website. To learn more, read the [Merchant Search Guide](https://docs.spade.com/reference/merchant-search-guide). # Enrich a batch of transfers Source: https://docs.spade.com/api-reference/transfer-enrichment/enrich-a-batch-of-transfers /api-reference/spec.yml post /batches/transactions/transfers/enrich Submit a batch of transfers for enrichment. This endpoint will return a `batchId` which can be used in the `/batches/{batchId}` and `/batches/{batchId}/results` endpoints to check the status of the batch job and retrieve the results. You can pass `?synchronous=true` to enrich smaller sets of transactions synchronously. See the [Microbatch enrichment guide](https://docs.spade.com/reference/microbatch-enrichment-guide) for details. Note that we impose a rate limit on the number of batch requests made in a rolling 12 hour window. Please reach out to sales@spade.com with any questions. # Enrich a transfer transaction Source: https://docs.spade.com/api-reference/transfer-enrichment/enrich-a-transfer-transaction /api-reference/spec.yml post /transactions/transfers/enrich Enrich a transfer transaction such as an ACH withdrawal, ACH deposit, or wire transfer. When using this endpoint, take care to ensure you are using unaltered and correct data in each field. Note that transfers enrichment is currently private, please reach out to sales@spade.com to request access. # Get metadata for the batch transfer enrichment endpoint Source: https://docs.spade.com/api-reference/transfer-enrichment/get-metadata-for-the-batch-transfer-enrichment-endpoint /api-reference/spec.yml options /batches/transactions/transfers/enrich Returns metadata about this endpoint, including the current `synchronousMax` — the cap on items per request when `?synchronous=true`. Can be used to fetch the current cap dynamically. # Get the results of a batch transfer enrichment job Source: https://docs.spade.com/api-reference/transfer-enrichment/get-the-results-of-a-batch-transfer-enrichment-job /api-reference/spec.yml get /batches/{transferEnrichmentBatchId}/results Get the results of a batch transfer enrichment job. # Get the status of a batch transfer enrichment job Source: https://docs.spade.com/api-reference/transfer-enrichment/get-the-status-of-a-batch-transfer-enrichment-job /api-reference/spec.yml get /batches/{transferEnrichmentBatchId} Get the status of a batch transfer enrichment job. # Enrich a batch of universal transactions Source: https://docs.spade.com/api-reference/universal-enrichment/enrich-a-batch-of-universal-transactions /api-reference/spec.yml post /batches/transactions/universal/enrich Submit a batch of universal transactions for enrichment. This endpoint will return a `batchId` which can be used in the `/batches/{batchId}` and `/batches/{batchId}/results` endpoints to check the status of the batch job and retrieve the results. You can pass `?synchronous=true` to enrich smaller sets of transactions synchronously. See the [Microbatch enrichment guide](https://docs.spade.com/reference/microbatch-enrichment-guide) for details. Note that we impose a rate limit on the number of batch requests made in a rolling 12 hour window. Please reach out to sales@spade.com with any questions. # Enrich a universal transaction Source: https://docs.spade.com/api-reference/universal-enrichment/enrich-a-universal-transaction /api-reference/spec.yml post /transactions/universal/enrich Enrich any type of transaction such as an aggregator transaction, card transaction, ACH withdrawal, ACH deposit, or wire transfer. Note that universal enrichment is currently in *beta*, please reach out to sales@spade.com to request access. # Get metadata for the batch universal enrichment endpoint Source: https://docs.spade.com/api-reference/universal-enrichment/get-metadata-for-the-batch-universal-enrichment-endpoint /api-reference/spec.yml options /batches/transactions/universal/enrich Returns metadata about this endpoint, including the current `synchronousMax` — the cap on items per request when `?synchronous=true`. Can be used to fetch the current cap dynamically. # Get the results of a batch universal enrichment job Source: https://docs.spade.com/api-reference/universal-enrichment/get-the-results-of-a-batch-universal-enrichment-job /api-reference/spec.yml get /batches/{universalEnrichmentBatchId}/results Get the results of a batch universal enrichment job. # Changelog Source: https://docs.spade.com/changelog/changelog ## Larger Action Trigger Registrations (up to 300k) Account-scope and program-scope action trigger registrations now support a total cap of **300,000 active triggers per scope**. The per-request limit remains **100,000 triggers**, so to register more than 100,000 triggers you split them into batches of up to 100,000 and submit them with **sequential PATCH `add` operations** — waiting for each batch to reach `succeeded` before sending the next (only one registration can be in progress per scope at a time). See [Registering large trigger sets](/reference/realtime-action-triggers-guide#registering-large-trigger-sets) for details. ## Microbatch Enrichment Batch enrichment endpoints now support **microbatching** — pass `?synchronous=true` to receive enriched results inline in the response. Use this feature if you need to enrich a small number of records quickly. ```bash theme={null} curl https://east.sandbox.spade.com/batches/transactions/universal/enrich?synchronous=true \ -H "X-Api-Key: " \ -d '{ "transactions": [ ... ] }' ``` Microbatching is available on: * `POST /batches/merchants/enrich` — up to **50** merchants per microbatch * `POST /batches/transactions/cards/enrich` — up to **100** transactions per microbatch * `POST /batches/transactions/cards/enrich/parse` — up to **100** transactions per microbatch * `POST /batches/transactions/transfers/enrich` — up to **100** transactions per microbatch * `POST /batches/transactions/universal/enrich` — up to **100** transactions per microbatch Microbatching is not enabled by default. To request access, please contact us at [sales@spade.com](mailto:sales@spade.com). See the [Microbatch enrichment guide](/reference/microbatch-enrichment-guide) for details. ## Triggered Action `source` Field Triggered actions in enrichment responses now include a `source` field indicating whether the action was triggered by the matched `counterparty` or `third_party`. `source` is set to `null` when an `ALLOW_ONLY` action trigger was set up whose condition the current transaction did not meet (the `authRecommendation` in this case will be `BLOCK`). ```json theme={null} "actions": [ { "id": "trigger-123", "type": "merchant_trigger", "action": { "type": "REWARD", "points": 100 }, "scope": "account", "source": "counterparty" } ] ``` Read the [Merchant Action Triggers Guide](/reference/merchant-action-triggers-guide) for details. ## Incremental Updates for Merchant Action Triggers You can now add and remove individual merchants from an action trigger registration without replacing your entire list. Previously, any change required re-submitting all triggers through PUT, which meant minutes of batch processing for large registrations. The new PATCH endpoint supports two operations: * **add**: Register new merchants or update existing ones. Existing merchants that aren't in the request are left untouched. Submitting a trigger ID that already exists replaces that registration (upsert). * **remove**: Remove merchants by trigger ID. Completes in under 100ms regardless of registration size. ```json theme={null} // Add merchants { "operation": "add", "merchantTriggers": [ { "id": "trigger-123", "merchantName": "Starbucks", "action": { "type": "REWARD", "points": 100 } } ] } // Remove merchants { "operation": "remove", "triggerIds": ["trigger-123", "trigger-456"] } ``` The GET endpoint now also returns the full list of active merchant triggers (`merchantTriggers` and `totalCount`) alongside the registration status and version. PATCH is available at all four scopes (account, program, user, card). Read the [Merchant Action Triggers Guide](/reference/merchant-action-triggers-guide) for details. ## Program-Scoped Merchant Action Triggers Merchant action triggers now support a program scope, allowing you to register triggers that apply to all transactions where the enrichment request includes a matching `programId`. The new endpoints follow the same PUT/PATCH/GET/DELETE pattern as the existing account, user, and card scopes: ``` GET /programs/{programId}/merchant-action-triggers PUT /programs/{programId}/merchant-action-triggers PATCH /programs/{programId}/merchant-action-triggers DELETE /programs/{programId}/merchant-action-triggers ``` Program-scoped triggers are applied in addition to account-scoped triggers when `programId` is present in the enrichment request. Read the [Merchant Action Triggers Guide](/reference/merchant-action-triggers-guide) for details on scope inheritance. ## Merchant Action Triggers You can now register custom actions to be returned when a transaction matches a specific merchant. Merchant action triggers let you attach arbitrary data to a set of merchants — rewards, block/allow rules, custom metadata — and have that data returned automatically in enrichment responses when a transaction matches. Use the PUT endpoint to register a list of merchant triggers at a given scope (account, user, or card). Spade matches each submitted merchant against our database and returns your action data in the enrichment response when a transaction matches. ```json theme={null} // Register triggers at account scope PUT /merchant-action-triggers { "merchantTriggers": [ { "id": "trigger-123", "merchantName": "Starbucks", "action": { "type": "REWARD", "points": 100 } } ] } ``` ```json theme={null} // Matched trigger in enrichment response "actions": [ { "id": "trigger-123", "type": "merchant_trigger", "action": { "type": "REWARD", "points": 100 }, "scope": "account" } ] ``` Registrations with more than 100 merchants at the account scope are processed asynchronously — poll the GET endpoint to monitor completion. User and card scopes are limited to 100 merchants per request. Two special action types are also supported: `BLOCK` and `ALLOW_ONLY`, which populate an `authRecommendation` field in the enrichment response for use in authorization decisioning. Read the [Merchant Action Triggers Guide](/reference/merchant-action-triggers-guide) to get started. ## New Category: Mortgage New year, new category! We now support classifying transactions as `Mortgage` transactions. This is a new Tier 3 category that falls under`Banking and Finance`: > `001-008-001-000` = `Banking and Finance -> Lending -> Mortgage` > > Example: United Wholesale Mortgage will be categorized as `Mortgage` moving forward. Our full industry list can be accessed via API (details [here](/api-reference/category-personalization/get-all-default-and-custom-integration-level-categories)). ## New Category: AI Services We’re excited to share an update to our merchant categorization system! We have added a Tier 3 industry `AI Services` under `Technology -> Software`: > `013-009-013-000` = `Technology -> Software -> AI Services` > > Example: OpenAI will be categorized as `AI Services` moving forward. Our full industry list can be accessed via API (details [here](/api-reference/category-personalization/get-all-default-and-custom-integration-level-categories)). ## Mobile App Data We've added an exciting new premium field to card transaction enrichment! We now support matching to mobile apps purchased through the Google Play Store and to in-app purchases made in these apps. If mobile app matching is part of your product package, you'll now have access to the `mobileAppInfo` object in your card enrichment responses when a transaction matches to a Google Play Store app. ```json theme={null} "mobileAppInfo": { "id": "0d1ef10f-2652-39bd-98f1-af34ae03a4a7", "name": "Artificial Life", "url": "https://play.google.com/store/apps/details?id=com.mooncoder.alife", "logo": null, "developerName": "Mooncoder", "developerId": "f6fdc9b1-f0ea-3dda-8e15-ead6fe5755fe", "category": "Education and Parenting", "hasGamblingOrRewards": false, "hasSimulatedGambling": false, "ageRating": "Children" }, ``` Learn more about mobile app data by reading our [Mobile App Data Guide](/reference/mobile-app-data). ## Third Party Websites Going forward, enrichments will include third party websites. ```json theme={null} "thirdParties": [ { "id": "9063bc0f-0a3f-4b1f-bf64-4f9862e17002", "name": "PayPal", "type": "platform", "logo": "https://static.v2.spadeapi.com/logos/9063bc0f0a3f4b1fbf644f9862e17002/light.png", "website": "http://www.paypal.com/" } ], ``` ## New Category: Golf Courses ⛳ We’re excited to share another improvement to our merchant categorization system! We have added a Tier 3 industry 'Golf Courses' under `Entertainment -> Recreation`: > `004-002-013-000` = `Entertainment -> Recreation -> Golf Courses` > > Example: Canyon Springs Golf Club will be categorized as ‘Golf Courses’ moving forward. Our full industry list can be accessed via API (details [here](/api-reference/category-personalization/get-all-default-and-custom-integration-level-categories)). ## Spade Categorization Update We’re excited to share updates on improvements to our merchant categorization system! These changes focus on adding granularity to our category tree and enhancing differentiation for risky and ambiguous categories. Here's what’s new: 1. **New categories and naming updates:** Please note that all newly added categories are tier 3 or tier 4 categories that fall under our existing category tree, ensuring compatibility with your integration. Contact [support@spade.com](mailto:support@spade.com) for the full file instead * **Cannabis:** * Renamed 011-013-001-000 Retail -> Specialty Retail -> Cannabis Dispensary to Retail -> Specialty Retail -> Cannabis (Category code stays the same) * New tier 4 categories under Cannabis: * `011-013-001-001 Retail -> Specialty Retail -> Cannabis -> Dispensary` (Example: Food4Thought, Houston, TX) * `011-013-001-002 Retail -> Specialty Retail -> Cannabis -> CBD and Unscheduled Cannabis Products` (Example: Drink Brez) * **Firearms:** * New tier 3 category Firearms under Retail -> Specialty Retail, and two new tier 4 categories under that: * `011-013-022-000 Retail -> Specialty Retail -> Firearms` (Example: Kroll International) * `011-013-022-001 Retail -> Specialty Retail -> Firearms -> Guns` (Example: Primary Arms) * `011-013-022-002 Retail -> Specialty Retail -> Firearms -> Ammo and Accessories` (Example: Lucky Gunner) * **Vitamins and Supplements:** * New tier 3 category Vitamins and Supplements under Retail -> Specialty Retail: * `011-013-023-000 Retail -> Specialty Retail -> Vitamins and Supplements` (Example: GNC) * **Firing Ranges and Clubs:** * New tier 3 category Firing Ranges and Clubs under Entertainment -> Recreation: * `004-002-012-000 Entertainment -> Recreation -> Firing Ranges and Clubs` (Example: Guntry Club Of Maryland) 2. **Categorization Improvements:** in addition to adding new categories, we are rolling out improvements to existing categorizations: * **Adult Entertainment:** Enhanced categorization for merchants without a dedicated MCC, including online platforms (e.g., Chaturbate) and physical clubs (e.g., The Box NYC). * **Casinos and Gambling:** Better classification of online gambling games and sports betting merchants (e.g., Underdog Sports, Modo). * **Convenience Stores vs. Bodegas:** Improved differentiation between convenience stores and specialty food retailers (e.g., Chelsea Gourmet Deli categorized as Specialty Food). * **Cafes vs. Restaurants/Fast Food:** Refined categorization for cafes that often share MCCs with restaurants or fast food (e.g., Bluestone Lane categorized as Cafes, while Lisbon Cafe remains a Restaurant). We’re confident these updates will provide more precise categorization for your use cases. Let us know if you have any questions or feedback! ## Address Line 1 and 2 Going forward, enrichments will include two new fields: `addressLine1` and `addressLine2`. This change will hopefully improve usability of our location data. The existing `address` field will continue to exist unchanged. ```json theme={null} "location": [ { ... "address": "1234 W 5th Ave Suite 100", "addressLine1": "1234 W 5th Ave", "addressLine2": "Suite 100", ... } ] ``` # Action triggers guide Source: https://docs.spade.com/reference/action-triggers-guide Register rules that trigger custom actions when transactions match your criteria ## Overview Action triggers let you register rules that fire custom actions when enriched transactions match your criteria. When a match occurs, the enrichment response includes your custom `action` data — enabling real-time rewards, spending controls, and transaction routing without any post-processing. Spade supports two types of action triggers: Trigger actions when transactions match specific merchants. Supports location-level and corporation-level matching with up to 100,000 triggers per scope. Trigger actions when transactions match Spade categories such as Travel, Gambling, or Groceries. Up to 300 triggers per scope, processed synchronously. ## Shared concepts ### Scope hierarchy Both trigger types support four registration scopes. Higher scopes cascade down to lower scopes: | Scope | Applies to | | ----------- | -------------------------------------------------- | | **Account** | All transactions across your account | | **Program** | All transactions where `programId` matches | | **User** | All transactions for all cards belonging to a user | | **Card** | Only transactions for a specific card | When a transaction is enriched, Spade checks for matching triggers across all applicable scopes — card, user, program, then account. Multiple matches from different scopes can appear in a single response. ### What is a program? A **program** is a freeform identifier you define — it requires no upfront configuration. You supply a `programId` string on your enrichment requests to group transactions however makes sense for your business (e.g., by card product, customer, or business line). `programId`, `userId`, and `cardId` each have a maximum length of 512 characters. ### Action types The `action` object on each trigger is your custom JSON payload. The `type` field supports reserved values with special behavior: | Type | Behavior | | --------------- | --------------------------------------------------------------------------------------------- | | `BLOCK` | Adds `authRecommendation: "BLOCK"` to the response | | `ALLOW_ONLY` | Adds `authRecommendation: "ALLOW"` when matched; `"BLOCK"` when unmatched (inverse semantics) | | `REWARD` | Passed through as-is | | Any other value | Passed through as-is | ### Triggered actions in enrichment responses Matched triggers appear in the `actions` array of the enrichment response. Each entry includes the trigger `id`, `type`, your custom `action` data, the `scope` it was registered at, and the `source` of the match. ```json theme={null} { "actions": [ { "id": "trigger-1", "type": "merchant_trigger", "action": { "type": "REWARD", "rewardPercent": 5 }, "scope": "account", "source": "counterparty" }, { "id": "cat-trigger-1", "type": "category_trigger", "action": { "type": "REWARD", "rewardPercent": 3 }, "scope": "account", "source": "counterparty", "categoryId": "020-001-000-000", "categoryName": "Travel" } ] } ``` The `actions` field is `null` when no triggers match, and is omitted entirely if your account does not have the actions feature enabled. # Batch enrichment guide Source: https://docs.spade.com/reference/batch-enrichment-guide Enrich many transactions at once If you don't need realtime enrichment or are providing historical transaction data when first integrating with Spade you can use our batch endpoints instead of sending transactions individually. Batch enrichment allows you to process up to `50,000` transactions in a single request and retrieve the enriched results once processing is complete. Below you'll find the data types supported and their corresponding endpoints: **Card Enrichment** * [/batches/transactions/cards/enrich](/api-reference/card-enrichment/enrich-a-batch-of-card-transactions) * [/batches/transactions/cards/enrich/parse](/api-reference/card-enrichment/enrich-a-batch-of-card-transactions-with-de43-data) **Transfers enrichment** * [/batches/transactions/transfers/enrich](/api-reference/transfer-enrichment/enrich-a-batch-of-transfers) **Universal data enrichment** * [/batches/transactions/universal/enrich](/api-reference/universal-enrichment/enrich-a-batch-of-universal-transactions) Each batch endpoint also supports **microbatching** for use cases where you need to enrich a small number of records quickly. See the [Microbatch enrichment guide](/reference/microbatch-enrichment-guide) for details. If you have a large number of records and need to optimize for throughput, the async batch enrichment system detailed below will provide better performance. ## Getting Started Let's walk through the process of enriching a batch of transactions: ### Preparing your batch Depending on the type of data you're sending different fields may be required but regardless of whether you're sending card, transfer or aggregated data we recommend the following: **Using unique Transaction IDs** Ensure each transaction in your batch has a unique `transactionId`. Results are not guaranteed to be returned in the order in which they were submitted, so you can use the `transactionId` to correlate transactions in the results with the original requests. Duplicate IDs will cause the batch enrichment request to fail. **Sending data in chronological order** We recommend sending your oldest transactions first as this can have an impact on the insights we can provide on transaction data such as recurrence. **Using Custom attributes** If you have additional data elements that you would like to associate to any transactions, you can use the `customAttributes` object which will be passed back alongside enriched transaction data. ### Sending your batch Once you have prepared your batch(es) you can send up to 10 requests per second to our batch enrichment endpoints. Uploading large batches can take a while, so we recommend increasing the timeout of your client. We support up to a 120 second timeout window. Below we've provided an example request for reference. ```bash shell theme={null} curl --request POST \ --url https://east.sandbox.spade.com/batches/transactions/cards/enrich \ --header 'content-type: application/json' \ --header 'X-Api-Key: SPADE-API-KEY' \ --data '{ "callbackUrl": "https://example.com/callback", "transactions": [ { "transactionId": "53DCD6BD-6220-408C-8B49-7761C9D35FC3", "userId": "2AF17111-7270-498F-8731-C023B1A85A48", "merchantName": "Wal-Mart Super Center", "amount": "5.00", "location": { "city": "PORT ORANGE", "region": "12", "country": "US", "postalCode": "11111" }, "acquirerId": "", "occurredAt": "2021-01-01", "categoryCode": "5488", "categoryType": "MCC", "currencyCode": "USD" }, { "transactionId": "747DF4B4-4D30-4E44-B1C7-0C3CA99AD55D", "userId": "7BDCC2E9-0E87-435E-866D-54C2552282C0", "merchantName": "Amazon", "acquirerId": "000000000123456", "amount": "25.23", "currencyCode": "USD", "occurredAt": "2022-06-15 18:27:51Z", "categoryCode": "5812", "categoryType": "MCC", "location": { "address": "1234 W 5th Ave Suite 100", "city": "New York", "region": "NY", "country": "USA", "postalCode": "10001", "latitude": 45, "longitude": 120 } } ] }' ``` ```python python example theme={null} import requests import time # Prepare your batch of transactions transactions = [ { "userId": "user_123", "merchantName": "Walmart", "amount": 50.23, "currencyCode": "USD", "occurredAt": "2024-03-21T15:30:00Z", "categoryType": "MCC", "categoryCode": "5411", "transactionId": "tx_123", # Must be unique within the batch "location": { "city": "Seattle", "region": "WA", "country": "USA" } }, # ... more transactions ... ] # Submit the batch (without a callbackUrl) response = requests.post( "https://east.sandbox.spade.com/batches/transactions/cards/enrich", json={"transactions": transactions}, headers={"X-Api-Key": ""} ) # Or submit with a callbackUrl response = requests.post( "https://east.sandbox.spade.com/batches/transactions/cards/enrich", json={ "transactions": transactions, "callbackUrl": "https://your-callback-endpoint.com/batch-notifications" }, headers={"X-Api-Key": ""} ) batch_id = response.json()["batchId"] print(f"Batch submitted with ID: {batch_id}") ``` Once successfully submitted we will send back a response object that includes the `batchId` which you'll use later to retrieve the enriched transactions. ```json Batch response theme={null} { "batchId": "8590f0f5-2a4b-431e-baa8-cd3bfff22020", "status": "pending", "batchSize": 2, "submittedAt": "2025-06-30T21:46:10.827344Z" } ``` You must use the `batchId` to retrieve enriched data, so make sure you are storing it! ### Handling failed batches If your submitted batch fails we will return a `400` response with error details. Below is an example of an error response with duplicate `transactionIds`: ```json theme={null} { "transactions": [ "Transaction at index 1 has a duplicate ID." ] } ``` Using the error details, make the changes required and re-submit the batch. ## Checking batch status A batch job can be in one of four states: \* `pending`: The batch has been accepted but processing hasn't started \* `running`: The batch is currently being processed \* `completed`: All transactions have been enriched and results are ready \* `failed`: The batch encountered an error and couldn't be processed We provide options for updates on the batch status either via webhooks or polling the status endpoint. We strongly recommend using webhooks as this reduces integration complexity as well as unnecessary API calls. **Using webhooks** If you provided a `callbackUrl` in your batch submission, we'll send a POST request to that URL when processing completes with the `batchId` and the `status`. We will also include a token in the `X-Webhook-Token` header that you should compare against the callback token provided to you by your Spade representative. Contact your Spade representative if you do not have this token. Here is an example Flask endpoint you could use to receive callbacks. ```python Callback example expandable theme={null} from flask import Flask, request, jsonify app = Flask(__name__) @app.route('/batch-notifications', methods=['POST']) def batch_notification(): webhook_token = request.headers.get('X-Webhook-Token') expected_token = "your-callback-token-from-spade" # Store this securely if not webhook_token or webhook_token != expected_token: raise InvalidTokenError() data = request.json batch_id = data["batchId"] status = data["status"] if status == "completed": fetch_batch_results(batch_id) elif status == "failed": log_batch_failure(batch_id) ``` **Polling for status** You can poll the status endpoint until the status changes to a `completed` status. We recommend implementing an exponential backoff strategy when polling for status. ```python Polling example expandable theme={null} def wait_for_batch_completion_with_backoff(batch_id, max_attempts=10, initial_delay=60): """Wait for batch completion with exponential backoff""" delay = initial_delay while True: response = requests.get( f"https://east.sandbox.spade.com/batches/{batch_id}", headers={"X-Api-Key": ""} ) status = response.json()["status"] if status == "completed": return True elif status == "failed": raise Exception("Batch processing failed") print(f"Sleeping for {delay} seconds before checking status again") time.sleep(delay) delay = max(delay * 2, 60) raise Exception("Timeout waiting for batch completion") ``` If the batch results in a `failed` status you will need to re-send the batch for processing. ### Retrieving enriched data Once the batch status is `completed` you can retrieve the enriched data by calling the /results endpoint. ```json theme={null} { "batchId": "8590f0f5-2a4b-431e-baa8-cd3bfff22020", "status": "completed" } ``` The `/batches/{batchId}/results` endpoint will only return results when the batch status is `completed`. If you request results before completion, you'll receive a 202 status code indicating that the results aren't yet available. The results endpoint will return the `batchId` in addition to a `results` array that includes all of the transactions sent in the batch. Each transaction object in the `results` array will contain: * Your original`transactionId ` * Any `customAttributes` provided in the transaction * A`status` code (either `200` or `400`) * If successful, the enriched transaction data will include an `enrichmentId` * If unsuccessful an error object Below is an example response returned: ```json expandable theme={null} { "batchId": "8590f0f5-2a4b-431e-baa8-cd3bfff22020", "results": [ { "statusCode": 200, "transactionInfo": { "type": "spending", "subType": null, "display": { "name": "Walmart", "categoryName": "Department Stores", "graphic": "https://static.v2.spadeapi.com/logos/d730906bf1a849f19939f27390170a6d/light.png", "graphicSource": "counterparty" }, "thirdParties": [], "spendingInfo": { "channel": { "value": "physical" } }, "transferInfo": null, "atmInfo": null, "isAccountVerification": null, "isPeerToPeer": null, "isDigitalWallet": null, "transactionId": "53DCD6BD-6220-408C-8B49-7761C9D35FC3", "recurrenceInfo": null, "riskInsights": { "irregularWebPresenceDetected": false, "negativeOnlineSentiment": false, "highRiskEntity": false, "riskyIndustry": false, "cardAcceptanceHistory": "extensive" } }, "counterparty": [ { "id": "d730906b-f1a8-49f1-9939-f27390170a6d", "name": "Walmart", "legalName": "Walmart Inc.", "industry": [ { "id": "011-000-000-000", "name": "Retail", "icon": "https://static.v2.spadeapi.com/categories/ee4ee39fd5474d31ac42f9e606b9040a/light.png" }, { "id": "011-018-000-000", "name": "General Goods", "icon": "https://static.v2.spadeapi.com/categories/ee4ee39fd5474d31ac42f9e606b9040a/light.png" }, { "id": "011-018-002-000", "name": "Department Stores", "icon": "https://static.v2.spadeapi.com/categories/ee4ee39fd5474d31ac42f9e606b9040a/light.png" } ], "location": [ { "id": "380e18b7-bf9e-3545-b27e-80e36301c540", "address": "1590 Dunlawton Ave", "addressLine1": "1590 Dunlawton Ave", "addressLine2": null, "city": "Port Orange", "region": "FL", "postalCode": "32127", "country": "USA", "phoneNumber": "+13867610191", "latitude": 29.116753, "longitude": -81.019165, "matchScore": 85.35 } ], "matchScore": 89.62, "logo": "https://static.v2.spadeapi.com/logos/d730906bf1a849f19939f27390170a6d/light.png", "medianSpendPerTransaction": null, "phoneNumber": "+14792734000", "website": "https://www.walmart.com/" } ], "enrichmentId": "967c0acc-92a0-4212-bbed-3a7209b58bf6" }, { "statusCode": 200, "transactionInfo": { "type": "spending", "subType": null, "display": { "name": "Amazon", "categoryName": "Online Marketplace", "graphic": "https://static.v2.spadeapi.com/logos/5f35110e8de74f9cadc6b28bf87dd5b6/light.png", "graphicSource": "counterparty" }, "thirdParties": [], "spendingInfo": { "channel": { "value": "digital" } }, "transferInfo": null, "atmInfo": null, "isAccountVerification": null, "isPeerToPeer": null, "isDigitalWallet": null, "transactionId": "747DF4B4-4D30-4E44-B1C7-0C3CA99AD55D", "recurrenceInfo": null, "riskInsights": { "irregularWebPresenceDetected": false, "negativeOnlineSentiment": false, "highRiskEntity": false, "riskyIndustry": false, "cardAcceptanceHistory": "limited" } }, "counterparty": [ { "id": "5f35110e-8de7-4f9c-adc6-b28bf87dd5b6", "name": "Amazon", "legalName": "Amazon.com, Inc.", "industry": [ { "id": "011-000-000-000", "name": "Retail", "icon": "https://static.v2.spadeapi.com/categories/ee4ee39fd5474d31ac42f9e606b9040a/light.png" }, { "id": "011-010-000-000", "name": "Online Marketplace", "icon": "https://static.v2.spadeapi.com/categories/b4b0d249b40249acb7445027d4574fc5/light.png" } ], "location": [ { "id": null, "address": "1234 W 5th Ave Suite 100", "addressLine1": "1234 W 5th Ave Suite 100", "addressLine2": null, "city": "New York", "region": "NY", "postalCode": "10001", "country": "USA", "phoneNumber": null, "latitude": 45, "longitude": 120, "matchScore": null } ], "matchScore": 98.26, "logo": "https://static.v2.spadeapi.com/logos/5f35110e8de74f9cadc6b28bf87dd5b6/light.png", "medianSpendPerTransaction": null, "phoneNumber": "+12062661000", "website": "https://www.amazon.com" } ], "enrichmentId": "aae9c6bc-1cea-4ca8-8f70-dc85e2d21224" } ] } ``` ```python Retrieving results theme={null} response = requests.get( f"https://east.sandbox.spade.com/batches/{batch_id}/results", headers={"X-Api-Key": ""} ) results = response.json()["results"] for enriched_transaction in results: print(f"Enriched transaction: {enriched_transaction['enrichmentId']}") # ... process the enrichment ... ``` ### **Handling errors**: For transactions that resulted in errors we will provide an `errors` object that includes detailed information that caused the error. An example is included below for a request that did not include a `merchantName` ```json theme={null} { "statusCode": 400, "transactionInfo": { "transactionId": "53DCD6BD-6220-408C-8B49-7761C9D35FC3" }, "errors": { "merchantName": [ "This field is required." ] } } ``` See our [enrichment guide](https://docs.spade.com/reference/getting-started#implementation-notes) for more details on handling errors from our API. # Card enrichment guide Source: https://docs.spade.com/reference/card-enrichment-guide ## Overview This guide outlines best practices for integrating with our card enrichment endpoints and how to use the enriched data that is returned. ## Best practices ### Choosing the correct card endpoint * If you receive parsed data from your issuer, or parse the data yourself, you should send data to our [**/transactions/cards/enrich**](https://docs.spade.com/api-reference/card-enrichment/enrich-a-card-transaction) endpoint. See an example of parsed data below: ``` MerchantName: WMSUPERCENTER#582 City: PORT ORANGE Region: FL Country: US ``` * If you receive raw DE43 data or an unparsed merchant data string from your issuer you should send data to our [**/transactions/cards/enrich/parse**](https://docs.spade.com/api-reference/card-enrichment/enrich-a-card-transaction-with-an-unparsed-de43) endpoint. See an example of a raw DE43 string below: ```json theme={null} DE43: WMSUPERCENTER#582 PORT ORANGE FL ``` If you're unsure if your data follows the DE43 format you can validate it by pasting your string in [this regex validator](https://regex101.com/r/UU2gIo/1). ### Mapping card data to required Spade fields Below we've provided generic examples as well as example fields from common issuers to pass to Spade to enrich transactions. We are issuer agnostic and can enrich any card data you may have. For assistance please work with your account team or contact [support@spade.com](mailto:support@spade.com). Below is a generic example of the field mapping to use our [cards/enrich](https://docs.spade.com/api-reference/card-enrichment/enrich-a-card-transaction) endpoint if you received parsed data from your issuer | **Spade fields** | **Required** | Example | Notes | | :---------------- | :----------- | ------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- | | **merchantName** | required | `WMSUPERCENTER#582` | Send raw merchant name information when possible such as in cases where your issuer cleanses or enriches merchant data | | **city** | required | `PORT ORANGE` | May occasionally include a website or phone number | | **postalCode** | optional | `32127` | Recommended to provide if it is present | | **region** | optional | `FL` | Recommended to provide if it is present | | **country** | required | `USA` or `840` | Supports 3 letter country code or 3 digit country code | | **acquirerId** | optional | `444590000` | May be called merchant ID or acceptor. Typically 8-16 characters | | **categoryCode** | required | `1234` | Required. If you do not receive one on the transaction you can use a fallback of `1111 `however this will negatively impact performance | | **amount** | required | `12.34` | Use negative amounts for credit and positive amounts for debits | | **currencyCode** | required | `USD` | Use the currency code that matches to the amount you're providing | | **occurredAt** | required | `2025-12-01` | | | **transactionId** | required | `t-1234` | Unique identifier for the transaction | | **programId** | optional | `p-1234` | Identifier for the program associated with the transaction, used to scope action triggers at the program level. | | **cardId** | optional | `c-1234` | Unique identifier for the card (do not use actual card numbers). | | **userId** | required | `u-1234` | Unique user identifier associated with the transaction. | Below is an example field mapping if you received unparsed data from your issuer: | **Spade fields** | **Required** | **Example** | Notes | | :---------------------- | :----------- | :---------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- | | **DE43** | required | `WMSUPERCENTER#582 PORT ORANGE FL` | Send raw merchant name information when possible such as in cases where your issuer cleanses or enriches merchant data. | | **location.city** | optional | `PORT ORANGE` | This field may not be available as it is in the DE43 field. May occasionally include a website or phone number | | **location.postalCode** | optional | `32127` | Recommended to provide if present | | **location.region** | optional | `FL` | Recommended to provide if present | | **location.country** | optional | `USA` or `840` | Supports 3 letter country code or 3 digit country code | | **acquirerId** | optional | `444590000` | May be called merchant ID or acceptor. Typically 8-16 characters | | **categoryCode** | required | `1234` | Required. If you do not receive one on the transaction you can use a fallback of `1111 `however this will negatively impact performance | | **amount** | required | `12.34` | Use negative amounts for credit and positive amounts for debits | | **currencyCode** | required | `USD` | Use the currency code that matches to the amount you're providing | | **occurredAt** | required | `2025-12-01` | | | **transactionId** | required | `t-1234` | Unique identifier for the transaction | | **programId** | optional | `p-1234` | Identifier for the program associated with the transaction, used to scope action triggers at the program level. | | **cardId** | optional | `c-1234` | Unique identifier for the card (do not use actual card numbers). | | **userId** | required | `u-1234` | Unique user identifier associated with the transaction. | Below is an example of which merchant-related fields we recommend you use from Marqeta to send to our [/cards/enrich endpoint](https://docs.spade.com/api-reference/card-enrichment/enrich-a-card-transaction). We've omitted the fields for identifiers as you may choose to use your own. For more information on using identifiers see the section below. | Marqeta fields | **Spade fields** | **Required** | | :--------------------------- | :---------------------- | :----------- | | card\_acceptor.name | **merchantName** | required | | card\_acceptor.city | **location.city** | required | | card\_acceptor.postal\_code | **location.postalCode** | optional | | card\_acceptor.state | **location.region** | optional | | card\_acceptor.country\_code | **location.country** | required | | card\_acceptor.mid | **acquirerId** | optional | | card\_acceptor.mcc | **categoryCode** | required | Below is an example of which merchant-related fields we recommend you use from Galileo to send to our [/cards/enrich/parse endpoint](https://docs.spade.com/api-reference/card-enrichment/enrich-a-card-transaction-with-an-unparsed-de43). We've omitted the fields for identifiers as you may choose to use your own. For more information on using identifiers see the section below. | Galileo | **Spade fields** | **Required** | | :------------------------------ | :---------------------- | :----------- | | merchant.merchant\_description | **DE43** | required | | merchant.merchant\_location | **location.city** | optional | | merchant.merchant\_postal\_code | **location.postalCode** | optional | | merchant.merchant\_state | **location.region** | optional | | merchant.merchant\_country | **location.country** | optional | | merchant.merchant\_number | **acquirerId** | optional | | mcc | **categoryCode** | required | Below is an example of which merchant-related fields we recommend you use from Stripe to send to our [/cards/enrich endpoint](https://docs.spade.com/api-reference/card-enrichment/enrich-a-card-transaction). We've omitted the fields for identifiers as you may choose to use your own. For more information on using identifiers see the section below. | Stripe Fields | **/enrich fields** | **Required** | | :----------------------------- | :---------------------- | :----------- | | merchant\_data\_name | **merchantName** | required | | merchant\_data\_city | **location.city** | required | | merchant\_data\_postal\_code | **location.postalCode** | optional | | merchant\_data\_state | **location.region** | optional | | merchant\_data\_country | **location.country** | required | | merchant\_data\_network\_id | **acquirerId** | optional | | merchant\_data\_category\_code | **categoryCode** | required | Below is an example of which merchant-related fields we recommend you use from Lithic to send to our [/cards/enrich endpoint](https://docs.spade.com/api-reference/card-enrichment/enrich-a-card-transaction). We've omitted the fields for identifiers as you may choose to use your own. For more information on using identifiers see the section below. | **Lithic Fields** | **/enrich fields** | **Required** | | :-------------------- | :---------------------- | :----------- | | merchant.descriptor | **merchantName** | required | | merchant.city | **location.city** | required | | | **location.postalCode** | optional | | merchant.state | **location.region** | optional | | merchant.country | **location.country** | required | | merchant.acceptor\_id | **acquirerId** | optional | | merchant.mcc | **categoryCode** | required | Below is an example of which merchant-related fields we recommend you use from Adyen to send to our [/cards/enrich endpoint](https://docs.spade.com/api-reference/card-enrichment/enrich-a-card-transaction). We've omitted the fields for identifiers as you may choose to use your own. For more information on using identifiers see the section below. | **Adyen Fields** | **/enrich fields** | **Required** | | :-------------------------------- | :---------------------- | :----------- | | merchantData.nameLocation.name | **merchantName** | required | | merchantData.nameLocation.city | **location.city** | required | | merchantData.postalCode | **location.postalCode** | optional | | | **location.region** | optional | | merchantData.nameLocation.country | **location.country** | required | | merchantData.merchantId | **acquirerId** | optional | | merchantData.mcc | **categoryCode** | required | Below is an example of which merchant-related fields we recommend you use from Fiserv to send to our [/cards/enrich endpoint](https://docs.spade.com/api-reference/card-enrichment/enrich-a-card-transaction). We've omitted the fields for identifiers as you may choose to use your own. For more information on using identifiers see the section below. | **Fiserv Fields** | **/enrich fields** | **Required** | | :-------------------- | :---------------------- | :----------- | | merchantName | **merchantName** | required | | merchantCity | **location.city** | required | | merchantZipCode | **location.postalCode** | optional | | merchantStateProvince | **location.region** | optional | | merchantCountryCode | **location.country** | required | | merchantNumber | **acquirerId** | optional | | merchantCategoryCode | **categoryCode** | required | It is common to receive a phone number or website in the city field. In these instances, you should still pass that data to Spade as we account for that when enriching transactions. ### Using identifiers We recommend providing a `transactionId` so that you can map our enrichment response and corresponding `enrichmentId` to your transaction-level data store. Additional metadata about the transaction or user can be provided in the `customAttributes` object and the enrichment response will return the original values provided. We require a `userId` for all enrichment endpoints and you can optionally provide a `cardId` and `programId`, however none of these fields should include any PII data. Each of these identifiers has a maximum length of 512 characters. For premium features such as recurrence and category personalization a unique`userId` is required. If you don’t have the concept of a user on your platform you can use any identifier that groups transactions made by the same individual or organization. ### Calling Spade in the authorization flow We recommend calling Spade in the authorization flow which can improve match rates and to use the enriched data for authorization decisioning. ### Batch vs real-time * **Real time enrichment**: send single transactions and received enhanced data with a p99 of under 50ms. Using real time enrichment is recommend for most use cases, especially those with low latency requirements or data models that receive transactions in single events. * **Batch enrichment**: send batches of up to 50,000 transactions at once for mass enrichment. Results can be retrieved through webhooks or endpoint polling. These endpoints are best for use cases with no latency requirements such as sending historical card data. Learn more about batch enrichment [**here**](https://docs.spade.com/reference/batch-enrichment-guide). ### Example request and response Below are example requests for both the /enrich and /enrich/parse endpoints: ```json Enrich theme={null} { "merchantName": "WMSUPERCENTER#582", "acquirerId": "123456789", "userId": "user123", "cardId": "card123", "programId": "program123", "transactionId": "166c5ad8-8a94-4964-a659-03cdb64525f2", "amount": "42.00", "currencyCode": "USD", "location": { "city": "PORT ORANGE", "region": "FLORIDA", "country": "US" }, "occurredAt": "2025-06-05T1:42:00Z", "categoryCode": "5469", "categoryType": "MCC" } ``` ```json Parse theme={null} { "de43": "WMSUPERCENTER#582 PORT ORANGE FL", "userId": "user_id_123456789", "programId": "program_id_123", "amount": "25.23", "currencyCode": "USD", "occurredAt": "2025-06-15 18:27:51Z", "categoryType": "MCC", "categoryCode": "5812", "cardId": "card_id_123456789", "cardFirstSix": "123456", "cardLastFour": "7890", "transactionId": "166c5ad8-8a94-4964-a659-03cdb64525f2", "acquirerId": "123456789", "customAttributes": { "custom_attribute_1": "value_1", "custom_attribute_2": "value_2" } } ``` **Example response:** ```json expandable theme={null} { "transactionInfo": { "type": "spending", "subType": null, "display": { "name": "Walmart", "categoryName": "Department Stores", "graphic": "https://static.v2.spadeapi.com/logos/d730906bf1a849f19939f27390170a6d/light.png", "graphicSource": "counterparty" }, "thirdParties": [], "spendingInfo": { "channel": { "value": "physical" } }, "transferInfo": null, "atmInfo": null, "isAccountVerification": null, "isPeerToPeer": null, "isDigitalWallet": null, "transactionId": "transaction_id_123456789", "recurrenceInfo": null, "riskInsights": { "irregularWebPresenceDetected": null, "negativeOnlineSentiment": null, "highRiskEntity": null, "riskyIndustry": false, "cardAcceptanceHistory": "extensive" } }, "counterparty": [ { "id": "d730906b-f1a8-49f1-9939-f27390170a6d", "name": "Walmart", "legalName": "Walmart Inc.", "industry": [ { "id": "011-000-000-000", "name": "Retail", "icon": "https://static.v2.spadeapi.com/categories/ee4ee39fd5474d31ac42f9e606b9040a/light.png" }, { "id": "011-018-000-000", "name": "General Goods", "icon": "https://static.v2.spadeapi.com/categories/ee4ee39fd5474d31ac42f9e606b9040a/light.png" }, { "id": "011-018-002-000", "name": "Department Stores", "icon": "https://static.v2.spadeapi.com/categories/ee4ee39fd5474d31ac42f9e606b9040a/light.png" } ], "location": [ { "id": "380e18b7-bf9e-3545-b27e-80e36301c540", "address": "1590 Dunlawton Ave", "addressLine1": "1590 Dunlawton Ave", "addressLine2": null, "city": "Port Orange", "region": "FL", "postalCode": "32127", "country": "USA", "phoneNumber": "+13867562711", "latitude": 29.116727, "longitude": -81.019428, "matchScore": 77.43 } ], "matchScore": 72.15, "logo": "https://static.v2.spadeapi.com/logos/d730906bf1a849f19939f27390170a6d/light.png", "medianSpendPerTransaction": null, "phoneNumber": "+14792734000", "website": "https://www.walmart.com/" } ], "enrichmentId": "050d3a73-c212-4f89-b9c0-0e75da0efeea", "mobileAppInfo": null, "customAttributes": { "custom_attribute_1": "value_1", "custom_attribute_2": "value_2" } } ``` The example response includes premium data fields for [Risk Insights](/reference/risk-insights). If you don’t have access to this feature, those fields will not be included in your enrichment response. If you would like access to any premium features, please reach out to [sales@spade.com](mailto:sales@spade.com). ### Error handling Typically requests fail due to missing required fields and we will provide details in the error response: ```json theme={null} { "city": [ "This field is required." ] } ``` If you receive an error for a missing field, you should not attempt to retry the transaction without that field as it will fail again. ## Using enriched card data ### Counterparty, third party, and location info We will always return a clean counterparty name in the enriched response, and if we match to a verified merchant in our database we will return a `counterparty.id` as well as details about the counterparty such as their logo and website. Additionally, if we match to a location associated with that counterparty we will return a `location.id` with address and geolocation information. If a third party is identified we will return a `thirdParty.id` as well as the third party name and type. For more information on matching about Spade see our guide on [understanding enriched data](/reference/understanding-enriched-data). ### Spade industries We always return an industry or category when enriching transactions. We can return up to four levels of industries (or categories) in the `counterparty.industry` array with the most granular industry returned as the last object in the array as well as in the `display.categoryName.`This level of granularity provides you the opportunity to more permissive or restrictive with category-level card controls or rewards. Below is an example of the industries that would be returned for an in-flight internet purchase. | Industry Name | Industry ID | | :---------------------------------- | :------------------ | | Travel | **014**-000-000-000 | | Transportation | **014-003**-000-000 | | Airlines and Aviation | **014-003-002**-000 | | Inflight Internet and Entertainment | **014-003-002-001** | For example, if you want to issue a virtual card that could be used to only book an airline ticket you could choose to only authorize transactions with the Airlines and Aviation industry `014-003-002-000`. But if you wanted to issue a card that could be used for any transportation related expense you could authorize any industry that falls below it by allowing any industry that starts with `014-003-*`. ### Display object We provide everything need to make transactions easily identifiable to end users in our enriched response. The `display` object contains our recommendation for the name, logo and category name to use in your UI/UX based on the parties identified in the transaction. For more information, see our guide on [improving UI/UX](/reference/improved-ux) with Spade. ### Premium data fields We offer additional premium data fields that are returned if enabled. These premium data fields include: * [Recurrence](/reference/recurring-transaction-guide) - identify recurring payments * [Risk Insights](/reference/risk-insights) - merchant-level flags to identify the riskiness of a merchant To get access to our premium data fields, please reach out to [sales@spade.com](mailto:sales@spade.com). # Category action triggers guide Source: https://docs.spade.com/reference/category-action-triggers-guide Register category action triggers and receive triggered actions in your enrichment responses This guide covers category action triggers. For an overview of all trigger types, see the [Action triggers guide](/reference/action-triggers-guide). For merchant action triggers, see the [Merchant action triggers guide](/reference/merchant-action-triggers-guide). ## Overview Category action triggers let you register rules that fire custom actions when transactions are enriched into specific Spade categories. Triggers match on Spade's category taxonomy rather than individual merchants, so a single trigger can cover every merchant in a category — to target specific merchants instead, see [merchant action triggers](/reference/merchant-action-triggers-guide). **Common use cases:** * **Category-based rewards:** 3x points on all Travel transactions * **Spending controls:** Block all Gambling transactions * **Category allowlists:** Only allow transactions in approved categories (e.g., Fuel, Office Supplies) ## Quick Start 1. **Discover categories** — Call `GET /categories` to get the list of available category IDs 2. **Register triggers** — Use the PUT endpoint with your category trigger configuration 3. **Enrich transactions** — Matched triggers appear in the `actions` field of the enrichment response 4. *(Optional)* **Update triggers incrementally** — Use the PATCH endpoint to [add or remove individual triggers](#updating-triggers) without re-registering the full list ## Discovering categories Use the `GET /categories` endpoint to retrieve the full list of Spade categories. Each category has an `id` that you'll use as the `categoryId` when registering triggers. ```bash theme={null} curl --request GET \ --url https://east.api.spade.com/categories \ --header 'X-Api-Key: YOUR_API_KEY' ``` The response includes category IDs and names: ```json theme={null} [ { "id": "020-001-000-000", "name": "Travel", "icon": "https://static.v2.spadeapi.com/categories/..." }, { "id": "019-001-000-000", "name": "Gambling", "icon": "https://static.v2.spadeapi.com/categories/..." } ] ``` Use the `id` value as the `categoryId` in your trigger registrations. ## Registration Scopes Category action triggers can be registered at four scopes. See the [Action triggers guide](/reference/action-triggers-guide#scope-hierarchy) for how scope inheritance works. | Scope | Endpoint | Limit | | ------- | --------------------------------------------------------- | ------------ | | Account | `/category-action-triggers` | 300 triggers | | Program | `/programs/{programId}/category-action-triggers` | 300 triggers | | User | `/users/{userId}/category-action-triggers` | 300 triggers | | Card | `/users/{userId}/cards/{cardId}/category-action-triggers` | 300 triggers | All registrations are processed **synchronously** — there is no batch processing. Status will be `succeeded` immediately upon a successful request. ## Registering category action triggers Use the PUT endpoint to register category action triggers. **PUT replaces your entire trigger list.** Every PUT request overwrites all previously registered triggers at that scope. If you need to add or remove a few triggers, use the PATCH endpoint instead. See the API reference for PATCH endpoint details. ### Request body Each trigger requires: * `id` — Your unique identifier for this trigger (max 512 characters) * `categoryId` — A valid Spade category ID (use `GET /categories` to discover valid IDs) * `action` — Your custom action data (same semantics as merchant triggers) You do not need to provide a `categoryName` — it is resolved from the system during enrichment and returned in the response. ### Example: REWARD trigger on Travel ```bash bash theme={null} curl --request PUT \ --url https://east.api.spade.com/category-action-triggers \ --header 'Content-Type: application/json' \ --header 'X-Api-Key: YOUR_API_KEY' \ --data '{ "categoryTriggers": [ { "id": "travel-reward", "categoryId": "020-001-000-000", "action": { "type": "REWARD", "rewardPercent": 3, "offerId": "travel-3x-promo" } } ] }' ``` ```python python theme={null} import requests response = requests.put( "https://east.api.spade.com/category-action-triggers", headers={ "Content-Type": "application/json", "X-Api-Key": "YOUR_API_KEY" }, json={ "categoryTriggers": [ { "id": "travel-reward", "categoryId": "020-001-000-000", "action": { "type": "REWARD", "rewardPercent": 3, "offerId": "travel-3x-promo" } } ] } ) print(response.json()) ``` ### Example: BLOCK trigger on Gambling ```bash bash theme={null} curl --request PUT \ --url https://east.api.spade.com/category-action-triggers \ --header 'Content-Type: application/json' \ --header 'X-Api-Key: YOUR_API_KEY' \ --data '{ "categoryTriggers": [ { "id": "block-gambling", "categoryId": "019-001-000-000", "action": { "type": "BLOCK", "reason": "Gambling transactions not permitted" } } ] }' ``` ```python python theme={null} import requests response = requests.put( "https://east.api.spade.com/category-action-triggers", headers={ "Content-Type": "application/json", "X-Api-Key": "YOUR_API_KEY" }, json={ "categoryTriggers": [ { "id": "block-gambling", "categoryId": "019-001-000-000", "action": { "type": "BLOCK", "reason": "Gambling transactions not permitted" } } ] } ) print(response.json()) ``` ### Response ```json theme={null} { "status": "succeeded", "version": 1 } ``` ## Checking Registration Status Use the GET endpoint to check the status of your category action trigger registration. ### Status Values | Status | Description | | ----------- | ------------------------------------------------- | | `succeeded` | Registration complete, triggers are now active | | `failed` | Registration failed, check your request and retry | Since category trigger registrations are processed synchronously, the status is `succeeded` or `failed` immediately after a PUT or PATCH request. **Important:** Triggers are only applied to enrichment responses when the status is `succeeded`. ### Example: Check Status ```bash bash theme={null} curl --request GET \ --url https://east.api.spade.com/category-action-triggers \ --header 'X-Api-Key: YOUR_API_KEY' ``` ```python python theme={null} import requests response = requests.get( "https://east.api.spade.com/category-action-triggers", headers={"X-Api-Key": "YOUR_API_KEY"} ) print(response.json()) ``` ### Response ```json theme={null} { "status": "succeeded", "version": 1, "categoryTriggers": [ { "id": "travel-reward", "action": { "type": "REWARD", "rewardPercent": 3, "offerId": "travel-3x-promo" } }, { "id": "block-gambling", "action": { "type": "BLOCK", "reason": "Gambling transactions not permitted" } } ], "totalCount": 2 } ``` ## Updating Triggers Use the PATCH endpoint to incrementally add or remove triggers without replacing the entire list. ### Adding triggers Use `operation: "add"` with `categoryTriggers` to register new triggers or update existing ones. If a trigger ID already exists, its registration is replaced (upsert behavior). ```bash theme={null} curl --request PATCH \ --url https://east.api.spade.com/category-action-triggers \ --header 'Content-Type: application/json' \ --header 'X-Api-Key: YOUR_API_KEY' \ --data '{ "operation": "add", "categoryTriggers": [ { "id": "groceries-reward", "categoryId": "005-001-000-000", "action": { "type": "REWARD", "rewardPercent": 2, "offerId": "groceries-2x" } } ] }' ``` ### Removing triggers Use `operation: "remove"` with `triggerIds` to deactivate triggers. Trigger IDs that are not currently active are silently ignored (idempotent). ```bash theme={null} curl --request PATCH \ --url https://east.api.spade.com/category-action-triggers \ --header 'Content-Type: application/json' \ --header 'X-Api-Key: YOUR_API_KEY' \ --data '{ "operation": "remove", "triggerIds": ["block-gambling"] }' ``` ## Clearing Triggers Use the DELETE endpoint to clear all category triggers at a given scope. DELETE creates a new version with an empty trigger list. The version number is incremented, not reset. ### Example: Clear Account-Scoped Triggers ```bash bash theme={null} curl --request DELETE \ --url https://east.api.spade.com/category-action-triggers \ --header 'X-Api-Key: YOUR_API_KEY' ``` ```python python theme={null} import requests response = requests.delete( "https://east.api.spade.com/category-action-triggers", headers={"X-Api-Key": "YOUR_API_KEY"} ) # Returns 204 No Content on success print(f"Status code: {response.status_code}") ``` ## ALLOW\_ONLY Inverse Semantics When category triggers use `ALLOW_ONLY`, the behavior inverts for non-matching transactions: * **Transaction in a registered category:** `authRecommendation: "ALLOW"` * **Transaction in any other category:** `authRecommendation: "BLOCK"`, `source: null` This allows you to create category allowlists where only transactions in approved categories are permitted. ### Example: Allow only Fuel and Office Supplies ```bash theme={null} curl --request PUT \ --url https://east.api.spade.com/category-action-triggers \ --header 'Content-Type: application/json' \ --header 'X-Api-Key: YOUR_API_KEY' \ --data '{ "categoryTriggers": [ { "id": "allow-fuel", "categoryId": "016-001-000-000", "action": { "type": "ALLOW_ONLY" } }, { "id": "allow-office", "categoryId": "010-001-000-000", "action": { "type": "ALLOW_ONLY" } } ] }' ``` ## Receiving Triggered Actions When a transaction matches a category trigger, the enrichment response includes the trigger in the `actions` array with `type: "category_trigger"`. Category triggers include additional `categoryId` and `categoryName` fields. ### Example: Enrichment Response with Category Trigger ```json theme={null} { "enrichmentId": "d1f7490c-719e-4c75-8bfa-535c8a32b936", "transactionInfo": { "type": "spending", "display": { "name": "United Airlines", "categoryName": "Travel" } }, "actions": [ { "id": "travel-reward", "type": "category_trigger", "action": { "type": "REWARD", "rewardPercent": 3, "offerId": "travel-3x-promo" }, "scope": "account", "source": "counterparty", "categoryId": "020-001-000-000", "categoryName": "Travel" } ] } ``` ### Example: Both Merchant and Category Triggers When a transaction matches both merchant and category triggers, all matched triggers appear in the `actions` array: ```json theme={null} { "enrichmentId": "a2b3c4d5-e6f7-8901-abcd-ef2345678901", "transactionInfo": { "type": "spending", "display": { "name": "Starbucks", "categoryName": "Coffee Shop" } }, "actions": [ { "id": "starbucks-reward", "type": "merchant_trigger", "action": { "type": "REWARD", "rewardPercent": 5, "offerId": "summer-2024-promo" }, "scope": "account", "source": "counterparty" }, { "id": "dining-reward", "type": "category_trigger", "action": { "type": "REWARD", "rewardPercent": 2, "offerId": "dining-2x" }, "scope": "program", "source": "counterparty", "categoryId": "008-001-000-000", "categoryName": "Coffee Shop" } ] } ``` The `actions` field is `null` when no triggers match, and is omitted entirely if your account does not have the actions feature enabled. ## Best Practices ### Unique Trigger IDs Trigger IDs must be unique **within each scope**. A scope is defined by the URL path you register against (account, program, user, or card level). * **Within a single request:** All trigger IDs must be unique. Duplicate IDs in the same request will return a `400` error. * **Across requests in the same scope:** Submitting a trigger with an ID that already exists in that scope will replace the previous registration (upsert). This applies to both `PUT` (full replacement) and `PATCH add` operations. * **Across different scopes:** The same trigger ID can be used independently in different scopes without conflict. ### Action Structure Design your `action` schema upfront. This is your custom data — use it to store reward amounts, offer IDs, or any information you need when processing matched transactions. The `type` field is **required**. Reserved keywords (`BLOCK`, `ALLOW_ONLY`) have special authorization behavior. `REWARD` and all other values are passed through as-is. ### Scope Selection * Use **account scope** for triggers that apply to all users (e.g., company-wide category restrictions) * Use **program scope** for triggers scoped to a specific program (e.g., different reward tiers per card product) * Use **user scope** for user-specific preferences * Use **card scope** for card-specific rules (e.g., a dedicated rewards card with higher multipliers) ### Choosing PUT vs. PATCH * Use **PUT** when replacing your full set of triggers (e.g., initial setup or a periodic full sync) * Use **PATCH** when adding or removing a small number of triggers relative to your total list ### Error Handling * Implement retry logic for 5xx errors * Handle 409 Conflict by waiting for the in-progress registration to complete * Validate your trigger data before submission to avoid 400 errors # Category personalization guide Source: https://docs.spade.com/reference/category-personalization-guide Category personalization is not enabled by default. To request access, please contact us at [sales@spade.com](mailto:sales@spade.com). ## Overview Category personalization makes it easy to customize Spade's categorization system to support additional use cases. You can use our category personalization endpoints to create custom categories and recategorize past and future transactions at both the integration and user level. Category personalization is supported across [card](/reference/card-enrichment-guide), [transfer](/reference/transfer-enrichment), and [universal](/reference/universal-data-enrichment) enrichment. Personalized categories are returned in the `personalization` object of the enrichment response. Our category personalization feature is designed to enable use cases including personal financial management and budgeting tools, allowing your users to quickly and easily recategorize transactions: To learn how to personalize categories at the integration level, see our [Integration Category Personalization Guide](/reference/integration-category-personalization-guide). To learn how to personalize categories at the user level, see our [User Category Personalization Guide](/reference/user-category-personalization-guide). # Glossary Source: https://docs.spade.com/reference/concepts The following outlines the major concepts you will encounter while integrating with the Spade Card Transaction Enrichment API. Our goal is to enrich your transactions with high quality data, providing as much insight and clarity as possible. *** We do this through a data-driven approach to transaction enrichment. When you send us a transaction, we work to match it with a merchant in our database covering 99.9% of all places you can swipe or type a card in the US. Once we match, we return granular merchant, category, and geolocation information. If we are not able to match, we will clean and return the information provided in the original transaction (e.g., merchant name, city/state, category). ## Counterparty A counterparty is the entity that you interact with directly when you make a purchase (in most cases, a merchant). In our API, `counterparty` is represented as a list, typically this list will contain a single matched counterparty, however in a small percentage of cases, multiple results may be returned if there is a lower degree of certainty. For large merchants, a counterparty can be thought of as a brand, e.g. all Starbucks locations fall under the same counterparty regardless of which location you purchase from. Accompanying a matched counterparty is a `matchScore` which represents how strongly correlated the returned counterparty with the transaction (see Match Score section for more details). Additionally, regardless of the counterparty results, we may also return third parties (see below). #### Counterparty ID Every time we match on a counterparty, we return a counterparty ID. The counterparty ID is consistent between transactions regardless of the location. When we do not return a counterparty ID, this indicates we did not find a counterparty match in our database. However, we still return cleaned information derived from the original request, and we may also have matched on a third party (see below). #### Location Spade also includes precise geographic information underneath our counterparty objects, down to the latitude and longitude. When matched to our backing data a location object will include a `locationId`, which is a unique id for that specific location. e.g. each Starbucks shop will have a different `locationId`. As with counterparties, we return a list of possible locations. This list can currently contain up to one location. #### Industry This field represents the primary industry that a given counterparty does business in (not the particular transaction). Our custom industry classification system is built as a tree where tree nodes are sub-industries of their parents – the deeper into the tree you go, the more specific the industry is. For instance, Walmart's industry is: ```bash bash theme={null} [ { "id": "011-000-000-000", "name": "Retail", "icon": "/categories/ee4ee39fd5474d31ac42f9e606b9040a/light.png" }, { "id": "011-018-000-000", "name": "General Goods", "icon": "/categories/ee4ee39fd5474d31ac42f9e606b9040a/light.png" }, { "id": "011-018-002-000", "name": "Department Stores", "icon": "/categories/ee4ee39fd5474d31ac42f9e606b9040a/light.png" } ] ``` In this example, `Retail` is the most general industry. Moving down the tree, we see that `General Goods` is the sub-industry of `Retail` to which Walmart belongs (notice that it is more specific). Other sub-industries of `Retail` include `Gas Stations`, `Pharmacies`, and `Office Supplies`. Moving down the tree further, we see that `Department Stores` is the sub-industry of `General Goods` to which Walmart belongs (notice that this is yet more specific). Other sub-industries of `General Goods` include `Discount Stores`, `Flea Markets`, and `Pawn Shops`. To obtain a full list of industries, please reach out to [sales@spade.com](mailto:sales@spade.com). #### Website & Phone Number In addition to location data, certain merchants have their website and phone number available. This data is either returned from our database, or extracted automatically from raw transactions in the `merchantName` or `city` fields. #### Match Score When we connect your transaction with one of the millions of merchants in our database, we also return a score between 0 and 100 called the match score. This score indicates how strongly correlated the matched counterparty is to the transaction. The match score is not probabilistic. A null match score indicates we did not find a match in our database. To learn more about match scores and how to interpret them, please read [understanding match scores](/reference/match-score-guide). ## Third Parties A third party is any entity besides the counterparty involved in a transaction, but not the direct recipient of the payment (buy-now-pay-later providers, delivery services, marketplaces, payment processors, POS systems, etc). Multiple third parties can be involved with a single transaction. When a third party is involved in a transaction, we will return that information in the `thirdParties` field. We will also attempt to match on a counterparty – however, different third parties provide varying levels of information on counterparties in their transactions. When we are able to identify a counterparty or clean the text sufficiently, we will return that information in the counterparty object in addition to the third party object. In implementation, we recommend always checking the third party field, even if no counterparty ID is returned, as there may be cases where only the third party is identified (e.g., for UberEats or other food delivery services). #### Third Party ID As with counterparties, each third party is returned with a third party ID. When a given entity (e.g. Instacart, Walmart, Apple) shows up in a transaction, it will have the same ID regardless of whether or not it is acting as a third party or a counterparty. #### Third Party Types We divide third parties into 4 categories: 1. **Buy-Now-Pay-Later (BNPL)** Providers that offer short-term financing that allows customers to purchase an item and pay for it over time in installments. *Example third parties: “Affirm”, “AfterPay”, “Klarna”* 2. **Delivery Services** Services that facilitate the purchase and delivery of something to you. The most obvious examples are food & grocery delivery. *Example third parties: “Uber Eats”, “Instacart”* 3. **Payment Processors** Companies that facilitate payments between a business and its customers. *Example third parties: “Google”, “Square”, “Toast”, “Paddle”, “Intuit”* 4. **Platforms** Entities that facilitate money movement between multiple parties, often providing more than one service. *Example platforms: “PayPal”, “Cash App”, “Venmo”, “Navan”, “RealPage”* # Overview Source: https://docs.spade.com/reference/enrichment-guides ## Overview Before you enrich transactions, it's important to determine which endpoints you will be hitting, what your integration pattern will be, and what type of data you will receive. This set of guides will help you design your integration and start enriching your transaction data through our API. ## Endpoints We enrich card and transfer data via two separate endpoints to capture inputs unique to each type of spend, and return a single, unified response. You can determine which endpoint to integrate with based on the following criteria: * **Card enrichment**: our card enrichment endpoint is designed to ingest first party and streamed card data from the ISO8583 message. * If you receive raw DE43 data from your issuer (e.g., `WALMART.COM BENTONVILLE ARUS"`) you should hit our [/transactions/cards/enrich/parse](/api-reference/card-enrichment/enrich-a-card-transaction-with-an-unparsed-de43) endpoint. * If you receive parsed data or already do parsing on your end (looks like `MerchantName: Walmart.com`, `City: BENTONVILLE`, `Region: AR`, `Country: US`), you should hit our [/transactions/cards/enrich](/api-reference/card-enrichment/enrich-a-card-transaction) endpoint. * **Transfer enrichment**: our transfers endpoint is designed to ingest first party transfer data (e.g., wires, ACH payments, ATM transactions). Head to our [transfer enrichment API reference](/api-reference/transfer-enrichment/enrich-a-transfer-transaction) to learn more. * \*\*Universal data enrichment: \*\*our universal enrichment endpoint is designed to ingest third party data (e.g. data from aggregators, accounting software, panel data). ## Integration methods * **Real time enrichment**: send single transactions and received enhanced data with a p99 of under 50ms. These endpoints are best for use cases with low latency requirements or data models that receive transactions in single events. * **Batch enrichment**: send batches of up to 50,000 transactions at once for mass enrichment. Results can be retrieved through webhooks or endpoint polling. These endpoints are best for use cases with no latency requirements. Learn more about batch enrichment [here](/reference/batch-enrichment-guide) or head to our [batch card ](/api-reference/card-enrichment/enrich-a-batch-of-card-transactions)or [batch transfers](/api-reference/transfer-enrichment/enrich-a-batch-of-transfers) API references. ## Enriched data types We offer two levels of enriched data: * **Core data enrichment:** our foundational layer of factual transaction including factual details like merchant name, logo, category, counterparty ID, and location – delivered in real time and designed for consistency across rails. These are included in our base product package. * **Premium data features**: premium fields and predictive signals – like risk signals and recurrence detection – that can be activated based on your use case. These add-ons and can be purchased by contacting [sales@spade.com](mailto:sales@spade.com). # Error reporting guide Source: https://docs.spade.com/reference/error-reporting-guide Report potential enrichment errors and receive updates via callbacks ## Overview If you believe an enrichment response may be inaccurate, you can submit a report using the [**/transactions/report**](https://docs.spade.com/api-reference/feedback-and-reporting/report-a-card-enrichment-as-incorrect) endpoint. Our team will review your report and determine whether a correction is needed. If a correction is made, we'll send the returned enrichment data to your callback URL so you can update your records automatically. ## Prerequisites Before submitting error reports, you'll need: 1. **A callback URL** — Provide an HTTPS endpoint to your Spade representative where we can deliver returned enrichments. Contact [support@spade.com](mailto:support@spade.com) to configure this. 2. **A callback token** — Your Spade representative will provide you with a token to validate that incoming callbacks are from Spade. Store this token securely on your server. 3. **An enrichment ID** — The `enrichmentId` from the enrichment response you want to report. Your callback token is a shared secret between your system and Spade. Do not expose it in client-side code, public repositories, or logs. Store it in a secure secrets manager with restricted access. ## Submitting an error report Send a POST request to the `/transactions/report` endpoint with the `enrichmentId` of the enrichment you want to report and a description of what you believe may be inaccurate. ### Request fields | Field | Required | Type | Description | | :----------------------------------- | :------- | :------ | :---------------------------------------------------------------------------- | | **enrichmentId** | required | string | The `enrichmentId` from the enrichment response | | **errorDescription** | required | string | A description of the potential issue (max 1024 characters) | | **incorrectCounterparty** | optional | boolean | Set to `true` if you believe the counterparty information may be inaccurate | | **incorrectCounterpartyDescription** | optional | string | Details about the suspected counterparty issue (max 1024 characters) | | **incorrectLocation** | optional | boolean | Set to `true` if you believe the location information may be inaccurate | | **incorrectLocationDescription** | optional | string | Details about the suspected location issue (max 1024 characters) | | **incorrectCategory** | optional | boolean | Set to `true` if you believe the category/industry may be inaccurate | | **incorrectCategoryDescription** | optional | string | Details about the suspected category issue (max 1024 characters) | | **incorrectChannel** | optional | boolean | Set to `true` if you believe the channel (physical/digital) may be inaccurate | | **incorrectChannelDescription** | optional | string | Details about the suspected channel issue (max 1024 characters) | Setting the optional boolean fields and providing detailed descriptions helps our team investigate your report more efficiently. Be as specific as possible in your descriptions. ### Example request ```bash shell theme={null} curl --request POST \ --url https://east.sandbox.spade.com/transactions/report \ --header 'content-type: application/json' \ --header 'X-Api-Key: SPADE-API-KEY' \ --data '{ "enrichmentId": "050d3a73-c212-4f89-b9c0-0e75da0efeea", "errorDescription": "Location returned does not match the physical store location", "incorrectLocation": true, "incorrectLocationDescription": "Location returned is for Dunlawton Ave, but the actual store is on Taylor Rd" }' ``` ```python Python theme={null} import requests response = requests.post( "https://east.sandbox.spade.com/transactions/report", json={ "enrichmentId": "050d3a73-c212-4f89-b9c0-0e75da0efeea", "errorDescription": "Location returned does not match the physical store location", "incorrectLocation": True, "incorrectLocationDescription": "Location returned is for Dunlawton Ave, but the actual store is on Taylor Rd", }, headers={"X-Api-Key": ""}, ) print(response.json()) ``` ### Example response A successful report returns an HTTP 200 response: ```json theme={null} { "details": "Enrichment successfully reported." } ``` If validation fails, you'll receive an HTTP 400 with details about which fields are invalid: ```json theme={null} { "enrichmentId": [ "Must be a valid UUID." ] } ``` ## Receiving returned enrichments If our team determines that a correction is warranted after reviewing your report, we'll send the returned enrichment data to the callback URL you configured with your Spade representative. ### Callback format The callback is a POST request to your callback URL containing the returned enrichment response in the same format as the original [card enrichment response](https://docs.spade.com/api-reference/card-enrichment/enrich-a-card-transaction). The request includes an `X-Webhook-Token` header that you should validate against the callback token provided by your Spade representative. ### Validating the callback You must verify the `X-Webhook-Token` header matches the token provided to you by Spade before processing the returned enrichment. This ensures the callback is genuinely from Spade and has not been tampered with. Always validate the `X-Webhook-Token` before processing any callback data. Reject requests with missing or mismatched tokens. ### Example callback handler ```bash shell theme={null} # Example of what the callback request from Spade looks like: # POST https://your-domain.com/spade/corrections # Headers: # Content-Type: application/json # X-Webhook-Token: your-callback-token-from-spade # Body: returned card enrichment response JSON # You can test your callback endpoint with curl: curl --request POST \ --url https://your-domain.com/spade/corrections \ --header 'content-type: application/json' \ --header 'X-Webhook-Token: your-callback-token-from-spade' \ --data '{ "transactionInfo": { "type": "spending", "display": { "name": "Walmart Neighborhood Market", "categoryName": "Grocery Stores" } }, "enrichmentId": "050d3a73-c212-4f89-b9c0-0e75da0efeea" }' ``` ```python Python theme={null} from flask import Flask, request, jsonify app = Flask(__name__) @app.route('/spade/corrections', methods=['POST']) def handle_returned_enrichment(): # Validate the webhook token webhook_token = request.headers.get('X-Webhook-Token') expected_token = "your-callback-token-from-spade" # Store this securely if not webhook_token or webhook_token != expected_token: return jsonify({"error": "Invalid token"}), 403 # Process the returned enrichment returned_enrichment = request.json enrichment_id = returned_enrichment["enrichmentId"] # Update your records with the returned data update_enrichment_in_database(enrichment_id, returned_enrichment) return jsonify({"status": "received"}), 200 def update_enrichment_in_database(enrichment_id, returned_enrichment): # Save the changes to your systems as necessary print(f"Saving Enrichment {enrichment_id}: {returned_enrichment}") ``` The Python example above uses Flask for illustrative purposes only. You can implement your callback handler using any web framework or language that can receive HTTP POST requests. The returned enrichment callback body uses the same schema as the [card enrichment response](https://docs.spade.com/api-reference/card-enrichment/enrich-a-card-transaction). Refer to the API reference for the full response object documentation. ## Workflow summary 1. You enrich a transaction and receive a response with an `enrichmentId` 2. You believe the enrichment may be inaccurate and submit a report to `/transactions/report` 3. Spade's team reviews the report and determines whether a correction is needed 4. If a correction is made, Spade sends the returned enrichment to your callback URL with the `X-Webhook-Token` header 5. Your server validates the token and updates your records ## Error handling | Status Code | Description | | :---------- | :----------------------------------------------------------------------- | | **200** | Report submitted successfully | | **400** | Validation error — check the response body for details on invalid fields | | **403** | Authentication failed — verify your API key | | **500** | Internal server error — retry the request after a brief delay | For questions about error reporting or to set up your callback URL, contact [support@spade.com](mailto:support@spade.com). # FAQ Source: https://docs.spade.com/reference/faq *** ### How fast is it? > Our core business is to provide enrichment in real time. While latency varies depending on a number of factors, our p99 enrichment processing time is sub-50 ms. To minimize network latency with your servers, we operate both east and west coast infrastructures. Sometimes, integration patterns can impact latency -- learn more about how to optimize your integration to reduce latency [here](/reference/reducing-latency). ### What can I do to optimize performance? > You should expect to see match rates increase over time as our tooling and processes begin optimizing performance. However, the data you send us will impact performance as well. Our enrichment API works best when we receive unaltered data in each field, and the more fields you send, the better. A few of our request fields are optional, but we strongly recommend including them whenever possible, particularly region and city. If you don't currently receive those fields, we recommend reaching out to your issuer or bank partner to see if they're available. It's also critical that you send us unaltered data. Don't do any cleansing on the merchant name or description, and send us the exact data your issuer or bank partner is sending you in other fields (even if for example the city field has a phone number or website -- send that in the city field anyway!). ### What is an `acquirerId`? > An acquirerID is an ID assigned to a merchant by the payment networks. It may also be known by several other names including "network ID", "card acceptor ID", or "merchant ID". > > This is an alphanumeric string with a maximum length of 15 characters. > > In the ISO 8583 protocol it is represented by field 42. ### What are third parties? > Third parties are entities that are involved in a transaction in some way, but not the direct recipient (the “counterparty”). This can include delivery services, buy-now-pay-later providers, payment processors, and POS systems. > > [learn more about third parties](/reference/concepts#third-parties) ### What if I notice a data problem? > We are constantly working to improve and verify our data. If you notice an issue in an enrichment, please utilize our error reporting endpoint, message us on Slack, or reach out to [support@spade.com](mailto:support@spade.com) so that we can correct it. ### Why are the `counterparty` and `location` fields arrays? > These lists will typically return a single item per transaction; however, there may be cases where we can't be sure of the exact counterparty or location. To avoid false positives, in these situations we will return multiple results accompanied by a `matchScore` indicating how confident we are about each result. > > [learn more](/reference/concepts#counterparty) ### What regions do you support? > We currently focus on enriching transactions with United States merchants. ### How do I get access? > Please contact us at [sales@spade.com](mailto:sales@spade.com), or click "Request Access" on our [website](https://www.spade.com). # Improving user experience with Spade Source: https://docs.spade.com/reference/improved-ux *** Spade’s enriched data—including clean names, logos, and merchant contact details—can be used to power user experiences that can provide clarity, increase engagement, and reduce disputes. There are two ways to integrate Spade's data in your UI: * Build custom display logic using Spade's counterparty, third party and industry names and graphics * Leverage Spade's recommended display information with the `display` object # Examples To illustrate how this works we've provided two examples of transactions with the same counterparty but different third parties and how this can impact the recommended `display.name`. | Description | Counterparty | Third Party | Display Name | Display Logo | | --------------- | ------------ | ----------- | ---------------------- | ------------ | | SQ\*SPADE PIZZA | Spade Pizza | Square | Spade Pizza | Spade Pizza | | DD\*SPADEPIZZA | Spade Pizza | DoorDash | DoorDash - Spade Pizza | Spade Pizza | ```bash SQ*SPADE PIZZA expandable theme={null} { "transactionInfo": { "display": { "name": "Spade Pizza", "categoryName": "Restaurant", "graphic": "https://static.v2.spadeapi.com/logos/....png", "graphicSource": "counterparty" }, "thirdParties": [ { "id": "ab345...", "name": "Square", "type": "payment_processor", "logo": "https://v1.spadeapi.com/logos/....png" } ] }, "counterparty": [ { "name": "Spade Pizza", "logo": "https://static.v2.spadeapi.com/logos/....png", "industry": [ { "id": "005-006-000-000", "name": "Restaurants", "icon": "https://static.v2.spadeapi.com/categories/....png" } ] } ] } ``` ```bash DD*SPADEPIZZA expandable theme={null} { "transactionInfo": { "display": { "name": "DoorDash - Spade Pizza", "categoryName": "Restaurant", "graphic": "https://static.v2.spadeapi.com/logos/....png", "graphicSource": "counterparty" }, "thirdParties": [ { "id": "ac48cef2...", "name": "DoorDash", "type": "delivery_service", "logo": "https://v1.spadeapi.com/logos/....png" } ] }, "counterparty": [ { "name": "Spade Pizza", "logo": "https://static.v2.spadeapi.com/logos/....png", "industry": [ { "id": "005-006-000-000", "name": "Restaurants", "icon": "https://static.v2.spadeapi.com/categories/....png" } ] } ] } ``` In these examples, the user knows they made a purchase from Spade Pizza, but may not recognize the payment processor used at the restaurant’s POS system (e.g., Square). In contrast, a user ordering through DoorDash may be more likely to recognize the delivery service when reviewing their past purchases. # How to use the display object Spade has created the `display` object to easily surface the information most recognizable to a user for you to plug into your UI. The `display` object consists of a combination of information from the counterparty and third party sections of the response. Provided below is the `display` object returned from the previous example of a user ordering Spade Pizza via DoorDash. ```bash bash theme={null} { "display": { "name": "DoorDash - Spade Pizza", "categoryName": "Restaurants", "graphic": "https://static.v2.spadeapi.com/logos/...png", "graphicSource": "counterparty" } } ``` The `display` object can be found in the `transactionInfo` of both card and transfer enrichments. * **`name`** - The recommended display name * **`categoryName`** - The best description of the category, usually this is the counterparty industry, but it may also reflect some third parties, MCC, etc. * **`graphic`** - The recommended logo or icon to display * **`graphicSource`** - Indicates whether the graphic was sourced from either a`counterparty`, `third_party`, or `category`. This is helpful if for example you use your own category iconography For more information on displaying logos and icons, see [Spade graphics](/reference/spade-graphics). # Additional display details ## Location data When Spade matches the transaction to a location we will provide both address details as well as latitude and longitude information which allows you to display a detailed map of where the transaction took place. Note: We recommend only displaying location data when `spendingChannel `= physical to avoid confusion for online purchases that will show corporate locations that end users may not be familiar with. ### Displaying addresses Spade will return the full street address in `location.address`. The street address will be parsed and will always return a value in `addressLine1` but will only return a value in `addressLine2` if the address has multiple lines. In the first example below the address should be shown as: > 1234 W 5th Ave Suite 100 New York, NY 10001 ```bash Address theme={null} { "location": [ { "id": "fdf79470-3deb-4638-956a-6859e473b9d8", "address": "1234 W 5th Ave Suite 100", "addressLine1": "1234 W 5th Ave", "addressLine2": "Suite 100", "city": "New York", "region": "NY", "country": "USA", "postalCode": "10001", "latitude": 45, "longitude": 120, "phoneNumber": "+18664862360", "matchScore": 93.5 } ] } ``` For addresses with a single line you would simply use the `address` field: > 1234 W 5th Ave New York, NY 10001 ```bash Address theme={null} { "location": [ { "id": "fdf79470-3deb-4638-956a-6859e473b9d8", "address": "1234 W 5th Ave", "addressLine1": "1234 W 5th Ave", "addressLine2": null, "city": "New York", "region": "NY", "country": "USA", "postalCode": "10001", "latitude": 45, "longitude": 120, "phoneNumber": "+18664862360", "matchScore": 93.5 } ] } ``` ### Displaying geolocation By showing a transaction’s location on a map, users can more easily confirm purchases and identify potentially fraudulent activity. When available we will provide the`location.latitude` and `location.longitude` for you to use to generate a map in your UI. ## Phone numbers We recommend using the `counterparty.phoneNumber` when it is present and falling back to `counterparty.location.phoneNumber` . When the counterparty is a corporation, such as WalMart, the counterparty phone number represents the corporate phone number and the location phone number is the phone number for the specific store location. ## Original description In addition to the enriched transaction description, we recommend displaying a field with the original descriptor (e.g., APPEARS ON STATEMENT: SBX1234) to ensure parity with statements. # Integrating with Spade's API Source: https://docs.spade.com/reference/integrate-with-spades-api *** ## Authentication Spade's APIs use secret API key authentication, because it is the fastest approach available for low latency applications. Each of Spade's API environments requires a unique key which is passed in the  `X-Api-Key` header: ```bash theme={null} curl --request POST \ --url https://east.sandbox.spade.com/transactions/cards/enrich \ --header 'content-type: application/json' \ --header 'X-Api-Key: SPADE-API-KEY' \ --data '{ request }' ``` ## Environments Spade provides servers located on both the East and West Coast as well as both a Production and Sandbox environment for each geo-located server. | | East | West | | -------------- | -------------------------------- | -------------------------------- | | **Sandbox** | `https://east.sandbox.spade.com` | `https://west.sandbox.spade.com` | | **Production** | `https://east.api.spade.com` | `https://west.api.spade.com` | Your development environments should use our sandbox endpoints and your production environment should hit our production endpoints. Sandbox and Production environments do not share the same API keys, recurrence, and custom user generated categories and category personalization. We have servers located on both the East and West Coasts and recommend calling the server closest to your server location to minimize latency.\ \ For best practices on reducing latency see our guide on [reducing latency](/reference/reducing-latency). Counterparty and Location information is common across all environments. You can expect the same metadata to be returned for the same enrichment payload, regardless of environment. ### Supporting multiple data sources and clients Note: This feature is not enabled by defaut. To access this feature please contact [sales@spade.com](mailto:sales@spade.com) For clients that have multiple data sources or support other clients as an embedded service provider or reseller we support segmenting requests through use of a `billingProfile` and `organizationId` . The `billingProfile` flag supports values of: * `standard` - typically used for segmenting different data sources * `resell` - used for segmenting different clients In addition to the `billingProfile` you'll want to specify an `organizationId` that aligns to the data source or client associated with the request you're sending to us. Below is an example of how this information is provided in a card enrichment request: ```json Reseller example {2,3} theme={null} { "billingProfile": "resell", "organizationId": "ClientABC", "userId": "12345", "cardId": "card_5678", "programId": "program_123", "transactionId": "txn-1234", "merchantName": "JOE'S PIZZA", "amount": 18.35, "currencyCode": "USD", "categoryCode": "4814", "categoryType": "MCC", "city": "New York", "state": "NY", "country": "USA", "occurredAt": "2025-08-15T18:27:51.000Z" } ``` ## Enrich your first transaction ## Configuration You can create a fork of Spade's Postman collection by clicking [this link](https://app.getpostman.com/run-collection/45578594-3ac97973-91ba-4d95-ad07-c058a347bddc?action=collection%2Ffork\&source=rip_markdown\&collection-url=entityId%3D45578594-3ac97973-91ba-4d95-ad07-c058a347bddc%26entityType%3Dcollection%26workspaceId%3D7489b801-6d27-43ad-8815-39195c96483e) First you'll want to ensure you update the collection with your correct API Key and environment URL by entering them in the corresponding current value fields and clicking 'Save.' Postman Image 1 Pn ## Make the Postman request The postman collection includes this test transaction we've provided, so all you need to do is click Send! Postman Image 2 Pn Copy the example request below and replace SPADE-API-KEY with your environment specific credential in your terminal of choice (we like [Warp](https://www.warp.dev/)!) ```bash theme={null} curl --location 'https://east.sandbox.spade.com/transactions/cards/enrich' \ --header 'Content-Type: application/json' \ --header 'Accept: application/json' \ --header 'X-Api-Key: SPADE-API-KEY' \ --data '{ "merchantName": "SQ*WMSUPERCENTER#582", "acquirerId": "123456789", "userId": "user123", "cardId": "card123", "programId": "program123", "transactionId": "166c5ad8-8a94-4964-a659-03cdb64525f2", "amount": "42.00", "currencyCode": "USD", "location": { "city": "PORT ORANGE", "region": "FLORIDA", "country": "US" }, "occurredAt": "2025-06-05T1:42:00Z", "categoryCode": "5469", "categoryType": "MCC" }' ``` Congratulations on enriching your first transaction! We recommend checking out our [**Enrichment guides**](/reference/enrichment-guides) to get started sending your own data. # Integration category personalization guide Source: https://docs.spade.com/reference/integration-category-personalization-guide *** ## Fetch the default categories for your integration The first thing you’ll likely want to do is fetch the default categories for your integration. This list contains all of the categories that Spade provides, plus any custom categories that you’ve added at integration-level (which should be none at this point). ```bash bash theme={null} import requests response = requests.get("https://east.sandbox.spade.com/categories", headers={"X-Api-Key": ""}) print(response.json()) ``` Sending the above request returns a list containing all of Spade's 250+ categories: ```bash bash theme={null} [ { "id": "001-000-000-000", "name": "Banking and Finance", "icon": "https://static.v2.spadeapi.com/categories/61897b3a5af545bab45ca1bf20498e65/light.png", "fullCategoryHierarchy": [ { "id": "001-000-000-000", "name": "Banking and Finance", "icon": "https://static.v2.spadeapi.com/categories/61897b3a5af545bab45ca1bf20498e65/light.png" } ] }, { "id": "001-001-000-000", "name": "Accounting and Bookkeeping", "icon": "https://static.v2.spadeapi.com/categories/61897b3a5af545bab45ca1bf20498e65/light.png", "fullCategoryHierarchy": [ { "id": "001-000-000-000", "name": "Banking and Finance", "icon": "https://static.v2.spadeapi.com/categories/61897b3a5af545bab45ca1bf20498e65/light.png" }, { "id": "001-001-000-000", "name": "Accounting and Bookkeeping", "icon": "https://static.v2.spadeapi.com/categories/61897b3a5af545bab45ca1bf20498e65/light.png" } ] }, ... ] ``` Note that Spade’s categories are hierarchical, so `Accounting and Bookkeeping`, for instance, sits underneath the `Banking and Finance` category, which sits at the root level. For convenience, each serialized category includes a `fullCategoryHierarchy` object, which contains the full hierarchy of the category. ## Creating a custom category Spade's default categories are designed to meet the majority of use cases. However, there are some use cases where you may want to create custom categories specific to your integration. Custom categories can sit at the root level, under Spade's categories, or under other custom integration-level categories. Here’s how you can create a custom category that sits at the root level: ```bash bash theme={null} import requests custom_category = { "name": "Family", "parentId": "" } response = requests.post("https://east.sandbox.spade.com/categories", json=custom_category, headers={"X-Api-Key": ""}) print(response.json()) ``` Sending the above request creates a new custom category and returns a copy of the serialized category: ```bash bash theme={null} { "id": "7235be6a-669c-47f2-9ab6-cac3f3edbab9", "name": "Family", "icon": null, "fullCategoryHierarchy": [ { "id": "7235be6a-669c-47f2-9ab6-cac3f3edbab9", "name": "Family", "icon": null } ] } ``` Here’s how you can create a custom category that sits under this category: ```bash bash theme={null} import requests custom_category = { "name": "Day Care", "parentId": "7235be6a-669c-47f2-9ab6-cac3f3edbab9" # Replace with the ID of the category you just created } response = requests.post("https://east.sandbox.spade.com/categories", json=custom_category, headers={"X-Api-Key": ""}) print(response.json()) ``` Sending the above request creates the new custom category and returns a copy of the serialized category: ```bash bash theme={null} { "id": "ba8d4e26-052a-4260-bcf4-6523469fcf48", "name": "Day Care", "icon": null, "fullCategoryHierarchy": [ { "id": "7235be6a-669c-47f2-9ab6-cac3f3edbab9", "name": "Family", "icon": null }, { "id": "ba8d4e26-052a-4260-bcf4-6523469fcf48", "name": "Day Care", "icon": null } ] } ``` ## Re-fetching the default categories for your integration Now, if you re-fetch the default categories for your integration, you'll see the two custom categories you just created: ```bash bash theme={null} import requests response = requests.get("https://east.sandbox.spade.com/categories", headers={"X-Api-Key": ""}) print(response.json()) ``` Sending the above request returns a list containing all of Spade's 250+ categories plus the new custom categories: ```bash bash theme={null} [ // Spade's default categories will show up first { "id": "001-000-000-000", "name": "Banking and Finance", "icon": "https://static.v2.spadeapi.com/categories/61897b3a5af545bab45ca1bf20498e65/light.png", "fullCategoryHierarchy": [ { "id": "001-000-000-000", "name": "Banking and Finance", "icon": "https://static.v2.spadeapi.com/categories/61897b3a5af545bab45ca1bf20498e65/light.png" } ] }, { "id": "001-001-000-000", "name": "Accounting and Bookkeeping", "icon": "https://static.v2.spadeapi.com/categories/61897b3a5af545bab45ca1bf20498e65/light.png", "fullCategoryHierarchy": [ { "id": "001-000-000-000", "name": "Banking and Finance", "icon": "https://static.v2.spadeapi.com/categories/61897b3a5af545bab45ca1bf20498e65/light.png" }, { "id": "001-001-000-000", "name": "Accounting and Bookkeeping", "icon": "https://static.v2.spadeapi.com/categories/61897b3a5af545bab45ca1bf20498e65/light.png" } ] }, ... // Your integration's custom categories will show up next { "id": "7235be6a-669c-47f2-9ab6-cac3f3edbab9", "name": "Family", "icon": null, "fullCategoryHierarchy": [ { "id": "7235be6a-669c-47f2-9ab6-cac3f3edbab9", "name": "Family", "icon": null } ] }, { "id": "ba8d4e26-052a-4260-bcf4-6523469fcf48", "name": "Day Care", "icon": null, "fullCategoryHierarchy": [ { "id": "7235be6a-669c-47f2-9ab6-cac3f3edbab9", "name": "Family", "icon": null }, { "id": "ba8d4e26-052a-4260-bcf4-6523469fcf48", "name": "Day Care", "icon": null } ] } ] ``` Notice that custom category IDs are UUIDs, while Spade's default categories use a hierarchical numerical ID system. This is by design: our system uses different ID formats for custom and default categories to make it easy for you to distinguish between the two. ## Recategorizing past transactions To recategorize specific past transactions onto a custom category, you’ll want to update those transaction records in your internal system. For instance: ```bash bash theme={null} import requests # Update each transaction's category in your internal system. # Note that the PATCH body will depend on your internal data schema. requests.patch( "https://your.backend.com/enriched-transactions/1234", { "category": [ { "id": "ba8d4e26-052a-4260-bcf4-6523469fcf48", "name": "Day Care", "icon": null } ] }, headers=YOUR_AUTH_HEADERS, ) ``` ## Recategorizing all future transactions for a specific counterparty One of the most powerful features Spade provides is the ability for you to recategorize future transactions before they take place. This feature takes advantage of Spade’s ability to consistently match transactions to stable counterparty IDs using ground truth data. Here’s how you can recategorize future transactions for a specific counterparty across your entire integration: ```bash bash theme={null} import requests counterparty_category_personalization = { # KinderCare Learning Centers "counterpartyId": "1d4cb97d-939e-36e7-b3aa-d7116af2f87c", # Day Care "categoryId": "ba8d4e26-052a-4260-bcf4-6523469fcf48" } requests.put("https://east.sandbox.spade.com/counterparty-category-personalization", json=counterparty_category_personalization, headers={"X-Api-Key": ""}) ``` With this personalization in place, any future transactions that take place at `KinderCare Learning Centers` will be enriched with the custom category, which will appear in the `personalization` section of the enrichment as the counterparty's `industry`: ```bash bash theme={null} { "transactionInfo": { "type": "spending", "thirdParties": [], "spendingInfo": { "channel": { "value": "digital" } }, "transactionId": "cb1d4fc5-6d72-4d67-a911-d479c96b30b2", "irregularWebPresenceDetected": false, "recurrenceInfo": null }, "counterparty": [ { "id": "1d4cb97d-939e-36e7-b3aa-d7116af2f87c", "name": "Kindercare Learning Centers", "legalName": "KinderCare Learning Companies, Inc.", // Spade still returns the original industry here so you can use it for internal analysis "industry": [ { "id": "012-000-000-000", "name": "Services", "icon": "https://static.dev.v2.spadeapi.com/categories/922d5d4ec3a64304a93723bea02d3890/light.png" }, { "id": "012-010-000-000", "name": "Household Services", "icon": "https://static.dev.v2.spadeapi.com/categories/922d5d4ec3a64304a93723bea02d3890/light.png" }, { "id": "012-010-001-000", "name": "Childcare", "icon": "https://static.dev.v2.spadeapi.com/categories/922d5d4ec3a64304a93723bea02d3890/light.png" } ], "location": [ { "id": "cfec00c3-c1e0-3d38-97dd-65769bd10c71", "address": "650 NE Holladay St Suite 100", "addressLine1": "650 NE Holladay St", "addressLine2": "Suite 100", "city": "Portland", "region": "OR", "postalCode": "97232", "country": "USA", "phoneNumber": null, "latitude": 45.529718, "longitude": -122.659176, "matchScore": null } ], "matchScore": 97.76, "logo": "https://static.v2.spadeapi.com/logos/1d4cb97d939e36e7b3aad7116af2f87c/light.png", "medianSpendPerTransaction": null, "phoneNumber": null, "website": "kindercare.com" } ], "personalization": { "counterparty": [ { "id": "1d4cb97d-939e-36e7-b3aa-d7116af2f87c", // Here's the personalized category "industry": [ { "id": "7235be6a-669c-47f2-9ab6-cac3f3edbab9", "name": "Family", "icon": null }, { "id": "ba8d4e26-052a-4260-bcf4-6523469fcf48", "name": "Day Care", "icon": null } ] } ] }, "enrichmentId": "4f88dd8d-8d0f-4e3a-b2cf-8d4ba686d667" } ``` ## Deleting custom categories You can delete custom categories all at once or individually. To delete all custom integration-level categories, send a `DELETE` request to the `categories` endpoint: ```bash bash theme={null} import requests requests.delete("https://east.sandbox.spade.com/categories", headers={"X-Api-Key": ""}) ``` To delete a specific custom integration-level category, send a `DELETE` request to the `categories/:id` endpoint with the category's ID: ```bash bash theme={null} import requests requests.delete("https://east.sandbox.spade.com/categories/aeb3b2e5-c46d-417c-9513-62868a6b0410", headers={"X-Api-Key": ""}) ``` ## Deleting counterparty category personalizations You can delete counterparty category personalizations all at once or individually. To delete all counterparty category personalizations, send a `DELETE` request to the `/counterparty-category-personalizations` endpoint: ```bash bash theme={null} import requests requests.delete("https://east.sandbox.spade.com/counterparty-category-personalizations", headers={"X-Api-Key": ""}) ``` To delete a specific counterparty category personalization, send a `DELETE` request to the `/counterparty-category-personalizations/:counterpartyId` endpoint with the `counterpartyId` of the personalization you want to delete: ```bash bash theme={null} import requests requests.delete("https://east.sandbox.spade.com/counterparty-category-personalizations/ec5ac9b9-0ea5-4248-a666-3197d945b85a", headers={"X-Api-Key": ""}) ``` ## FAQs ### What if I want to recategorize transactions based on the industry instead of the counterparty? We don't currently support category-wide personalizations. However, you can achieve this on your end by creating Spade category -> custom category mappings on your end. ## Conclusion You've just learned how Spade's category personalization endpoints can be used to create custom categories at the integration-level and recategorize transactions onto those categories. ## Next Steps > [User Category Personalization Guide](/reference/user-category-personalization-guide) # Introduction Source: https://docs.spade.com/reference/introduction *** ## **About Spade** Spade transforms raw transaction details into clean, high-fidelity insights that power better workflows. Our API enriches debit and credit card payments, bank transfers, and third-party transactions to provide teams with richly tagged data in a single unified platform. *** ## Getting Started [Request API access](mailto:sales@spade.com) [Integrate with Spade's API](/reference/integrate-with-spades-api) *** ## Browse by product **Spade Enrichment** *Factual transaction details and enhanced data features telling you everything you need to know about the merchant on the other side of a transaction* **Spade Tooling** *Tools to help you easily use enriched data to power product features and workflows* # Understanding match scores Source: https://docs.spade.com/reference/match-score-guide *** > ⓘ Reading Time: 5 mins ## Overview Spade’s unique approach to transaction enrichment involves matching each transaction to a real merchant entity (”counterparty”) in our database, and returning granular merchant, category, and location information. Matching accuracy is critical, and we have developed a “match score” model that assesses how confident we are that each match is correct. This is a machine learning model that is constantly fine-tuned to create more and more accurate predictions. In addition to using this model to filter out matches that don’t meet a quality bar, we surface results via our API to allow you to make decisions about when and how to use our data. ## How do I interpret match scores? Match scores range in value from 0.00 to 100.00 — the higher the match score, the higher the likelihood that a counterparty or location returned was the one involved in a transaction. We return two types of match scores: counterparty match score and location match score. This is not a probability of a match being correct, but simply a representation of our confidence. Over 99% of our matches are accurately scored by the model, despite most confidence scores falling in the 85-95 range. Note, the returned model score is not a probability of a match. While these numbers are not representative of probabilities, they are directionally relevant to the probability of match accuracy. You can be more confident in a 99 score than a 90 score, and so on. Across our data, it is exceedingly rare for a prediction >90 to be incorrect. Scores in the 80-90 range represent a correct match >99% of the time. Scores in the 70-80 range represent a correct match >95% of the time. Scores below 70 represent a correct match \~90% of the time. ## What is the counterparty match score? * Counterparty match score is an assessment of how confident we are that a specific counterparty we return is the one involved in a transaction (e.g., how likely it is that `WALMART002191BRYANOH` is a transaction occurring at Walmart) * Counterparty match scores appear in the `counterparty` portion of the response. ## What is the location match score? Note: location match scores are currently in beta. * Location match score is an assessment of how confident we are that a specific location we return is the one involved in a transaction (e.g., how likely it is that `WALMART002191BRYANOH` is a transaction occurring at the Walmart at `1215 S Main Street, Bryan, Ohio`). * Location match scores are only returned when we match on a location that is identified as physical in the `spendingChannel` field (it is `null` for digital transactions) * Location match scores appear in the location portion of the enrichment ## When is a match score not returned? * In the rare event we are unable to match a transaction to a counterparty or location in our backing data we will return a null match score. * This indicates that the returned data is not based on a match and is instead derived from what was in the request. * It can also be identified by the presence of a counterparty or location ID - if no ID is present, no “match” has been made. * At times, we may be able to find a counterparty match but not a location match. In these events, the match score in the location portion of the response will be `null` but the match score in the counterparty portion will be a numerical value. ## How should match scores be used? Match scores can help you make decisions about how to use enriched data in your systems. For example, for decision-making processes (e.g. card authorization flows or fraud assessments) we suggest only using counterparties with a match score of >=90, whereas for customer analysis, budgeting, or UX/UI improvements, counterparties with scores >=80 are sufficient. \**Note that as our model improves and becomes more accurate, these guidelines may be adjusted.* # Merchant action triggers guide Source: https://docs.spade.com/reference/merchant-action-triggers-guide Register merchant action triggers and receive triggered actions in your enrichment responses This guide covers merchant action triggers. For an overview of all trigger types, see the [Action triggers guide](/reference/action-triggers-guide). For category-based triggers, see the [Category action triggers guide](/reference/category-action-triggers-guide). ## Overview Spade's merchant action triggers allow you to register rules that trigger custom actions when transactions match specific merchants. When a transaction matches one of your registered triggers, the enrichment response includes your custom `action` data. **Common use cases:** * **Merchant rewards:** Identify transactions at specific merchants to trigger reward multipliers * **Transaction flagging:** Flag transactions for review based on a merchant * **Program-scoped triggers:** Register different trigger rules per program — for example, an issuer with both corporate and consumer card programs can register distinct merchant action triggers for each program without duplicating them across every user or card ## Quick Start 1. **Ensure the actions feature is enabled** - Contact your Spade representative if you need access 2. **Register your triggers** using the PUT endpoint with your trigger configuration 3. **Wait for registration to complete** - Check the GET endpoint until status is `succeeded` 4. **Enrich transactions** - Matched triggers appear in the `actions` field ## Registration Scopes Merchant action triggers can be registered at four scopes. See the [Action triggers guide](/reference/action-triggers-guide#scope-hierarchy) for how scope inheritance works. | Scope | Endpoint | Applies To | Per-request limit | Total cap per scope | | ------- | --------------------------------------------------------- | -------------------------------------------------------------- | ----------------- | ------------------- | | Account | `/merchant-action-triggers` | All transactions for all users in your account | 100,000 triggers | 300,000 triggers | | Program | `/programs/{programId}/merchant-action-triggers` | All enrichment requests where `programId` matches this program | 100,000 triggers | 300,000 triggers | | User | `/users/{userId}/merchant-action-triggers` | All transactions for all cards belonging to that user | 100 triggers | 100 triggers | | Card | `/users/{userId}/cards/{cardId}/merchant-action-triggers` | Only transactions for that specific card | 100 triggers | 100 triggers | At **account and program scope**, the **per-request limit** (100,000 triggers) and the **total cap per scope** (300,000 triggers) are different limits: a single request may carry up to 100,000 triggers, but a scope can hold up to 300,000 active triggers in total. To register more than 100,000 triggers, split them into ≤100,000 batches and send them sequentially — see [Registering large trigger sets](#registering-large-trigger-sets). User and card scopes are capped at 100 triggers total. **Scope inheritance:** When a transaction is enriched, Spade checks for matching triggers across all applicable scopes. For example, a transaction with a `programId`, `userId`, and `cardId` will check: 1. Card-level triggers (for that card) 2. User-level triggers (for that user) 3. Program-level triggers (for that program) 4. Account-level triggers (for your account) Multiple matches from different scopes can be returned in a single enrichment response. ### What is a program? A **program** is a freeform identifier you define — it requires no upfront configuration. You supply a `programId` string on your enrichment requests to group transactions however makes sense for your business (e.g., by card product, customer, or business line). `programId`, `userId`, and `cardId` each have a maximum length of 512 characters. ## Registering merchant action triggers Use the PUT endpoint to register merchant action triggers for action matching. **PUT replaces your entire trigger list.** Every PUT request overwrites all previously registered triggers at that scope. If you have 50,000 registered merchants and need to add or remove a handful, use the PATCH endpoint instead — sending the full list via PUT to change a few entries is inefficient and unnecessarily slow. See the API reference for PATCH endpoint details. **Two matching modes for merchant action triggers:** 1. **Location-level:** (default) Applies triggers to a specific physical location (e.g. one specific Costco location) 2. **Corporation-level:** Applies triggers to a whole brand (e.g. Costco in general) ### Example: Register Merchant Action Triggers at the Account Scope The example below registers triggers at the **account scope**. To register at other scopes, replace the URL path accordingly — for example, use `/programs/{programId}/merchant-action-triggers` for program scope. See the API reference for full endpoint details. ```bash bash theme={null} curl --request PUT \ --url https://east.sandbox.spade.com/merchant-action-triggers \ --header 'Content-Type: application/json' \ --header 'X-Api-Key: YOUR_API_KEY' \ --data '{ "merchantTriggers": [ { "id": "trigger-1", "merchantName": "Starbucks", "website": "https://www.starbucks.com", "level": "corporation", "action": { "type": "REWARD", "rewardPercent": 5, "offerId": "summer-2024-promo" } }, { "id": "trigger-2", "merchantName": "Target", "address": "1000 Nicollet Mall", "city": "Minneapolis", "region": "MN", "country": "USA", "postalCode": "55403", "level": "location", "action": { "type": "REWARD", "rewardPercent": 3, "offerId": "target-cashback" } } ] }' ``` ```python python theme={null} import requests response = requests.put( "https://east.sandbox.spade.com/merchant-action-triggers", headers={ "Content-Type": "application/json", "X-Api-Key": "YOUR_API_KEY" }, json={ "merchantTriggers": [ { "id": "trigger-1", "merchantName": "Starbucks", "website": "https://www.starbucks.com", "level": "corporation", "action": { "type": "REWARD", "rewardPercent": 5, "offerId": "summer-2024-promo" } }, { "id": "trigger-2", "merchantName": "Target", "address": "1000 Nicollet Mall", "city": "Minneapolis", "region": "MN", "country": "USA", "postalCode": "55403", "level": "location", "action": { "type": "REWARD", "rewardPercent": 3, "offerId": "target-cashback" } } ] } ) print(response.json()) ``` ### Response ```json theme={null} { "status": "succeeded", "version": 1 } ``` ## Checking Registration Status Use the GET endpoint to check the status of your action trigger registration. ### Status Values | Status | Description | | ----------- | --------------------------------------------------------- | | `pending` | Registration received, processing not yet started | | `running` | Registration is being processed (for batch registrations) | | `succeeded` | Registration complete, triggers are now active | | `failed` | Registration failed, check your request and retry | **Important:** Triggers are only applied to enrichment responses when the status is `succeeded`. Transactions enriched while status is `pending` or `running` will NOT include the new triggers. ### Example: Check Status ```bash bash theme={null} curl --request GET \ --url https://east.sandbox.spade.com/merchant-action-triggers \ --header 'X-Api-Key: YOUR_API_KEY' ``` ```python python theme={null} import requests response = requests.get( "https://east.sandbox.spade.com/merchant-action-triggers", headers={"X-Api-Key": "YOUR_API_KEY"} ) print(response.json()) ``` ## Batch Registrations (Account and Program Scope) Account-scope and program-scope registrations with more than 100 merchant action triggers are processed asynchronously. The PUT request returns immediately with `status: "pending"`, then progresses through `running` → `succeeded` (or `failed`). Poll the GET endpoint to monitor progress. **Per-request limits:** User-scope and card-scope registrations are limited to **100 merchant action triggers**. Requests exceeding this limit will receive a `400` error. This does not limit the total number of users or cards you can register triggers for — you can make as many requests as needed across different scopes. For large account-scope or program-scope trigger lists, we recommend implementing a polling loop with exponential backoff. Small registrations complete within a few minutes, but large batches (tens of thousands of triggers) can take **well over an hour** — size your polling timeout accordingly rather than assuming a few minutes. ### Example: Polling for Completion ```python python theme={null} import requests import time def wait_for_trigger_success(api_key, max_attempts=145, base_delay=2): """Poll until action trigger status is succeeded or failed. Large batch registrations can take well over an hour, so max_attempts is generous (~2.5 hours at the capped poll interval). Backoff is capped so we keep polling politely rather than hammering the endpoint. """ for attempt in range(max_attempts): response = requests.get( "https://east.sandbox.spade.com/merchant-action-triggers", headers={"X-Api-Key": api_key} ) data = response.json() status = data.get("status") if status == "succeeded": print(f"Registration complete! Version: {data.get('version')}") return True elif status == "failed": print("Registration failed. Please check your request and retry.") return False else: # Exponential backoff, capped to poll politely delay = base_delay * (2 ** min(attempt, 5)) print(f"Status: {status}. Checking again in {delay}s...") time.sleep(delay) print("Timed out waiting for registration to complete") return False ``` ## Registering large trigger sets There are two distinct limits at account and program scope: * **Per-request limit — 100,000 triggers.** A single PUT or PATCH `add` request may carry at most 100,000 triggers. Requests exceeding this return a `400` error. * **Total cap per scope — 300,000 triggers.** A scope can hold up to 300,000 active triggers in total across all requests. A PATCH `add` whose net-new merchants would push the scope past 300,000 returns a `400` error (PUT cannot exceed the cap, since it is itself limited to 100,000 triggers per request). Because the per-request limit (100,000) is lower than the total cap (300,000), registering a large set requires splitting it into **batches of up to 100,000 triggers** and sending them with sequential PATCH `add` operations. **One registration at a time per scope.** Only one registration or operation can be in progress per scope. While a batch is `pending` or `running`, any other PUT or PATCH to the same scope returns a `409` Conflict. Batches must therefore be sent **sequentially** — wait for each batch to reach `succeeded` before submitting the next. **Recommended workflow:** 1. Split your full trigger list into chunks of **≤100,000 triggers**. 2. Submit the first chunk: * Use **PUT** if this registration should replace whatever is currently in the scope — a clean full sync. This is the safe default when you want the scope to contain exactly the set you're about to register. * Use **PATCH `add`** if you're adding to triggers that already exist and want to keep them. 3. Poll the GET endpoint until status is `succeeded`. 4. Submit each remaining chunk with PATCH `add` (**not** PUT) and repeat until all chunks are registered. Only the **first** batch may use PUT, and only when you want to replace the scope. Every subsequent batch must use PATCH `add`: PUT replaces the entire trigger list, so using it for a later batch would discard the triggers registered in earlier batches. PATCH `add` is additive — each request only processes the merchants it carries and leaves existing triggers untouched. ## Clearing Triggers Use the DELETE endpoint to clear all triggers at a given scope. DELETE creates a new version with an empty trigger list. The version number is incremented, not reset. ### Example: Clear an Account-Scoped Trigger ```bash bash theme={null} curl --request DELETE \ --url https://east.sandbox.spade.com/merchant-action-triggers \ --header 'X-Api-Key: YOUR_API_KEY' ``` ```python python theme={null} import requests response = requests.delete( "https://east.sandbox.spade.com/merchant-action-triggers", headers={"X-Api-Key": "YOUR_API_KEY"} ) # Returns 204 No Content on success print(f"Status code: {response.status_code}") ``` ## Receiving Triggered Actions When a transaction matches one or more of your registered triggers, the enrichment response includes the `actions` field. ### Triggered Action Response Fields | Field | Description | | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | Your trigger ID (as provided during registration) | | `type` | The type of trigger that matched (e.g., `merchant_trigger`) | | `action` | Your custom action data from registration | | `scope` | The scope at which this trigger was registered (`account`, `program`, `user`, or `card`) | | `source` | Whether the action was triggered by the matched `counterparty` or `third_party`. Set to `null` when an `ALLOW_ONLY` action trigger is set up whose condition the current transaction did not meet (the `authRecommendation` in this case will be `BLOCK`). | ### Example: Enrichment Response with Triggered Actions ```json theme={null} { "enrichmentId": "d1f7490c-719e-4c75-8bfa-535c8a32b936", "transactionInfo": { "type": "spending", "display": { "name": "Starbucks", "categoryName": "Coffee Shop" } }, "counterparty": [ { "id": "abc123-def456", "name": "Starbucks", "matchScore": 95.2 } ], "actions": [ { "id": "trigger-1", "type": "merchant_trigger", "action": { "type": "REWARD", "rewardPercent": 5, "offerId": "summer-2024-promo" }, "scope": "account", "source": "counterparty" } ] } ``` The `actions` field is `null` when no triggers match, and is omitted entirely if your account does not have the actions feature enabled. ## Best Practices ### Unique Trigger IDs Trigger IDs must be unique **within each scope**. A scope is defined by the URL path you register against (account, program, user, or card level). * **Within a single request:** All trigger IDs must be unique. Duplicate IDs in the same request will return a `400` error. * **Across requests in the same scope:** Submitting a trigger with an ID that already exists in that scope will replace the previous registration (upsert). This applies to both `PUT` (full replacement) and `PATCH add` operations. * **Across different scopes:** The same trigger ID can be used independently in different scopes. For example, `"starbucks-reward"` can exist in both a user-level and a program-level scope without conflict. ### Action Structure Design your `action` schema upfront. This is your custom data - use it to store reward amounts, offer IDs, or any information you need when processing matched transactions. The `type` field is **required**. There is currently one reserved keyword: * `REWARD` - For reward-based actions (e.g., cashback, points multipliers) Spade will execute pre-defined behavior on any reserved type. You may also use custom type values for your own categorization needs. ### Scope Selection * Use **account scope** for triggers that apply to all users (e.g., card-linked offer partners) * Use **program scope** for triggers scoped to a specific program (e.g., a subset of customers or product line) * Use **user scope** for user-specific preferences * Use **card scope** for card-specific rules (e.g., category-specific rewards cards) * Triggers cascade downward — for example, an account-level trigger will match transactions for all programs, users, and cards. A program-level trigger will match all users and cards that send enrichments with that `programId`, but won't affect other programs ### Choosing PUT vs. PATCH * Use **PUT** when replacing your full set of triggers (e.g., initial setup or a periodic full sync) * Use **PATCH** to add or remove merchants relative to your total trigger list — including registering more than 100,000 triggers via sequential `add` batches (see [Registering large trigger sets](#registering-large-trigger-sets)) ### Error Handling * Implement retry logic for 5xx errors * Handle 409 Conflict by waiting for the in-progress registration to complete — only one registration can be in progress per scope, so send batched registrations sequentially * Validate your trigger data before submission to avoid 400 errors # Merchant search guide Source: https://docs.spade.com/reference/merchant-search-guide Search Spade's merchant database to power autocomplete experiences Merchant search is in beta and is not enabled by default. To request access, contact us at [sales@spade.com](mailto:sales@spade.com). ## Overview Merchant search lets you query Spade's merchant database by name. It is designed for powering autocomplete experiences — as a user types, your application queries `GET /corporations` and displays matching merchants with their name, logo, and website. The endpoint returns up to five results ordered by relevance. Use the returned `name` and `website` to register merchants with Spade's [action triggers](/reference/merchant-action-triggers-guide) endpoint for rewards, merchant-locked cards, and other transaction-level actions. ## Searching for a merchant Send a `GET` request to `/corporations` with the merchant name as a query parameter. ```python Python theme={null} import requests response = requests.get( "https://east.sandbox.spade.com/corporations", params={"name": "star"}, headers={"X-Api-Key": ""}, ) print(response.json()) ``` ```bash cURL theme={null} curl -G "https://east.sandbox.spade.com/corporations" \ --data-urlencode "name=star" \ -H "X-Api-Key: " ``` ### Response ```json theme={null} { "corporations": [ { "name": "Starbucks", "logo": "https://static.v2.spadeapi.com/logos/.../light.png", "website": "starbucks.com" }, { "name": "Starlink", "logo": "https://static.v2.spadeapi.com/logos/.../light.png", "website": "starlink.com" } ] } ``` ### Response fields | Field | Type | Description | | --------- | -------------- | ---------------------------------------------------- | | `name` | string | The merchant's name | | `logo` | string \| null | URL of the merchant's logo, or `null` if unavailable | | `website` | string \| null | The merchant's website, or `null` if unavailable | ## Building a merchant autocomplete ### Backend proxy Your backend should proxy requests to Spade so that your API key is never exposed to the client. Never send your API key to the client. This includes any client-side application code bundles (websites, mobile apps, etc.). ```python theme={null} from flask import Flask, request, jsonify import requests import os app = Flask(__name__) spade_api_key = os.environ["SPADE_API_KEY"] @app.get("/corporations") def search_corporations(): name = request.args.get("name", "") response = requests.get( "https://east.sandbox.spade.com/corporations", params={"name": name}, headers={"X-Api-Key": spade_api_key}, ) return jsonify(response.json()), response.status_code ``` ### Frontend debounce Because the endpoint is rate-limited, debounce requests from the frontend so that a request is only sent after the user pauses typing (300-400 ms is a good starting point). Most autocomplete libraries handle this automatically — if yours does not, use a utility like `lodash.debounce`. ## Next steps * [Merchant-based rewards](/reference/merchant-based-rewards) — build a rewards program on top of merchant search * [Merchant-locked cards](/reference/merchant-locked-cards) — restrict card spending to specific merchants * [Merchant action triggers](/reference/merchant-action-triggers-guide) — register rules that trigger custom actions during enrichment # Microbatch enrichment guide Source: https://docs.spade.com/reference/microbatch-enrichment-guide Submit small batches and receive enriched results inline. Microbatching is not enabled by default. To request access, please contact us at [sales@spade.com](mailto:sales@spade.com). ## Overview Microbatching lets you submit a small batch to one of Spade's batch enrichment endpoints and receive the enriched results inline in the response, instead of receiving a `batchId` and polling for results once asynchronous processing finishes. ## Choosing a batch mode * **Microbatch (`?synchronous=true`):** Processes records synchronously and returns results inline in the response. * Limited to each endpoint's `synchronousMax`. * Use when you have a small number of items (more than one, fewer than a few hundred) and need results back quickly. * **Asynchronous (default):** Returns a `batchId` immediately and processes records in the background. * Supports each endpoint's full async batch size limit and supports `callbackUrl` for completion notifications. * Use when you need to process thousands of records and you need to optimize for overall throughput. ## Enabling microbatching Append `?synchronous=true` to any supported batch endpoint. The request schema is identical for synchronous and asynchronous processing. ```bash theme={null} curl https://east.sandbox.spade.com/batches/transactions/universal/enrich?synchronous=true \ -H "Content-Type: application/json" \ -H "X-Api-Key: " \ -d '{ "transactions": [ { "transactionId": "48FA5E16-6870-42D2-AECE-AF393714EB1C", "description": "SQ* BLACK SEED BAGELS BROOKLYN NY", "amount": "14.25", "currencyCode": "USD", "occurredAt": "2025-09-15", "userId": "u-1234" } ] }' ``` `callbackUrl` is ignored when `synchronous=true`, as the response is returned inline. ## Per-endpoint limits Microbatch sizes are capped per endpoint. Submitting more items than the cap returns a `400`. | Endpoint | `synchronousMax` | | ----------------------------------------------- | ---------------- | | `POST /batches/merchants/enrich` | 50 | | `POST /batches/transactions/cards/enrich` | 100 | | `POST /batches/transactions/cards/enrich/parse` | 100 | | `POST /batches/transactions/transfers/enrich` | 100 | | `POST /batches/transactions/universal/enrich` | 100 | If you'd like to fetch the current cap programmatically, each endpoint's `OPTIONS` response includes a `synchronousMax` field with the current value. ## Synchronous response shape When `synchronous=true` is set, the response returns a `results` array. Each entry contains the same per-item shape that the endpoint's asynchronous `/results` endpoint would return — a `statusCode` plus either the enriched payload (for `200`) or an `errors` object (for `400`). ```json theme={null} { "results": [ { "statusCode": 200, "transactionId": "7866cfe1-edde-4b45-a8da-c7076dab0255" // ... endpoint-specific response fields ... }, { "statusCode": 400, "transactionId": "1ae0922a-f343-49ab-b52b-8566a5f0bc11", "errors": { "currencyCode": ["This field is required."] } } ] } ``` See each endpoint's guide for the exact per-item enrichment shape: * [Card enrichment guide](/reference/card-enrichment-guide) * [Transfer enrichment](/reference/transfer-enrichment) * [Universal data enrichment guide](/reference/universal-data-enrichment) * [Batch merchant enrichment guide](/reference/batch-merchant-enrichment-guide) ## Errors * **400 on the submission itself** — the request is malformed (no items, duplicate IDs) or exceeds the endpoint's `synchronousMax` limit. * **400 per item** — invalid items appear in the `results` array with `statusCode: 400` and an `errors` object describing what went wrong. Valid items in the same submission are still processed normally. * **403** — invalid or missing API key, or microbatching is not enabled on your account. * **500** — Spade infrastructure issue. Retry with exponential back-off before resubmitting. # Mobile app data Source: https://docs.spade.com/reference/mobile-app-data ## Overview Mobile app data provides detailed, app-level information about in-app and mobile app purchases. This guide outlines the additional data made available by this feature, explains how to interpret that data, and describes how to use mobile app data to support different use cases. ## Example enrichment response ```json Example response expandable {62,63,64,65,66,67,68,69,70,71,72,73} theme={null} { "transactionInfo": { "type": "spending", "subType": null, "display": { "name": "Artificial Life - Google Play", "categoryName": "Mobile App: Education and Parenting", "graphic": "https://static.v2.spadeapi.com/logos/58c03a73629c46b8aa4c1e15cb4f0d0b/light.png", "graphicSource": "third_party" }, "thirdParties": [ { "id": "58c03a73-629c-46b8-aa4c-1e15cb4f0d0b", "name": "Google Play", "type": "marketplace", "logo": "https://static.v2.spadeapi.com/logos/58c03a73629c46b8aa4c1e15cb4f0d0b/light.png", "website": "https://play.google.com/" } ], "spendingInfo": { "channel": { "value": "digital" } }, "transferInfo": null, "atmInfo": null, "isAccountVerification": null, "isPeerToPeer": null, "isDigitalWallet": null, "transactionId": "166c5ad8-8a94-4964-a659-03cdb64525f2" }, "counterparty": [ { "id": "0d1ef10f-2652-39bd-98f1-af34ae03a4a7", "name": "Artificial Life", "legalName": null, "industry": [], "location": [ { "id": null, "address": null, "addressLine1": null, "addressLine2": null, "city": "Tampa", "region": "FL", "postalCode": null, "country": "USA", "phoneNumber": null, "latitude": null, "longitude": null, "matchScore": null } ], "matchScore": 90.13, "logo": null, "medianSpendPerTransaction": null, "phoneNumber": null, "website": "https://play.google.com/store/apps/details?id=com.mooncoder.alife" } ], "enrichmentId": "2c7dab64-8f37-44b3-b482-cc1c8f71ab0d", "mobileAppInfo": { "id": "0d1ef10f-2652-39bd-98f1-af34ae03a4a7", "name": "Artificial Life", "url": "https://play.google.com/store/apps/details?id=com.mooncoder.alife", "logo": null, "developerName": "Mooncoder", "developerId": "f6fdc9b1-f0ea-3dda-8e15-ead6fe5755fe", "category": "Education and Parenting", "hasGamblingOrRewards": false, "hasSimulatedGambling": false, "ageRating": "Children" } } ``` ## Understanding mobile app data When a match is found: * Detailed app information is returned in the `mobileAppInfo` object. * The mobile app store - i.e. **Google Play Store** - is added as a `thirdParty` in the enrichment response. * The **spending channel** is set to `digital`. If no match is found: * `mobileAppInfo` will return`null` The `mobileAppInfo` object contains the following fields: | Field | Description | | :--------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | ID of the matched mobile app | | `name` | App name | | `url` | Link to the app, either in the Google Play Store or the app's homepage | | `logo` | Link to a downloadable app logo image | | `developerName` | App developer name | | `developerId` | App developer ID | | `category` | Mobile app category (separate from Spade’s category tree).
Possible values:
`Auto and Transportation`, `Creative and Design`, `Education and Parenting`, `Entertainment and Media`, `Finance and Business`, `Games`, `Health and Lifestyle`, `Shopping and Marketplaces`, `Social and Communication`, `Travel and Events`, `Utility and Tools` | | `hasGamblingOrRewards` | Indicates whether the app has gambling features or cash prizes/rewards | | `hasSimulatedGambling` | Indicates whether the app contains simulated gambling | | `ageRating` | App’s age rating. Possible values: `Children`, `Teen`, `Adult` | If a mobile app ID is present, it will be represented consistently as the counterparty ID. ## Using mobile app data Mobile app data can be used in a variety of contexts, such as displaying detailed purchase information in your transaction feed or preventing a purchase based on the app’s age rating or gambling features. **Displaying mobile app information in your UI/UX** For a clean display of the mobile app name and its category, we recommend using the `display` object, which returns our recommendation for how to present information in your UI. Below is an example based on the previous enrichment for a Google Play Store purchase made for an app called “Artificial Life,” categorized as “Education and Parenting”: * Display name: "Artificial Life - Google Play" * Display category: "Mobile App - Education and Parenting" We also provide more information in the `mobileAppInfo` object, such as the app’s logo, category, developer name, and URL, which you can use within your application. If you are using [counterparty category personalization](/reference/category-personalization-guide), you can use the app's `counterparty.id` to return a custom category for the mobile app. The custom category will be returned in the `personalization` object. ### Card locking, fraud prevention and controlling spend With detailed mobile app information you can: * Prevent spending on gambling apps by using the `hasGamblingOrRewards` or `hasSimulatedGambling` fields, which return `true` if the app contains either feature. * Issue virtual cards to be used only for a specific app by allowing spending associated solely with a specific `counterparty.id`, as returned in the enrichment response. * Limit purchases on cards to specified age ratings based on the returned `mobileAppInfo.ageRating`. # Recurring transaction guide Source: https://docs.spade.com/reference/recurring-transaction-guide Understand how we identify recurring transactions *** Recurrence detection is not enabled by default. To request access, please contact us at [sales@spade.com](mailto:sales@spade.com). Please note, enabling this feature may add \~10ms of latency to your enrichment requests. ## Overview Our recurrence flag leverages transaction history and merchant information to identify recurring spend. We define recurring transactions as spend that happens on a consistent basis to a specific merchant (often via a subscription). Examples of recurring transactions that we flag include subscription payments to Netflix and a Con Edison electricity bill - shopping at the same grocery store on a regular basis is not considered a recurring transaction. We use a combination of merchant data and user transaction history to determine if a specific transaction is recurring. ### Understanding Recurrence Data Our recurring transaction flag includes the following fields: * `intervalDays`: The number of days between recurring transactions * `intervalType`: The type of recurrence pattern (weekly, biweekly, monthly, quarterly, etc.) * `nextPaymentExpected`: The predicted date of the next payment in this pattern * `recentRecurrences`: An array of up to 5 recent transactions in this pattern, including: * `amount`: The transaction amount * `occurredAt`: Timestamp of when the transaction occurred * `enrichmentId`: Spade's unique identifier for the enrichment * `transactionId`: Your original transaction identifier ## Getting started To get started: * Ensure this feature is enabled by contacting [sales@spade.com](mailto:sales@spade.com) * Verify that your integration includes the following: * Unique and consistent `userId` parameters * Correct `occurredAt` dates * Accurate transaction `amount` In order to get the best results, we recommend sending at least 3 months of backfilled transactions with the correct `occurredAt` dates (not the date the transaction was backfilled). For historical recurring transactions, we need 3 or more transactions with matching characteristics to establish a recurring pattern. Please contact your account representative to discuss backfill approach. ## Testing for Recurring Transactions Our API allows you to detect recurring transactions through our enrichment endpoints. Here's how to get started: ### 1. Send Transaction Data First, send your first transaction to our enrichment endpoint: ```bash bash theme={null} import requests raw_transaction = { "amount": "22.99", "cardId": null, "userId": "user_1234", "location": { "city": "LOS GATOS", "region": "CA", "country": "USA" }, "occurredAt": "2024-06-01", "categoryCode": "4899", "categoryType": "MCC", "currencyCode": "USD", "merchantName": "NETFLIX.COM", "transactionId": "transaction_1" } response = requests.post("https://east.sandbox.spade.com/transactions/cards/enrich", json=raw_transaction, headers={"X-Api-Key": ""}) enriched_transaction = response.json() print(enriched_transaction) ``` ### 2. Check Recurrence Information As the previous request is one that we had predicted to be a recurring transaction (subscription), the enrichment response will include a `recurrenceInfo` object for transactions that are part of a recurring pattern: ```bash bash theme={null} { "transactionInfo": { "type": "spending", "thirdParties": [], "spendingInfo": { "channel": { "value": "digital" } }, "transactionId": "5678", "recurrenceInfo": { "intervalType": "monthly", "intervalDays": 30, "nextPaymentExpected": "2024-07-01", "recentRecurrences": null } }, ... } ``` Any transaction that is not part of a predicted or historical recurring pattern will have `null` for the `recurrenceInfo` object. ### 3. Send Additional Transaction Data As you send additional transactions, you will see the `recurrenceInfo` object update with the new data: ```bash bash theme={null} import requests raw_transaction = { "amount": "22.99", "cardId": null, "userId": "user_1234", "location": { "city": "LOS GATOS", "region": "CA", "country": "USA" }, "occurredAt": "2024-07-01", "categoryCode": "4899", "categoryType": "MCC", "currencyCode": "USD", "merchantName": "NETFLIX.COM", "transactionId": "transaction_2" } response = requests.post("https://east.sandbox.spade.com/transactions/cards/enrich", json=raw_transaction, headers={"X-Api-Key": ""}) enriched_transaction = response.json() print(enriched_transaction) ``` ### 4. Check Recurrence Information The enrichment response will now include a `recentRecurrences` object under the `recurrenceInfo` object: ```bash bash theme={null} { "transactionInfo": { "type": "spending", "thirdParties": [], "spendingInfo": { "channel": { "value": "digital" } }, "transactionId": "transaction_2", "recurrenceInfo": { "intervalType": "monthly", "intervalDays": 30, "nextPaymentExpected": "2024-07-31", "recentRecurrences": [ { "amount": 22.99, "enrichmentId": "227dd480-2bd5-4805-b88d-185c306ee9bb", "occurredAt": "2024-06-01 00:00:00+00:00", "transactionId": "transaction_1" } ] } }, ... } ``` As you add additional recurring transactions, the `recentRecurrences` array will be updated with the new transactions, up to a maximum of 5. In addition to predicting recurring transactions, we also use historical transactions to find recurring patterns. For example, if you send transactions for a user on 2024-07-01, 2024-08-01, 2024-09-01, and 2024-10-01, all with similar amounts, we will use the historical data to determine if the transaction is recurring. If so, it will mark the last transaction as recurring, and the `intervalType` will be set to monthly. ### Troubleshooting Common reasons why transactions might not be recognized as recurring: * Transaction amounts that vary significantly between occurrences * Irregular timing (varying more than a few days from the expected pattern) * Missing or incorrect `userId` values * Insufficient transaction history If you're not seeing expected recurring patterns, ensure that: 1. The `amount` of the transactions are correct 2. The `userId` is consistent across transactions 3. The `occurredAt` dates reflect the actual transaction dates 4. You've sent at least 3 occurrences from the same merchant # Reducing latency Source: https://docs.spade.com/reference/reducing-latency *** Low latency is critical to customers who use Spade’s data to make decisions in the authorization flow. Spade’s API is highly performant and able to process enrichment requests in \<50ms. However, the network and systems outside of Spade’s service can have an impact on latency. We’ve outlined common pitfalls that can impact performance and recommendations for testing and integration that help mitigate and deliver the lowest round-trip latencies possible. ### 1. Geography The most important factor impacting latency is the distance between the client and server, and the performance of the network in between. Spade offers environments in the eastern (Virginia) and western (Oregon) United States to reduce the distance to wherever you are making requests from. **Recommendation:** Integrate and test with the closest Spade environment to you. Run tests remotely if needed but be aware of cloud throughput limits (see below). ### 2. Network Impact There will always be variability in network latency, driven by factors including network congestion, transmission medium (fibre-optic, copper, wireless, etc.), routing efficiency, and Wi-Fi signal strength. You may even see noticeable differences in latency at different times of the day as network congestion ebbs and flows. Please bear these factors in mind when measuring latency, especially if you are running tests from your home or office internet. **Recommendation:** Understand that the network itself is usually the most variable component of high latencies. Test and integrate under the best conditions possible. ### 3. Short Lived Connections & Repeated DNS Lookups The process of creating and closing connections adds a substantial amount of overhead when you are trying to make many requests rapidly. Many request libraries will also perform a [DNS lookup](https://en.wikipedia.org/wiki/Domain_Name_System) for every connection opened, which will additionally add latency. If you are attempting to achieve extremely high throughput, then you may also run into limitations using a single connection. A conventional HTTP1.1 connection cannot handle multiple requests in parallel. **Recommendation:** Use a [client session](https://requests.readthedocs.io/en/latest/user/advanced/) when making repeated requests. This will avoid making repeated costly DNS lookups. Avoid opening and closing connections unnecessarily. If you’re trying to enrich high volumes use multiple parallel connections to enrich. ### 4. Cloud Throughput Limits If you are attempting to integrate or test from a cloud instance (e.g. EC2) it is not uncommon for the cloud provider to impose limits on the amount of data or number of requests you can make, especially on lower cost instances. Some instances also have burstable traffic limits, so you may also observe a decline in results as time goes on. **Recommendation:** Understand the limits of your instances and scale up or out appropriately to avoid hitting bottlenecks. ### 5. VPN Routing traffic through a VPN can substantially impact latency regardless of whether your VPN exit point is located close to the enrichment server or not. This is especially true if you are testing from a local machine. **Recommendation:** Don't route traffic through a VPN or any other unnecessary paths to improve latency. # Risk insights Source: https://docs.spade.com/reference/risk-insights Risk insights is currently in **beta**. Risk Insights is an add-on feature, please contact [sales@spade.com](mailto:sales@spade.com) for more information. ## Overview Risk insights provide visibility into the potential risk of a transaction and the merchants involved. Our signals are designed to be leveraged during the authorization window to improve decisioning as well as for customer or account analysis. The insights returned are based on our counterparty data asset as well as extensive data analysis. If you plan to use this data to prevent transactions from occurring you'll want to ensure you're sending us data in the authorization flow. ## Understanding Risk Insights fields Below we've provided a list of the fields we return in the `riskInsights` object. | **Field** | **Risk Level** | **Definition** | | ----------------------------- | :------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **High risk entity** | Very High | The counterparty is known to engage in fraudulent practices | | **Irregular web presence** | High | Has engaged in deceptive online practices or has irregular website structure or content | | **Negative online sentiment** | Moderate | Negative online consumer feedback associated with the counterparty | | **Card acceptance history** | See definition | Possible values:
`extensive`(most)
`established`
`stable`
`limited`
`very_limited `(least)
`null `(no data)

Counterparties with lower levels of card acceptance history have a higher likelihood of chargebacks or fraudulent activity | | **Risky industry** | See definition | Industry is correlated with increased disputes (based on Spade industries)

Example: "Casinos and Gambling" will return a value of `true` | ## Using Risk Insights Risk Insights are best used in combination with other risk and fraud signals when making authorization or account closure decisions. Additionally, Risk Insights should only be used when a counterparty match occurs. Below we've provided an example response for a known high-risk entity: ```json Example response {24,25,26,27,28,29,30} theme={null} { "transactionInfo": { "type": "spending", "subType": null, "display": { "name": "Contentbodega.net", "categoryName": "Media", "graphic": "https://static.v2.spadeapi.com/categories/ff2e4086c1f14e368e086aadab182b19/light.png", "graphicSource": "category" }, "thirdParties": [], "spendingInfo": { "channel": { "value": "digital" } }, "transferInfo": null, "atmInfo": null, "isAccountVerification": null, "isPeerToPeer": null, "isDigitalWallet": null, "transactionId": "txn-1234", "recurrenceInfo": null, "riskInsights": { "irregularWebPresenceDetected": true, "negativeOnlineSentiment": true, "highRiskEntity": true, "riskyIndustry": false, "cardAcceptanceHistory": null } }, "counterparty": [ { "id": "f364863c-7266-30bc-aad1-7ddfbdf1edc2", "name": "Contentbodega.net", "legalName": "Gills Unlimited", "industry": [ { "id": "004-000-000-000", "name": "Entertainment", "icon": "https://static.v2.spadeapi.com/categories/ff2e4086c1f14e368e086aadab182b19/light.png" }, { "id": "004-008-000-000", "name": "Media", "icon": "https://static.v2.spadeapi.com/categories/ff2e4086c1f14e368e086aadab182b19/light.png" } ], "location": [ { "id": "e713c5fa-1b31-3004-833b-4ce7a7b5c66b", "address": "776 Divina Vista St", "addressLine1": "776 Divina Vista St", "addressLine2": null, "city": "Monterey Park", "region": "CA", "postalCode": "91754", "country": "USA", "phoneNumber": "+18556600894", "latitude": 34.051158, "longitude": -118.13354, "matchScore": null } ], "matchScore": 92.37, "logo": null, "medianSpendPerTransaction": null, "phoneNumber": "+18556600894", "website": "contentbodega.net" } ], "transactionRuleInfo": null, "enrichmentId": "5d16d9ac-e91b-4ba7-bece-ec69c7aed4a9" } ``` In the example provided, we've flagged the counterparty: * is a `highRiskEntity` * has `irregularWebPresenceDetected` * and has `negativeOnlineSentiment` When one of our counterparty-focused fields is `true`, we have identified that the counterparty involved in the transaction has the characteristics of that field. However, a value of `false` for a field such as `highRiskEntity` does not guarantee that the merchant is *not* a high risk entity. Additionally, a value of `null` means that there is no data available for this field for this counterparty. If we do not return any industries for the counterparty, the `riskyIndustry` field will return a `null` value If we are unable to match to a counterparty each `riskInsights` field will return a `null` value ## Feedback We recommend using our feedback endpoints when a chargeback has occurred or when fraudulent activity related to the merchant has occurred. * `enrichments/ ```bash bash theme={null} radius = 100 # Radius of the circle we will fit to # the hypotenuse of half the dimensions of the image is the images long diagonal image_diagonal = hypotenuse(image.width/2, image.height/2) scale_factor = radius / image_diagonal) # Now that we have the scale factor, set the new image dimensions image.width = image.width * scale_factor image.height = image.weight * scale_factor ``` > If you wish to allow for a small amount of padding around the graphic and the circle edge, multiply the `scale_factor` by `0.95` or similar. ## Comparison chart Below you can see the result of using the different scaling options on a variety of graphics, with radius scaling resulting in the full graphic being rendered inside of the circle. ## Advanced strategies Beyond the options outlined above, there are some more advanced strategies available to those who are willing to do analysis of the image data itself. These approaches can offer better results at the expense of some computational costs. * **Scale graphic to the pixel farthest from the center point** By finding the hypotenuse to the outermost non-transparent pixel, instead of the corner of the image, you can find the largest size possible without overflowing the circle. For example, the Starbucks logo has only empty space in it's corners and as a result the radius scaling leaves white space around the logo. * **Fill circle background with border color** If the color around the perimeter of the logo is non-transparent and exactly constant, we can likely assume that using this color as a background for the circle will also be visually consistent. For example, the Uber logo above could be placed on a black background using this technique. There may be edge cases where sampling the interior graphic color may be more advantageous than using the color of the perimeter. # Transfer enrichment guide Source: https://docs.spade.com/reference/transfer-enrichment ## Overview The transfer enrichment endpoint supports enriching ACH, Wire, ATM, P2P, and Check transactions. This guide outlines best practices for integrating with the transfer enrichment endpoint and how to use the enriched data that is returned. Transfer data enrichment is currently in **beta**; request and response fields may change as the product evolves. For access, please contact [sales@spade.com](mailto:sales@spade.com). ## Best practices Below we've outlined recommendations for how to send data to the transfer enrichment endpoint. The fields for this endpoint are flexible and we recommend working with the Spade team to optimize your integration for the best results. ### Description The description field typically will include a combination of: * A company or financial institution name (e.g. "Spade Bank") * A description of the transaction (e.g. "AUTOPAY") The full description you provide to Spade may exist as a single field or multiple fields within your current platform. If there are multiple fields they should be combined so that the company or financial institution name preceeds the transaction description. Below we've provided an example of how that would be provided in the API request: ` "description": "Spade Bank AUTOPAY"` ### Memo Memo is not a required field; however, if the transaction has a memo associated with it, such as for an ATM transaction, you should include that in the request. Alternatively, if the ATM-involved transaction includes location data, you can pass it in the memo field and combine the address, city, and state like: `"memo": "1 E FORDHAM RD BR BRONX NY"` ### Transaction type In addition to the description, you'll need to provide a `transactionType` which will be one of: * `ACH_DEPOSIT` * `ACH_RETURN` * `ACH_WITHDRAWAL` * `ATM` * `CHECK_DEPOSIT` * `CHECK_PAYMENT` * `INTERNATIONAL_WIRE_INCOMING` * `INTERNATIONAL_WIRE_OUTGOING` * `RTP_DEPOSIT` * `RTP_WITHDRAWAL` * `US_WIRE_INCOMING` * `US_WIRE_OUTGOING` * `OTHER_INCOMING` * `OTHER_OUTGOING` If you have a system that has custom transaction types that don't fall into the values required for `transactionType` work with your Spade representative for alternative options. ### Transaction amount and direction We recommend using a negative `amount` to indicate incoming transfers, and a positive `amount` to indicate outgoing transfers. Alternatively, you can use the `direction` field to specify whether the transfer is a `credit` or `debit.` ### Using identifiers We recommend providing a `transactionId` so that you can map our enrichment response and corresponding `enrichmentId` to your transaction-level data store. This field is required for batch transfer enrichment. A `userId` is required for all enrichment endpoints (max 512 characters). This should not include any PII - if you don't have the concept of a user on your platform you can use any identifier that groups transactions made by the same individual or organization. For premium features such as recurrence and [category personalization](/reference/category-personalization-guide) a unique `userId` is required. Additional metadata about the transaction or user can be provided in the `customAttributes` object and the enrichment response will return the original values provided. ### Example transfer enrichment requests and responses Below are example requests and responses for different transfer transaction types: ```json ACH withdrawal request theme={null} { "amount": "10.00", "userId": "user_id_123456789", "occurredAt": "2022-06-15 18:27:51Z", "description": "CITI CARDS ONLINE PMT", "currencyCode": "USD", "transactionId": "transaction_id_123456789", "transactionType": "ACH_WITHDRAWAL" } ``` ```json ACH withdrawal response theme={null} { "transactionInfo": { "type": "debt", "subType": "payment", "display": { "name": "Citi", "categoryName": "Banking and Finance", "graphic": "https://static.v2.spadeapi.com/logos/3f971f36fe7549c29a2c494a8a990334/light.png", "graphicSource": "counterparty" }, "thirdParties": [], "spendingInfo": null, "transferInfo": { "direction": "outgoing", "transferMethod": "ach", "transferType": "external", "isAdjustmentOrRefund": false }, "atmInfo": null, "isAccountVerification": false, "isPeerToPeer": false, "isDigitalWallet": false, "transactionId": "transaction_id_123456789", "recurrenceInfo": null, "irregularWebPresenceDetected": false }, "counterparty": [ { "id": "3f971f36-fe75-49c2-9a2c-494a8a990334", "name": "Citi", "legalName": "Citigroup Inc.", "industry": [ { "id": "001-000-000-000", "name": "Banking and Finance", "icon": "https://static.v2.spadeapi.com/categories/61897b3a5af545bab45ca1bf20498e65/light.png" } ], "location": [ { "id": null, "address": null, "addressLine1": null, "addressLine2": null, "city": null, "region": null, "postalCode": null, "country": null, "phoneNumber": null, "latitude": null, "longitude": null, "matchScore": null } ], "matchScore": null, "logo": "https://static.v2.spadeapi.com/logos/3f971f36fe7549c29a2c494a8a990334/light.png", "medianSpendPerTransaction": null, "phoneNumber": "+16462912727", "website": "https://www.citi.com/" } ], "enrichmentId": "7e40312d-91a4-4744-a987-1fac6e574bde" } ``` ```json ACH deposit request theme={null} { "amount": "-10.00", "userId": "user_id_123456789", "occurredAt": "2025-06-15 19:12:43Z", "description": "Fanatics SptsBk ViaTrustly", "currencyCode": "USD", "transactionId": "transaction_id_123456789", "transactionType": "ACH_DEPOSIT" } ``` ```json ACH deposit response theme={null} { "transactionInfo": { "type": "income", "subType": "other", "display": { "name": "Fanatics Sportsbook", "categoryName": "Casinos and Gambling", "graphic": "https://static.v2.spadeapi.com/logos/f36c7cc562c23b209af06e1e98c6d25f/light.png", "graphicSource": "counterparty" }, "thirdParties": [], "spendingInfo": null, "transferInfo": { "direction": "outgoing", "transferMethod": "ach", "transferType": "external", "isAdjustmentOrRefund": false }, "atmInfo": null, "isAccountVerification": false, "isPeerToPeer": false, "isDigitalWallet": false, "transactionId": "transaction_id_123456789", "recurrenceInfo": null, "irregularWebPresenceDetected": false }, "counterparty": [ { "id": "f36c7cc5-62c2-3b20-9af0-6e1e98c6d25f", "name": "Fanatics Sportsbook", "legalName": "Fanatics Holdings, Inc.", "industry": [ { "id": "004-000-000-000", "name": "Entertainment", "icon": "https://static.v2.spadeapi.com/categories/ff2e4086c1f14e368e086aadab182b19/light.png" }, { "id": "004-007-000-000", "name": "Casinos and Gambling", "icon": "https://static.v2.spadeapi.com/categories/ff2e4086c1f14e368e086aadab182b19/light.png" } ], "location": [ { "id": null, "address": null, "addressLine1": null, "addressLine2": null, "city": null, "region": null, "postalCode": null, "country": null, "phoneNumber": null, "latitude": null, "longitude": null, "matchScore": null } ], "matchScore": null, "logo": "https://static.v2.spadeapi.com/logos/f36c7cc562c23b209af06e1e98c6d25f/light.png", "medianSpendPerTransaction": null, "phoneNumber": "+18002540320", "website": "https://www.fanaticsinc.com/" } ], "enrichmentId": "c4a482e3-7c25-48da-8d56-4eee7b307635" } ``` ```json ATM request theme={null} { "userId": "user123", "accountId": "account123", "transactionId": "transaction_id_123456789", "amount": "20.00", "transactionType": "ATM", "occurredAt": "2025-8-15T1:42:00Z", "currencyCode": "USD", "memo": "1 E FORDHAM RD BR BRONX NY" } ``` ```json ATM response theme={null} { "transactionInfo": { "type": "spending", "subType": null, "display": { "name": "Citizens Bank - ATM", "categoryName": "Banking and Finance", "graphic": "https://static.v2.spadeapi.com/logos/33a58f5dfe9c3235af7cbafe781e5f85/light.png", "graphicSource": "counterparty" }, "thirdParties": [], "spendingInfo": null, "transferInfo": { "direction": "outgoing", "transferMethod": "unknown", "transferType": "unknown", "isAdjustmentOrRefund": false }, "atmInfo": { "ownerName": "Citizens Bank", "sponsorName": "Fiserv" }, "isAccountVerification": false, "isPeerToPeer": false, "isDigitalWallet": false, "transactionId": "transaction_id_123456789", "recurrenceInfo": null }, "counterparty": [ { "id": "33a58f5d-fe9c-3235-af7c-bafe781e5f85", "name": "Citizens Bank", "legalName": "Citizens Financial Group, Inc.", "industry": [ { "id": "001-000-000-000", "name": "Banking and Finance", "icon": "https://static.v2.spadeapi.com/categories/61897b3a5af545bab45ca1bf20498e65/light.png" } ], "location": [ { "id": "d1bce13f-caf3-39c1-b7e2-b75c1b661858", "address": "1 E Fordham Rd", "addressLine1": "1 E Fordham Rd", "addressLine2": null, "city": "Bronx", "region": "NY", "postalCode": "10468", "country": "USA", "phoneNumber": "+17185039811", "latitude": 40.862779, "longitude": -73.900569, "matchScore": null } ], "matchScore": null, "logo": "https://static.v2.spadeapi.com/logos/33a58f5dfe9c3235af7cbafe781e5f85/light.png", "medianSpendPerTransaction": null, "phoneNumber": "+18009229999", "website": "https://www.citizensbank.com/HomePage.aspx" } ], "enrichmentId": "1a6f18c3-be5f-42a2-b190-2d51c8b5dfda" } ``` ### **Batch vs real-time** We offer both [real-time](/reference/transfer-enrichment) and [batch](/reference/batch-enrichment-guide) enrichment for transfers. If you don't need the data in real-time, we recommend batching transfer data, ideally in batches of 50k transactions at a time (see our [batch enrichment guide](/reference/batch-enrichment-guide) for more information). ### Error Handling The most common errors result from not sending required fields or providing invalid values for fields such as `transactionType` . The error response object will include specific details to reprocess the request. ```json theme={null} { "transactionType": [ "Invalid value: ''. Must be one of ['ACH_DEPOSIT', 'ACH_RETURN', 'ACH_WITHDRAWAL', 'ATM', 'CHECK_DEPOSIT', 'CHECK_PAYMENT', 'INTERNATIONAL_WIRE_INCOMING', 'INTERNATIONAL_WIRE_OUTGOING', 'RTP_DEPOSIT', 'RTP_WITHDRAWAL', 'US_WIRE_INCOMING', 'US_WIRE_OUTGOING', 'OTHER_INCOMING', 'OTHER_OUTGOING']." ] } ``` ## Understanding enriched transfer data For this guide, we will focus on enriched fields unique to transfer enrichment. More information can be found in our [understanding enriched data guide](/reference/understanding-enriched-data). ### Transaction types and subtypes In the enrichment response we provide a `type` and if possible a `subType` which can provide more detail about the transaction. | type | subType | description | | :--------------------: | :----------------------------------------------------------------------------------------------------------------------------------------: | --------------------------------------------------------------------------- | | `atm` | `deposit`
`withdrawal`
`transfer` | ATM involved transactions | | `debt` | `bnpl`
`disbursement`
`payment`
`other` | Debt involved transactions such as paying off a loan or credit card | | `fee` | `atm`
`cash_advance`
`nsf`
`overdraft`
`transfer`
`other` | Fee related transactions | | `income` | `earned_income`
`payout`
`investment`
`government_benefits`
`alimony_or_child_support`
`rental`
`retirement` | Income related transactions | | `reimbursement` | `tax_refund`
`business`
`insurance`
`other` | Reimbursement related transactions such as tax refunds or insurance payouts | | `spending` | null | Outgoing transfers that don't fall into another type/sub-type | | `other_money_movement` | null | Incoming transfers that don't fall into another type/sub-type | ### Transfer transaction flags Additionally, we provide the following boolean flags which, when `true` , provide additional context about the transaction: | flag | definition | | ----------------------- | ------------------------------------------------------------------- | | `isAccountVerification` | Used to denote account verification transfers such as microdeposits | | `isPeerToPeer` | Whether the transaction is a peer-to-peer transaction (e.g. Zelle) | | `isDigitalWallet` | Whether the transaction is to a digital wallet (e.g. Apple Wallet) | ### Using transfer data in your UI We provide a `display` object in the response that provides our recommendation for displaying a clean merchant name, category. and logo. If you want to customize what is displayed to end users, you can use the individual data elements returned in the enrichment response. When a transaction takes place at an ATM we will return a `display.name` that appends " - ATM" to the name of the ATM sponsor or financial institution like so: ```json theme={null} "display": { "name": "Spade Bank - ATM", "graphic": "https://static.v2.spadeapi.com/logos/1234/light.png", "categoryName": "Banking and Finance", "graphicSource": "counterparty" } ``` Transfer enrichments will often return a category of 'Banking and Finance' when the counterparty is a financial institution. Depending on your use case you may want to use the `transactionInfo.type` and `transactionInfo.subType` in lieu of the category. For best practices, check out our guide on [improving user experience with Spade](/reference/improved-ux). # Understanding enriched data Source: https://docs.spade.com/reference/understanding-enriched-data ## Overview Before starting integration, it's important to understand Spade's approach to enrichment and key concepts for successful implementation. Spade takes a unique approach to card transaction enrichment. We have built data partnerships with a variety of providers, including players in the payments value chain, first-party merchants, location data providers, state registry databases, and more. When we receive transaction data from you, we match it to a real merchant entity in our database -- returning granular merchant information including clean name, custom category, geo-location, and more in less than 50ms. ## Example transaction The best way to understand the value of our enriched data is to walk through an example transaction. Below you'll a see a sample transaction with the information provided to Spade in the enrichment request, the data returned, and an example of how you might display that in your UI: ## Spade "Matching" When we match a transaction to a real merchant identity in our database, we will return a variety of data on all parties involved. Typically, with a proper implementation you will see match rates of at least 95%. Even when we don't match on a merchant, we will still clean the merchant name, sort the data (e.g., move a phone number from the city to phone number field), and provide a category. ## Core Data Enrichment Our foundational layer of factual transaction including factual details like merchant name, logo, category, counterparty ID, and location – delivered in real time and designed for consistency across rails. ## Counterparties and Third Parties When we match on a transaction, we will separate the parties involved into **counterparties** and **third parties** objects: The entity you interact with directly when you make a purchase (in most cases a merchant). We'll return the following information: * counterparty ID * name * logo * website * location In our example transaction, the counterparty is Walmart. Any entity besides the counterparty involved in a transaction, e.g., a payment processor, delivery service, or BNPL provider. We'll return the following information: * third party ID * type * name * logo In our example transaction, the third party is Square, a payment processor. **Counterparty IDs** Our counterparty ID is a unified identifier for a brand -- for example, every Walmart location shares a single Spade counterparty ID (vs. >40,000 merchant IDs coming through the networks). Counterparty IDs should be used as your core identifier of a merchant, as they will stay consistent even through events like a rebrand. These IDs are critical for high value use cases such as card locking, fraud prevention, rewards attribution, and more. They can be accessed via the enrichment APIs on a transaction level. **Third Party IDs** Similarly, third party IDs are unified identifiers for a third party -- for example, every DoorDash transaction shares a single DoorDash counterparty ID. ## Location In addition to identifying the parties involved in a transaction, we will attempt to match on the exact geolocation of the counterparty involved. For physical transactions, this is the store where someone has made a purchase -- for online transactions, it is typically the merchant's headquarters. The location object includes a variety of information, including address, city, state, postal code, lat, long, and location phone number. It also has a location ID, a proprietary and unique Spade identifier for the exact location of the merchant. For example, the location ID for Walmart at 1590 Dunlawton Ave is different from the Walmart a few minutes away at 3811 Clyde Morris Blvd. Every transaction where we match on a location will include a location ID. Location data is also commonly used in UI/UX -- learn more about how to use location data in your UI/UX [here](/reference/improved-ux#location-data). ## Industries All transactions, regardless of whether or not they match on a merchant, will be categorized by Spade. **Counterparty industry** Spade has built a custom industry system leveraging our proprietary merchant database to more accurately categorize merchants. Our industry classification system is built as a tree, where tree nodes are sub-industries of their parents – the deeper into the tree you go, the more specific the industry is. Spade's industry system excels in areas of common MCC weakness, for example classifying risky transactions and categorizing software spend. To obtain a full list of industries, please reach out to [sales@spade.com](mailto:sales@spade.com) or hit the [the categories](/api-reference/category-personalization/get-all-default-and-custom-integration-level-categories) of the API. **Third party type** Third parties are also assigned a type in the third parties object. Possible types are buy-now-pay-later (e.g., Affirm), delivery services (e.g., DoorDash), payment processors (e.g., Square), and platforms (e.g., Venmo). Learn more about each type in our [glossary](/reference/concepts#third-parties). ## Display Information In addition to providing counterparty and third party information separately, each transaction includes a 'display' object that you can use in your UI to easily surface the information most recognizable to a user for you to plug into your UI. The display object consists of a combination of information from the counterparty and third party sections of the response. More information about the display object can be found [here](/reference/improved-ux#/how-to-use-the-display-object). # Premium Data Features Premium fields and predictive signals – like recurrence detection and fraud signals – that can be activated based on your use case. Enhanced data features are add-ons and can be purchased by contacting [sales@spade.com](mailto:sales@spade.com). ## Recurrence detection Our recurrence flag leverages transaction history and merchant information to identify recurring spend, from purchases at subscription merchants like Netflix to utility bill payments at Con Edison. When a recurring transaction is identified, we provide information including the recurrence interval (e.g., monthly vs. annual), the date of the next payment expected, and an array of recent recurrences. Learn more about about our recurrence detection [here](/reference/recurring-transaction-guide/). ## Risk insights (beta) We offers merchant and transaction level data features designed design to be leveraged on the transaction and customer level to fight fraud and reduce risk. Data fields include content based merchant classification, business age and location age, and more. Learn more about Risk insights [here](/reference/risk-insights). # Mobile app data App store purchases represent one of the most highly disputed transaction categories for many of our issuers. We now offer developer, and application level data for 100% of Google Play store apps through our enrichment API. Learn more about Mobile app data [here](/reference/mobile-app-data). # Universal data enrichment guide Source: https://docs.spade.com/reference/universal-data-enrichment ## Overview This guide outlines best practices for integrating with the universal data enrichment endpoint. This endpoint is designed to support transaction data acquired from an outside source, such as: * Aggregated transaction data * Consumer transaction panel data * Accounting software (Quickbooks, Xero, etc.) * Dispute data * Other external data sources Universal data enrichment is currently in **beta**; request and response fields may change as the product evolves. For access, please contact [sales@spade.com](mailto:sales@spade.com)  ## Best practices Below we've outlined how to send data to the universal data enrichment endpoint. Regardless of the type of data you're sending, providing raw, unaltered data yields the best results. ### Sending universal data Universal data enrichment relies primarily on two fields: `description` and `amount`. * For the `description` field, pass the transaction’s original description. * For the `amount` field, a negative value (-) indicates a credit or incoming transfer, while a positive value (+) indicates a debit or outgoing transfer. * Alternatively use the `direction` field to specify whether the transaction is a `credit` or `debit` Below we've provided examples of sources of data and best practices for how to map that data to Spade fields: | **data source** | **description** | **amount** | direction | | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | | **Plaid**
([docs](https://plaid.com/docs/api/products/transactions/#transactionsget)) | `original_description`

Note: when calling /transactions/sync set the flag `options.include_original_descritption`to `true`

Alternatively use the `name` field if `original_description` is not available | `amount` | optional: set direction to `credit` for negative values and `debit` for positive values | | **MX**
**(**[**docs**](https://docs.mx.com/api-reference/platform-api/reference/list-transactions)**)** | `original_description` | `amount ` | use the `type` field from MX to set `credit` or `debit` | | **Mastercard**
**Open Banking**
([docs](https://developer.mastercard.com/open-banking-us/documentation/api-reference/?view=api#GetCustomerAccountTransactions)) | `description` | `amount` | use the `type` field from Mastercard to set `credit` or `debit` | | **Yodlee**
**(**[**docs**](https://developer.yodlee.com/resources/yodlee/data-model/docs/transactions)**)** | `description.original` | `amount` | use the `baseType` field from Yodlee to set `credit` or `debit` | | **Akoya**
**(**[**docs**](https://docs.akoya.com/reference/transactions#supported-data-elements)**)** | `description` | `amount`

when`balanceType`
is `LIABILITY`set amount to (-) | | | **Quiltt**
**(**[**docs**](https://www.quiltt.dev/api/core-resources/transactions)**)** | `description` | `amount` | use the `entryType` from Quiltt field to set `credit` or `debit` | If your data source is not listed here, please work with your Spade team to help map your data to the required fields Additionally, we require the following fields: * `currencyCode` - the currency of the transaction, e.g., USD. * `userId` — associated with a user or organization; used for recurrence and [category personalization](/reference/category-personalization-guide). * `occurredAt` — the timestamp of the transaction in date-time format. * `transactionId` — required for batch enrichment and recommended for all integrations to associate with your internal transaction identifier. ### Batch vs real-time We support enriching individual or multiple transactions via API: * For real-time enrichment, use the `/transactions/universal/enrich` endpoint. * For non-real-time enrichment, use the `/batches/transactions/universal/enrich` endpoint. This endpoint supports up to 50,000 transactions per async batch or up to **100** transactions per microbatch (`?synchronous=true`). See the [Microbatch enrichment guide](/reference/microbatch-enrichment-guide) for details. For best practices on sending transactions in batches, see our [batch enrichment guide](/reference/batch-enrichment-guide). ### Example request and response Below we've provided an example request to the `/transactions/universal/enrich` endpoint: ```json theme={null} { "description": "SQ* BLACK SEED BAGELS BROOKLYN NY", "amount": "14.25", "currencyCode": "USD", "occurredAt": "2025-09-15", "userId": "u-1234", "transactionId": "48FA5E16-6870-42D2-AECE-AF393714EB1C" } ``` And a successful enrichment response, identifying the counterparty “Black Seed Bagels” as well as providing detailed location information for where the transaction occurred. ```json expandable theme={null} { "transactionInfo": { "type": "spending", "subType": null, "display": { "name": "Black Seed Bagels", "categoryName": "Bakeries", "graphic": "https://static.v2.spadeapi.com/logos/f6c2a3614b92313cb06e9a19e8a7cfbd/light.png", "graphicSource": "counterparty" }, "thirdParties": [], "spendingInfo": { "channel": { "value": "digital" } }, "transferInfo": null, "atmInfo": null, "isAccountVerification": false, "isPeerToPeer": false, "isDigitalWallet": false, "transactionId": "166c5ad8-8a94-4964-a659-03cdb64525f2", "recurrenceInfo": null }, "counterparty": [ { "id": "f6c2a361-4b92-313c-b06e-9a19e8a7cfbd", "name": "Black Seed Bagels", "legalName": null, "industry": [ { "id": "005-000-000-000", "name": "Food and Drink", "icon": "https://static.v2.spadeapi.com/categories/18785677534e4e3a8ec37e4dc7dc34d9/light.png" }, { "id": "005-001-000-000", "name": "Bakeries", "icon": "https://static.v2.spadeapi.com/categories/18785677534e4e3a8ec37e4dc7dc34d9/light.png" } ], "location": [ { "id": "4f41c8c6-2d57-3dd4-9c48-364456d8500c", "address": "214 Berry St", "addressLine1": "214 Berry St", "addressLine2": null, "city": "Brooklyn", "region": "NY", "postalCode": "11249", "country": "USA", "phoneNumber": "+16468762257", "latitude": 40.716225, "longitude": -73.961818, "matchScore": null } ], "matchScore": null, "logo": "https://static.v2.spadeapi.com/logos/f6c2a3614b92313cb06e9a19e8a7cfbd/light.png", "medianSpendPerTransaction": null, "phoneNumber": "+13329006050", "website": "http://blackseedbagels.com" } ], "enrichmentId": "1dcee2b3-2c16-40e3-bdaf-5dd2f248a692", "mobileAppInfo": null } ``` ### Error handling The most common errors result from missing required fields. In a `400` response, we return specific details needed to reprocess the request. ```json theme={null} { "currencyCode": [ "This field is required." ] } ``` ## Understanding enrichment results When enriching transaction data through this endpoint, we always return a cleansed counterparty name and if matched to a verified merchant in our database, we will return a `counterparty.id` along with details about the merchant. Because this endpoint supports all transaction types, the fields that are populated will vary by the type of transaction you are enriching. For example, if the transaction is an ACH payment the `transferInfo` object will be populated. For more information on For more information on interpreting enrichment responses, see our guide on [understanding enriched data](/reference/understanding-enriched-data). # User category personalization guide Source: https://docs.spade.com/reference/user-category-personalization-guide *** This guide walks you through creating custom categories and recategorizing transactions at the user-level. for information on creating integration-level personalizations, please see the [integration category personalization guide](/reference/integration-category-personalization-guide). ## Integration note For simplicity, the examples in this guide shows you how to send requests to Spade directly. However, you will likely want to build user category personalization into client applications. Because client-side code is public and your API key is secret, you will need to proxy user category personalization requests through your backend to Spade's servers. The [merchant search guide](/reference/merchant-search-guide) demonstrates how to proxy requests through your backend to Spade's API. ***NEVER send your API key to the client. This includes any client-side application code bundles (websites, mobile apps, etc...).*** ## Fetch the default categories for your integration The first thing you’ll likely want to do is fetch the default categories for your integration. This list contains all of the categories that Spade provides, plus any custom categories that you’ve added on an integration-wide level. ```bash bash theme={null} import requests response = requests.get("https://east.sandbox.spade.com/categories", headers={"X-Api-Key": ""}) print(response.json()) ``` Sending the above request returns a list containing all of Spade's 250+ categories, plus any custom categories that you’ve added on an integration-wide level. ```bash bash theme={null} [ // Spade's default categories will show up first { "id": "001-000-000-000", "name": "Banking and Finance", "icon": "https://static.v2.spadeapi.com/categories/61897b3a5af545bab45ca1bf20498e65/light.png", "fullCategoryHierarchy": [ { "id": "001-000-000-000", "name": "Banking and Finance", "icon": "https://static.v2.spadeapi.com/categories/61897b3a5af545bab45ca1bf20498e65/light.png" } ] }, { "id": "001-001-000-000", "name": "Accounting and Bookkeeping", "icon": "https://static.v2.spadeapi.com/categories/61897b3a5af545bab45ca1bf20498e65/light.png", "fullCategoryHierarchy": [ { "id": "001-000-000-000", "name": "Banking and Finance", "icon": "https://static.v2.spadeapi.com/categories/61897b3a5af545bab45ca1bf20498e65/light.png" }, { "id": "001-001-000-000", "name": "Accounting and Bookkeeping", "icon": "https://static.v2.spadeapi.com/categories/61897b3a5af545bab45ca1bf20498e65/light.png" } ] }, ... // Your integration's custom categories will show up next { "id": "7235be6a-669c-47f2-9ab6-cac3f3edbab9", "name": "Family", "icon": null, "fullCategoryHierarchy": [ { "id": "7235be6a-669c-47f2-9ab6-cac3f3edbab9", "name": "Family", "icon": null } ] }, { "id": "ba8d4e26-052a-4260-bcf4-6523469fcf48", "name": "Day Care", "icon": null, "fullCategoryHierarchy": [ { "id": "7235be6a-669c-47f2-9ab6-cac3f3edbab9", "name": "Family", "icon": null }, { "id": "ba8d4e26-052a-4260-bcf4-6523469fcf48", "name": "Day Care", "icon": null } ] } ] ``` Note that Spade’s categories are hierarchical, so `Accounting and Bookkeeping`, for instance, sits underneath the `Banking and Finance` category, which sits at the root level. For convenience, each serialized category includes a `fullCategoryHierarchy` object, which contains the full hierarchy of the category. ## Creating a custom user category As with default categories and custom integration-wide categories, custom user categories are hierarchical. They can sit at the root level, under Spade's categories, under custom integration-level categories, or under other custom user-level categories. For instance, here’s how you can create a custom user category that sits at the root level: ```bash bash theme={null} import requests custom_category = { "name": "Kitchen Remodel 2024", "parentId": "" } response = requests.post("https://east.sandbox.spade.com/users/1234/categories", json=custom_category, headers={"X-Api-Key": ""}) print(response.json()) ``` Sending the above request creates a new custom user category and returns a copy of the serialized category: ```bash bash theme={null} { "id": "9fb31902-1f80-47a9-b717-cc3b1db0ad1f", "name": "Kitchen Remodel 2024", "icon": null, "fullCategoryHierarchy": [ { "id": "9fb31902-1f80-47a9-b717-cc3b1db0ad1f", "name": "Kitchen Remodel 2024", "icon": null } ] } ``` Here’s how you can create a custom user category that sits under this category: ```bash bash theme={null} import requests custom_category = { "name": "Hardware", "parentId": "9fb31902-1f80-47a9-b717-cc3b1db0ad1f" # Replace with the ID of the category you just created } response = requests.post("https://east.sandbox.spade.com/users/1234/categories", json=custom_category, headers={"X-Api-Key": ""}) print(response.json()) ``` Sending the above request creates the new custom user category and returns a copy of the serialized category: ```bash bash theme={null} { "id": "6bf9b0cd-20b5-4024-9dc2-446af3bda72c", "name": "Hardware", "icon": null, "fullCategoryHierarchy": [ { "id": "9fb31902-1f80-47a9-b717-cc3b1db0ad1f", "name": "Kitchen Remodel 2024", "icon": null }, { "id": "6bf9b0cd-20b5-4024-9dc2-446af3bda72c", "name": "Hardware", "icon": null } ] } ``` ## Fetching custom user categories In order to support curated user experiences, you’ll likely want to fetch custom user categories whenever you fetch the default categories for your integration. Here’s how you can fetch custom user categories: ```bash bash theme={null} response = requests.get("https://east.sandbox.spade.com/users/1234/categories", headers={"X-Api-Key": ""}) print(response.json()) ``` Sending the above request returns: ```bash bash theme={null} [ { "id": "9fb31902-1f80-47a9-b717-cc3b1db0ad1f", "name": "Kitchen Remodel 2024", "icon": null, "fullCategoryHierarchy": [ { "id": "9fb31902-1f80-47a9-b717-cc3b1db0ad1f", "name": "Kitchen Remodel 2024", "icon": null } ] }, { "id": "6bf9b0cd-20b5-4024-9dc2-446af3bda72c", "name": "Hardware", "icon": null, "fullCategoryHierarchy": [ { "id": "9fb31902-1f80-47a9-b717-cc3b1db0ad1f", "name": "Kitchen Remodel 2024", "icon": null }, { "id": "6bf9b0cd-20b5-4024-9dc2-446af3bda72c", "name": "Hardware", "icon": null } ] } ] ``` Now that you have both the default and custom categories, you can combine them to create a list containing all of the categories that the user has access to: ```bash bash theme={null} all_categories = [*categories, *user_categories] ``` If you’re building a UX such as a transaction feed, you can then feed `all_categories` to the frontend and display them as a tree, a searchable input, etc… Category search ## Recategorizing a specific transaction for a specific user To recategorize a specific transaction, you’ll want to update that transaction record in your internal system. For instance: ```bash bash theme={null} import requests # Update each transaction's category in your internal system. # Note that the PATCH body will depend on your internal data schema. requests.patch( "https://your.backend.com/enriched-transactions/1234", { "category": [ { "id": "9fb31902-1f80-47a9-b717-cc3b1db0ad1f", "name": "Kitchen Remodel 2024", "icon": null }, { "id": "6bf9b0cd-20b5-4024-9dc2-446af3bda72c", "name": "Hardware", "icon": null } ] }, headers=YOUR_AUTH_HEADERS, ) ``` ## Recategorizing all future transactions for a specific counterparty One of the most powerful features Spade provides is the ability for users to recategorize future transactions before they take place. This feature takes advantage of Spade’s ability to consistently match transactions to stable counterparty IDs using ground truth data. Here’s how a user can recategorize future transactions for a specific counterparty: ```bash bash theme={null} counterparty_category_personalization = { # Lowe's "counterpartyId": "83aa2f0a-bd1e-4613-807d-6b6f4bb39b4c", # Hardware "categoryId": "6bf9b0cd-20b5-4024-9dc2-446af3bda72c" } requests.put("https://east.sandbox.spade.com/users/1234/counterparty-category-personalizations", json=counterparty_category_personalization, headers={"X-Api-Key": ""}) ``` With this personalization in place, any future transactions that take place at `Lowe's` for this specific user will be enriched with the custom category, which will appear in the `personalization` section of the enrichment as the counterparty's `industry`: ```bash bash theme={null} { "transactionInfo": { "type": "spending", "thirdParties": [], "spendingInfo": { "channel": { "value": "physical" } }, "transactionId": "6d315cc3-3c9e-47be-9711-d84b4b1c21c9", "irregularWebPresenceDetected": false, "recurrenceInfo": null }, "counterparty": [ { "id": "83aa2f0a-bd1e-4613-807d-6b6f4bb39b4c", "name": "Lowe's", "legalName": "Lowe's Companies, Inc.", // Spade still returns the original industry here so you can use it for internal analysis "industry": [ { "id": "011-000-000-000", "name": "Retail", "icon": "https://static.v2.spadeapi.com/categories/ee4ee39fd5474d31ac42f9e606b9040a/light.png" }, { "id": "011-007-000-000", "name": "Hardware and Home Improvement", "icon": "https://static.v2.spadeapi.com/categories/ee4ee39fd5474d31ac42f9e606b9040a/light.png" } ], "location": [ { "id": "f39d9c63-3be5-390e-b349-ee993fda44b1", "address": "1801 Fordham Blvd", "addressLine1": "1801 Fordham Blvd", "addressLine2": null, "city": "Chapel Hill", "region": "NC", "postalCode": "27514", "country": "USA", "phoneNumber": "+19199673289", "latitude": 35.94765, "longitude": -79.013007, "matchScore": 91.67 } ], "matchScore": 88.79, "logo": "https://static.v2.spadeapi.com/logos/83aa2f0abd1e4613807d6b6f4bb39b4c/light.png", "medianSpendPerTransaction": null, "phoneNumber": null, "website": "lowes.com" } ], "personalization": { "counterparty": [ { "id": "83aa2f0a-bd1e-4613-807d-6b6f4bb39b4c", // Here's the personalized merchant category "industry": [ { "id": "9fb31902-1f80-47a9-b717-cc3b1db0ad1f", "name": "Kitchen Remodel 2024", "icon": null }, { "id": "6bf9b0cd-20b5-4024-9dc2-446af3bda72c", "name": "Hardware", "icon": null } ] } ] }, "enrichmentId": "c4ca89ce-4fe0-4ed0-93e0-09dc4d74cba5" } ``` ## Deleting custom user-level categories You can delete custom user-level categories all at once or individually. To delete all custom user-level categories, send a `DELETE` request to the `/users/:userId/categories` endpoint: ```bash bash theme={null} import requests requests.delete("https://east.sandbox.spade.com/users/1234/categories", headers={"X-Api-Key": ""}) ``` To delete a specific custom user-level category, send a `DELETE` request to the `/users/:userId/categories/:id` endpoint with the category's ID: ```bash bash theme={null} import requests requests.delete("https://east.sandbox.spade.com/users/1234/categories/77f376cf-ec09-47a5-b90b-7406bb018dc9", headers={"X-Api-Key": ""}) ``` ## Deleting user-level counterparty category personalizations You can delete user-level counterparty category personalizations all at once or individually. To delete all user-level counterparty category personalizations, send a `DELETE` request to the `/users/:userId/counterparty-category-personalizations` endpoint: ```bash bash theme={null} import requests requests.delete("https://east.sandbox.spade.com/users/1234/counterparty-category-personalizations", headers={"X-Api-Key": ""}) ``` To delete a specific user-level counterparty category personalization, send a `DELETE` request to the `/users/:userId/counterparty-category-personalizations/:counterpartyId` endpoint with the `counterpartyId` of the personalization you want to delete: ```bash bash theme={null} import requests requests.delete("https://east.sandbox.spade.com/users/1234/counterparty-category-personalizations/4ad9e96c-b8a4-4421-88fe-112ac23e37cc", headers={"X-Api-Key": ""}) ``` ## Conclusion You've just learned how Spade's category personalization endpoints can be used to create custom categories at the user-level and recategorize transactions onto those categories.