{"openapi":"3.0.3","info":{"title":"Plain API","version":"1.0.0","description":"Plain sells business insurance to Danish companies. This API lets an agent or a website quote Plain's\nproducts for a new company and prepare offers that the customer signs on Plain.\n\n## Flow\n1. `GET /api/v1/onboarding/catalog?cvr=…` lists the products and the questions to answer.\n2. `POST /api/v1/onboarding/quotes` returns indicative prices for the selected products.\n3. Optionally, `POST /api/v1/onboarding/start` with the same quote request creates the company and its offers, ready\n   for the customer to sign on Plain. The person confirms their email with the emailed code (`/verify-email`) and\n   opens a one-time link (`/payment-handoff`) to review and sign the offers and set up payment on Plain.\n\n## Binding and prices\nThe API never binds insurance and never charges anyone: only the customer can accept the offers, on Plain. Quotes are\nindicative. Prices are recalculated when the offers are created and can change with the answers or before the customer\nsigns. Prices are in DKK (kroner, major units) per billing period (Monthly or Annual) and include fees (statutory fees and taxes).\n\n## Errors\nEvery error response has the body `{ \"errors\": [...] }`. Each error has a machine-readable `code` and a\n`nextAction`: fixAnswer, askUser, retryLater, contactPlain or none. 422 means the request was valid but the\nonboarding rules rejected it, for example a missing answer or an unknown CVR.\n\n## Rate limits\n- Per client: 20 requests per 10 seconds across `/api/v1/onboarding/*`; a client over the limit is blocked for 10 seconds.\n- Overall: 1,200 reads per minute and 300 writes per hour.\n- At most three signups per email address per day.\n\nResponses 429 and 503 carry a `Retry-After` header in seconds.\n\n## Browsers\n`GET /catalog`, `POST /quotes` and `GET /openapi` can be called from any website, without credentials.\n`/start`, `/verify-email` and `/payment-handoff` are for servers and Plain's own pages only.","termsOfService":"https://assets.plain.insurance/terms/A%20Plain%20Insurance%20-%20General%20Terms%20-%201.pdf","x-general-terms":{"termsPdf":"https://assets.plain.insurance/terms/A%20Plain%20Insurance%20-%20General%20Terms%20-%201.pdf","termsMarkdown":"https://assets.plain.insurance/terms/markdown/PLAIN-GENERAL-01.md"},"contact":{"name":"Plain","email":"hello@plain.insurance","url":"https://plain.insurance"}},"servers":[{"url":"https://app.plain.insurance"}],"tags":[{"name":"Onboarding","description":"Quote and prepare insurance offers for a new Danish company."}],"paths":{"/api/v1/onboarding/catalog":{"get":{"operationId":"list_onboarding_catalog","summary":"List products and questions","description":"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, but their questions remain listed so answers can still be collected. Enum fields use {value, label: {da, en}}; use value for logic and label for display. Read-only: nothing is created. Unknown query parameters, including tracking keys such as utm_source, are ignored.","tags":["Onboarding"],"security":[],"parameters":[{"name":"region","in":"query","required":false,"description":"Market. Only Denmark (dk) is supported. Optional; defaults to dk.","schema":{"default":"dk","type":"string","enum":["dk"]}},{"name":"cvr","in":"query","required":false,"description":"Danish company registration number (CVR), eight digits, e.g. 39488914.","schema":{"type":"string","pattern":"^[0-9]{8}$"}}],"responses":{"200":{"description":"Success.","headers":{"X-Robots-Tag":{"$ref":"#/components/headers/RobotsTag"},"Link":{"$ref":"#/components/headers/DescribedBy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Catalog"},"example":{"products":[{"product":"erhvervsansvarsforsikring","name":"Erhvervsansvarsforsikring","description":"Erhvervsansvarsforsikringen beskytter virksomheden mod erstatningskrav, hvis du eller en ansat kommer til at lave skade på andre personer eller på andres ting.","coverage":"Din erhvervsansvarsforsikring dækker, hvis du eller dine medarbejdere kommer til at lave skade på andre personer eller på andres ting. …","termsUrl":"https://assets.plain.insure/terms/PLAIN-EAF-01.pdf","termsDocuments":{"termsPdf":"https://assets.plain.insure/terms/PLAIN-EAF-01.pdf","ipidPdf":"https://assets.plain.insure/terms/IPID%20PLAIN-EAF-01.pdf","ipidYaml":"https://assets.plain.insure/terms/ipid/PLAIN-EAF-01.yml","termsMarkdown":"https://assets.plain.insure/terms/markdown/PLAIN-EAF-01.md"},"region":"dk","currency":"DKK","billingFrequency":{"value":"Monthly","label":{"da":"Månedlig","en":"Monthly"}},"automaticQuote":true,"pricePerEmployee":false,"questions":[{"question":{"name":"fte.fullTime","title":"Hvor mange fuldtidsansatte arbejder der i virksomheden?","description":"Tæl personer, der arbejder mere end 16 timer om ugen. Ejere og ulønnet personale tæller også med.","type":{"value":"NumberWhole","label":{"da":"Heltal","en":"Whole number"}},"min":0,"max":9999,"step":1},"required":true},{"question":{"name":"fte.partTime","title":"Hvor mange deltidsansatte arbejder der i virksomheden?","description":"Deltid er op til og med 16 timer om ugen. Ejere og ulønnet personale tæller også med.","type":{"value":"NumberWhole","label":{"da":"Heltal","en":"Whole number"}},"min":0,"max":9999,"step":1},"required":true}]},{"product":"loesoereforsikring","name":"Løsøreforsikring","description":"Din virksomheds indbosforsikring. På kontoret, i hjemmet og på farten. Løsøreforsikringen dækker skader på virksomhedens ting, varer og IT-udstyr ved f.eks. tyveri, vandskade, brand eller anden skade.","coverage":"Løsøreforsikringen er din virksomheds indbosforsikring. …","termsUrl":"https://assets.plain.insure/terms/PLAIN-L%C3%98S-01.pdf","termsDocuments":{"termsPdf":"https://assets.plain.insure/terms/PLAIN-L%C3%98S-01.pdf","ipidPdf":"https://assets.plain.insure/terms/IPID%20PLAIN-L%C3%98S-01.pdf","ipidYaml":"https://assets.plain.insure/terms/ipid/PLAIN-L%C3%98S-01.yml","termsMarkdown":"https://assets.plain.insure/terms/markdown/PLAIN-L%C3%98S-01.md"},"region":"dk","currency":"DKK","billingFrequency":{"value":"Monthly","label":{"da":"Månedlig","en":"Monthly"}},"automaticQuote":true,"pricePerEmployee":false,"questions":[{"question":{"name":"basement","title":"Har I adgang til en kælder, som I benytter?","description":"","type":{"value":"Boolean","label":{"da":"Ja eller nej","en":"Yes or no"}}},"required":true},{"question":{"name":"property","title":"Hvor meget har I cirka værdier for i virksomheden?","description":"Det kan være IT-udstyr, maskiner, inventar, kontormøbler og varelager.","type":{"value":"Select","label":{"da":"Vælg én","en":"Select one"}},"unit":"DKK","options":[{"value":"250000","label":{"da":"Under 250.000 kr."}},{"value":"500000","label":{"da":"250.000-500.000 kr."}},{"value":"750000","label":{"da":"500.000-750.000 kr."}},{"value":"1500000","label":{"da":"1.000.000-1.500.000 kr."}},{"value":"2000000","label":{"da":"1.500.000-2.000.000 kr."}},{"value":"3000000","label":{"da":"2.000.000-3.000.000 kr."}},{"value":"10000000","label":{"da":"Over 3.000.000 kr."}}]},"required":true}]}],"productDependencies":[],"errors":[]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"description":"The CVR number is not registered (cvr_not_found).","headers":{"X-Robots-Tag":{"$ref":"#/components/headers/RobotsTag"},"Link":{"$ref":"#/components/headers/DescribedBy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorList"},"example":{"errors":[{"code":{"value":"cvr_not_found","label":{"da":"CVR-nummer ikke fundet","en":"CVR number not found"}},"message":"No active Danish company with CVR 00000001","nextAction":{"value":"fixAnswer","label":{"da":"Ret oplysningerne og prøv igen","en":"Correct the request and try again"}}}]}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/api/v1/onboarding/quotes":{"post":{"operationId":"quote_onboarding_products","summary":"Quote products","description":"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. Give the customer continueUrl to review a public signup quote without creating anything. Also available as GET /api/v1/onboarding/quote: put request fields and answer names directly in the query. Separate products and MultiSelect values with commas; an empty MultiSelect value means an empty list. POST /quotes remains supported.","tags":["Onboarding"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteRequest"},"example":{"cvr":"39488914","products":["erhvervsansvarsforsikring"],"answers":{"fte.fullTime":110,"fte.partTime":20}}}}},"responses":{"200":{"description":"Success.","headers":{"X-Robots-Tag":{"$ref":"#/components/headers/RobotsTag"},"Link":{"$ref":"#/components/headers/DescribedBy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteResult"},"example":{"quotes":[{"product":"erhvervsansvarsforsikring","name":"Erhvervsansvarsforsikring","termsUrl":"https://assets.plain.insure/terms/PLAIN-EAF-01.pdf","termsDocuments":{"termsPdf":"https://assets.plain.insure/terms/PLAIN-EAF-01.pdf","ipidPdf":"https://assets.plain.insure/terms/IPID%20PLAIN-EAF-01.pdf","ipidYaml":"https://assets.plain.insure/terms/ipid/PLAIN-EAF-01.yml","termsMarkdown":"https://assets.plain.insure/terms/markdown/PLAIN-EAF-01.md"},"status":{"value":"quoted","label":{"da":"Pris beregnet","en":"Quoted"}},"currency":"DKK","billingFrequency":{"value":"Monthly","label":{"da":"Månedlig","en":"Monthly"}},"price":{"totalPrice":1920,"premium":1898.88,"fees":21.12},"annualPrice":{"totalPrice":23040,"premium":22786.56,"fees":253.44},"pricePerEmployee":false,"coverage":{"sumInsured":"10.000.000 kr.","deductible":"5.000 kr.","summary":"Dækker erstatningskrav for skader på personer og ting."},"display":{"da":"Erhvervsansvarsforsikring: 1.920,00 kr. pr. måned (præmie 1.898,88 kr. + afgifter 21,12 kr.).","en":"Erhvervsansvarsforsikring: DKK 1,920.00 per month (premium DKK 1,898.88 + fees DKK 21.12)."}},{"product":"retshjaelpsforsikring","name":"Retshjælpsforsikring","termsUrl":null,"termsDocuments":null,"status":{"value":"contactPlain","label":{"da":"Kontakt Plain","en":"Contact Plain"}},"currency":"DKK","billingFrequency":{"value":"Annual","label":{"da":"Årlig","en":"Annual"}},"pricePerEmployee":false,"coverage":{"sumInsured":null,"deductible":null,"summary":null},"issue":{"code":{"value":"manual_review_required","label":{"da":"Individuelt tilbud påkrævet","en":"Individual offer required"}},"message":"This product needs an individual offer from Plain.","nextAction":{"value":"contactPlain","label":{"da":"Kontakt Plain","en":"Contact Plain"}},"product":"retshjaelpsforsikring"},"display":{"da":"Retshjælpsforsikring kræver et individuelt tilbud. Kontakt Plain for en pris.","en":"Retshjælpsforsikring needs an individual offer. Contact Plain for a price."}}],"totals":{"monthly":{"totalPrice":1920,"premium":1898.88,"fees":21.12},"annual":{"totalPrice":23040,"premium":22786.56,"fees":253.44},"excludedProducts":["retshjaelpsforsikring"],"display":{"da":"I alt 1.920,00 kr. pr. måned, 23.040,00 kr. pr. år (præmie 22.786,56 kr. + afgifter 253,44 kr. pr. år). Ikke medregnet: Retshjælpsforsikring.","en":"Total DKK 1,920.00 per month, DKK 23,040.00 per year (premium DKK 22,786.56 + fees DKK 253.44 per year). Not included: Retshjælpsforsikring."}},"quoteId":"bc3cda09c799a4b6ba282d3b04c3e05e6d0c883f91614557ad192ca97b1e9ace","startDate":"2026-06-01","expiresAt":"2026-06-02T00:00:00.000Z","continueUrl":"https://app.plain.insurance/onboarding/quote?request=eyJjdnIiOiIzOTQ4ODkxNCJ9","disclaimer":{"da":"Priserne er vejledende og kan ændre sig, hvis oplysningerne ændres. Intet er bindende, før du har gennemgået og underskrevet tilbuddet på Plain.","en":"Prices are indicative and may change if the information changes. Nothing is binding until you have reviewed and signed the offer on Plain."}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"description":"No prices: an answer is missing or invalid, a product is unknown or ineligible, the CVR is not registered, the start date is out of range, or a price is unavailable. Each error's code, question and nextAction say what to do.","headers":{"X-Robots-Tag":{"$ref":"#/components/headers/RobotsTag"},"Link":{"$ref":"#/components/headers/DescribedBy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorList"},"example":{"errors":[{"code":{"value":"invalid_answer","label":{"da":"Ugyldigt svar","en":"Invalid answer"}},"message":"The answer does not match the question type or options.","nextAction":{"value":"fixAnswer","label":{"da":"Ret oplysningerne og prøv igen","en":"Correct the request and try again"}},"question":"property","allowedValues":["250000","500000","750000","1500000","2000000","3000000","10000000"]}]}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/api/v1/onboarding/start":{"post":{"operationId":"create_onboarding","summary":"Create unsigned insurance offers","description":"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. Public signup creates a new company. email and name are required; an existing account or company must sign in instead. Send the idempotency key as `idempotencyKey` or in the `Idempotency-Key` header; when both are sent they must be equal.","tags":["Onboarding"],"security":[],"parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{32,128}$"},"description":"Alternative to the idempotencyKey body field. When both are sent they must be equal, otherwise the response is 422."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartRequest"},"example":{"quote":{"cvr":"39488914","products":["erhvervsansvarsforsikring"],"answers":{"fte.fullTime":110,"fte.partTime":20}},"email":"anna.jensen@example.com","name":"Anna Jensen","quoteId":"bc3cda09c799a4b6ba282d3b04c3e05e6d0c883f91614557ad192ca97b1e9ace","idempotencyKey":"0b6f2c1e-8d4a-4c7b-9e2f-5a3d1c8b7e60"}}}},"responses":{"200":{"description":"Success.","headers":{"X-Robots-Tag":{"$ref":"#/components/headers/RobotsTag"},"Link":{"$ref":"#/components/headers/DescribedBy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingResult"},"example":{"onboardingId":"3f1d9a5c0e7b4a2f9c8d6e5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c7b6a59","capability":"9b7e5c3a1f2d4e6b8a0c9d7e5f3b1a2c4d6e8f0a1b3c5d7e9f2a4b6c8d0e1f3a","state":{"value":"awaiting_verification","label":{"da":"Afventer e-mailbekræftelse","en":"Awaiting email verification"}},"policyIds":[]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"description":"The offers could not be created: the quote is no longer valid (the same errors as for quotes), prices changed (price_changed), the email must sign in (sign_in_required), the idempotency key was reused with another body (idempotency_conflict) or differs from the Idempotency-Key header (idempotency_key_mismatch), or the email has had too many signups today.","headers":{"X-Robots-Tag":{"$ref":"#/components/headers/RobotsTag"},"Link":{"$ref":"#/components/headers/DescribedBy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorList"},"example":{"errors":[{"code":{"value":"price_changed","label":{"da":"Prisen er ændret","en":"Price changed"}},"message":"Prices changed. Request a new quote before continuing.","nextAction":{"value":"fixAnswer","label":{"da":"Ret oplysningerne og prøv igen","en":"Correct the request and try again"}}}]}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/api/v1/onboarding/verify-email":{"post":{"operationId":"verify_onboarding_email","summary":"Confirm the signup email","description":"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.","tags":["Onboarding"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyEmailRequest"},"example":{"onboardingId":"3f1d9a5c0e7b4a2f9c8d6e5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c7b6a59","capability":"9b7e5c3a1f2d4e6b8a0c9d7e5f3b1a2c4d6e8f0a1b3c5d7e9f2a4b6c8d0e1f3a","code":"48213907"}}}},"responses":{"200":{"description":"Success.","headers":{"X-Robots-Tag":{"$ref":"#/components/headers/RobotsTag"},"Link":{"$ref":"#/components/headers/DescribedBy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingResult"},"example":{"onboardingId":"3f1d9a5c0e7b4a2f9c8d6e5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c7b6a59","capability":"9b7e5c3a1f2d4e6b8a0c9d7e5f3b1a2c4d6e8f0a1b3c5d7e9f2a4b6c8d0e1f3a","state":{"value":"verified","label":{"da":"Tilbud klar","en":"Offers ready"}},"policyIds":["PL-D9C2EC9H-8DCF507D"],"continuationToken":"5e3c1a9f7d2b4e6a8c0f1e3d5b7a9c2e4f6a8b0d1c3e5f7a9b2d4c6e8f0a1b3c"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"description":"The code is wrong or expired, or the five attempts are used up (invalid_verification).","headers":{"X-Robots-Tag":{"$ref":"#/components/headers/RobotsTag"},"Link":{"$ref":"#/components/headers/DescribedBy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorList"},"example":{"errors":[{"code":{"value":"invalid_verification","label":{"da":"Ugyldig eller udløbet bekræftelseskode","en":"Invalid or expired verification code"}},"message":"The verification code is invalid or expired.","nextAction":{"value":"askUser","label":{"da":"Bed kunden om oplysninger eller login","en":"Ask the customer for input or to sign in"}}}]}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/api/v1/onboarding/payment-handoff":{"post":{"operationId":"get_onboarding_payment_link","summary":"Get the link to sign on Plain","description":"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. continuationToken from /verify-email is required.","tags":["Onboarding"],"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentHandoffRequest"},"example":{"onboardingId":"3f1d9a5c0e7b4a2f9c8d6e5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0e9d8c7b6a59","capability":"9b7e5c3a1f2d4e6b8a0c9d7e5f3b1a2c4d6e8f0a1b3c5d7e9f2a4b6c8d0e1f3a","continuationToken":"5e3c1a9f7d2b4e6a8c0f1e3d5b7a9c2e4f6a8b0d1c3e5f7a9b2d4c6e8f0a1b3c"}}}},"responses":{"200":{"description":"Success.","headers":{"X-Robots-Tag":{"$ref":"#/components/headers/RobotsTag"},"Link":{"$ref":"#/components/headers/DescribedBy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentLink"},"example":{"url":"https://app.plain.insurance/onboarding/continue#YW5uYS5qZW5zZW5AZXhhbXBsZS5jb20.4k9QzR2xV7bN1mC5tY8wE3uJ6hL0pS2a.dG9rZW4tZXhhbXBsZS1kby1ub3QtdXNlLTAxMjM0NTY3ODk","expiresAt":"2026-10-24T09:05:00Z"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/Unavailable"}}}},"/api/v1/onboarding/quote":{"get":{"operationId":"quote_onboarding_products_get","summary":"Quote products using query parameters","tags":["Onboarding"],"security":[],"description":"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. Give the customer continueUrl to review a public signup quote without creating anything. Also available as GET /api/v1/onboarding/quote: put request fields and answer names directly in the query. Separate products and MultiSelect values with commas; an empty MultiSelect value means an empty list. POST /quotes remains supported. Example: /api/v1/onboarding/quote?cvr=39488914&products=erhvervsansvarsforsikring&fte.fullTime=8&fte.partTime=2. Answer parameters use the exact catalog question name, without an answers prefix. MultiSelect answers use comma-separated values, including a single value without a comma; an empty value means an empty list. Text and Select answers are scalar strings. Unknown question parameters are ignored and returned as warnings. Duplicate parameters are rejected. GET creates nothing.","parameters":[{"name":"region","in":"query","required":false,"description":"Market. Only Denmark (dk) is supported. Optional; defaults to dk.","schema":{"default":"dk","type":"string","enum":["dk"]}},{"name":"cvr","in":"query","required":true,"description":"Danish company registration number (CVR), eight digits, e.g. 39488914.","schema":{"type":"string","pattern":"^[0-9]{8}$"}},{"name":"startDate","in":"query","required":false,"description":"Policy start date (YYYY-MM-DD). Defaults to today. Must be between today and one year ahead.","schema":{"type":"string","format":"date"}},{"name":"partnerCode","in":"query","required":false,"description":"Optional partner discount code, 2-64 letters, digits, hyphens or underscores. Must be active.","schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{2,64}$"}},{"name":"products","in":"query","required":true,"description":"The products to quote: 1 to 20 distinct slugs, including any products they require.","schema":{"minItems":1,"maxItems":20,"type":"array","items":{"type":"string","maxLength":100,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$","description":"Product slug from the catalog."},"uniqueItems":true},"style":"form","explode":false,"example":["erhvervsansvarsforsikring"]},{"name":"fte.fullTime","in":"query","required":false,"description":"Example answer parameter from the catalog. Supply both FTE counts when required, including zero.","schema":{"type":"integer","minimum":0,"maximum":9999}},{"name":"fte.partTime","in":"query","required":false,"description":"Example answer parameter from the catalog. Supply both FTE counts when required, including zero.","schema":{"type":"integer","minimum":0,"maximum":9999}}],"responses":{"200":{"description":"Success.","headers":{"X-Robots-Tag":{"$ref":"#/components/headers/RobotsTag"},"Link":{"$ref":"#/components/headers/DescribedBy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteResult"},"example":{"quotes":[{"product":"erhvervsansvarsforsikring","name":"Erhvervsansvarsforsikring","termsUrl":"https://assets.plain.insure/terms/PLAIN-EAF-01.pdf","termsDocuments":{"termsPdf":"https://assets.plain.insure/terms/PLAIN-EAF-01.pdf","ipidPdf":"https://assets.plain.insure/terms/IPID%20PLAIN-EAF-01.pdf","ipidYaml":"https://assets.plain.insure/terms/ipid/PLAIN-EAF-01.yml","termsMarkdown":"https://assets.plain.insure/terms/markdown/PLAIN-EAF-01.md"},"status":{"value":"quoted","label":{"da":"Pris beregnet","en":"Quoted"}},"currency":"DKK","billingFrequency":{"value":"Monthly","label":{"da":"Månedlig","en":"Monthly"}},"price":{"totalPrice":1920,"premium":1898.88,"fees":21.12},"annualPrice":{"totalPrice":23040,"premium":22786.56,"fees":253.44},"pricePerEmployee":false,"coverage":{"sumInsured":"10.000.000 kr.","deductible":"5.000 kr.","summary":"Dækker erstatningskrav for skader på personer og ting."},"display":{"da":"Erhvervsansvarsforsikring: 1.920,00 kr. pr. måned (præmie 1.898,88 kr. + afgifter 21,12 kr.).","en":"Erhvervsansvarsforsikring: DKK 1,920.00 per month (premium DKK 1,898.88 + fees DKK 21.12)."}},{"product":"retshjaelpsforsikring","name":"Retshjælpsforsikring","termsUrl":null,"termsDocuments":null,"status":{"value":"contactPlain","label":{"da":"Kontakt Plain","en":"Contact Plain"}},"currency":"DKK","billingFrequency":{"value":"Annual","label":{"da":"Årlig","en":"Annual"}},"pricePerEmployee":false,"coverage":{"sumInsured":null,"deductible":null,"summary":null},"issue":{"code":{"value":"manual_review_required","label":{"da":"Individuelt tilbud påkrævet","en":"Individual offer required"}},"message":"This product needs an individual offer from Plain.","nextAction":{"value":"contactPlain","label":{"da":"Kontakt Plain","en":"Contact Plain"}},"product":"retshjaelpsforsikring"},"display":{"da":"Retshjælpsforsikring kræver et individuelt tilbud. Kontakt Plain for en pris.","en":"Retshjælpsforsikring needs an individual offer. Contact Plain for a price."}}],"totals":{"monthly":{"totalPrice":1920,"premium":1898.88,"fees":21.12},"annual":{"totalPrice":23040,"premium":22786.56,"fees":253.44},"excludedProducts":["retshjaelpsforsikring"],"display":{"da":"I alt 1.920,00 kr. pr. måned, 23.040,00 kr. pr. år (præmie 22.786,56 kr. + afgifter 253,44 kr. pr. år). Ikke medregnet: Retshjælpsforsikring.","en":"Total DKK 1,920.00 per month, DKK 23,040.00 per year (premium DKK 22,786.56 + fees DKK 253.44 per year). Not included: Retshjælpsforsikring."}},"quoteId":"bc3cda09c799a4b6ba282d3b04c3e05e6d0c883f91614557ad192ca97b1e9ace","startDate":"2026-06-01","expiresAt":"2026-06-02T00:00:00.000Z","continueUrl":"https://app.plain.insurance/onboarding/quote?request=eyJjdnIiOiIzOTQ4ODkxNCJ9","disclaimer":{"da":"Priserne er vejledende og kan ændre sig, hvis oplysningerne ændres. Intet er bindende, før du har gennemgået og underskrevet tilbuddet på Plain.","en":"Prices are indicative and may change if the information changes. Nothing is binding until you have reviewed and signed the offer on Plain."}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"413":{"$ref":"#/components/responses/TooLarge"},"422":{"description":"No prices: an answer is missing or invalid, a product is unknown or ineligible, the CVR is not registered, the start date is out of range, or a price is unavailable. Each error's code, question and nextAction say what to do.","headers":{"X-Robots-Tag":{"$ref":"#/components/headers/RobotsTag"},"Link":{"$ref":"#/components/headers/DescribedBy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorList"},"example":{"errors":[{"code":{"value":"invalid_answer","label":{"da":"Ugyldigt svar","en":"Invalid answer"}},"message":"The answer does not match the question type or options.","nextAction":{"value":"fixAnswer","label":{"da":"Ret oplysningerne og prøv igen","en":"Correct the request and try again"}},"question":"property","allowedValues":["250000","500000","750000","1500000","2000000","3000000","10000000"]}]}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"$ref":"#/components/responses/Unavailable"}}}}},"components":{"schemas":{"QuoteRequest":{"type":"object","properties":{"region":{"default":"dk","description":"Market. Only Denmark (dk) is supported. Optional; defaults to dk.","type":"string","enum":["dk"]},"cvr":{"type":"string","pattern":"^[0-9]{8}$","description":"Danish company registration number (CVR), eight digits, e.g. 39488914."},"startDate":{"type":"string","format":"date","description":"Policy start date (YYYY-MM-DD). Defaults to today. Must be between today and one year ahead."},"partnerCode":{"description":"Optional partner discount code, 2-64 letters, digits, hyphens or underscores. Must be active.","type":"string","pattern":"^[A-Za-z0-9_-]{2,64}$"},"products":{"minItems":1,"maxItems":20,"type":"array","items":{"type":"string","maxLength":100,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$","description":"Product slug from the catalog."},"description":"The products to quote: 1 to 20 distinct slugs, including any products they require.","uniqueItems":true},"answers":{"$ref":"#/components/schemas/Answers"}},"required":["cvr","products","answers"],"additionalProperties":false,"description":"The company, products and answers to price. Send the same object again in /start."},"Answers":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/AnswerValue"},"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},"AnswerValue":{"anyOf":[{"type":"string","maxLength":2000},{"type":"number"},{"type":"boolean"},{"maxItems":100,"type":"array","items":{"type":"string","maxLength":256}}],"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."},"StartRequest":{"type":"object","properties":{"quote":{"$ref":"#/components/schemas/QuoteRequest"},"email":{"description":"Email address of the person signing up; a verification code is sent here.","type":"string","maxLength":254,"format":"email"},"name":{"description":"Full name of the person signing up. Must not contain links or markup.","type":"string","minLength":1,"maxLength":200},"quoteId":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"The `quoteId` of the quote the person approved."},"idempotencyKey":{"description":"Random key of 32-128 letters, digits, hyphens or underscores. Retry a failed request with the same key and body.","type":"string","pattern":"^[A-Za-z0-9_-]{32,128}$"}},"required":["quote","quoteId"],"additionalProperties":false,"description":"Creates unsigned offers from an approved quote; public signup also creates the company."},"VerifyEmailRequest":{"type":"object","properties":{"onboardingId":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"From the /start result."},"capability":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"From the /start result."},"code":{"type":"string","pattern":"^[0-9]{8}$","description":"The eight-digit code from the verification email. Valid for 15 minutes, five attempts."}},"required":["onboardingId","capability","code"],"additionalProperties":false,"description":"Confirms the signup email with the emailed code."},"PaymentHandoffRequest":{"type":"object","properties":{"onboardingId":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"From the /start result."},"capability":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"From the /start result."},"continuationToken":{"description":"The continuationToken from the /verify-email result.","type":"string","pattern":"^[a-f0-9]{64}$"}},"required":["onboardingId","capability"],"additionalProperties":false,"description":"Requests a link for the person to review the offers and set up payment on Plain."},"Error":{"type":"object","properties":{"code":{"$ref":"#/components/schemas/ErrorCode"},"message":{"type":"string","description":"Human-readable explanation in English."},"nextAction":{"$ref":"#/components/schemas/NextAction"},"field":{"description":"Request field to correct, using dot notation for nested fields.","type":"string"},"product":{"description":"Slug of the product the error concerns, when it concerns one.","type":"string"},"didYouMean":{"description":"Suggested catalog product slug or answer key. Retry only after confirming the intended value.","type":"string"},"question":{"description":"Answer key of the question the error concerns, as used in `answers`.","type":"string"},"questionLabel":{"description":"Danish question label, when the question can be identified in the catalog.","type":"object","properties":{"da":{"type":"string","description":"Danish question label from the catalog."}},"required":["da"],"additionalProperties":false},"reason":{"description":"For price_unavailable: industry_not_covered when the product has no tariff for the company's industry, otherwise unknown.","type":"object","properties":{"value":{"type":"string","enum":["industry_not_covered","unknown"],"description":"Stable machine-readable value.","x-enum-labels":{"industry_not_covered":{"da":"Branchen er ikke dækket","en":"Industry not covered"},"unknown":{"da":"Ukendt årsag","en":"Unknown reason"}}},"label":{"type":"object","properties":{"da":{"type":"string","minLength":1,"description":"Danish label."},"en":{"type":"string","minLength":1,"description":"English label."}},"required":["da","en"],"additionalProperties":false,"description":"Human-readable labels by language."}},"required":["value","label"],"additionalProperties":false},"allowedValues":{"description":"For invalid_answer on a select question: the option keys that are accepted.","type":"array","items":{"type":"string"}},"min":{"description":"Inclusive minimum: the earliest start date (YYYY-MM-DD) for invalid_start_date, or the numeric lower bound for invalid_answer.","anyOf":[{"type":"string","format":"date"},{"type":"number"}]},"max":{"description":"Inclusive maximum: the latest start date (YYYY-MM-DD) for invalid_start_date, or the numeric upper bound for invalid_answer.","anyOf":[{"type":"string","format":"date"},{"type":"number"}]}},"required":["code","message","nextAction"],"additionalProperties":false,"description":"One problem with a request."},"ErrorList":{"type":"object","properties":{"errors":{"minItems":1,"type":"array","items":{"$ref":"#/components/schemas/Error"},"description":"Every problem found; fix all of them before retrying."},"warnings":{"description":"Ignored answer keys, including suggested corrections when available.","type":"array","items":{"type":"object","properties":{"code":{"type":"string","enum":["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":{"description":"Suggested answer key. Confirm it before resending; answers are never auto-corrected.","type":"string"},"questionLabel":{"description":"Danish label of the suggested question, when available.","type":"object","properties":{"da":{"type":"string","description":"Danish question label from the catalog."}},"required":["da"],"additionalProperties":false}},"required":["code","question"],"additionalProperties":false}}},"required":["errors"],"additionalProperties":false,"description":"The body of every error response."},"ErrorCode":{"type":"object","properties":{"value":{"type":"string","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"],"description":"Stable machine-readable value.","x-enum-labels":{"invalid_answer":{"da":"Ugyldigt svar","en":"Invalid answer"},"missing_answer":{"da":"Svar mangler","en":"Missing answer"},"ambiguous_question":{"da":"Spørgsmålet er ikke entydigt","en":"Ambiguous question"},"invalid_start_date":{"da":"Ugyldig startdato","en":"Invalid start date"},"cvr_not_found":{"da":"CVR-nummer ikke fundet","en":"CVR number not found"},"invalid_products":{"da":"Ugyldige produkter","en":"Invalid products"},"missing_product":{"da":"Påkrævet produkt mangler","en":"Required product missing"},"invalid_request":{"da":"Ugyldig forespørgsel","en":"Invalid request"},"request_too_large":{"da":"Forespørgslen er for stor","en":"Request too large"},"unsupported_media_type":{"da":"Indholdstypen understøttes ikke","en":"Unsupported media type"},"wrong_identity_mode":{"da":"Forkert godkendelsesmetode","en":"Wrong authentication method"},"idempotency_key_mismatch":{"da":"Idempotensnøglerne er forskellige","en":"Idempotency keys do not match"},"idempotency_conflict":{"da":"Idempotensnøglen er allerede brugt til andre oplysninger","en":"Idempotency key already used for different input"},"price_changed":{"da":"Prisen er ændret","en":"Price changed"},"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"},"sign_in_required":{"da":"Login påkrævet","en":"Sign-in required"},"onboarding_expired":{"da":"Tilmeldingen er udløbet","en":"Signup expired"},"not_authenticated":{"da":"Ikke logget ind","en":"Not signed in"},"not_authorized":{"da":"Ingen adgang","en":"Not authorized"},"consent_required":{"da":"Samtykke påkrævet","en":"Consent required"},"temporarily_unavailable":{"da":"Midlertidigt utilgængelig","en":"Temporarily unavailable"},"invalid_backend_response":{"da":"Ugyldigt svar fra serveren","en":"Invalid backend response"},"rate_limited":{"da":"For mange forespørgsler","en":"Too many requests"},"operation_in_progress":{"da":"Handlingen er i gang","en":"Operation in progress"},"price_unavailable":{"da":"Prisen kan ikke beregnes","en":"Price unavailable"},"manual_review_required":{"da":"Individuelt tilbud påkrævet","en":"Individual offer required"},"pricing_configuration":{"da":"Prisopsætningen skal gennemgås","en":"Pricing configuration needs review"},"unsupported_input":{"da":"Oplysningerne understøttes ikke","en":"Unsupported input"},"unexpected_error":{"da":"Uventet fejl","en":"Unexpected error"},"not_found":{"da":"Ikke fundet","en":"Not found"}}},"label":{"type":"object","properties":{"da":{"type":"string","minLength":1,"description":"Danish label."},"en":{"type":"string","minLength":1,"description":"English label."}},"required":["da","en"],"additionalProperties":false,"description":"Human-readable labels by language."}},"required":["value","label"],"additionalProperties":false,"description":"Error code and its translated labels."},"NextAction":{"type":"object","properties":{"value":{"type":"string","enum":["fixAnswer","askUser","retryLater","contactPlain","none"],"description":"Stable machine-readable value.","x-enum-labels":{"fixAnswer":{"da":"Ret oplysningerne og prøv igen","en":"Correct the request and try again"},"askUser":{"da":"Bed kunden om oplysninger eller login","en":"Ask the customer for input or to sign in"},"retryLater":{"da":"Prøv igen senere","en":"Try again later"},"contactPlain":{"da":"Kontakt Plain","en":"Contact Plain"},"none":{"da":"Ingen handling nødvendig","en":"No action needed"}}},"label":{"type":"object","properties":{"da":{"type":"string","minLength":1,"description":"Danish label."},"en":{"type":"string","minLength":1,"description":"English label."}},"required":["da","en"],"additionalProperties":false,"description":"Human-readable labels by language."}},"required":["value","label"],"additionalProperties":false,"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)."},"Catalog":{"type":"object","properties":{"products":{"type":"array","items":{"$ref":"#/components/schemas/Product"},"description":"Products that can be offered."},"productDependencies":{"type":"array","items":{"$ref":"#/components/schemas/ProductDependency"},"description":"Products that require another product."},"errors":{"type":"array","items":{"$ref":"#/components/schemas/Error"},"description":"Products that are listed but cannot currently be quoted, e.g. pricing_configuration."}},"required":["products","productDependencies","errors"],"additionalProperties":false,"description":"Products and questions to collect answers for before quoting."},"Product":{"type":"object","properties":{"product":{"type":"string","maxLength":100,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$","description":"Product slug. Use it in `products` and to qualify answer keys."},"name":{"type":"string","description":"Product name in Danish."},"description":{"nullable":true,"description":"Short product description in Danish.","type":"string"},"coverage":{"nullable":true,"description":"What the insurance covers, in Danish.","type":"string"},"termsUrl":{"nullable":true,"description":"Link to the full insurance terms as PDF. Same as `termsDocuments.termsPdf`.","type":"string"},"termsDocuments":{"nullable":true,"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.","type":"object","properties":{"termsPdf":{"type":"string","description":"Full insurance terms as PDF. Give this link to the customer."},"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."},"termsMarkdown":{"type":"string","description":"Full insurance terms as Markdown, for agents to read and answer questions about coverage."}},"required":["termsPdf","ipidPdf","ipidYaml","termsMarkdown"],"additionalProperties":false},"region":{"type":"string","description":"Market of the product: dk."},"currency":{"type":"string","description":"ISO 4217 currency of all prices for this product: DKK."},"billingFrequency":{"type":"object","properties":{"value":{"type":"string","enum":["Monthly","Annual"],"description":"Stable machine-readable value.","x-enum-labels":{"Monthly":{"da":"Månedlig","en":"Monthly"},"Annual":{"da":"Årlig","en":"Annual"}}},"label":{"type":"object","properties":{"da":{"type":"string","minLength":1,"description":"Danish label."},"en":{"type":"string","minLength":1,"description":"English label."}},"required":["da","en"],"additionalProperties":false,"description":"Human-readable labels by language."}},"required":["value","label"],"additionalProperties":false,"description":"How often the customer pays: Monthly or Annual. Prices are per period of this frequency."},"automaticQuote":{"type":"boolean","description":"Whether this product supports an instant quote. False means manual review is required; its questions are still listed for collecting answers. True does not guarantee a price for every company or set of answers."},"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."},"questions":{"type":"array","items":{"$ref":"#/components/schemas/ProductQuestion"},"description":"Questions for this product, including company-wide questions shared with other products."}},"required":["product","name","description","coverage","termsUrl","termsDocuments","region","currency","billingFrequency","automaticQuote","pricePerEmployee","questions"],"additionalProperties":false,"description":"An insurance product that can be offered to a Danish company."},"Question":{"type":"object","properties":{"name":{"type":"string","description":"Answer key: use it as the key in `answers`."},"title":{"nullable":true,"description":"The question as shown to the customer, in Danish.","type":"string"},"description":{"nullable":true,"description":"Help text in Danish.","type":"string"},"type":{"type":"object","properties":{"value":{"type":"string","enum":["Text","NumberWhole","NumberDecimal","Date","Boolean","Select","MultiSelect"],"description":"Stable machine-readable value.","x-enum-labels":{"Text":{"da":"Tekst","en":"Text"},"NumberWhole":{"da":"Heltal","en":"Whole number"},"NumberDecimal":{"da":"Decimaltal","en":"Decimal number"},"Date":{"da":"Dato","en":"Date"},"Boolean":{"da":"Ja eller nej","en":"Yes or no"},"Select":{"da":"Vælg én","en":"Select one"},"MultiSelect":{"da":"Vælg flere","en":"Select multiple"}}},"label":{"type":"object","properties":{"da":{"type":"string","minLength":1,"description":"Danish label."},"en":{"type":"string","minLength":1,"description":"English label."}},"required":["da","en"],"additionalProperties":false,"description":"Human-readable labels by language."}},"required":["value","label"],"additionalProperties":false,"description":"Answer type: Text, NumberWhole (integer), NumberDecimal, Date (YYYY-MM-DD), Boolean, Select (one key of `options`) or MultiSelect (array of keys)."},"options":{"description":"Available options in display order. Omitted when there are none. Answer with `value`, not the label.","minItems":1,"type":"array","items":{"type":"object","properties":{"value":{"type":"string","description":"Option value to send as the answer."},"label":{"type":"object","properties":{"da":{"type":"string","description":"Danish option label from the backend."}},"required":["da"],"additionalProperties":false,"description":"Localized option label. Currently only Danish is available."}},"required":["value","label"],"additionalProperties":false}},"unit":{"description":"Answer unit from the question's unit setting, when configured.","type":"string"},"period":{"description":"Answer period from the question's period setting, when configured.","type":"string"},"min":{"description":"Inclusive minimum numeric answer, when configured. Employee counts have a minimum of 0.","type":"number"},"max":{"description":"Inclusive maximum numeric answer, when configured. Employee counts have a maximum of 9999.","type":"number"},"step":{"description":"Suggested numeric input increment, when configured. Employee counts use 1 (whole numbers).","type":"number"}},"required":["name","title","description","type"],"additionalProperties":false,"description":"A question to answer before pricing."},"ProductQuestion":{"type":"object","properties":{"question":{"$ref":"#/components/schemas/Question"},"required":{"type":"boolean","description":"Whether the product cannot be priced without this answer."}},"required":["question","required"],"additionalProperties":false,"description":"A question for this product and whether its answer is required."},"ProductDependency":{"type":"object","properties":{"product":{"type":"string","maxLength":100,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$","description":"Product slug."},"requiredProduct":{"type":"string","maxLength":100,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$","description":"Slug of a product that must be selected together with `product`."}},"required":["product","requiredProduct"],"additionalProperties":false,"description":"A product that can only be bought together with another."},"QuoteResult":{"type":"object","properties":{"quotes":{"type":"array","items":{"$ref":"#/components/schemas/Quote"},"description":"One quote per selected product, in request order."},"warnings":{"description":"Non-fatal problems with ignored answers. Omitted when there are none.","type":"array","items":{"type":"object","properties":{"code":{"type":"string","enum":["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":{"description":"Suggested answer key. Confirm it before resending; answers are never auto-corrected.","type":"string"},"questionLabel":{"description":"Danish label of the suggested question, when available.","type":"object","properties":{"da":{"type":"string","description":"Danish question label from the catalog."}},"required":["da"],"additionalProperties":false}},"required":["code","question"],"additionalProperties":false}},"totals":{"nullable":true,"description":"Totals of the quoted products; null when no product is included.","type":"object","properties":{"monthly":{"description":"Sum per month; annual products count as a twelfth of their yearly price.","allOf":[{"$ref":"#/components/schemas/Price"}]},"annual":{"description":"Sum per year.","allOf":[{"$ref":"#/components/schemas/Price"}]},"excludedProducts":{"type":"array","items":{"type":"string","maxLength":100,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$","description":"Product slug."},"description":"Selected products not in the totals: not quoted, or priced per employee."},"display":{"type":"object","properties":{"da":{"type":"string","description":"Danish."},"en":{"type":"string","description":"English."}},"required":["da","en"],"additionalProperties":false,"description":"How to state the totals to the customer."}},"required":["monthly","annual","excludedProducts","display"],"additionalProperties":false},"quoteId":{"nullable":true,"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.","type":"string"},"startDate":{"type":"string","format":"date","description":"The insurance start date the prices apply to; today when the request has none."},"expiresAt":{"nullable":true,"description":"Until when quoteId can be used for /start, unless prices change first; then request a new quote.","type":"string","format":"date-time"},"continueUrl":{"type":"string","format":"uri","description":"Where the customer reviews this quote on Plain and continues to sign up. Opening it creates nothing."},"disclaimer":{"type":"object","properties":{"da":{"type":"string","description":"Danish."},"en":{"type":"string","description":"English."}},"required":["da","en"],"additionalProperties":false,"description":"Show with the prices: they are indicative, and nothing binds until the customer signs on Plain."}},"required":["quotes","totals","quoteId","startDate","expiresAt","continueUrl","disclaimer"],"additionalProperties":false,"description":"Indicative prices. Nothing is bound or charged."},"Quote":{"type":"object","properties":{"product":{"type":"string","maxLength":100,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$","description":"Product slug."},"name":{"type":"string","description":"Product name to show next to the price."},"termsUrl":{"nullable":true,"description":"Link to the product's insurance terms as PDF. Same as `termsDocuments.termsPdf`.","type":"string"},"termsDocuments":{"nullable":true,"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.","type":"object","properties":{"termsPdf":{"type":"string","description":"Full insurance terms as PDF. Give this link to the customer."},"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."},"termsMarkdown":{"type":"string","description":"Full insurance terms as Markdown, for agents to read and answer questions about coverage."}},"required":["termsPdf","ipidPdf","ipidYaml","termsMarkdown"],"additionalProperties":false},"status":{"type":"object","properties":{"value":{"type":"string","enum":["quoted","contactPlain","unavailable","alreadyInsured"],"description":"Stable machine-readable value.","x-enum-labels":{"quoted":{"da":"Pris beregnet","en":"Quoted"},"contactPlain":{"da":"Kontakt Plain","en":"Contact Plain"},"unavailable":{"da":"Kan ikke tilbydes","en":"Unavailable"},"alreadyInsured":{"da":"Allerede forsikret","en":"Already insured"}}},"label":{"type":"object","properties":{"da":{"type":"string","minLength":1,"description":"Danish label."},"en":{"type":"string","minLength":1,"description":"English label."}},"required":["da","en"],"additionalProperties":false,"description":"Human-readable labels by language."}},"required":["value","label"],"additionalProperties":false,"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."},"currency":{"type":"string","description":"ISO 4217 currency: DKK."},"billingFrequency":{"type":"object","properties":{"value":{"type":"string","enum":["Monthly","Annual"],"description":"Stable machine-readable value.","x-enum-labels":{"Monthly":{"da":"Månedlig","en":"Monthly"},"Annual":{"da":"Årlig","en":"Annual"}}},"label":{"type":"object","properties":{"da":{"type":"string","minLength":1,"description":"Danish label."},"en":{"type":"string","minLength":1,"description":"English label."}},"required":["da","en"],"additionalProperties":false,"description":"Human-readable labels by language."}},"required":["value","label"],"additionalProperties":false,"description":"How often the customer pays: Monthly or Annual. Prices are per period of this frequency."},"price":{"description":"The price per billing period; present when quoted.","allOf":[{"$ref":"#/components/schemas/Price"}]},"annualPrice":{"description":"The price per year, to compare monthly and annual products; present when quoted.","allOf":[{"$ref":"#/components/schemas/Price"}]},"pricePerEmployee":{"type":"boolean","description":"Whether `price` is per covered employee. The customer selects the employees when purchasing on Plain, which sets the total."},"coverage":{"type":"object","properties":{"sumInsured":{"nullable":true,"description":"The sum insured, as text.","type":"string"},"deductible":{"nullable":true,"description":"The deductible (selvrisiko), as text.","type":"string"},"summary":{"nullable":true,"description":"What the insurance covers, in brief.","type":"string"}},"required":["sumInsured","deductible","summary"],"additionalProperties":false,"description":"What the price buys. Fields are null when the product does not state them."},"notice":{"description":"A message to show the customer with this price, e.g. that it is per employee.","type":"string"},"issue":{"description":"Why the product is not quoted.","allOf":[{"$ref":"#/components/schemas/Error"}]},"display":{"type":"object","properties":{"da":{"type":"string","description":"Danish."},"en":{"type":"string","description":"English."}},"required":["da","en"],"additionalProperties":false,"description":"How to state this quote to the customer: total, premium and fees split, and period."}},"required":["product","name","termsUrl","termsDocuments","status","currency","billingFrequency","pricePerEmployee","coverage","display"],"additionalProperties":false,"description":"The indicative price of one product, or why it has none."},"Price":{"type":"object","properties":{"totalPrice":{"type":"number","description":"What the customer pays per billing period, in DKK (major units, kroner): premium plus government fees."},"premium":{"type":"number","description":"Insurance premium per billing period, in DKK."},"fees":{"type":"number","description":"Statutory fees and taxes per billing period, in DKK."}},"required":["totalPrice","premium","fees"],"additionalProperties":false,"description":"A price per billing period. Always mention totalPrice together with its premium and fees split."},"OnboardingResult":{"type":"object","properties":{"onboardingId":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Identifies this signup."},"capability":{"type":"string","pattern":"^[a-f0-9]{64}$","description":"Secret proof of this signup. Keep it private and send it with onboardingId."},"state":{"type":"object","properties":{"value":{"type":"string","enum":["awaiting_verification","email_confirmed","verified"],"description":"Stable machine-readable value.","x-enum-labels":{"awaiting_verification":{"da":"Afventer e-mailbekræftelse","en":"Awaiting email verification"},"email_confirmed":{"da":"E-mail bekræftet","en":"Email confirmed"},"verified":{"da":"Tilbud klar","en":"Offers ready"}}},"label":{"type":"object","properties":{"da":{"type":"string","minLength":1,"description":"Danish label."},"en":{"type":"string","minLength":1,"description":"English label."}},"required":["da","en"],"additionalProperties":false,"description":"Human-readable labels by language."}},"required":["value","label"],"additionalProperties":false,"description":"awaiting_verification: ask the person for the emailed code; email_confirmed: call /verify-email again to finish; verified: offers are ready."},"policyIds":{"type":"array","items":{"type":"string"},"description":"IDs of the offered policies, once created."},"continuationToken":{"description":"Secret token for /payment-handoff, valid for 15 minutes after verification.","nullable":true,"type":"string","pattern":"^[a-f0-9]{64}$"}},"required":["onboardingId","capability","state","policyIds"],"additionalProperties":false,"description":"The state of a signup."},"PaymentLink":{"type":"object","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","description":"When the link expires (ISO 8601 date-time)."}},"required":["url","expiresAt"],"additionalProperties":false,"description":"A browser link to Plain where the person reviews and signs the offers."}},"responses":{"BadRequest":{"description":"The request does not match the schema (invalid_request). Each error identifies its field and how to fix it.","headers":{"X-Robots-Tag":{"$ref":"#/components/headers/RobotsTag"},"Link":{"$ref":"#/components/headers/DescribedBy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorList"},"example":{"errors":[{"code":{"value":"invalid_request","label":{"da":"Ugyldig forespørgsel","en":"Invalid request"}},"message":"cvr must be 8 digits.","nextAction":{"value":"fixAnswer","label":{"da":"Ret oplysningerne og prøv igen","en":"Correct the request and try again"}},"field":"cvr"}]}}}},"Forbidden":{"description":"An Authorization header was sent (wrong_identity_mode), or the signup proof is invalid, expired or unverified (not_authorized).","headers":{"X-Robots-Tag":{"$ref":"#/components/headers/RobotsTag"},"Link":{"$ref":"#/components/headers/DescribedBy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorList"}}}},"Conflict":{"description":"Another request for this signup is in progress, or the person must sign in instead.","headers":{"X-Robots-Tag":{"$ref":"#/components/headers/RobotsTag"},"Link":{"$ref":"#/components/headers/DescribedBy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorList"}}}},"TooLarge":{"description":"The request body exceeds 64 KiB (request_too_large).","headers":{"X-Robots-Tag":{"$ref":"#/components/headers/RobotsTag"},"Link":{"$ref":"#/components/headers/DescribedBy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorList"}}}},"UnsupportedMediaType":{"description":"The body is not application/json (unsupported_media_type).","headers":{"X-Robots-Tag":{"$ref":"#/components/headers/RobotsTag"},"Link":{"$ref":"#/components/headers/DescribedBy"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorList"}}}},"RateLimited":{"description":"Too many requests (rate_limited). Wait for Retry-After seconds.","headers":{"X-Robots-Tag":{"$ref":"#/components/headers/RobotsTag"},"Link":{"$ref":"#/components/headers/DescribedBy"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorList"}}}},"Unavailable":{"description":"Onboarding is temporarily unavailable (temporarily_unavailable). Retry after Retry-After seconds.","headers":{"X-Robots-Tag":{"$ref":"#/components/headers/RobotsTag"},"Link":{"$ref":"#/components/headers/DescribedBy"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorList"}}}}},"headers":{"RetryAfter":{"description":"Seconds to wait before retrying.","schema":{"type":"integer","minimum":1}},"RobotsTag":{"description":"Quote and onboarding responses must not be indexed or followed.","schema":{"type":"string","enum":["noindex, nofollow"]}},"DescribedBy":{"description":"Agent-readable API instructions.","schema":{"type":"string","enum":["<https://app.plain.insurance/llms.txt>; rel=\"describedby\""]}}}}}