{
  "openapi": "3.1.1",
  "info": {
    "title": "Rapport Contact Deduplicator API",
    "version": "1.0.0",
    "summary": "Find duplicate contacts in a CSV (same e-mail, phone or similar name) and merge them.",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://rapportcsvdedupe.usefulweb.app"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Find duplicate contacts in a CSV (same e-mail, phone or similar name) and merge them.",
        "description": "Checks a contacts CSV (Rapport, HubSpot, Pipedrive or any file with name, e-mail, phone and company columns) for duplicates: the same e-mail even with different case, Gmail dots or +tags, the same phone number written differently, and very similar names at the same company. Shows every group with its reasons and gives you a merged CSV. Same rules as Rapport’s duplicate finder.\n\nMethodology:\n- Same e-mail: compared in lower case; for every provider a +tag is ignored (anna+news@ = anna@), for Gmail also dots (an.na@gmail.com = anna@gmail.com).\n- Same phone: only digits count and the last nine are compared, so +49 40 2345678 and 040 2345678 match. Numbers with fewer than seven digits are ignored.\n- Similar name: Jaro–Winkler similarity of the folded full names (accents, case and punctuation removed) of at least 0.92, AND the same company (legal forms like GmbH, Inc. or B.V. ignored) or the same non-free-mail e-mail domain. Identical names without any company or e-mail on one side are flagged as weaker matches.\n- Pairs are joined into groups. In the merged CSV the first row of each group is kept; its empty fields are filled from the others, tags are combined, notes appended and the newest documented lawful basis/consent wins.\n\nCheck each group before you delete anything in your CRM: two real people can share a phone number or a name.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "rapport_csv_dedupe",
        "x-owner-app": "rapport",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RapportCsvDedupeInput"
              },
              "examples": {
                "example1": {
                  "summary": "HubSpot contacts export with three duplicate pairs",
                  "value": {
                    "csv": "Record ID,First Name,Last Name,Email,Phone Number,Job Title,Company Name,Contact owner,Create Date,Last Activity Date,Marketing contact status,Legal basis for processing contact's data\r\n101,Lena,Vogt,lena.vogt@mediq.example,+49 89 7654321,Marketing Director,Mediq Health GmbH,Mara Lindqvist,2026-08-12 09:14,2026-11-20 15:02,Marketing contact,Freely given consent from contact\r\n102,Lena,Vogt,Lena.Vogt@mediq.example,,Marketing,Mediq Health,Jonas Becker,2026-11-03 11:40,,Non-marketing contact,\r\n103,Paul,Hansen,paul@hansen-kaffee.example,040 2345678,Owner,Kaffeerösterei Hansen,Jonas Becker,2025-12-01 10:00,2026-11-10 09:30,Non-marketing contact,Performance of a contract\r\n104,Paul,Hansen,,+49 40 2345678,Inhaber,Kaffeeroesterei Hansen,Jonas Becker,2026-02-14 16:20,,Non-marketing contact,\r\n105,Daan,de Vries,daan.devries@greenway.example,+31 10 555 0101,CIO,Greenway Logistics B.V.,Mara Lindqvist,2026-09-15 13:00,2026-11-18 10:15,Non-marketing contact,Legitimate interest – prospect/lead\r\n106,Sanne,Bakker,sanne.bakker@greenway.example,+31 10 555 0144,E-commerce Manager,Greenway Logistics B.V.,Ada Okafor,2026-09-20 08:45,2026-11-18 10:15,Marketing contact,Freely given consent from contact\r\n107,Sanne,Baker,s.bakker+events@greenway.example,,E-commerce,Greenway Logistics,Ada Okafor,2026-10-30 18:02,,Non-marketing contact,\r\n108,Ingrid,Halvorsen,ingrid.halvorsen@fjordline.example,+47 55 12 34 56,Head of Digital,Fjordline Ferries AS,Mara Lindqvist,2026-04-19 09:00,2026-11-21 14:00,Non-marketing contact,Legitimate interest – existing customer\r\n109,Yusuf,Demir,yusuf.demir@gmail.example,,Founder,,Ada Okafor,2026-11-07 20:11,,Marketing contact,Freely given consent from contact\r\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RapportCsvDedupeResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tool": {
      "get": {
        "operationId": "getToolDefinition",
        "summary": "Inputs, output schema and examples for this tool",
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "RapportCsvDedupeInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "csv": {
            "title": "Contacts CSV",
            "description": "With a header row. Names, e-mail, phone and company are used; other columns are kept in the merged file when Rapport knows them.",
            "type": "string",
            "maxLength": 5000000,
            "default": ""
          },
          "csvFile": {
            "title": "CSV file (optional)",
            "description": "Used instead of the pasted text when given. Upload as {filename, contentType?, contentBase64}. Max 9766 KiB. Accepts .csv,text/csv.",
            "type": "object",
            "additionalProperties": false,
            "required": [
              "filename",
              "contentBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              },
              "contentBase64": {
                "type": "string"
              }
            }
          }
        },
        "required": []
      },
      "RapportCsvDedupeResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "rows",
              "groups",
              "duplicates"
            ],
            "properties": {
              "rows": {
                "type": "integer"
              },
              "duplicates": {
                "type": "integer"
              },
              "groups": {
                "type": "array"
              },
              "mapping": {
                "type": "object"
              },
              "warnings": {
                "type": "array"
              },
              "mergedArtifact": {
                "type": "string"
              },
              "reportArtifact": {
                "type": "string"
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}