{
  "openapi": "3.1.1",
  "info": {
    "title": "Rapport vCard Converter API",
    "version": "1.0.0",
    "summary": "Convert vCards (.vcf) to a contacts CSV and back: vCard 2.1, 3.0 and 4.0.",
    "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://rapportvcardconvert.usefulweb.app"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Convert vCards (.vcf) to a contacts CSV and back: vCard 2.1, 3.0 and 4.0.",
        "description": "Turns a .vcf file with one or many contact cards into a clean CSV with first name, last name, e-mail, phone, company, job title, address and tags — or a contacts CSV (Rapport, HubSpot, Pipedrive, Outlook-style headers) into vCards. Folded lines, escaped commas and old QUOTED-PRINTABLE phone exports are handled. Same converter as Rapport’s vCard import and export.\n\nMethodology:\n- vCards are unfolded first (a line break followed by a space or tab continues the line), then split into properties; group prefixes (item1.EMAIL) are ignored and TYPE parameters choose the preferred e-mail, phone and address (PREF first, then work).\n- N gives last and first name; when it is missing, FN is split at the last space. ORG gives the company, ADR is joined into one address line (street, postal code + city, region, country). Values are unescaped (\\n, \\, and \\;). vCard 2.1 QUOTED-PRINTABLE values are decoded as UTF-8 (or Windows-1252 when the card says so).\n- CSV columns are recognised by their header names: Rapport’s own export, HubSpot and Pipedrive exports and common English and German names (Vorname, Nachname, Firma …). The CSV is RFC 4180 with comma, semicolon or tab.\n- vCards are written with CRLF line ends and folded at 75 octets without splitting a UTF-8 character; text is escaped. vCard 4.0 phones are written as tel: URIs.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "rapport_vcard_convert",
        "x-owner-app": "rapport",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RapportVcardConvertInput"
              },
              "examples": {
                "example1": {
                  "summary": "Three vCards (4.0, 3.0 with a folded note, 2.1 quoted-printable) to CSV",
                  "value": {
                    "text": "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:Camille Rousseau\r\nN:Rousseau;Camille;;;\r\nORG:Atelier Rousseau SARL\r\nTITLE:Creative Director\r\nEMAIL;TYPE=work:camille@rousseau.example\r\nTEL;VALUE=uri;TYPE=work:tel:+33472000111\r\nADR;TYPE=work:;;12 rue de la République;Lyon;;69002;FR\r\nCATEGORIES:fashion,lead\r\nEND:VCARD\r\nBEGIN:VCARD\r\nVERSION:3.0\r\nFN:Tobias Krüger\r\nN:Krüger;Tobias;;;\r\nORG:Mediq Pharma Services\r\nTITLE:Communications Lead\r\nEMAIL;TYPE=INTERNET,WORK:tobias.krueger@mediq-pharma.example\r\nTEL;TYPE=CELL:+49 170 1234567\r\nNOTE:Met at the health-tech fair\\, interested in an intranet. Prefers calls in the\r\n morning.\r\nEND:VCARD\r\nBEGIN:VCARD\r\nVERSION:2.1\r\nN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:M=C3=BCller;J=C3=BCrgen;;;\r\nFN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:J=C3=BCrgen M=C3=BCller\r\nTEL;WORK;VOICE:+49 30 1234567\r\nEMAIL;INTERNET:juergen.mueller@example.org\r\nEND:VCARD\r\n"
                  }
                },
                "example2": {
                  "summary": "HubSpot contacts export to vCard 3.0",
                  "value": {
                    "text": "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",
                    "version": "3.0"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RapportVcardConvertResult"
                }
              }
            }
          },
          "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"
                }
              }
            }
          }
        }
      },
      "RapportVcardConvertInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "text": {
            "title": "vCards or contacts CSV",
            "description": "Paste vCards (BEGIN:VCARD … END:VCARD) to get a CSV, or a CSV with a header row to get vCards.",
            "type": "string",
            "maxLength": 5000000,
            "default": ""
          },
          "file": {
            "title": "File (optional)",
            "description": "Used instead of the pasted text when given. Upload as {filename, contentType?, contentBase64}. Max 9766 KiB. Accepts .vcf,.vcard,.csv,text/vcard,text/csv.",
            "type": "object",
            "additionalProperties": false,
            "required": [
              "filename",
              "contentBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              },
              "contentBase64": {
                "type": "string"
              }
            }
          },
          "version": {
            "title": "vCard version to write",
            "type": "string",
            "enum": [
              "4.0",
              "3.0"
            ],
            "default": "4.0"
          }
        },
        "required": []
      },
      "RapportVcardConvertResult": {
        "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": [
              "direction",
              "count"
            ],
            "properties": {
              "direction": {
                "type": "string"
              },
              "count": {
                "type": "integer"
              },
              "versions": {
                "type": "array"
              },
              "contacts": {
                "type": "array"
              },
              "warnings": {
                "type": "array"
              },
              "csvArtifact": {
                "type": "string"
              },
              "vcfArtifact": {
                "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"
              }
            }
          }
        }
      }
    }
  }
}