> ## Documentation Index
> Fetch the complete documentation index at: https://docs.spade.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 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.

<Warning>
  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.
</Warning>

## 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 merchant name or identity is incorrect. Use `incorrectLogo`, `incorrectWebsite`, or `incorrectPhoneNumber` instead for issues limited to those specific attributes |
| **incorrectCounterpartyDescription** | optional | string | Details about the suspected merchant name or identity 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) |
| **incorrectLogo** | optional | boolean | Set to `true` if you believe the merchant logo may be inaccurate |
| **incorrectLogoDescription** | optional | string | Details about the suspected logo issue (max 1024 characters) |
| **incorrectWebsite** | optional | boolean | Set to `true` if you believe the merchant website may be inaccurate |
| **incorrectWebsiteDescription** | optional | string | Details about the suspected website issue (max 1024 characters) |
| **incorrectPhoneNumber** | optional | boolean | Set to `true` if you believe the merchant phone number may be inaccurate |
| **incorrectPhoneNumberDescription** | optional | string | Details about the suspected phone number issue (max 1024 characters) |

<Tip>
  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.
</Tip>

### Example request

<CodeGroup>
  ```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": "<Your API Key Here>"},
  )

  print(response.json())
  ```
</CodeGroup>

### Example response

A successful report returns an HTTP 200 response:

```json theme={null}
{
  "errorReportId": "8e21c6a4-2f3d-4b7a-9c1e-5d6f0a9b7c3e",
  "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 the outcome

Once our team reaches a decision on your report, we automatically send the outcome to the callback URL you configured with your Spade representative — you don't need to poll or wait for a manual response.

### Callback format

The callback is a POST request to your callback URL. Its body is a normal [card enrichment response](https://docs.spade.com/api-reference/card-enrichment/enrich-a-card-transaction) — see that page for the full field reference (`enrichmentId`, `transactionInfo`, `counterparty`, etc.) — with the fields below merged in at the top level to describe the report's outcome. The same enrichment fields are also nested under `correctedData`, for integrations that prefer reading them from there instead of the top level. The request includes an `X-Webhook-Token` header that you should validate against the callback token provided by your Spade representative.

| Field | Type | Description |
| :- | :- | :- |
| **errorReportId** | string | The ID of the report this callback is about (the same `errorReportId` returned when you submitted the report) |
| **status** | string | Either `accepted` or `rejected` |
| **reason** | string \| null | Set when `status` is `rejected`; `null` when accepted. See below for the current set of codes — this list may grow over time |
| **correctedData** | object | The full [card enrichment response](https://docs.spade.com/api-reference/card-enrichment/enrich-a-card-transaction) reflecting the outcome — identical to the fields at the top level of this body: the corrected data when `status` is `accepted`, or the original unchanged enrichment when `status` is `rejected` (a rejection means the original enrichment was verified correct). Never `null`. |

<Note>
  `reason` is usually one of a fixed set of internal codes (e.g. `counterparty_correct_spade_verified`, `location_correct_spade_verified`, `logo_correct_spade_verified`, `category_correct_indicated_by_mcc`) describing why a report was rejected. When no fixed code fits, it instead carries the reviewer's free-text explanation in place of the literal code `other`. Treat the fixed codes as a growing enum rather than an exhaustive list.
</Note>

### 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.

<Warning>
  Always validate the `X-Webhook-Token` before processing any callback data. Reject requests with missing or mismatched tokens.
</Warning>

### Example callback handler

<CodeGroup>
  ```bash shell theme={null}
  # Example of what an accepted-report callback from Spade looks like:
  # POST https://your-domain.com/spade/corrections
  # Headers:
  #   Content-Type: application/json
  #   X-Webhook-Token: your-callback-token-from-spade

  # 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",
    "errorReportId": "8e21c6a4-2f3d-4b7a-9c1e-5d6f0a9b7c3e",
    "status": "accepted",
    "reason": null,
    "correctedData": {
      "transactionInfo": {
        "type": "spending",
        "display": {
          "name": "Walmart Neighborhood Market",
          "categoryName": "Grocery Stores"
        }
      },
      "enrichmentId": "050d3a73-c212-4f89-b9c0-0e75da0efeea"
    }
  }'
  ```

  ```json Rejected example theme={null}
  {
    "transactionInfo": {
      "type": "spending",
      "display": {
        "name": "Walmart Neighborhood Market",
        "categoryName": "Grocery Stores"
      }
    },
    "enrichmentId": "050d3a73-c212-4f89-b9c0-0e75da0efeea",
    "errorReportId": "8e21c6a4-2f3d-4b7a-9c1e-5d6f0a9b7c3e",
    "status": "rejected",
    "reason": "location_correct_spade_verified",
    "correctedData": {
      "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_report_outcome():
      # 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

      outcome = request.json
      report_id = outcome["errorReportId"]

      # correctedData is always present: the corrected enrichment when accepted, or the
      # original unchanged enrichment when rejected.
      update_enrichment_in_database(report_id, outcome["correctedData"])

      if outcome["status"] == "rejected":
          # Log or surface the rejection reason to your team
          log_rejected_report(report_id, outcome["reason"])

      return jsonify({"status": "received"}), 200

  def update_enrichment_in_database(report_id, corrected_data):
      # Save the changes to your systems as necessary
      print(f"Saving corrected enrichment for report {report_id}: {corrected_data}")

  def log_rejected_report(report_id, reason):
      print(f"Report {report_id} rejected: {reason}")
  ```
</CodeGroup>

<Info>
  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.
</Info>

## 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 either accepts it with a correction, or rejects it
4. Once reviewed, Spade automatically sends the outcome to your callback URL with the `X-Webhook-Token` header
5. Your server validates the token, updates its records with `correctedData`, and handles the rejection reason when `status` is `rejected`

## 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 |

<Info>
  For questions about error reporting or to set up your callback URL, contact [support@spade.com](mailto:support@spade.com).
</Info>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.