Koskamo

Plain Insurance MCP

An MCP server for Plain Insurance that allows AI assistants to discover business insurance products, collect company details like CVR, and retrieve live indicative quotes for Danish companies without requiring an account or API key for the quoting phase.

Endpoint and access

No sign-in to connect and list tools. Individual tools may still need a vendor account, credentials, or permissions; check the documented prerequisites.

Plain Insurance MCP logo, by PlainVendor evidenceStreamable HTTPNo sign-inEndpoint checked 2026-11-07Operated by Plain
You need:Plain sign-in, consent and the relevant permissions
MCP endpoint (Streamable HTTP)
https://app.plain.insurance/api/mcp
Vendor documentation

What its tools do

8 tools, grouped by the hints the server advertises. Hints are self-declared, not verified behavior.

8 of 8 have a description · 8 of 8 declare behavior hints · 5 of 6 with parameters document them all

Checks and signals

Each line is separate evidence, not a trust score.

  • Passed Endpoint answered the live check ·
  • Passed Official MCP Registry namespace is tied to plain.insurance ·
  • Passed Endpoint is on the registry namespace domain ·
  • Availability 1 of 1 checks passed in 30 days

What you can do with it

  • Discover suitable business insurance products for a Danish company
  • Collect required company answers and product questions
  • Retrieve live indicative prices and quotes
  • Prepare unsigned offers for customer review

Source and maintenance

Vendor evidenceRegistry 1.0.0

Protocol and capabilities

Server
plain 1.0.0
MCP protocol negotiated in check
2025-11-25
Tools
8 advertised
Resources
Not advertised
Resource templates
Not advertised
Prompts
Not advertised
Server-advertised capabilities
{
  "tools": {
    "listChanged": true
  }
}

Tools

8 advertised

list_onboarding_catalog — List products and questions
Advertised by the server during the last successful anonymous check.
Advertised read-only

Lists the insurance products a Danish company can be offered and the questions to answer before quoting. Pass the company's CVR to resolve industry-specific questions and options. Use product slugs in `products` and question names as keys in `answers`. Employee FTE questions are listed as two whole-number questions, `<name>.fullTime` and `<name>.partTime`, each from 0 to 9999 inclusive; when required, send both counts, including zero. Numeric questions publish min/max limits when configured. Products with automaticQuote:false need manual review and list no questions. A product's requiredProducts must be selected with it; its questions include theirs. MultiSelect answers may be left out and default to an empty list. Enum fields use {value, label: {da, en}}; use value for logic and label for display. Read-only: nothing is created. Works without signing in. For an existing customer, call list_companies first and select the company with set_company_context. Use its CVR when collecting questions and quoting.

list_onboarding_catalog input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {
    "cvr": {
      "type": "string",
      "pattern": "^[0-9]{8}$",
      "description": "Danish company registration number (CVR), eight digits, e.g. 39488914."
    },
    "region": {
      "type": "string",
      "const": "dk",
      "default": "dk",
      "description": "Market. Only Denmark (dk) is supported. Optional; defaults to dk."
    }
  },
  "additionalProperties": false
}
list_onboarding_catalog output schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "products",
    "productDependencies",
    "errors"
  ],
  "properties": {
    "errors": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "code",
          "message",
          "nextAction"
        ],
        "properties": {
          "max": {
            "anyOf": [
              {
                "type": "string",
                "format": "date",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"
              },
              {
                "type": "number"
              }
            ],
            "description": "Inclusive maximum: the latest start date (YYYY-MM-DD) for invalid_start_date, or the numeric upper bound for invalid_answer."
          },
          "min": {
            "anyOf": [
              {
                "type": "string",
                "format": "date",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"
              },
              {
                "type": "number"
              }
            ],
            "description": "Inclusive minimum: the earliest start date (YYYY-MM-DD) for invalid_start_date, or the numeric lower bound for invalid_answer."
          },
          "code": {
            "type": "object",
            "required": [
              "value",
              "label"
            ],
            "properties": {
              "label": {
                "type": "object",
                "required": [
                  "da",
                  "en"
                ],
                "properties": {
                  "da": {
                    "type": "string",
                    "minLength": 1,
                    "description": "Danish label."
                  },
                  "en": {
                    "type": "string",
                    "minLength": 1,
                    "description": "English label."
                  }
                },
                "description": "Human-readable labels by language.",
                "additionalProperties": false
              },
              "value": {
                "enum": [
                  "invalid_answer",
                  "missing_answer",
                  "ambiguous_question",
                  "invalid_start_date",
                  "cvr_not_found",
                  "invalid_products",
                  "missing_product",
                  "invalid_request",
                  "request_too_large",
                  "unsupported_media_type",
                  "wrong_identity_mode",
                  "idempotency_key_mismatch",
                  "idempotency_conflict",
                  "price_changed",
                  "invalid_partner_code",
                  "invalid_verification",
                  "sign_in_required",
                  "onboarding_expired",
                  "not_authenticated",
                  "not_authorized",
                  "consent_required",
                  "temporarily_unavailable",
                  "invalid_backend_response",
                  "rate_limited",
                  "operation_in_progress",
                  "price_unavailable",
                  "manual_review_required",
                  "pricing_configuration",
                  "unsupported_input",
                  "unexpected_error",
                  "not_found"
                ],
                "type": "string",
                "description": "Stable machine-readable value.",
                "x-enum-labels": {
                  "not_found": {
                    "da": "Ikke fundet",
                    "en": "Not found"
                  },
                  "rate_limited": {
                    "da": "For mange forespørgsler",
                    "en": "Too many requests"
                  },
                  "cvr_not_found": {
                    "da": "CVR-nummer ikke fundet",
                    "en": "CVR number not found"
                  },
                  "price_changed": {
                    "da": "Prisen er ændret",
                    "en": "Price changed"
                  },
                  "invalid_answer": {
                    "da": "Ugyldigt svar",
                    "en": "Invalid answer"
                  },
                  "missing_answer": {
                    "da": "Svar mangler",
                    "en": "Missing answer"
                  },
                  "not_authorized": {
                    "da": "Ingen adgang",
                    "en": "Not authorized"
                  },
                  "invalid_request": {
                    "da": "Ugyldig forespørgsel",
                    "en": "Invalid request"
                  },
                  "missing_product": {
                    "da": "Påkrævet produkt mangler",
                    "en": "Required product missing"
                  },
                  "consent_required": {
                    "da": "Samtykke påkrævet",
                    "en": "Consent required"
                  },
                  "invalid_products": {
                    "da": "Ugyldige produkter",
                    "en": "Invalid products"
                  },
                  "sign_in_required": {
                    "da": "Login påkrævet",
                    "en": "Sign-in required"
                  },
                  "unexpected_error": {
                    "da": "Uventet fejl",
                    "en": "Unexpected error"
                  },
                  "not_authenticated": {
                    "da": "Ikke logget ind",
                    "en": "Not signed in"
                  },
                  "price_unavailable": {
                    "da": "Prisen kan ikke beregnes",
                    "en": "Price unavailable"
                  },
                  "request_too_large": {
                    "da": "Forespørgslen er for stor",
                    "en": "Request too large"
                  },
                  "unsupported_input": {
                    "da": "Oplysningerne understøttes ikke",
                    "en": "Unsupported input"
                  },
                  "ambiguous_question": {
                    "da": "Spørgsmålet er ikke entydigt",
                    "en": "Ambiguous question"
                  },
                  "invalid_start_date": {
                    "da": "Ugyldig startdato",
                    "en": "Invalid start date"
                  },
                  "onboarding_expired": {
                    "da": "Tilmeldingen er udløbet",
                    "en": "Signup expired"
                  },
                  "wrong_identity_mode": {
                    "da": "Forkert godkendelsesmetode",
                    "en": "Wrong authentication method"
                  },
                  "idempotency_conflict": {
                    "da": "Idempotensnøglen er allerede brugt til andre oplysninger",
                    "en": "Idempotency key already used for different input"
                  },
                  "invalid_partner_code": {
                    "da": "Ugyldig partnerkode",
                    "en": "Invalid partner code"
                  },
                  "invalid_verification": {
                    "da": "Ugyldig eller udløbet bekræftelseskode",
                    "en": "Invalid or expired verification code"
                  },
                  "operation_in_progress": {
                    "da": "Handlingen er i gang",
                    "en": "Operation in progress"
                  },
                  "pricing_configuration": {
                    "da": "Prisopsætningen skal gennemgås",
                    "en": "Pricing configuration needs review"
                  },
                  "manual_review_required": {
                    "da": "Individuelt tilbud påkrævet",
                    "en": "Individual offer required"
                  },
                  "unsupported_media_type": {
                    "da": "Indholdstypen understøttes ikke",
                    "en": "Unsupported media type"
                  },
                  "temporarily_unavailable": {
                    "da": "Midlertidigt utilgængelig",
                    "en": "Temporarily unavailable"
                  },
                  "idempotency_key_mismatch": {
                    "da": "Idempotensnøglerne er forskellige",
                    "en": "Idempotency keys do not match"
                  },
                  "invalid_backend_response": {
                    "da": "Ugyldigt svar fra serveren",
                    "en": "Invalid backend response"
                  }
                }
              }
            },
            "description": "Error code and its translated labels.",
            "additionalProperties": false
          },
          "field": {
            "type": "string",
            "description": "Request field to correct, using dot notation for nested fields."
          },
          "reason": {
            "type": "object",
            "required": [
              "value",
              "label"
            ],
            "properties": {
              "label": {
                "type": "object",
                "required": [
                  "da",
                  "en"
                ],
                "properties": {
                  "da": {
                    "type": "string",
                    "minLength": 1,
                    "description": "Danish label."
                  },
                  "en": {
                    "type": "string",
                    "minLength": 1,
                    "description": "English label."
                  }
                },
                "description": "Human-readable labels by language.",
                "additionalProperties": false
              },
              "value": {
                "enum": [
                  "industry_not_covered",
                  "unknown"
                ],
                "type": "string",
                "description": "Stable machine-readable value.",
                "x-enum-labels": {
                  "unknown": {
                    "da": "Ukendt årsag",
                    "en": "Unknown reason"
                  },
                  "industry_not_covered": {
                    "da": "Branchen er ikke dækket",
                    "en": "Industry not covered"
                  }
                }
              }
            },
            "description": "For price_unavailable: industry_not_covered when the product has no tariff for the company's industry, otherwise unknown.",
            "additionalProperties": false
          },
          "message": {
            "type": "string",
            "description": "Human-readable explanation in English."
          },
          "product": {
            "type": "string",
            "description": "Slug of the product the error concerns, when it concerns one."
          },
          "question": {
            "type": "string",
            "description": "Answer key of the question the error concerns, as used in `answers`."
          },
          "didYouMean": {
            "type": "string",
            "description": "Suggested catalog product slug or answer key. Retry only after confirming the intended value."
          },
          "nextAction": {
            "type": "object",
            "required": [
              "value",
              "label"
            ],
            "properties": {
              "label": {
                "type": "object",
                "required": [
                  "da",
                  "en"
                ],
                "properties": {
                  "da": {
                    "type": "string",
                    "minLength": 1,
                    "description": "Danish label."
                  },
                  "en": {
                    "type": "string",
                    "minLength": 1,
                    "description": "English label."
                  }
                },
                "description": "Human-readable labels by language.",
                "additionalProperties": false
              },
              "value": {
                "enum": [
                  "fixAnswer",
                  "askUser",
                  "retryLater",
                  "contactPlain",
                  "none"
                ],
                "type": "string",
                "description": "Stable machine-readable value.",
                "x-enum-labels": {
                  "none": {
                    "da": "Ingen handling nødvendig",
                    "en": "No action needed"
                  },
                  "askUser": {
                    "da": "Bed kunden om oplysninger eller login",
                    "en": "Ask the customer for input or to sign in"
                  },
                  "fixAnswer": {
                    "da": "Ret oplysningerne og prøv igen",
                    "en": "Correct the request and try again"
                  },
                  "retryLater": {
                    "da": "Prøv igen senere",
                    "en": "Try again later"
                  },
                  "contactPlain": {
                    "da": "Kontakt Plain",
                    "en": "Contact Plain"
                  }
                }
              }
            },
            "description": "What to do next: fixAnswer (correct the request and retry), askUser (ask the person for new input or to sign in), retryLater (retry the same request later), contactPlain (Plain must review this case; refer the person to Plain), none (nothing to do).",
            "additionalProperties": false
          },
          "allowedValues": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "For invalid_answer on a select question: the option keys that are accepted."
          },
          "questionLabel": {
            "type": "object",
            "required": [
              "da"
            ],
            "properties": {
              "da": {
                "type": "string",
                "description": "Danish question label from the catalog."
              }
            },
            "description": "Danish question label, when the question can be identified in the catalog.",
            "additionalProperties": false
          }
        },
        "description": "One problem with a request.",
        "additionalProperties": false
      },
      "description": "Products that are listed but cannot currently be quoted, e.g. pricing_configuration."
    },
    "products": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "product",
          "name",
          "description",
          "coverage",
          "termsUrl",
          "termsDocuments",
          "region",
          "currency",
          "billingFrequency",
          "automaticQuote",
          "pricePerEmployee",
          "requiredProducts",
          "questions"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Product name in Danish."
          },
          "region": {
            "type": "string",
            "description": "Market of the product: dk."
          },
          "product": {
            "type": "string",
            "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
            "maxLength": 100,
            "description": "Product slug. Use it in `products` and to qualify answer keys."
          },
          "coverage": {
            "type": [
              "string",
              "null"
            ],
            "description": "What the insurance covers, in Danish."
          },
          "currency": {
            "type": "string",
            "description": "ISO 4217 currency of all prices for this product: DKK."
          },
          "termsUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "Link to the full insurance terms as PDF. Same as `termsDocuments.termsPdf`."
          },
          "questions": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "question",
                "required"
              ],
              "properties": {
                "question": {
                  "type": "object",
                  "required": [
                    "name",
                    "title",
                    "description",
                    "type"
                  ],
                  "properties": {
                    "max": {
                      "type": "number",
                      "description": "Inclusive maximum numeric answer, when configured. Employee counts have a maximum of 9999."
                    },
                    "min": {
                      "type": "number",
                      "description": "Inclusive minimum numeric answer, when configured. Employee counts have a minimum of 0."
                    },
                    "name": {
                      "type": "string",
                      "description": "Answer key: use it as the key in `answers`."
                    },
                    "step": {
                      "type": "number",
                      "description": "Suggested numeric input increment, when configured. Employee counts use 1 (whole numbers)."
                    },
                    "type": {
                      "type": "object",
                      "required": [
                        "value",
                        "label"
                      ],
                      "properties": {
                        "label": {
                          "type": "object",
                          "required": [
                            "da",
                            "en"
                          ],
                          "properties": {
                            "da": {
                              "type": "string",
                              "minLength": 1,
                              "description": "Danish label."
                            },
                            "en": {
                              "type": "string",
                              "minLength": 1,
                              "description": "English label."
                            }
                          },
                          "description": "Human-readable labels by language.",
                          "additionalProperties": false
                        },
                        "value": {
                          "enum": [
                            "Text",
                            "NumberWhole",
                            "NumberDecimal",
                            "Date",
                            "Boolean",
                            "Select",
                            "MultiSelect"
                          ],
                          "type": "string",
                          "description": "Stable machine-readable value.",
                          "x-enum-labels": {
                            "Date": {
                              "da": "Dato",
                              "en": "Date"
                            },
                            "Text": {
                              "da": "Tekst",
                              "en": "Text"
                            },
                            "Select": {
                              "da": "Vælg én",
                              "en": "Select one"
                            },
                            "Boolean": {
                              "da": "Ja eller nej",
                              "en": "Yes or no"
                            },
                            "MultiSelect": {
                              "da": "Vælg flere",
                              "en": "Select multiple"
                            },
                            "NumberWhole": {
                              "da": "Heltal",
                              "en": "Whole number"
                            },
                            "NumberDecimal": {
                              "da": "Decimaltal",
                              "en": "Decimal number"
                            }
                          }
                        }
                      },
                      "description": "Answer type: Text, NumberWhole (integer), NumberDecimal, Date (YYYY-MM-DD), Boolean, Select (one key of `options`) or MultiSelect (array of keys).",
                      "additionalProperties": false
                    },
                    "unit": {
                      "type": "string",
                      "description": "Answer unit from the question's unit setting, when configured."
                    },
                    "title": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "The question as shown to the customer, in Danish."
                    },
                    "period": {
                      "type": "string",
                      "description": "Answer period from the question's period setting, when configured."
                    },
                    "options": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "value",
                          "label"
                        ],
                        "properties": {
                          "label": {
                            "type": "object",
                            "required": [
                              "da"
                            ],
                            "properties": {
                              "da": {
                                "type": "string",
                                "description": "Danish option label from the backend."
                              }
                            },
                            "description": "Localized option label. Currently only Danish is available.",
                            "additionalProperties": false
                          },
                          "value": {
                            "type": "string",
                            "description": "Option value to send as the answer."
                          }
                        },
                        "additionalProperties": false
                      },
                      "minItems": 1,
                      "description": "Available options in display order. Omitted when there are none. Answer with `value`, not the label."
                    },
                    "description": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Help text in Danish."
                    }
                  },
                  "description": "A question to answer before pricing.",
                  "additionalProperties": false
                },
                "required": {
                  "type": "boolean",
                  "description": "Whether the product cannot be priced without this answer."
                }
              },
              "description": "A question for this product and whether its answer is required.",
              "additionalProperties": false
            },
            "description": "Questions to answer for this product, including company-wide questions shared with other products and the questions of its `requiredProducts`. Empty when automaticQuote is false."
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "Short product description in Danish."
          },
          "insuranceId": {
            "type": [
              "string",
              "null"
            ],
            "description": "Original insurance ID for matching existing content product references. Use the product slug, not this ID, when quoting."
          },
          "automaticQuote": {
            "type": "boolean",
            "description": "Whether this product supports an instant quote. False means manual review is required: its questions are not listed and the quote returns contactPlain without answers. True does not guarantee a price for every company or set of answers."
          },
          "termsDocuments": {
            "anyOf": [
              {
                "type": "object",
                "required": [
                  "termsPdf",
                  "ipidPdf",
                  "ipidYaml",
                  "termsMarkdown"
                ],
                "properties": {
                  "ipidPdf": {
                    "type": "string",
                    "description": "Insurance product information document (IPID) as PDF. Give this link to the customer."
                  },
                  "ipidYaml": {
                    "type": "string",
                    "description": "The IPID as YAML, for agents to read the key facts: what is and is not covered, restrictions and obligations."
                  },
                  "termsPdf": {
                    "type": "string",
                    "description": "Full insurance terms as PDF. Give this link to the customer."
                  },
                  "termsMarkdown": {
                    "type": "string",
                    "description": "Full insurance terms as Markdown, for agents to read and answer questions about coverage."
                  }
                },
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ],
            "description": "Links to the product's terms documents, or null when it has none. Read the YAML and Markdown to answer questions; give the customer the PDF links. External terms use the same link for all four."
          },
          "billingFrequency": {
            "type": "object",
            "required": [
              "value",
              "label"
            ],
            "properties": {
              "label": {
                "type": "object",
                "required": [
                  "da",
                  "en"
                ],
                "properties": {
                  "da": {
                    "type": "string",
                    "minLength": 1,
                    "description": "Danish label."
                  },
                  "en": {
                    "type": "string",
                    "minLength": 1,
                    "description": "English label."
                  }
                },
                "description": "Human-readable labels by language.",
                "additionalProperties": false
              },
              "value": {
                "enum": [
                  "Monthly",
                  "Annual"
                ],
                "type": "string",
                "description": "Stable machine-readable value.",
                "x-enum-labels": {
                  "Annual": {
                    "da": "Årlig",
                    "en": "Annual"
                  },
                  "Monthly": {
                    "da": "Månedlig",
                    "en": "Monthly"
                  }
                }
              }
            },
            "description": "How often the customer pays: Monthly or Annual. Prices are per period of this frequency.",
            "additionalProperties": false
          },
          "pricePerEmployee": {
            "type": "boolean",
            "description": "Whether the quoted price is per covered employee. The employees are selected when purchasing on Plain, so no question asks for them."
          },
          "requiredProducts": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
              "maxLength": 100,
              "description": "Product slug."
            },
            "description": "Products that must be selected together with this one, directly or indirectly. Add them to `products` when quoting."
          }
        },
        "description": "An insurance product that can be offered to a Danish company.",
        "additionalProperties": false
      },
      "description": "Products that can be offered."
    },
    "productDependencies": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "product",
          "requiredProduct"
        ],
        "properties": {
          "product": {
            "type": "string",
            "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
            "maxLength": 100,
            "description": "Product slug."
          },
          "requiredProduct": {
            "type": "string",
            "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
            "maxLength": 100,
            "description": "Slug of a product that must be selected together with `product`."
          }
        },
        "description": "A product that can only be bought together with another.",
        "additionalProperties": false
      },
      "description": "Products that require another product."
    }
  },
  "description": "Products and questions to collect answers for before quoting.",
  "additionalProperties": false
}
list_onboarding_catalog annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
quote_onboarding_products — Quote products
Advertised by the server during the last successful anonymous check.
Advertised read-only

Calculates indicative prices for up to twenty products for the company with this CVR, from one flat `answers` object. A plain key answers a question for the company and for every selected product that asks it; `<product>.<name>` answers one product's question. Stored answers and company discounts are not reused; supply required answers explicitly. An optional partnerCode applies that partner's discount. Enum fields use {value, label: {da, en}}. Each product has a status.value: only `quoted` products have a price; `contactPlain` products need an individual offer from Plain and `unavailable` ones cannot be offered (see `issue`). Prices are in DKK (major units) per billingFrequency.value period, with annualPrice for comparison and totals across products. State prices with each quote's `display` text in the customer's language, and always show the `disclaimer`. When pricePerEmployee is true, the price is per employee: say so using the quote's notice, as the customer selects the covered employees on Plain. Unknown answer keys are ignored and reported in warnings; required answers are never inferred. Nothing is bound, created or charged. Works without signing in, as a public new-company quote. Quotes for an existing customer must use the selected, authorized company's CVR. Active products return status.value alreadyInsured with no new-offer price and are excluded from totals and creation; held prerequisites do not need to be selected again. Check list_companies.canCreateOffers before promising to create offers. Quoting itself only needs policies:read. For existing companies, use get_onboarding_payment_link after creating offers for authenticated review; continueUrl is the public new-company signup page and does not apply private policy exclusions.

quote_onboarding_products input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "cvr",
    "products",
    "answers"
  ],
  "properties": {
    "cvr": {
      "type": "string",
      "pattern": "^[0-9]{8}$",
      "description": "Danish company registration number (CVR), eight digits, e.g. 39488914."
    },
    "region": {
      "type": "string",
      "const": "dk",
      "default": "dk",
      "description": "Market. Only Denmark (dk) is supported. Optional; defaults to dk."
    },
    "answers": {
      "type": "object",
      "description": "Every answer in one object, keyed by the catalog question `name`. A plain name answers that question for the company and for every selected product that asks it; `<product>.<name>` (e.g. loesoereforsikring.basement) answers only that product's question and takes precedence. Employee FTE questions are answered as `<name>.fullTime` and `<name>.partTime` head counts: integers from 0 to 9999 inclusive. When required, send both counts explicitly, including zero.",
      "maxProperties": 100,
      "propertyNames": {
        "type": "string",
        "maxLength": 300,
        "minLength": 1
      },
      "additionalProperties": {
        "anyOf": [
          {
            "type": "string",
            "maxLength": 2000
          },
          {
            "type": "number"
          },
          {
            "type": "boolean"
          },
          {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 256
            },
            "maxItems": 100
          }
        ],
        "description": "The answer's JSON type follows the catalog question's `type.value`: Text is a string; NumberWhole is an integer and NumberDecimal a number (numeric strings are accepted); Date is a YYYY-MM-DD string; Boolean is true or false; Select is one option key from `options` as a string; MultiSelect is an array of option keys."
      }
    },
    "products": {
      "type": "array",
      "items": {
        "type": "string",
        "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
        "maxLength": 100,
        "description": "Product slug from the catalog."
      },
      "maxItems": 20,
      "minItems": 1,
      "description": "The products to quote: 1 to 20 distinct slugs, including any products they require.",
      "uniqueItems": true
    },
    "startDate": {
      "type": "string",
      "format": "date",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$",
      "description": "Policy start date (YYYY-MM-DD). Defaults to today. Must be between today and one year ahead."
    },
    "partnerCode": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{2,64}$",
      "description": "Optional partner discount code, 2-64 letters, digits, hyphens or underscores. Must be active."
    }
  },
  "description": "The company, products and answers to price. Send the same object again in /start.",
  "additionalProperties": false
}
quote_onboarding_products output schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "quotes",
    "totals",
    "quoteId",
    "startDate",
    "expiresAt",
    "continueUrl",
    "disclaimer"
  ],
  "properties": {
    "quotes": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "product",
          "name",
          "termsUrl",
          "termsDocuments",
          "status",
          "currency",
          "billingFrequency",
          "pricePerEmployee",
          "coverage",
          "display"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "Product name to show next to the price."
          },
          "issue": {
            "type": "object",
            "required": [
              "code",
              "message",
              "nextAction"
            ],
            "properties": {
              "max": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date",
                    "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"
                  },
                  {
                    "type": "number"
                  }
                ],
                "description": "Inclusive maximum: the latest start date (YYYY-MM-DD) for invalid_start_date, or the numeric upper bound for invalid_answer."
              },
              "min": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date",
                    "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$"
                  },
                  {
                    "type": "number"
                  }
                ],
                "description": "Inclusive minimum: the earliest start date (YYYY-MM-DD) for invalid_start_date, or the numeric lower bound for invalid_answer."
              },
              "code": {
                "type": "object",
                "required": [
                  "value",
                  "label"
                ],
                "properties": {
                  "label": {
                    "type": "object",
                    "required": [
                      "da",
                      "en"
                    ],
                    "properties": {
                      "da": {
                        "type": "string",
                        "minLength": 1,
                        "description": "Danish label."
                      },
                      "en": {
                        "type": "string",
                        "minLength": 1,
                        "description": "English label."
                      }
                    },
                    "description": "Human-readable labels by language.",
                    "additionalProperties": false
                  },
                  "value": {
                    "enum": [
                      "invalid_answer",
                      "missing_answer",
                      "ambiguous_question",
                      "invalid_start_date",
                      "cvr_not_found",
                      "invalid_products",
                      "missing_product",
                      "invalid_request",
                      "request_too_large",
                      "unsupported_media_type",
                      "wrong_identity_mode",
                      "idempotency_key_mismatch",
                      "idempotency_conflict",
                      "price_changed",
                      "invalid_partner_code",
                      "invalid_verification",
                      "sign_in_required",
                      "onboarding_expired",
                      "not_authenticated",
                      "not_authorized",
                      "consent_required",
                      "temporarily_unavailable",
                      "invalid_backend_response",
                      "rate_limited",
                      "operation_in_progress",
                      "price_unavailable",
                      "manual_review_required",
                      "pricing_configuration",
                      "unsupported_input",
                      "unexpected_error",
                      "not_found"
                    ],
                    "type": "string",
                    "description": "Stable machine-readable value.",
                    "x-enum-labels": {
                      "not_found": {
                        "da": "Ikke fundet",
                        "en": "Not found"
                      },
                      "rate_limited": {
                        "da": "For mange forespørgsler",
                        "en": "Too many requests"
                      },
                      "cvr_not_found": {
                        "da": "CVR-nummer ikke fundet",
                        "en": "CVR number not found"
                      },
                      "price_changed": {
                        "da": "Prisen er ændret",
                        "en": "Price changed"
                      },
                      "invalid_answer": {
                        "da": "Ugyldigt svar",
                        "en": "Invalid answer"
                      },
                      "missing_answer": {
                        "da": "Svar mangler",
                        "en": "Missing answer"
                      },
                      "not_authorized": {
                        "da": "Ingen adgang",
                        "en": "Not authorized"
                      },
                      "invalid_request": {
                        "da": "Ugyldig forespørgsel",
                        "en": "Invalid request"
                      },
                      "missing_product": {
                        "da": "Påkrævet produkt mangler",
                        "en": "Required product missing"
                      },
                      "consent_required": {
                        "da": "Samtykke påkrævet",
                        "en": "Consent required"
                      },
                      "invalid_products": {
                        "da": "Ugyldige produkter",
                        "en": "Invalid products"
                      },
                      "sign_in_required": {
                        "da": "Login påkrævet",
                        "en": "Sign-in required"
                      },
                      "unexpected_error": {
                        "da": "Uventet fejl",
                        "en": "Unexpected error"
                      },
                      "not_authenticated": {
                        "da": "Ikke logget ind",
                        "en": "Not signed in"
                      },
                      "price_unavailable": {
                        "da": "Prisen kan ikke beregnes",
                        "en": "Price unavailable"
                      },
                      "request_too_large": {
                        "da": "Forespørgslen er for stor",
                        "en": "Request too large"
                      },
                      "unsupported_input": {
                        "da": "Oplysningerne understøttes ikke",
                        "en": "Unsupported input"
                      },
                      "ambiguous_question": {
                        "da": "Spørgsmålet er ikke entydigt",
                        "en": "Ambiguous question"
                      },
                      "invalid_start_date": {
                        "da": "Ugyldig startdato",
                        "en": "Invalid start date"
                      },
                      "onboarding_expired": {
                        "da": "Tilmeldingen er udløbet",
                        "en": "Signup expired"
                      },
                      "wrong_identity_mode": {
                        "da": "Forkert godkendelsesmetode",
                        "en": "Wrong authentication method"
                      },
                      "idempotency_conflict": {
                        "da": "Idempotensnøglen er allerede brugt til andre oplysninger",
                        "en": "Idempotency key already used for different input"
                      },
                      "invalid_partner_code": {
                        "da": "Ugyldig partnerkode",
                        "en": "Invalid partner code"
                      },
                      "invalid_verification": {
                        "da": "Ugyldig eller udløbet bekræftelseskode",
                        "en": "Invalid or expired verification code"
                      },
                      "operation_in_progress": {
                        "da": "Handlingen er i gang",
                        "en": "Operation in progress"
                      },
                      "pricing_configuration": {
                        "da": "Prisopsætningen skal gennemgås",
                        "en": "Pricing configuration needs review"
                      },
                      "manual_review_required": {
                        "da": "Individuelt tilbud påkrævet",
                        "en": "Individual offer required"
                      },
                      "unsupported_media_type": {
                        "da": "Indholdstypen understøttes ikke",
                        "en": "Unsupported media type"
                      },
                      "temporarily_unavailable": {
                        "da": "Midlertidigt utilgængelig",
                        "en": "Temporarily unavailable"
                      },
                      "idempotency_key_mismatch": {
                        "da": "Idempotensnøglerne er forskellige",
                        "en": "Idempotency keys do not match"
                      },
                      "invalid_backend_response": {
                        "da": "Ugyldigt svar fra serveren",
                        "en": "Invalid backend response"
                      }
                    }
                  }
                },
                "description": "Error code and its translated labels.",
                "additionalProperties": false
              },
              "field": {
                "type": "string",
                "description": "Request field to correct, using dot notation for nested fields."
              },
              "reason": {
                "type": "object",
                "required": [
                  "value",
                  "label"
                ],
                "properties": {
                  "label": {
                    "type": "object",
                    "required": [
                      "da",
                      "en"
                    ],
                    "properties": {
                      "da": {
                        "type": "string",
                        "minLength": 1,
                        "description": "Danish label."
                      },
                      "en": {
                        "type": "string",
                        "minLength": 1,
                        "description": "English label."
                      }
                    },
                    "description": "Human-readable labels by language.",
                    "additionalProperties": false
                  },
                  "value": {
                    "enum": [
                      "industry_not_covered",
                      "unknown"
                    ],
                    "type": "string",
                    "description": "Stable machine-readable value.",
                    "x-enum-labels": {
                      "unknown": {
                        "da": "Ukendt årsag",
                        "en": "Unknown reason"
                      },
                      "industry_not_covered": {
                        "da": "Branchen er ikke dækket",
                        "en": "Industry not covered"
                      }
                    }
                  }
                },
                "description": "For price_unavailable: industry_not_covered when the product has no tariff for the company's industry, otherwise unknown.",
                "additionalProperties": false
              },
              "message": {
                "type": "string",
                "description": "Human-readable explanation in English."
              },
              "product": {
                "type": "string",
                "description": "Slug of the product the error concerns, when it concerns one."
              },
              "question": {
                "type": "string",
                "description": "Answer key of the question the error concerns, as used in `answers`."
              },
              "didYouMean": {
                "type": "string",
                "description": "Suggested catalog product slug or answer key. Retry only after confirming the intended value."
              },
              "nextAction": {
                "type": "object",
                "required": [
                  "value",
                  "label"
                ],
                "properties": {
                  "label": {
                    "type": "object",
                    "required": [
                      "da",
                      "en"
                    ],
                    "properties": {
                      "da": {
                        "type": "string",
                        "minLength": 1,
                        "description": "Danish label."
                      },
                      "en": {
                        "type": "string",
                        "minLength": 1,
                        "description": "English label."
                      }
                    },
                    "description": "Human-readable labels by language.",
                    "additionalProperties": false
                  },
                  "value": {
                    "enum": [
                      "fixAnswer",
                      "askUser",
                      "retryLater",
                      "contactPlain",
                      "none"
                    ],
                    "type": "string",
                    "description": "Stable machine-readable value.",
                    "x-enum-labels": {
                      "none": {
                        "da": "Ingen handling nødvendig",
                        "en": "No action needed"
                      },
                      "askUser": {
                        "da": "Bed kunden om oplysninger eller login",
                        "en": "Ask the customer for input or to sign in"
                      },
                      "fixAnswer": {
                        "da": "Ret oplysningerne og prøv igen",
                        "en": "Correct the request and try again"
                      },
                      "retryLater": {
                        "da": "Prøv igen senere",
                        "en": "Try again later"
                      },
                      "contactPlain": {
                        "da": "Kontakt Plain",
                        "en": "Contact Plain"
                      }
                    }
                  }
                },
                "description": "What to do next: fixAnswer (correct the request and retry), askUser (ask the person for new input or to sign in), retryLater (retry the same request later), contactPlain (Plain must review this case; refer the person to Plain), none (nothing to do).",
                "additionalProperties": false
              },
              "allowedValues": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "For invalid_answer on a select question: the option keys that are accepted."
              },
              "questionLabel": {
                "type": "object",
                "required": [
                  "da"
                ],
                "properties": {
                  "da": {
                    "type": "string",
                    "description": "Danish question label from the catalog."
                  }
                },
                "description": "Danish question label, when the question can be identified in the catalog.",
                "additionalProperties": false
              }
            },
            "description": "Why the product is not quoted.",
            "additionalProperties": false
          },
          "price": {
            "type": "object",
            "required": [
              "totalPrice",
              "premium",
              "fees"
            ],
            "properties": {
              "fees": {
                "type": "number",
                "description": "Statutory fees and taxes per billing period, in DKK."
              },
              "premium": {
                "type": "number",
                "description": "Insurance premium per billing period, in DKK."
              },
              "totalPrice": {
                "type": "number",
                "description": "What the customer pays per billing period, in DKK (major units, kroner): premium plus government fees."
              }
            },
            "description": "The price per billing period; present when quoted.",
            "additionalProperties": false
          },
          "notice": {
            "type": "string",
            "description": "A message to show the customer with this price, e.g. that it is per employee."
          },
          "status": {
            "type": "object",
            "required": [
              "value",
              "label"
            ],
            "properties": {
              "label": {
                "type": "object",
                "required": [
                  "da",
                  "en"
                ],
                "properties": {
                  "da": {
                    "type": "string",
                    "minLength": 1,
                    "description": "Danish label."
                  },
                  "en": {
                    "type": "string",
                    "minLength": 1,
                    "description": "English label."
                  }
                },
                "description": "Human-readable labels by language.",
                "additionalProperties": false
              },
              "value": {
                "enum": [
                  "quoted",
                  "contactPlain",
                  "unavailable",
                  "alreadyInsured"
                ],
                "type": "string",
                "description": "Stable machine-readable value.",
                "x-enum-labels": {
                  "quoted": {
                    "da": "Pris beregnet",
                    "en": "Quoted"
                  },
                  "unavailable": {
                    "da": "Kan ikke tilbydes",
                    "en": "Unavailable"
                  },
                  "contactPlain": {
                    "da": "Kontakt Plain",
                    "en": "Contact Plain"
                  },
                  "alreadyInsured": {
                    "da": "Allerede forsikret",
                    "en": "Already insured"
                  }
                }
              }
            },
            "description": "quoted: priced and can be sent to /start. contactPlain: Plain makes an individual offer; the customer contacts Plain. unavailable: cannot be offered for these answers; see `issue`. alreadyInsured: active for the authorized company; excluded from new offers and totals. Only quoted products have a price.",
            "additionalProperties": false
          },
          "display": {
            "type": "object",
            "required": [
              "da",
              "en"
            ],
            "properties": {
              "da": {
                "type": "string",
                "description": "Danish."
              },
              "en": {
                "type": "string",
                "description": "English."
              }
            },
            "description": "How to state this quote to the customer: total, premium and fees split, and period.",
            "additionalProperties": false
          },
          "product": {
            "type": "string",
            "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
            "maxLength": 100,
            "description": "Product slug."
          },
          "coverage": {
            "type": "object",
            "required": [
              "sumInsured",
              "deductible",
              "summary"
            ],
            "properties": {
              "summary": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "What the insurance covers, in brief."
              },
              "deductible": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The deductible (selvrisiko), as text."
              },
              "sumInsured": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The sum insured, as text."
              }
            },
            "description": "What the price buys. Fields are null when the product does not state them.",
            "additionalProperties": false
          },
          "currency": {
            "type": "string",
            "description": "ISO 4217 currency: DKK."
          },
          "termsUrl": {
            "type": [
              "string",
              "null"
            ],
            "description": "Link to the product's insurance terms as PDF. Same as `termsDocuments.termsPdf`."
          },
          "annualPrice": {
            "type": "object",
            "required": [
              "totalPrice",
              "premium",
              "fees"
            ],
            "properties": {
              "fees": {
                "type": "number",
                "description": "Statutory fees and taxes per billing period, in DKK."
              },
              "premium": {
                "type": "number",
                "description": "Insurance premium per billing period, in DKK."
              },
              "totalPrice": {
                "type": "number",
                "description": "What the customer pays per billing period, in DKK (major units, kroner): premium plus government fees."
              }
            },
            "description": "The price per year, to compare monthly and annual products; present when quoted.",
            "additionalProperties": false
          },
          "termsDocuments": {
            "anyOf": [
              {
                "type": "object",
                "required": [
                  "termsPdf",
                  "ipidPdf",
                  "ipidYaml",
                  "termsMarkdown"
                ],
                "properties": {
                  "ipidPdf": {
                    "type": "string",
                    "description": "Insurance product information document (IPID) as PDF. Give this link to the customer."
                  },
                  "ipidYaml": {
                    "type": "string",
                    "description": "The IPID as YAML, for agents to read the key facts: what is and is not covered, restrictions and obligations."
                  },
                  "termsPdf": {
                    "type": "string",
                    "description": "Full insurance terms as PDF. Give this link to the customer."
                  },
                  "termsMarkdown": {
                    "type": "string",
                    "description": "Full insurance terms as Markdown, for agents to read and answer questions about coverage."
                  }
                },
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ],
            "description": "Links to the product's terms documents, or null when it has none. Read the YAML and Markdown to answer questions; give the customer the PDF links. External terms use the same link for all four."
          },
          "billingFrequency": {
            "type": "object",
            "required": [
              "value",
              "label"
            ],
            "properties": {
              "label": {
                "type": "object",
                "required": [
                  "da",
                  "en"
                ],
                "properties": {
                  "da": {
                    "type": "string",
                    "minLength": 1,
                    "description": "Danish label."
                  },
                  "en": {
                    "type": "string",
                    "minLength": 1,
                    "description": "English label."
                  }
                },
                "description": "Human-readable labels by language.",
                "additionalProperties": false
              },
              "value": {
                "enum": [
                  "Monthly",
                  "Annual"
                ],
                "type": "string",
                "description": "Stable machine-readable value.",
                "x-enum-labels": {
                  "Annual": {
                    "da": "Årlig",
                    "en": "Annual"
                  },
                  "Monthly": {
                    "da": "Månedlig",
                    "en": "Monthly"
                  }
                }
              }
            },
            "description": "How often the customer pays: Monthly or Annual. Prices are per period of this frequency.",
            "additionalProperties": false
          },
          "pricePerEmployee": {
            "type": "boolean",
            "description": "Whether `price` is per covered employee. The customer selects the employees when purchasing on Plain, which sets the total."
          }
        },
        "description": "The indicative price of one product, or why it has none.",
        "additionalProperties": false
      },
      "description": "One quote per selected product, in request order."
    },
    "totals": {
      "anyOf": [
        {
          "type": "object",
          "required": [
            "monthly",
            "annual",
            "excludedProducts",
            "display"
          ],
          "properties": {
            "annual": {
              "type": "object",
              "required": [
                "totalPrice",
                "premium",
                "fees"
              ],
              "properties": {
                "fees": {
                  "type": "number",
                  "description": "Statutory fees and taxes per billing period, in DKK."
                },
                "premium": {
                  "type": "number",
                  "description": "Insurance premium per billing period, in DKK."
                },
                "totalPrice": {
                  "type": "number",
                  "description": "What the customer pays per billing period, in DKK (major units, kroner): premium plus government fees."
                }
              },
              "description": "Sum per year.",
              "additionalProperties": false
            },
            "display": {
              "type": "object",
              "required": [
                "da",
                "en"
              ],
              "properties": {
                "da": {
                  "type": "string",
                  "description": "Danish."
                },
                "en": {
                  "type": "string",
                  "description": "English."
                }
              },
              "description": "How to state the totals to the customer.",
              "additionalProperties": false
            },
            "monthly": {
              "type": "object",
              "required": [
                "totalPrice",
                "premium",
                "fees"
              ],
              "properties": {
                "fees": {
                  "type": "number",
                  "description": "Statutory fees and taxes per billing period, in DKK."
                },
                "premium": {
                  "type": "number",
                  "description": "Insurance premium per billing period, in DKK."
                },
                "totalPrice": {
                  "type": "number",
                  "description": "What the customer pays per billing period, in DKK (major units, kroner): premium plus government fees."
                }
              },
              "description": "Sum per month; annual products count as a twelfth of their yearly price.",
              "additionalProperties": false
            },
            "excludedProducts": {
              "type": "array",
              "items": {
                "type": "string",
                "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
                "maxLength": 100,
                "description": "Product slug."
              },
              "description": "Selected products not in the totals: not quoted, or priced per employee."
            }
          },
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ],
      "description": "Totals of the quoted products; null when no product is included."
    },
    "quoteId": {
      "type": [
        "string",
        "null"
      ],
      "description": "Identifies these prices; pass it as `quoteId` to /start with the exact original quote request. Only quoted products become offers. Null when no product is quoted."
    },
    "warnings": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "code",
          "question"
        ],
        "properties": {
          "code": {
            "type": "string",
            "const": "unknown_question",
            "description": "An unrecognized answer key was ignored; no answer was inferred from it."
          },
          "question": {
            "type": "string",
            "description": "The ignored answer key. Check the catalog for the correct name."
          },
          "didYouMean": {
            "type": "string",
            "description": "Suggested answer key. Confirm it before resending; answers are never auto-corrected."
          },
          "questionLabel": {
            "type": "object",
            "required": [
              "da"
            ],
            "properties": {
              "da": {
                "type": "string",
                "description": "Danish question label from the catalog."
              }
            },
            "description": "Danish label of the suggested question, when available.",
            "additionalProperties": false
          }
        },
        "additionalProperties": false
      },
      "description": "Non-fatal problems with ignored answers. Omitted when there are none."
    },
    "expiresAt": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"
        },
        {
          "type": "null"
        }
      ],
      "description": "Until when quoteId can be used for /start, unless prices change first; then request a new quote."
    },
    "startDate": {
      "type": "string",
      "format": "date",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$",
      "description": "The insurance start date the prices apply to; today when the request has none."
    },
    "disclaimer": {
      "type": "object",
      "required": [
        "da",
        "en"
      ],
      "properties": {
        "da": {
          "type": "string",
          "description": "Danish."
        },
        "en": {
          "type": "string",
          "description": "English."
        }
      },
      "description": "Show with the prices: they are indicative, and nothing binds until the customer signs on Plain.",
      "additionalProperties": false
    },
    "continueUrl": {
      "type": "string",
      "format": "uri",
      "description": "Where the customer reviews this quote on Plain and continues to sign up. Opening it creates nothing."
    }
  },
  "description": "Indicative prices. Nothing is bound or charged.",
  "additionalProperties": false
}
quote_onboarding_products annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
create_onboarding — Create unsigned insurance offers
Advertised by the server during the last successful anonymous check.

Recalculates an approved quote and creates offered, unsigned policies, ready for the customer to review and sign on Plain. Send the exact quote request again with its quoteId before expiresAt; offers are created for the products with status.value quoted only. Active insurance is rechecked before creation. Retry a failed request on the same connection with the same idempotency key and body. No insurance is accepted, activated or charged. When state.value is awaiting_verification, ask the person for the emailed code. Creates offers for the selected, authorized existing company without recreating it, or onboards a new company. For an existing company, select it with set_company_context and use its CVR; administrator access is required. Uses the connected user's identity: omit email and name. Requires signing in with onboarding:write consent; calling it prompts sign-in when needed. Check list_companies first. idempotencyKey is required. If the connected user's email is confirmed, no email code is needed. Do not reuse a retry key across reconnections; obtain a fresh quote after expiry or insurance changes.

create_onboarding input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "quote",
    "quoteId"
  ],
  "properties": {
    "name": {
      "type": "string",
      "maxLength": 200,
      "minLength": 1,
      "description": "Full name of the person signing up. Must not contain links or markup."
    },
    "email": {
      "type": "string",
      "format": "email",
      "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
      "maxLength": 254,
      "description": "Email address of the person signing up; a verification code is sent here."
    },
    "quote": {
      "type": "object",
      "required": [
        "cvr",
        "products",
        "answers"
      ],
      "properties": {
        "cvr": {
          "type": "string",
          "pattern": "^[0-9]{8}$",
          "description": "Danish company registration number (CVR), eight digits, e.g. 39488914."
        },
        "region": {
          "type": "string",
          "const": "dk",
          "default": "dk",
          "description": "Market. Only Denmark (dk) is supported. Optional; defaults to dk."
        },
        "answers": {
          "type": "object",
          "description": "Every answer in one object, keyed by the catalog question `name`. A plain name answers that question for the company and for every selected product that asks it; `<product>.<name>` (e.g. loesoereforsikring.basement) answers only that product's question and takes precedence. Employee FTE questions are answered as `<name>.fullTime` and `<name>.partTime` head counts: integers from 0 to 9999 inclusive. When required, send both counts explicitly, including zero.",
          "maxProperties": 100,
          "propertyNames": {
            "type": "string",
            "maxLength": 300,
            "minLength": 1
          },
          "additionalProperties": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 2000
              },
              {
                "type": "number"
              },
              {
                "type": "boolean"
              },
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "maxLength": 256
                },
                "maxItems": 100
              }
            ],
            "description": "The answer's JSON type follows the catalog question's `type.value`: Text is a string; NumberWhole is an integer and NumberDecimal a number (numeric strings are accepted); Date is a YYYY-MM-DD string; Boolean is true or false; Select is one option key from `options` as a string; MultiSelect is an array of option keys."
          }
        },
        "products": {
          "type": "array",
          "items": {
            "type": "string",
            "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
            "maxLength": 100,
            "description": "Product slug from the catalog."
          },
          "maxItems": 20,
          "minItems": 1,
          "description": "The products to quote: 1 to 20 distinct slugs, including any products they require.",
          "uniqueItems": true
        },
        "startDate": {
          "type": "string",
          "format": "date",
          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$",
          "description": "Policy start date (YYYY-MM-DD). Defaults to today. Must be between today and one year ahead."
        },
        "partnerCode": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_-]{2,64}$",
          "description": "Optional partner discount code, 2-64 letters, digits, hyphens or underscores. Must be active."
        }
      },
      "description": "The company, products and answers to price. Send the same object again in /start.",
      "additionalProperties": false
    },
    "quoteId": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$",
      "description": "The `quoteId` of the quote the person approved."
    },
    "idempotencyKey": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{32,128}$",
      "description": "Random key of 32-128 letters, digits, hyphens or underscores. Retry a failed request with the same key and body."
    }
  },
  "description": "Creates unsigned offers from an approved quote; public signup also creates the company.",
  "additionalProperties": false
}
create_onboarding output schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "onboardingId",
    "capability",
    "state",
    "policyIds"
  ],
  "properties": {
    "state": {
      "type": "object",
      "required": [
        "value",
        "label"
      ],
      "properties": {
        "label": {
          "type": "object",
          "required": [
            "da",
            "en"
          ],
          "properties": {
            "da": {
              "type": "string",
              "minLength": 1,
              "description": "Danish label."
            },
            "en": {
              "type": "string",
              "minLength": 1,
              "description": "English label."
            }
          },
          "description": "Human-readable labels by language.",
          "additionalProperties": false
        },
        "value": {
          "enum": [
            "awaiting_verification",
            "email_confirmed",
            "verified"
          ],
          "type": "string",
          "description": "Stable machine-readable value.",
          "x-enum-labels": {
            "verified": {
              "da": "Tilbud klar",
              "en": "Offers ready"
            },
            "email_confirmed": {
              "da": "E-mail bekræftet",
              "en": "Email confirmed"
            },
            "awaiting_verification": {
              "da": "Afventer e-mailbekræftelse",
              "en": "Awaiting email verification"
            }
          }
        }
      },
      "description": "awaiting_verification: ask the person for the emailed code; email_confirmed: call /verify-email again to finish; verified: offers are ready.",
      "additionalProperties": false
    },
    "policyIds": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "IDs of the offered policies, once created."
    },
    "capability": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$",
      "description": "Secret proof of this signup. Keep it private and send it with onboardingId."
    },
    "onboardingId": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$",
      "description": "Identifies this signup."
    },
    "continuationToken": {
      "anyOf": [
        {
          "type": "string",
          "pattern": "^[a-f0-9]{64}$"
        },
        {
          "type": "null"
        }
      ],
      "description": "Secret token for /payment-handoff, valid for 15 minutes after verification."
    }
  },
  "description": "The state of a signup.",
  "additionalProperties": false
}
create_onboarding annotations
{
  "readOnlyHint": false,
  "openWorldHint": false,
  "destructiveHint": false
}
verify_onboarding_email — Confirm the signup email
Advertised by the server during the last successful anonymous check.

Confirms the signup email with the eight-digit code the person received. Codes expire after 15 minutes and allow five attempts. Keep the returned capability and continuationToken private. Does not activate insurance.

verify_onboarding_email input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "onboardingId",
    "capability",
    "code"
  ],
  "properties": {
    "code": {
      "type": "string",
      "pattern": "^[0-9]{8}$",
      "description": "The eight-digit code from the verification email. Valid for 15 minutes, five attempts."
    },
    "capability": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$",
      "description": "From the /start result."
    },
    "onboardingId": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$",
      "description": "From the /start result."
    }
  },
  "description": "Confirms the signup email with the emailed code.",
  "additionalProperties": false
}
verify_onboarding_email output schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "onboardingId",
    "capability",
    "state",
    "policyIds"
  ],
  "properties": {
    "state": {
      "type": "object",
      "required": [
        "value",
        "label"
      ],
      "properties": {
        "label": {
          "type": "object",
          "required": [
            "da",
            "en"
          ],
          "properties": {
            "da": {
              "type": "string",
              "minLength": 1,
              "description": "Danish label."
            },
            "en": {
              "type": "string",
              "minLength": 1,
              "description": "English label."
            }
          },
          "description": "Human-readable labels by language.",
          "additionalProperties": false
        },
        "value": {
          "enum": [
            "awaiting_verification",
            "email_confirmed",
            "verified"
          ],
          "type": "string",
          "description": "Stable machine-readable value.",
          "x-enum-labels": {
            "verified": {
              "da": "Tilbud klar",
              "en": "Offers ready"
            },
            "email_confirmed": {
              "da": "E-mail bekræftet",
              "en": "Email confirmed"
            },
            "awaiting_verification": {
              "da": "Afventer e-mailbekræftelse",
              "en": "Awaiting email verification"
            }
          }
        }
      },
      "description": "awaiting_verification: ask the person for the emailed code; email_confirmed: call /verify-email again to finish; verified: offers are ready.",
      "additionalProperties": false
    },
    "policyIds": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "IDs of the offered policies, once created."
    },
    "capability": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$",
      "description": "Secret proof of this signup. Keep it private and send it with onboardingId."
    },
    "onboardingId": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$",
      "description": "Identifies this signup."
    },
    "continuationToken": {
      "anyOf": [
        {
          "type": "string",
          "pattern": "^[a-f0-9]{64}$"
        },
        {
          "type": "null"
        }
      ],
      "description": "Secret token for /payment-handoff, valid for 15 minutes after verification."
    }
  },
  "description": "The state of a signup.",
  "additionalProperties": false
}
verify_onboarding_email annotations
{
  "readOnlyHint": false,
  "openWorldHint": false,
  "destructiveHint": false
}
get_onboarding_payment_link — Get the link to sign on Plain
Advertised by the server during the last successful anonymous check.

Returns a one-time link, valid for five minutes, where the person reviews the offers and sets up payment on Plain. The person must open and confirm it. Setting up payment does not accept the offers or charge money. Use the same authorized connection that created the offers and keep the target company selected; continuationToken may then be omitted.

get_onboarding_payment_link input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "onboardingId",
    "capability"
  ],
  "properties": {
    "capability": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$",
      "description": "From the /start result."
    },
    "onboardingId": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$",
      "description": "From the /start result."
    },
    "continuationToken": {
      "type": "string",
      "pattern": "^[a-f0-9]{64}$",
      "description": "The continuationToken from the /verify-email result."
    }
  },
  "description": "Requests a link for the person to review the offers and set up payment on Plain.",
  "additionalProperties": false
}
get_onboarding_payment_link output schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "url",
    "expiresAt"
  ],
  "properties": {
    "url": {
      "type": "string",
      "format": "uri",
      "description": "One-time link to open in the person's browser. Valid for five minutes."
    },
    "expiresAt": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
      "description": "When the link expires (ISO 8601 date-time)."
    }
  },
  "description": "A browser link to Plain where the person reviews and signs the offers.",
  "additionalProperties": false
}
get_onboarding_payment_link annotations
{
  "readOnlyHint": false,
  "openWorldHint": false,
  "destructiveHint": false
}
list_companies — List permitted companies
Advertised by the server during the last successful anonymous check.
Advertised read-only

Requires signing in to Plain; calling it prompts sign-in when needed. Lists companies explicitly approved for this Plain connection that you still have access to, and marks the active company. Reports granted scopes and whether the connection can create unsigned offers. Check this before quoting for an existing company or offering to create offers. Reconnect requesting onboarding:write to approve offer creation, or reconnect to approve additional companies. Company permissions are checked separately when creating offers.

list_companies input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "properties": {},
  "additionalProperties": false
}
list_companies output schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "companies",
    "scopes",
    "canCreateOffers"
  ],
  "properties": {
    "scopes": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "companies": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "companyId",
          "companyName",
          "isCurrent"
        ],
        "properties": {
          "companyId": {
            "type": "string"
          },
          "isCurrent": {
            "type": "boolean"
          },
          "companyName": {
            "type": "string"
          }
        },
        "additionalProperties": false
      }
    },
    "canCreateOffers": {
      "type": "boolean"
    }
  },
  "additionalProperties": false
}
list_companies annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}
set_company_context — Set active company
Advertised by the server during the last successful anonymous check.

Requires signing in to Plain. Changes the active Plain company for subsequent tool calls on this connection. Use an exact companyId from list_companies. All chats sharing this authorization share the selection. Already-running requests may finish for the previous company.

set_company_context input schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "companyId"
  ],
  "properties": {
    "companyId": {
      "type": "string",
      "maxLength": 256,
      "minLength": 1
    }
  },
  "additionalProperties": false
}
set_company_context output schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "companyId",
    "companyName",
    "isCurrent"
  ],
  "properties": {
    "companyId": {
      "type": "string"
    },
    "isCurrent": {
      "type": "boolean"
    },
    "companyName": {
      "type": "string"
    }
  },
  "additionalProperties": false
}
set_company_context annotations
{
  "readOnlyHint": false,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
list_active_policies — List active policies
Advertised by the server during the last successful anonymous check.
Advertised read-only

Requires signing in to Plain; calling it prompts sign-in when needed. Lists the active insurance policies for the currently selected Plain company on this connection, including product, carrier, dates, price and billing details. Amounts are in major currency units (e.g. DKK). totalPrice is the total price the customer pays; it is legally required to be split into the insurance premium and fees (statutory fees and taxes). Whenever you mention a price, state the total and its split into premium and fees.

list_active_policies input schema
{
  "type": "object",
  "properties": {}
}
list_active_policies output schema
{
  "type": "object",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "required": [
    "policies"
  ],
  "properties": {
    "policies": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "id",
          "policyNumber",
          "productName",
          "product",
          "carrierName",
          "description",
          "coverage",
          "startDate",
          "endDate",
          "totalPrice",
          "premium",
          "fees",
          "currency",
          "billingFrequency",
          "termsUrl"
        ],
        "properties": {
          "id": {
            "type": [
              "string",
              "null"
            ]
          },
          "fees": {
            "type": [
              "number",
              "null"
            ],
            "description": "Statutory fees and taxes part of totalPrice, in major currency units. Not part of the premium."
          },
          "endDate": {
            "type": [
              "string",
              "null"
            ]
          },
          "premium": {
            "type": [
              "number",
              "null"
            ],
            "description": "Insurance premium part of totalPrice, in major currency units. Excludes fees."
          },
          "product": {
            "type": [
              "string",
              "null"
            ],
            "description": "Public product slug for matching the onboarding catalog, when available."
          },
          "coverage": {
            "type": [
              "string",
              "null"
            ]
          },
          "currency": {
            "type": [
              "string",
              "null"
            ]
          },
          "termsUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "startDate": {
            "type": [
              "string",
              "null"
            ]
          },
          "totalPrice": {
            "type": [
              "number",
              "null"
            ],
            "description": "Total price per billing period in major currency units (e.g. DKK, not øre). This is the full amount the customer pays and equals premium + fees. Never present it as the premium alone."
          },
          "carrierName": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "productName": {
            "type": [
              "string",
              "null"
            ]
          },
          "policyNumber": {
            "type": [
              "string",
              "null"
            ]
          },
          "billingFrequency": {
            "anyOf": [
              {
                "type": "object",
                "required": [
                  "value",
                  "label"
                ],
                "properties": {
                  "label": {
                    "type": "object",
                    "required": [
                      "da",
                      "en"
                    ],
                    "properties": {
                      "da": {
                        "type": "string",
                        "minLength": 1,
                        "description": "Danish label."
                      },
                      "en": {
                        "type": "string",
                        "minLength": 1,
                        "description": "English label."
                      }
                    },
                    "description": "Human-readable labels by language.",
                    "additionalProperties": false
                  },
                  "value": {
                    "enum": [
                      "Monthly",
                      "Annual"
                    ],
                    "type": "string",
                    "description": "Stable machine-readable value.",
                    "x-enum-labels": {
                      "Annual": {
                        "da": "Årlig",
                        "en": "Annual"
                      },
                      "Monthly": {
                        "da": "Månedlig",
                        "en": "Monthly"
                      }
                    }
                  }
                },
                "description": "How often the customer pays: Monthly or Annual. Prices are per period of this frequency.",
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "additionalProperties": false
      }
    }
  },
  "additionalProperties": false
}
list_active_policies annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "destructiveHint": false
}

Prompts

Not advertised

No prompt definitions available.

Resources

Not advertised

No resource definitions available.

90-day uptime

A lightweight connection check about every six hours, sampled on hosts with many listings. It shows the server answered, not that its tools work.

Too few checks for a percentage yet (1 of 1 answered).

  1. 2026-11-07: 1 of 1 checks answered

30-day availability

Aggregated from read-only remote checks; this is not a tool-execution or security guarantee.

Successful checks
1 of 1 (100%)
Average latency
2116 ms
95th percentile latency
2116 ms

Maintenance history

Public check and source-refresh events. Editorial and ownership records remain private.

  • · probe · passed · 2116 ms · metadata changed · 8 tool(s)

Connect from your client

Each snippet registers the same endpoint; pick the client you use.

This server is operated by the named third party. Listing and endpoint checks are not an endorsement or a security audit. Review the operator's permissions, terms, and data handling before connecting. How listings are checked and corrected.

Claude Code

Run this once in a terminal. It registers the remote server. Use --scope project to write .mcp.json for the repo.
Claude Code configuration
claude mcp add --transport http plain-insurance-mcp https://app.plain.insurance/api/mcp

Codex

Run this once. Codex stores MCP servers in ~/.codex/config.toml (or .codex/config.toml in a trusted project).
Codex configuration
codex mcp add plain-insurance-mcp --url https://app.plain.insurance/api/mcp

Cursor

Merge this server into .cursor/mcp.json in your project, then enable plain-insurance-mcp in your MCP settings.
Cursor configuration
{
  "mcpServers": {
    "plain-insurance-mcp": {
      "url": "https://app.plain.insurance/api/mcp"
    }
  }
}

Cline

Merge this into cline_mcp_settings.json (Cline → MCP Servers → Configure).
Cline configuration
{
  "mcpServers": {
    "plain-insurance-mcp": {
      "type": "streamableHttp",
      "url": "https://app.plain.insurance/api/mcp",
      "disabled": false
    }
  }
}

VS Code

Merge this server into .vscode/mcp.json. Use MCP: List Servers to start plain-insurance-mcp and inspect its tools.
VS Code configuration
{
  "servers": {
    "plain-insurance-mcp": {
      "type": "http",
      "url": "https://app.plain.insurance/api/mcp"
    }
  }
}

Gemini CLI

Run this once. It writes .gemini/settings.json in the current project; pass --scope user for ~/.gemini/settings.json.
Gemini CLI configuration
gemini mcp add --transport http plain-insurance-mcp https://app.plain.insurance/api/mcp

Claude Desktop

Open Claude Settings > Connectors > Add custom connector, enter this server URL and enable it for the conversation. This is a manual remote-connector handoff, not a desktop JSON configuration.
Claude Desktop configuration
https://app.plain.insurance/api/mcp

Windsurf

Merge this into ~/.codeium/windsurf/mcp_config.json, then refresh MCP in Cascade. Windsurf remote servers use serverUrl, not url.
Windsurf configuration
{
  "mcpServers": {
    "plain-insurance-mcp": {
      "serverUrl": "https://app.plain.insurance/api/mcp"
    }
  }
}

Let your agent find its tools

Find servers like Plain Insurance MCP from your agent. Add Koskamo once to search the index for endpoints, access requirements, and client setup. Public discovery needs no key.

Do you run Plain Insurance MCP?

Prove ownership with a DNS record, a well-known file, or your Official MCP Registry entry, and your edits publish immediately. Proofs are re-checked weekly; admins can still hide or remove a listing.

Claim this listing