{"openapi":"3.1.0","info":{"title":"Råd til Bolig API","description":"API v2","contact":{"name":"Råd til Bolig","url":"https://raadtilbolig.dk","email":"kenneth@raadtilbolig.dk"},"license":{"name":""},"version":"2.0.0"},"paths":{"/v2/autocomplete/{query}":{"get":{"tags":["raad-til-bolig-api-v2"],"summary":"Search for addresses including rental unit addresses.","operationId":"autocomplete_v2","parameters":[{"name":"query","in":"path","description":"Search query string","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Autocomplete results including rental unit addresses","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AutocompleteQueryV2"}}}}},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Not enough permissions"},"404":{"description":"No results found"},"500":{"description":"Internal server error"}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]}},"/v2/beregn":{"post":{"tags":["raad-til-bolig-api-v2"],"summary":"Calculate loan scenarios for a property.","operationId":"calculate_v2","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIInput"}}},"required":true},"responses":{"200":{"description":"Calculation completed successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FinancerPropertyQuery"}}}}},"400":{"description":"Bad request - Invalid input data"},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Not enough permissions"},"500":{"description":"Internal server error"}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]}},"/v2/bolig/{address_slug}":{"get":{"tags":["raad-til-bolig-api-v2"],"summary":"Get housing information by address slug.","operationId":"bolig_v2","parameters":[{"name":"address_slug","in":"path","description":"Address slug to get information for","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Housing information retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HousingResponseV2"}}}},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Not enough permissions"},"404":{"description":"Housing information not found"},"500":{"description":"Internal server error"}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]}},"/v2/current-loan-rate":{"post":{"tags":["raad-til-bolig-api-v2"],"summary":"Calculate the current interest rate for an existing FKort/F1/F3/F5 mortgage loan.","operationId":"current_loan_rate_v2","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentLoanRateRequest"}}},"required":true},"responses":{"200":{"description":"Current loan rate calculated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CurrentLoanRateResponse"}}}},"400":{"description":"Bad request - unsupported loan variant or invalid input"},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Not enough permissions"},"500":{"description":"Internal server error"}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]}},"/v2/customer":{"post":{"tags":["customer"],"summary":"Create a customer, optionally with an initial property and loans.","operationId":"create_customer","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerInput"}}},"required":true},"responses":{"200":{"description":"Customer created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerResponse"}}}},"400":{"description":"Bad request - Invalid input data"},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Not enough permissions"},"500":{"description":"Internal server error"}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]},"delete":{"tags":["customer"],"summary":"Delete a customer and all customer-owned records.","operationId":"remove_customer","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteCustomer"}}},"required":true},"responses":{"200":{"description":"Customer deleted"},"400":{"description":"Bad request - Invalid input data"},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Not enough permissions"},"500":{"description":"Internal server error"}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]},"patch":{"tags":["customer"],"summary":"Patch a customer or customer-owned record field.","operationId":"update_customer","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchCustomer"}}},"required":true},"responses":{"200":{"description":"Customer updated"},"400":{"description":"Bad request - Invalid input data"},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Customer access denied"},"500":{"description":"Internal server error"}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]}},"/v2/data/property/{property_id}":{"get":{"tags":["data"],"summary":"Get normalized property data by property ID.","operationId":"data_by_property_id","parameters":[{"name":"property_id","in":"path","description":"Property ID","required":true,"schema":{"type":"integer","format":"int32"}}],"responses":{"200":{"description":"Normalized property data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PropertyData"}}}},"400":{"description":"Invalid property ID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Not enough permissions"},"404":{"description":"Property not found"},"500":{"description":"Internal server error"}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]}},"/v2/flight_photo/{slug}":{"get":{"tags":["raad-til-bolig-api"],"summary":"Get the latest cached or generated oblique flight photo for an address slug.","operationId":"flight_photo","parameters":[{"name":"slug","in":"path","description":"Address slug to generate a flight photo for","required":true,"schema":{"type":"string"}},{"name":"direction","in":"query","description":"Camera direction: north, south, east, west, or nadir","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Flight photo JPEG","content":{"image/jpeg":{}}},"400":{"description":"Bad request - invalid direction"},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Not enough permissions"},"404":{"description":"Property not found"},"500":{"description":"Internal server error"}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]}},"/v2/health":{"get":{"tags":["system"],"summary":"Health check.","operationId":"health","responses":{"200":{"description":"API is healthy","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/v2/notifications":{"get":{"tags":["notifications"],"summary":"List current notifications for the authenticated company.","operationId":"list_notifications","responses":{"200":{"description":"Notification list","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/NotificationRecord"}}}}},"401":{"description":"Unauthorized - Invalid or missing JWT token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"403":{"description":"Forbidden - Not enough permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]}},"/v2/notifications/{notification_id}/ack":{"patch":{"tags":["notifications"],"summary":"Acknowledge one notification.","operationId":"ack_notification","parameters":[{"name":"notification_id","in":"path","description":"Notification key","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationTarget"}}},"required":true},"responses":{"200":{"description":"Notification acknowledged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationEventRecord"}}}},"204":{"description":"Notification was already acknowledged or resolved"},"400":{"description":"Bad request - Invalid input data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing JWT token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"403":{"description":"Forbidden - Notification belongs to a different company","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"404":{"description":"Notification not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]}},"/v2/notifications/{notification_id}/resolve":{"patch":{"tags":["notifications"],"summary":"Resolve one notification.","operationId":"resolve_notification","parameters":[{"name":"notification_id","in":"path","description":"Notification key","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationTarget"}}},"required":true},"responses":{"200":{"description":"Notification resolved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationEventRecord"}}}},"204":{"description":"Notification was already resolved"},"400":{"description":"Bad request - Invalid input data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing JWT token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"403":{"description":"Forbidden - Notification belongs to a different company","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"404":{"description":"Notification not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]}},"/v2/property":{"get":{"tags":["property"],"summary":"Get one customer property record.","operationId":"get_property","parameters":[{"name":"customer_id","in":"query","description":"Customer key","required":true,"schema":{"type":"string"}},{"name":"property_key","in":"query","description":"Property key","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Property found","content":{"application/json":{"schema":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/CustomerProperty"}]}}}},"400":{"description":"Bad request - Invalid query parameters"},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Not enough permissions"},"500":{"description":"Internal server error"}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]},"post":{"tags":["property"],"summary":"Create a customer property or a calculation under an existing property.","operationId":"create_property","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PropertyInput"}}},"required":true},"responses":{"200":{"description":"Property created","content":{"text/plain":{"schema":{"type":"string"}}}},"400":{"description":"Bad request - Invalid input data"},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Not enough permissions"},"500":{"description":"Internal server error"}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]},"delete":{"tags":["property"],"summary":"Delete one customer property record.","operationId":"remove_property","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteProperty"}}},"required":true},"responses":{"200":{"description":"Property deleted"},"400":{"description":"Bad request - Invalid input data"},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Not enough permissions"},"500":{"description":"Internal server error"}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]},"patch":{"tags":["property"],"summary":"Refresh and return loans for a customer property from tinglysning.","operationId":"update_property_loans","parameters":[{"name":"customer_id","in":"query","description":"Customer key","required":true,"schema":{"type":"string"}},{"name":"property_key","in":"query","description":"Property key","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Property loans updated","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Loan"}}}}},"400":{"description":"Bad request - Invalid query parameters"},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Not enough permissions"},"500":{"description":"Internal server error"}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]}},"/v2/property/changeset":{"post":{"tags":["property"],"summary":"Validate a financing change for an existing property without persisting it.","operationId":"evaluate_property_changeset","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PropertyChangesetRequest"}}},"required":true},"responses":{"200":{"description":"Financing change is valid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Evaluation"}}}},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Property access denied"},"404":{"description":"Property not found"},"422":{"description":"Financing change contains validation issues","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Evaluation"}}}},"500":{"description":"Internal server error"}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]},"patch":{"tags":["property"],"summary":"Re-evaluate and atomically commit a financing change for an existing property.","operationId":"update_property_changeset","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PropertyChangesetRequest"}}},"required":true},"responses":{"200":{"description":"Financing change committed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Evaluation"}}}},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Property access denied"},"404":{"description":"Property not found"},"422":{"description":"Financing change contains validation issues","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Evaluation"}}}},"500":{"description":"Internal server error"}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]}},"/v2/rates":{"post":{"tags":["raad-til-bolig-api-v2"],"summary":"Compare new-loan costs across institutes and loan variants for a property.","operationId":"rates_quote_v2","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateQuoteRequest"}}},"required":true},"responses":{"200":{"description":"Comparison computed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateQuoteResponse"}}}},"400":{"description":"Bad request - Invalid input data"},"401":{"description":"Unauthorized - Invalid or missing JWT token"},"403":{"description":"Forbidden - Not enough permissions"},"500":{"description":"Internal server error"}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]}},"/v2/webhook-settings":{"get":{"tags":["webhooks"],"summary":"Get company-wide webhook delivery settings.","description":"The response indicates whether a signing secret exists but never returns the secret itself.\nCustom headers and the signing secret are shared by all webhook endpoints for the company.","operationId":"get_webhook_settings","responses":{"200":{"description":"Webhook settings","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicWebhookSettings"}}}},"401":{"description":"Unauthorized - Invalid or missing JWT token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"403":{"description":"Forbidden - Not enough permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]},"patch":{"tags":["webhooks"],"summary":"Replace company-wide webhook custom headers.","description":"The submitted list replaces all existing custom headers and applies to every endpoint. Header\nnames must be case-insensitively unique. Standard Webhooks headers and HTTP routing, content,\nconnection, and transfer-framing headers are reserved.","operationId":"update_webhook_settings","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookSettings"}}},"required":true},"responses":{"200":{"description":"Webhook settings updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicWebhookSettings"}}}},"400":{"description":"JSON syntax error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing JWT token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"403":{"description":"Forbidden - Not enough permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"413":{"description":"JSON request exceeds the body-size limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"415":{"description":"Content-Type is not application/json","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"422":{"description":"JSON shape or webhook settings are invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]}},"/v2/webhook-settings/reset-secret":{"post":{"tags":["webhooks"],"summary":"Generate and return a new company-wide webhook signing secret.","description":"Call this before creating the first endpoint and store the returned `whsec_...` value securely;\nit cannot be retrieved later. Calling it again immediately invalidates the previous secret for\nall endpoints. Receivers use this value to verify the Standard Webhooks signature.","operationId":"reset_webhook_secret","responses":{"200":{"description":"New shared secret","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetSecretResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing JWT token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"403":{"description":"Forbidden - Not enough permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]}},"/v2/webhooks":{"get":{"tags":["webhooks"],"summary":"List all webhook endpoint subscriptions for the authenticated company.","operationId":"list_webhooks","responses":{"200":{"description":"Webhook list","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PublicWebhookRecord"}}}}},"401":{"description":"Unauthorized - Invalid or missing JWT token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"403":{"description":"Forbidden - Not enough permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]},"post":{"tags":["webhooks"],"summary":"Create and immediately enable a webhook endpoint subscription.","description":"The endpoint must be a public HTTPS URL. Deliveries do not follow redirects. Configure and save\nthe company signing secret first with `reset_webhook_secret`. A company can have at most ten\nendpoint subscriptions.","operationId":"create_webhook","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhook"}}},"required":true},"responses":{"200":{"description":"Webhook created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicWebhookRecord"}}}},"400":{"description":"JSON syntax error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing JWT token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"403":{"description":"Forbidden - Not enough permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"413":{"description":"JSON request exceeds the body-size limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"415":{"description":"Content-Type is not application/json","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"422":{"description":"JSON shape, webhook values, or endpoint capacity are invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]}},"/v2/webhooks/{webhook_id}":{"get":{"tags":["webhooks"],"summary":"Get one webhook endpoint subscription owned by the authenticated company.","operationId":"get_webhook","parameters":[{"name":"webhook_id","in":"path","description":"Webhook key","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Webhook found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicWebhookRecord"}}}},"401":{"description":"Unauthorized - Invalid or missing JWT token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"403":{"description":"Forbidden - Not enough permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"404":{"description":"Webhook not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]},"delete":{"tags":["webhooks"],"summary":"Delete one webhook endpoint subscription.","description":"Pending deliveries for the endpoint are exhausted without being sent.","operationId":"delete_webhook","parameters":[{"name":"webhook_id","in":"path","description":"Webhook key","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Webhook deleted"},"401":{"description":"Unauthorized - Invalid or missing JWT token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"403":{"description":"Forbidden - Not enough permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"404":{"description":"Webhook not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]},"patch":{"tags":["webhooks"],"summary":"Patch one webhook endpoint subscription.","description":"Omitted fields remain unchanged. For `description`, send `null` to clear the value. Setting\n`status` to `disabled` stops new deliveries and pending retries for the endpoint.","operationId":"update_webhook","parameters":[{"name":"webhook_id","in":"path","description":"Webhook key","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhook"}}},"required":true},"responses":{"200":{"description":"Webhook updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicWebhookRecord"}}}},"400":{"description":"JSON syntax error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing JWT token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"403":{"description":"Forbidden - Not enough permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"404":{"description":"Webhook not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"413":{"description":"JSON request exceeds the body-size limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"415":{"description":"Content-Type is not application/json","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"422":{"description":"JSON shape or webhook values are invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]}},"/v2/webhooks/{webhook_id}/test":{"post":{"tags":["webhooks"],"summary":"Send one synchronous test notification to a webhook endpoint.","description":"The outbound body has `test: true` and otherwise uses the normal `WebhookEvent` contract. This\noperation itself returns 200 when the attempt was completed; inspect `status`, `status_code`, and\n`error` for the receiver result. Test deliveries are attempted once and are not retried.","operationId":"test_webhook","parameters":[{"name":"webhook_id","in":"path","description":"Webhook key","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Test delivery result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestWebhookResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing JWT token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"403":{"description":"Forbidden - Not enough permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"404":{"description":"Webhook not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiErrorResponse"}}}}},"security":[{"jwt_auth":[]},{"oauth2_client_credentials":[]}]}}},"components":{"schemas":{"APIInput":{"type":"object","required":["property","loans"],"properties":{"loans":{"type":"array","items":{"$ref":"#/components/schemas/Loan"}},"new_loans":{"type":"array","items":{"$ref":"#/components/schemas/Loan"}},"payout":{"type":["number","null"],"format":"double"},"property":{"$ref":"#/components/schemas/Property"}}},"ActualLoan":{"type":"object","required":["loan_type","payments_per_term","interest_only_terms","principal","lender"],"properties":{"bond_principal_balance":{"type":["number","null"],"format":"double","description":"Bond principal balance for the loan, calculated based on the bond term and interest rate"},"contribution_charges":{"type":["number","null"],"format":"double","description":"Contribution charges for the loan, calculated based on the LTV and principal"},"contribution_rate":{"type":["number","null"],"format":"double","description":"Contribution rate for the loan based on loan_variant, loan_type, lender and model price","example":0.6},"costs":{"type":["array","null"],"items":{"$ref":"#/components/schemas/LoanCostDetail"},"description":"Per-loan cost breakdown (termination costs for existing, origination costs for new)"},"date":{"type":["string","null"],"format":"date","description":"Origination date of the loan"},"interest_charges":{"type":["number","null"],"format":"double","description":"Interest charges for the loan, calculated based on the interest rate and principal"},"interest_only_terms":{"type":"number","format":"double","description":"Number of interest-only years, based on `staaendeLaan` = 30 and `mulighedForAfdragsfrihed` = 10 in Tinglysning","example":10},"interest_rate":{"type":["number","null"],"format":"double","description":"Interest rate for the loan","example":3.5},"is_existing":{"type":"boolean","description":"Indicates if the loan is an existing loan or a new loan"},"isin":{"type":["string","null"],"description":"ISIN for the loan, if applicable"},"keep_loan":{"type":["boolean","null"],"description":"Whether the loan should be kept or not, used for termination"},"lender":{"$ref":"#/components/schemas/Lender","description":"Lender of the loan"},"loan_characteristics":{"type":["string","null"],"description":"Text description of the loan characteristics\nProvides a multi-sentence explanation of the loan variant's key features"},"loan_term":{"type":["number","null"],"format":"double","description":"Original loan term in years. Inputs may omit this when PBS balance/payment can derive it.","example":30},"loan_to_value_range_end":{"type":["number","null"],"format":"double","description":"Loan to value range end or LTV end used for contribution calculation"},"loan_to_value_range_start":{"type":["number","null"],"format":"double","description":"Loan to value range start or LTV start used for contribution calculation"},"loan_type":{"$ref":"#/components/schemas/LoanType","description":"Type of loan (e.g. fastforrentet, f-kort, etc.)"},"loan_variant":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/LoanVariants","description":"Variant of the loan (e.g. F3, F5, etc.)"}]},"margin":{"type":["number","null"],"format":"double","description":"Interest rate margin for reference rate loans\ne.g. for loans tied to CITA, CIBOR or Nationalbanken rates"},"payment":{"type":["number","null"],"format":"double","description":"Monthly payment amount including contribution if applicable"},"payment_after_tax":{"type":["number","null"],"format":"double","description":"Monthly payment after tax (25.6% tax on interest and contribution)"},"payments_per_term":{"type":"number","format":"double","description":"Number of payments per term, defaults to 4 for Realkredit and 12 for Bank since this information is not in Tinglysning","example":4},"principal":{"type":"number","format":"double","description":"Registered principal amount","example":2000000},"principal_balance":{"type":["number","null"],"format":"double","description":"Principal balance for the loan, remaining amount after paydowns"},"principal_paydown":{"type":["number","null"],"format":"double","description":"Principal paydown for the loan, calculated based on the loan variant and interest rate"},"redemption_rate":{"type":["number","null"],"format":"double","description":"Redemption rate for the loan, only applicable for fixed-rate loans","example":98.6},"refinance_date":{"type":["string","null"],"format":"date","description":"Refinance date for the loan, if applicable\nUsed for calculating remaining term on f-kort and other refinancing loans"},"registered_interest_rate":{"type":["number","null"],"format":"double","description":"Registered interest rate from Tinglysning\nThis is the original interest rate registered in Tinglysning","example":3.5},"registered_margin":{"type":["number","null"],"format":"double","description":"Registered margin from Tinglysning\nThis is the original margin registered in Tinglysning for reference rate loans"},"remaining_interest_only_terms":{"type":["number","null"],"format":"double","description":"Remaining interest-only term in years, derived from the amortization schedule"},"remaining_loan_term":{"type":["number","null"],"format":"double","description":"Remaining loan term in years, derived from the amortization schedule length"},"selected_termination_type":{"type":["string","null"],"description":"Selected termination type for the loan, if applicable"},"termination_types":{"type":["array","null"],"items":{"type":"string"},"description":"List of available termination types for the loan, if applicable"}}},"AdvisorIssue":{"type":"object","description":"One advisor-facing error, warning, or informational consequence.","required":["path","code","message","severity","domain","source"],"properties":{"code":{"type":"string"},"context":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/IssueContext"}]},"domain":{"$ref":"#/components/schemas/IssueDomain"},"message":{"type":"string"},"path":{"type":"string"},"severity":{"$ref":"#/components/schemas/IssueSeverity"},"source":{"$ref":"#/components/schemas/IssueSource"}}},"AfterlonWorkRule":{"type":"string","description":"How one income source reduces efterløn.\n\nThe classification is explicit because tax classification does not prove whether hours are\ncontrollable or which self-employment permission the member received from the a-kasse.","enum":["controllable_hours","income_conversion","self_employment_80_17","self_employment_962","self_employment_400","self_employment_single_task","authority_assessed_hours","no_reduction"]},"Apartment":{"type":"object","properties":{"address_id":{"type":["string","null"]},"building_id":{"type":["string","null"]},"condo_property_id":{"type":["integer","null"],"format":"int32"},"floor_designation":{"type":["string","null"]},"floor_number":{"type":["integer","null"],"format":"int32"},"unit_address":{"type":["string","null"]},"unit_application":{"type":["integer","null"],"format":"int64"},"unit_area":{"type":["integer","null"],"format":"int32"},"unit_area_for_business":{"type":["integer","null"],"format":"int32"},"unit_area_for_residence":{"type":["integer","null"],"format":"int32"},"unit_bath_type":{"type":["string","null"]},"unit_id":{"type":["string","null"]},"unit_kitchen_type":{"type":["string","null"]},"unit_number_of_bathrooms":{"type":["integer","null"],"format":"int32"},"unit_number_of_rooms":{"type":["integer","null"],"format":"int32"},"unit_number_of_water_flush_toilets":{"type":["integer","null"],"format":"int32"},"unit_open_terrace_area":{"type":["integer","null"],"format":"int32"},"unit_point":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Point2d"}]},"unit_status":{"type":["string","null"]},"unit_toilet_type":{"type":["string","null"]},"unit_type":{"type":["string","null"]}}},"ApiErrorResponse":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}},"Asset":{"type":"object","description":"Asset owned by a person, tracked per-person","required":["name","asset_type","balance","investment_category"],"properties":{"asset_id":{"type":["string","null"],"description":"Stable identifier for references from plan-level events such as plan transfers."},"asset_type":{"$ref":"#/components/schemas/AssetType","description":"Type of asset"},"balance":{"type":"number","format":"double","description":"Opening balance at the plan boundary on the first day of next month, before projected\nreturn, contributions, withdrawals, and payouts."},"investment_category":{"$ref":"#/components/schemas/InvestmentCategory","description":"Return profile; possessions use `OtherAssets`, investments use Rådet's assumptions."},"name":{"type":"string","description":"Unique name for this asset (e.g., \"ApS\", \"Sommerhus\")"},"tax_basis":{"type":["number","null"],"format":"double","description":"Nominal acquisition/tax basis for realisation-taxed assets, stated in model-year DKK.\n\nRequired when `asset_type` is [`AssetType::SecuritiesRealisation`]. Plan contract\nvalidation rejects an omitted basis; there is no accepted-input fallback to `balance`.\nUnlike projected balances, the statutory basis is not increased with inflation."}},"additionalProperties":false},"AssetType":{"type":"string","description":"Asset types that can be tracked per person","enum":["personal_possession","free_funds","cash","aktiesparekonto","real_estate","securities_lager_listed_share_income","securities_lager_capital_income","securities_realisation"]},"AutoCompleteAddressV2":{"type":"object","required":["slug","geometry"],"properties":{"combined_polygon":{"type":["string","null"],"description":"Combined polygon of all jordstykker for the property in WKT (UTM32). Null for apartments and rentals."},"geometry":{"$ref":"#/components/schemas/Point2d","description":"Point coordinates for the address"},"property_id":{"type":["integer","null"],"format":"int32","description":"Property ID (BFE). Null for rental addresses.","example":6023005},"property_type":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PropertyType","description":"Property type (e.g. house, owner_apartment, vacation_home). Null for rental addresses."}]},"slug":{"type":"string","example":"orevadsvej-29-2400-kobenhavn-nv"}}},"AutocompleteQueryV2":{"type":"object","required":["query"],"properties":{"address":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/AutoCompleteAddressV2","description":"Address object containing property ID, slug, geometry, and property type"}]},"query":{"type":"string","description":"Search query string","example":"Ørevadsvej 29, 2400 København NV"}}},"BeneficiaryLink":{"type":"object","description":"Allocation of a protection coverage to a beneficiary.","required":["beneficiary_id"],"properties":{"beneficiary_id":{"type":"string"},"priority":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/BeneficiaryPriority"}]},"share":{"type":["number","null"],"format":"double","description":"1.0 means 100% of the benefit."}}},"BeneficiaryPriority":{"type":"string","description":"Beneficiary ordering for protection coverages.","enum":["primary","secondary","unknown"]},"Benefit":{"type":"object","description":"A statutory public benefit. Benefits are legal entitlements, not funded pension products.","required":["benefit_type"],"properties":{"annual_amount":{"type":"number","format":"double","description":"Official annual entitlement when the authority supplies an individual amount.\nSystem-calculated benefits leave this at zero."},"annual_paid_hours":{"type":"number","format":"double","description":"Annualized paid hours supplied by the a-kasse for a planned dagpenge period.\n\nThis is an authority fact, not hours inferred from the benefit amount. The supported\npension-planning row covers ordinary dagpenge hours that both attract ATP and reduce\nthe afterlønspræmie ceiling."},"benefit_id":{"type":["string","null"]},"benefit_type":{"type":"string"},"forecast_sets":{"type":"array","items":{"$ref":"#/components/schemas/PensionForecastSet"},"description":"Optional official projection used when a benefit amount varies by year.\n\nA folkepension projection is ordinary annual entitlement before deferral. It excludes\nwaiting supplements and lump sums. The plan applies the ordinary pension-supplement\nmeans test first, then derives the waiting supplement consistently for every deferral\npayout variant."},"is_under_payout":{"type":"boolean","description":"True when the benefit is already being paid when the projection opens."},"period":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/BenefitPeriod","description":"Awarded benefit period. Tidlig Pension requires both the authority-awarded start and\nexclusive end boundary; omission is a contract error because entitlement may be one,\ntwo, or three years and cannot be inferred from age alone."}]},"selected_forecast_set_id":{"type":["string","null"]},"strategy":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/BenefitStrategy"}]},"supp_election":{"type":"boolean","description":"Whether SUPP contributions are withheld while this førtids- or seniorpension is paid.\nThe benefit period is also the contribution period."},"voluntary_atp_election":{"type":["boolean","null"],"description":"Recipient election to pay voluntary ATP while receiving efterløn or fleksydelse.\n\n`None` means the election is unknown; it must not be interpreted as an opt-out."}}},"BenefitPeriod":{"type":"object","description":"Exact inclusive start and exclusive end boundary for a statutory benefit.","properties":{"end_date":{"type":["string","null"],"format":"date"},"end_month":{"type":["integer","null"],"format":"int32","minimum":0},"end_year":{"type":["integer","null"],"format":"int32","minimum":0},"start_date":{"type":["string","null"],"format":"date"},"start_month":{"type":["integer","null"],"format":"int32","minimum":0},"start_year":{"type":["integer","null"],"format":"int32","minimum":0}}},"BenefitStrategy":{"oneOf":[{"type":"object","description":"Folkepension can be deferred for up to ten years when the work requirement is met.","required":["payout_type","deferral_months","type"],"properties":{"deferral_months":{"type":"integer","format":"int32","description":"Requested deferral from the natural entitlement month.\n\nA folkepension strategy represents an actual deferral, so this must be 1..=120.","minimum":0},"payout_type":{"$ref":"#/components/schemas/FolkepensionPayoutType"},"type":{"type":"string","enum":["folkepension"]}}}],"description":"Strategy for a statutory public benefit."},"BondAlarmSnapshot":{"type":"object","description":"Snapshot for a bond-price alarm notification.","required":["coupon","year","interest_only_terms","target_price","actual_price"],"properties":{"actual_price":{"type":"number","format":"double"},"coupon":{"type":"number","format":"double"},"interest_only_terms":{"type":"number","format":"double"},"target_price":{"type":"number","format":"double"},"year":{"type":"number","format":"double"}}},"Business":{"type":"object","description":"A business projected separately from a person's ordinary financial assets.\n\nAll businesses using [`BusinessTaxRegime::Vso`] are combined into one VSO tax envelope per\ntaxpayer. VSO opening balances stored on individual businesses are additive slices of that\nshared position; economic value and cash movements remain business-specific.","required":["name","balance","growth_rate","tax_regime","vso_tax_layers"],"properties":{"annual_results":{"type":"array","items":{"$ref":"#/components/schemas/BusinessAnnualResult"},"description":"Regime-specific annual business-result assumptions."},"balance":{"type":"number","format":"double","description":"Opening business value in constant 2026 DKK at the plan boundary on the first day of next\nmonth, before return and owner cash movements."},"business_id":{"type":["string","null"],"description":"Stable identity used by linked salary, dividend and pension-contribution sources."},"company_group_id":{"type":["string","null"],"description":"Stable identity shared by connected companies for the group-wide LL § 8 N ceiling."},"distributable_reserves":{"type":["number","null"],"format":"double","description":"Maximum opening amount that may legally be distributed as dividends.\n\n`None` keeps the company outside automatic withdrawal optimization. The company balance\nis economic value, not evidence that the same amount is available as free reserves.\nForecast profit after company tax increases these reserves; owner payments reduce them."},"growth_rate":{"type":"number","format":"double","description":"Annual real return assumption for the projected business value."},"name":{"type":"string"},"tax_regime":{"$ref":"#/components/schemas/BusinessTaxRegime"},"vso_cessation_year":{"type":["integer","null"],"format":"int32","description":"Income year in which the VSO tax position is released through ordinary personal tax.\nRequired for cessation and restricted to the plan period by contract validation.","minimum":0},"vso_deposit_account_balance":{"type":"number","format":"double","description":"This business's additive slice of the opening deposit-account balance available after\ncurrent and retained profit."},"vso_horizon_treatment":{"$ref":"#/components/schemas/VsoHorizonTreatment","description":"Whether this VSO business continues. The shared VSO tax position is released only when all\nof the taxpayer's VSO businesses have ceased. Cessation does not model gains on selling\nunderlying business assets."},"vso_tax_free_withdrawable_balance":{"type":"number","format":"double","description":"This business's additive slice of opening amounts already taxed or owed to the owner and\nwithdrawable before VSL § 5."},"vso_tax_layers":{"type":"array","items":{"$ref":"#/components/schemas/VsoTaxLayer"},"description":"This business's additive slice of opening retained VSO profit, grouped by statutory\nprovisional-tax period.\nEmpty for companies and valid for VSO accounts without opening retained profit."}}},"BusinessAnnualResult":{"type":"object","description":"Annual business result in constant model-year DKK.\n\nFor companies, a non-negative operating profit after operating expenses, before owner\nremuneration and company tax. The amount replaces `growth_rate` in covered years and accrues\nevenly each month. For VSO, a signed annual result before withdrawals and business tax.\n`year` through `end_year` is inclusive; omitted bounds use the plan's start and end.\n\nFor planning, the adviser-supplied result may include net business rental profit. A forecast\nbusiness disposal belongs in `PlanEvent::BusinessSale`, where proceeds and taxable gain remain\nseparate and cannot be counted twice.","required":["result_before_business_tax"],"properties":{"end_year":{"type":["integer","null"],"format":"int32","minimum":0},"result_before_business_tax":{"type":"number","format":"double"},"year":{"type":["integer","null"],"format":"int32","minimum":0}}},"BusinessTaxRegime":{"oneOf":[{"type":"object","required":["regime"],"properties":{"regime":{"type":"string","enum":["company"]}}},{"type":"object","required":["regime"],"properties":{"regime":{"type":"string","enum":["vso"]}}}],"description":"Tax calculation selected for a separately projected business account."},"Cadastre":{"type":"object","properties":{"cadastral_district_name":{"type":["string","null"]},"cadastral_district_number":{"type":["string","null"]},"cadastre_area":{"type":["integer","null"],"format":"int32"},"cadastre_id":{"type":["string","null"]},"cadastre_number":{"type":["string","null"]},"cadastre_road_area":{"type":["integer","null"],"format":"int32"},"municipality_name":{"type":["string","null"]},"municipality_number":{"type":["string","null"]},"municipality_slug":{"type":["string","null"]},"parish_name":{"type":["string","null"]},"parish_number":{"type":["string","null"]},"property_id":{"type":["integer","null"],"format":"int32"},"region_name":{"type":["string","null"]},"region_number":{"type":["string","null"]},"region_slug":{"type":["string","null"]},"unified_property_id":{"type":["string","null"]}}},"Changeset_FinancerRequest":{"type":"object","description":"Phoenix/Ecto-style changeset data, independent of whether attributes arrived\nas JSON or an HTML form.","required":["valid"],"properties":{"constraints":{"type":"array","items":{"$ref":"#/components/schemas/FieldConstraint"}},"draft":{"type":"object","required":["property","loans"],"properties":{"loans":{"type":"array","items":{"$ref":"#/components/schemas/ExistingLoanRequest"}},"new_loans":{"type":"array","items":{"$ref":"#/components/schemas/NewLoanRequest"}},"payout":{"type":["number","null"],"format":"double"},"property":{"$ref":"#/components/schemas/FinancerPropertyRequest"}},"additionalProperties":false},"issues":{"type":"array","items":{"$ref":"#/components/schemas/AdvisorIssue"}},"valid":{"type":"boolean"}}},"Column":{"type":"string","enum":["loans","new_loans","association","value","name","email","phone","debt_to_respect","total_registered_debt","custom_costs","type","date","comment","payout","referral","address","persons","protection_coverages","civil_status","plan_years","properties","target_spend"]},"CommonLoanRequest":{"type":"object","required":["loan_type","payments_per_term","interest_only_terms","lender"],"properties":{"contribution_rate":{"type":["number","null"],"format":"double"},"interest_only_terms":{"type":"number","format":"double"},"interest_rate":{"type":["number","null"],"format":"double"},"isin":{"type":["string","null"]},"lender":{"$ref":"#/components/schemas/Lender"},"loan_type":{"$ref":"#/components/schemas/LoanType"},"loan_variant":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/LoanVariants"}]},"margin":{"type":["number","null"],"format":"double"},"payments_per_term":{"type":"number","format":"double"},"redemption_rate":{"type":["number","null"],"format":"double"},"refinance_date":{"type":["string","null"],"format":"date"},"registered_interest_rate":{"type":["number","null"],"format":"double"},"registered_margin":{"type":["number","null"],"format":"double"}}},"Construction":{"type":"object","required":["building_id"],"properties":{"building_additional_heating_installation":{"type":["integer","null"],"format":"int32"},"building_area_built_in_carport":{"type":["integer","null"],"format":"int32"},"building_area_built_in_conservatory_or_similar":{"type":["integer","null"],"format":"int32"},"building_area_built_in_garage":{"type":["integer","null"],"format":"int32"},"building_area_built_in_outbuilding":{"type":["integer","null"],"format":"int32"},"building_commercial_area":{"type":["integer","null"],"format":"int32"},"building_construction_year":{"type":["integer","null"],"format":"int32"},"building_covered_area":{"type":["integer","null"],"format":"int32"},"building_extension_year":{"type":["integer","null"],"format":"int32"},"building_exterior_wall_material":{"type":["integer","null"],"format":"int32"},"building_heating_installation":{"type":["integer","null"],"format":"int32"},"building_id":{"type":"string"},"building_residential_area":{"type":["integer","null"],"format":"int32"},"building_roofing_material":{"type":["integer","null"],"format":"int32"},"building_status":{"type":["string","null"]},"building_total_size":{"type":["integer","null"],"format":"int32"},"building_type":{"type":["integer","null"],"format":"int32"},"building_weighted_area":{"type":["number","null"],"format":"double"},"cadastre_area":{"type":["integer","null"],"format":"int32"},"cadastre_id":{"type":["string","null"]},"case_id":{"type":["string","null"]},"case_type":{"type":["string","null"]},"floor_size_in_basement":{"type":["integer","null"],"format":"int32"},"floor_size_in_roof":{"type":["integer","null"],"format":"int32"},"floors_size":{"type":["integer","null"],"format":"int32"},"floors_weighted_area":{"type":["number","null"],"format":"double"},"house_number_id":{"type":["string","null"]},"is_on_leased_plot":{"type":["boolean","null"]},"number_of_floors":{"type":["integer","null"],"format":"int32"},"property_id":{"type":["integer","null"],"format":"int32"},"regular_floor_size":{"type":["integer","null"],"format":"int32"},"residential_basement_floor_size":{"type":["integer","null"],"format":"int32"},"residential_roof_floor_size":{"type":["integer","null"],"format":"int32"}}},"ContingencyEventType":{"type":"string","description":"Contingency event that can trigger a protection coverage.","enum":["death","loss_of_earning_capacity","critical_illness","accident","other"]},"CostDetail":{"type":"object","properties":{"cost_amount":{"type":["number","null"],"format":"double"},"cost_description":{"type":["string","null"]},"cost_type":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/CostType"}]},"cost_value":{"type":["number","null"],"format":"double","description":"Value used for recomputing `cost_amount`:\n- `fixed`: absolute amount in DKK (`cost_amount = cost_value`).\n- any other variant: factor on principal (`cost_amount = principal * cost_value`).\n\nFor `interest_on_margin_variable` and `redemption_rate` the factor bakes in\nthe institute-specific formula (differencerente / kursværdi), so consumers\ndon't need to redo it. The raw kursværdi is available on the loan itself."},"cost_variant":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/CostVariant"}]},"custom_cost_amount":{"type":["number","null"],"format":"double"},"custom_cost_value":{"type":["number","null"],"format":"double"},"max_value":{"type":["number","null"],"format":"double"},"min_value":{"type":["number","null"],"format":"double"}}},"CostType":{"type":"string","enum":["loan_processing_fee","case_fee","brokerage_cost","establishment_commission","price_addition_cost","redemption_fee","price_deduction_cost","compensation_rate","redemption_rate","bank_cost","bank_cost_variable"]},"CostVariant":{"type":"string","enum":["fixed","variable","interest_on_margin_variable","redemption_rate"]},"CreateWebhook":{"type":"object","description":"Create and immediately enable a webhook endpoint subscription.","required":["url","events"],"properties":{"description":{"type":["string","null"],"description":"Optional operator-facing label.","example":"Production CRM notifications"},"events":{"type":"array","items":{"$ref":"#/components/schemas/EventType"},"description":"Unique event subscriptions. At least one and at most four are allowed.","maxItems":4,"minItems":1},"url":{"type":"string","description":"Public HTTPS receiver URL. Credentials, localhost, and non-public IP addresses are rejected;\nredirects are not followed during delivery.","example":"https://example.com/webhooks/raad-til-bolig"}},"additionalProperties":false},"CurrentLoanRateRequest":{"type":"object","required":["loanVariant","lender","originationDate"],"properties":{"lender":{"$ref":"#/components/schemas/Lender"},"loanVariant":{"$ref":"#/components/schemas/LoanVariants"},"originationDate":{"type":"string","format":"date"},"registeredInterestRate":{"type":["number","null"],"format":"double"}}},"CurrentLoanRateResponse":{"type":"object","required":["loanVariant","lender","originationDate"],"properties":{"interestRate":{"type":["number","null"],"format":"double"},"lender":{"$ref":"#/components/schemas/Lender"},"loanVariant":{"$ref":"#/components/schemas/LoanVariants"},"originationDate":{"type":"string","format":"date"},"refinanceDate":{"type":["string","null"],"format":"date"},"registeredInterestRate":{"type":["number","null"],"format":"double"}}},"CustomCost":{"type":"object","properties":{"bank_cost":{"type":["number","null"],"format":"double"},"bank_cost_variable":{"type":["number","null"],"format":"double"},"brokerage_cost":{"type":["number","null"],"format":"double"},"case_fee":{"type":["number","null"],"format":"double"},"compensation_rate":{"type":["number","null"],"format":"double"},"establishment_commission":{"type":["number","null"],"format":"double"},"loan_processing_fee":{"type":["number","null"],"format":"double"},"price_addition_cost":{"type":["number","null"],"format":"double"},"price_deduction_cost":{"type":["number","null"],"format":"double"},"redemption_fee":{"type":["number","null"],"format":"double"}}},"Customer":{"type":"object","required":["name","email"],"properties":{"email":{"type":"string"},"name":{"type":"string"},"persons":{"type":"array","items":{"$ref":"#/components/schemas/CustomerPerson"}},"phone":{"type":["string","null"]},"referral":{"type":["string","null"]}}},"CustomerInput":{"oneOf":[{"type":"object","required":["customer","property","loans"],"properties":{"customer":{"$ref":"#/components/schemas/Customer"},"loans":{"type":"array","items":{"$ref":"#/components/schemas/Loan"}},"property":{"$ref":"#/components/schemas/Property"}}},{"$ref":"#/components/schemas/Customer"}]},"CustomerPerson":{"type":"object","required":["name","birth_date","role"],"properties":{"birth_date":{"type":"string","format":"date"},"name":{"type":"string"},"role":{"$ref":"#/components/schemas/CustomerPersonRole"}}},"CustomerPersonRole":{"type":"string","enum":["adult","child"]},"CustomerProperty":{"type":"object","required":["customer_property_key","property_key","address","value","loans"],"properties":{"address":{"type":"string"},"calculation_name":{"type":["string","null"],"description":"Stored as \"name\" in DynamoDB, renamed to avoid collision with bond name column in query chain"},"comment":{"type":["string","null"]},"custom_costs":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/CustomCost"}]},"customer_property_key":{"type":"string"},"debt_to_respect":{"type":["number","null"],"format":"double"},"loans":{"type":"array","items":{"$ref":"#/components/schemas/Loan"}},"new_loans":{"type":["array","null"],"items":{"$ref":"#/components/schemas/Loan"}},"payout":{"type":["number","null"],"format":"double"},"property_key":{"type":"string"},"property_type":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PropertyType"}]},"redemption_date":{"type":["string","null"],"format":"date"},"total_registered_debt":{"type":["number","null"],"format":"double"},"value":{"type":"number","format":"double"}}},"CustomerResponse":{"type":"object","description":"Response from POST /customer","required":["customer_key","update_token"],"properties":{"customer_key":{"type":"string","description":"The customer key (sort key in DynamoDB)"},"property_key":{"type":["string","null"],"description":"The property key if a property was created"},"update_token":{"type":"string","description":"JWT token for updating this customer (valid for 1 hour)"}}},"DataSource":{"type":"string","description":"Source of imported or manually entered decumulation data","enum":["manual","pensions_info","advisor","optimizer"]},"Declaration":{"type":"object","description":"Represents a property declaration (servitude/easement)","required":["declaration_type","registration_date","priority_number","description","affects_property_value","usage_restrictions","building_restrictions","property_relation_restrictions","other_restrictions"],"properties":{"affects_property_value":{"type":"boolean"},"building_restrictions":{"type":"array","items":{"type":"string"}},"declaration_type":{"type":"string"},"description":{"type":"array","items":{"type":"string"}},"other_restrictions":{"type":"array","items":{"type":"string"}},"priority_number":{"type":"string"},"property_relation_restrictions":{"type":"array","items":{"type":"string"}},"registration_date":{"type":"string","format":"date"},"title":{"type":["string","null"]},"usage_restrictions":{"type":"array","items":{"type":"string"}}}},"Declarations":{"type":"array","items":{"$ref":"#/components/schemas/Declaration"},"description":"Container for multiple declarations"},"DeleteCustomer":{"type":"object","required":["customer_key"],"properties":{"customer_key":{"type":"string"}}},"DeleteProperty":{"type":"object","required":["customer_key","property_key"],"properties":{"customer_key":{"type":"string"},"property_key":{"type":"string"}}},"DeliveryStatus":{"type":"string","description":"Result of a webhook delivery attempt.","enum":["pending","succeeded","failed","exhausted"]},"EmploymentAtpContributionMethod":{"type":"string","description":"How employment ATP is determined for one salary source.","enum":["calculated_from_hours","reported_annual_total"]},"Evaluation":{"type":"object","description":"The single application result consumed by Tera/Turbo and JSON API adapters.","required":["changeset"],"properties":{"changeset":{"$ref":"#/components/schemas/Changeset_FinancerRequest"},"projection":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/FinancerPropertyQuery"}]}}},"EventType":{"type":"string","description":"Notification lifecycle event delivered to subscribed webhook endpoints.","enum":["notification.created","notification.escalated","notification.acknowledged","notification.resolved"]},"ExistingLoanRebalanceChange":{"type":"object","required":["loan_index","keep_loan"],"properties":{"keep_loan":{"type":"boolean"},"loan_index":{"type":"integer","minimum":0},"selected_termination_type":{"type":["string","null"]}},"additionalProperties":false},"ExistingLoanRequest":{"allOf":[{"$ref":"#/components/schemas/CommonLoanRequest"},{"type":"object","required":["principal"],"properties":{"bond_principal_balance":{"type":["number","null"],"format":"double"},"date":{"type":["string","null"],"format":"date"},"keep_loan":{"type":["boolean","null"]},"loan_term":{"type":["number","null"],"format":"double"},"payment":{"type":["number","null"],"format":"double"},"principal":{"type":"number","format":"double"},"principal_balance":{"type":["number","null"],"format":"double"},"selected_termination_type":{"type":["string","null"]},"termination_types":{"type":["array","null"],"items":{"type":"string"}}}}]},"FieldConstraint":{"type":"object","description":"Backend-calculated field metadata used by both Tera inputs and API clients.","required":["path","code"],"properties":{"available":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Quantity"}]},"code":{"type":"string"},"current":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Quantity"}]},"max":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Quantity"}]},"min":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Quantity"}]},"path":{"type":"string"},"remaining":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Quantity"}]}}},"FinancerCost":{"type":"object","properties":{"cost":{"type":["number","null"],"format":"double"},"new_cost":{"type":["number","null"],"format":"double"},"payment_term":{"type":["integer","null"],"format":"int32"},"real_cost":{"type":["number","null"],"format":"double","description":"Cumulative costs in constant model-year DKK, with each year's charges deflated before summing."},"real_new_cost":{"type":["number","null"],"format":"double"}}},"FinancerFormKind":{"type":"string","enum":["current_loans","rebalance"]},"FinancerPayment":{"type":"object","properties":{"new_payment":{"type":["number","null"],"format":"double"},"payment":{"type":["number","null"],"format":"double"},"payment_term":{"type":["integer","null"],"format":"int32"},"real_new_payment":{"type":["number","null"],"format":"double"},"real_payment":{"type":["number","null"],"format":"double","description":"Annual scheduled payments in constant model-year DKK."}}},"FinancerPaymentDetails":{"type":"object","properties":{"contribution":{"type":["number","null"],"format":"double"},"interest":{"type":["number","null"],"format":"double"},"new_contribution":{"type":["number","null"],"format":"double"},"new_interest":{"type":["number","null"],"format":"double"},"new_paydown":{"type":["number","null"],"format":"double"},"new_payment":{"type":["number","null"],"format":"double"},"paydown":{"type":["number","null"],"format":"double"},"payment":{"type":["number","null"],"format":"double"}}},"FinancerProperty":{"type":"object","required":["property_key","address","value"],"properties":{"address":{"type":"string"},"aggregated_principal_balance":{"type":["number","null"],"format":"double"},"aggregated_principal_balance_difference":{"type":["number","null"],"format":"double"},"comment":{"type":["string","null"]},"costs":{"type":["array","null"],"items":{"$ref":"#/components/schemas/FinancerCost"}},"debt_to_respect":{"type":["number","null"],"format":"double"},"declarations":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Declarations"}]},"default_custom_total_cost_difference":{"type":["number","null"],"format":"double"},"down_conversion_years_loss":{"type":["number","null"],"format":"double"},"f3_interest_rate":{"type":["number","null"],"format":"double"},"f5_interest_rate":{"type":["number","null"],"format":"double"},"f_kort_interest_rate":{"type":["number","null"],"format":"double"},"fast_forrentet_interest_rate":{"type":["number","null"],"format":"double"},"loan_to_value_range_end":{"type":["number","null"],"format":"double"},"loans":{"type":["array","null"],"items":{"$ref":"#/components/schemas/Loan"}},"monthly_cost":{"type":["number","null"],"format":"double"},"monthly_cost_after_tax":{"type":["number","null"],"format":"double"},"monthly_cost_difference":{"type":["number","null"],"format":"double"},"monthly_cost_difference_after_tax":{"type":["number","null"],"format":"double"},"name":{"type":["string","null"]},"new_aggregated_principal_balance":{"type":["number","null"],"format":"double"},"new_loan_to_value_range_end":{"type":["number","null"],"format":"double"},"new_loans":{"type":["array","null"],"items":{"$ref":"#/components/schemas/Loan"}},"new_monthly_cost":{"type":["number","null"],"format":"double"},"new_monthly_cost_after_tax":{"type":["number","null"],"format":"double"},"new_total_costs":{"type":["number","null"],"format":"double"},"npv_difference":{"type":["number","null"],"format":"double"},"original_payout":{"type":["number","null"],"format":"double"},"payment_details":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/FinancerPaymentDetails"}]},"payments":{"type":["array","null"],"items":{"$ref":"#/components/schemas/FinancerPayment"}},"payout":{"type":["number","null"],"format":"double"},"principal_balance_change":{"type":["number","null"],"format":"double"},"property_key":{"type":"string"},"property_type":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PropertyType"}]},"redemption_date":{"type":["string","null"],"format":"date"},"registration_costs":{"type":["number","null"],"format":"double"},"termination_date":{"type":["string","null"],"format":"date"},"total_costs":{"type":["number","null"],"format":"double"},"total_registered_debt":{"type":["number","null"],"format":"double"},"up_conversion_years_profit":{"type":["number","null"],"format":"double"},"value":{"type":"number","format":"double"}}},"FinancerPropertyQuery":{"allOf":[{"$ref":"#/components/schemas/FinancerProperty"},{"type":"object","properties":{"cost_details":{"type":["array","null"],"items":{"$ref":"#/components/schemas/CostDetail"}},"custom_costs":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/CustomCost"}]},"new_cost_details":{"type":["array","null"],"items":{"$ref":"#/components/schemas/CostDetail"}}}}]},"FinancerPropertyRequest":{"type":"object","required":["address","value"],"properties":{"address":{"type":"string"},"debt_to_respect":{"type":["number","null"],"format":"double"},"property_type":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PropertyType"}]},"total_registered_debt":{"type":["number","null"],"format":"double"},"value":{"type":"number","format":"double"}},"additionalProperties":false},"FolkepensionPayoutType":{"type":"string","description":"How to receive deferred folkepension amounts.\n\nWaiting percentages are calculated from the statutory denominator table for the\nrecipient's age when payment begins; they are not fixed annual percentages.","enum":["lifelong","ten_year","lump_sum"]},"ForecastProvenance":{"type":"object","description":"Source of a supplied annual forecast anchor.","required":["source","calculation_date"],"properties":{"calculation_date":{"type":"string","format":"date"},"source":{"$ref":"#/components/schemas/DataSource"}}},"Gender":{"type":"string","description":"Gender enum for life expectancy calculation\n\nSource: Danmarks Statistik\n- Overview: https://www.statistikbanken.dk/20015\n- Data: https://www.statistikbanken.dk/HISB7","enum":["male","female"]},"HousingQueryV2":{"type":"object","required":["address","address_slug","point","address_slug_prefix"],"properties":{"address":{"type":"string","description":"Address of the property","example":"Ørevadsvej 29, 2400 København NV"},"address_slug":{"type":"string","description":"Address slug based on the DAR address","example":"orevadsvej-29-2400-kobenhavn-nv"},"address_slug_prefix":{"type":"string","description":"First 3 characters of the address slug, used for HIVE partitioning the data"},"building_construction_year":{"type":["number","null"],"format":"double","description":"Year of the primary building construction"},"building_type":{"type":["number","null"],"format":"double","description":"BygAnvendelse from BBR (Building type)"},"cadastre_area":{"type":["number","null"],"format":"double","description":"Plot area in square meters"},"city":{"type":["string","null"],"description":"City name of the property"},"current_purchase_date":{"type":["string","null"],"format":"date-time","description":"Date of the latest purchase"},"current_purchase_price":{"type":["number","null"],"format":"double","description":"Price of the latest purchase"},"current_transfer_date":{"type":["string","null"],"format":"date-time","description":"Date of the latest transfer (the take over date)"},"current_transfer_type":{"type":["string","null"],"description":"Type of the current transfer e.g. Almindelig, Familie","example":"Almindelig fri handel"},"latest_plot_valuation":{"type":["number","null"],"format":"double","description":"Latest plot valuation year from SKAT"},"latest_property_valuation":{"type":["number","null"],"format":"double","description":"Latest property valuation from SKAT"},"latest_valuation_year":{"type":["integer","null"],"format":"int32","description":"Latest valuation year from SKAT"},"municipality_number":{"type":["string","null"],"description":"Municipality number (e.g. 101 for Copenhagen)"},"point":{"$ref":"#/components/schemas/Point2d","description":"Point coordinates for the address"},"postal_code":{"type":["string","null"],"description":"Postal code of the property"},"predicted_total_purchase_sum":{"type":["number","null"],"format":"double","description":"Model-predicted total property price. The default model uses 40 geographical, 10 structural, and 5 plot/demolition transaction references."},"property_id":{"type":["integer","null"],"format":"int32","description":"Property ID (BFE). Null for rental addresses."},"property_type":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PropertyType","description":"Property type (e.g. house, owner_apartment, vacation_home). Null for rental addresses."}]},"weighted_area":{"type":["number","null"],"format":"double","description":"Weighted area based on Finans Danmarks rules"}}},"HousingResponseV2":{"allOf":[{"$ref":"#/components/schemas/HousingQueryV2"},{"type":"object","properties":{"debt_to_respect":{"type":["number","null"],"format":"double"},"loans":{"type":"array","items":{"$ref":"#/components/schemas/Loan"}},"total_registered_debt":{"type":["number","null"],"format":"double"}}}]},"IncomeSource":{"type":"object","description":"An external income source with timing and tax treatment.\n\nThe source is projected only for the intersection of its own inclusive period and the plan\nwindow. Retirement age truncates the source only when `end_at_retirement` is selected.","required":["income_type"],"properties":{"afterlon_deduction_hours":{"type":["number","null"],"format":"double","description":"Exact annual deduction hours stated by the a-kasse for `authority_assessed_hours`."},"afterlon_income_basis":{"type":["number","null"],"format":"double","description":"Annual net amount used only for efterløn conversion/permission limits. For uncontrolled\nwork this is pay after documented necessary work expenses; for the 400-hour scheme it is\nthe member's accepted dækningsbidrag 1. Defaults to the source's annual income amount when\nabsent."},"afterlon_single_task_total_hours":{"type":["number","null"],"format":"double","description":"Actual total hours in an a-kasse-approved one-off self-employment task.\n\nThis is a task total for the complete explicitly dated period, not an annual run-rate.\nIt is required only for `self_employment_single_task`."},"afterlon_transition_month_hours":{"type":["number","null"],"format":"double","description":"Actual controllable work hours on or after the exact efterløn start date in the first\npartial transition month.\n\nThis source-specific value is required when controllable work overlaps the first partial\nordinary/deltid efterløn start month, whether the exact start is supplied explicitly or\ninferred from the legal/selected start date. It is used only for the statutory 128/104\nhour monthly ceiling; the annual `hours` run-rate continues to drive ordinary deduction."},"afterlon_work_rule":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/AfterlonWorkRule","description":"Explicit efterløn work classification. Salary with reported hours defaults to\ncontrollable work, while salary without hours and B-income default to income conversion.\nSelf-employment must select its approved scheme."}]},"annual_am_liable_salary":{"type":["number","null"],"format":"double","description":"Positive annual AM-contribution-liable salary after the employee's ATP contribution and\nemployee share of employer-administered pension, but before AM contribution and A-tax.\n\nThis is the salary source's annual cash and tax basis. Non-salary sources use\n`annual_amount` instead. An ATP contribution without positive salary is not represented as\na salary income source."},"annual_amount":{"type":["number","null"],"format":"double","description":"Gross annual cash amount for every non-salary income type.\n\nSalary uses `annual_am_liable_salary` instead."},"annual_total_atp_contribution":{"type":["number","null"],"format":"double","description":"Annualized full-year ATP contribution run-rate, used only when\n`atp_contribution_method` selects `reported_annual_total`.\nThe plan applies one twelfth in every active month. A total observed over part of a year\nmust therefore be annualized before it enters this field.\nIt increases the LL § 9 J employment-deduction base before AM, is AM-liable at source,\nand enters the LL § 9 L extra-pension-deduction base after AM. The salary amount already\nexcludes the employee ATP share, so this amount is not salary cash or ordinary taxable\npersonal income. When salary is linked to a business, this remains an employer cash expense\nand part of the LL § 8 N remuneration basis."},"atp_contribution_method":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/EmploymentAtpContributionMethod","description":"Selects automatic A-rate ATP from hours or an explicitly reported annual total.\n\nThis is required for salary and absent for every non-salary income type."}]},"business_id":{"type":["string","null"],"description":"Stable link to the business funding this salary, dividend or other owner cash flow."},"end_at_retirement":{"type":"boolean","description":"End the source in the person's inclusive retirement calendar month.\n\nThis follows later changes to the person's modeled retirement age and is mutually\nexclusive with `end_year` and `end_month`."},"end_month":{"type":["integer","null"],"format":"int32","description":"Inclusive last active calendar month. Requires `end_year` when present.","minimum":0},"end_year":{"type":["integer","null"],"format":"int32","description":"Inclusive end year (None = active through plan end).","minimum":0},"hours":{"type":["integer","null"],"format":"int32","description":"Annual reported/proven hours for salary, B-income or recurring self-employment.\n\nShared advisor-supplied hours for work requirements, including folkepension deferral\nand seniorpræmie. The advisor establishes qualifying hours for B-income/self-employment;\nincome and VSO withdrawals are not converted into hours. A missing value contributes zero.\nA one-off afterløn self-employment task uses `afterlon_single_task_total_hours` instead.","minimum":0},"income_source_id":{"type":["string","null"],"description":"Stable identifier for one contractual income source."},"income_type":{"$ref":"#/components/schemas/IncomeType","description":"Type of income (salary, dividend, capital)"},"is_qualifying_sports_income":{"type":"boolean","description":"True only for earned income that the adviser/importer has classified as income from\nqualifying professional sports activity under PBL § 15 B. This amount limits the\ndeductible sportspension contribution in the same income year."},"property_key":{"type":["string","null"],"description":"Stable link to the physical property whose rental allowance applies to this source."},"protection_coverage_id":{"type":["string","null"],"description":"Optional link to the potential disability-income coverage whose awarded claim this\nrealized income represents."},"source_name":{"type":["string","null"],"description":"Optional name of source (e.g., \"Holding ApS\", \"Sommerhus\")"},"start_month":{"type":["integer","null"],"format":"int32","description":"Inclusive first active calendar month. Requires `start_year` when present.","minimum":0},"start_year":{"type":["integer","null"],"format":"int32","description":"Inclusive start year (None = active from plan start).","minimum":0},"taxable_amount_rule":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/TaxableAmountRule","description":"Rule for reducing the gross annual income amount to a taxable amount.\nNone means gross amount is fully taxable (default)."}]}}},"IncomeType":{"type":"string","description":"Types of external income (non-pension)","enum":["salary","b_income","self_employment","dividend","capital_income","disability_insurance"]},"InvestmentCategory":{"type":"string","description":"Investment category used to resolve the official return assumptions published by\nRådet for Afkastforventninger.\n\nThe variants represent the Council's asset classes. Hedge funds are split by their\nyear-11+ mandate because the Council requires class 10 to be placed as bonds, equities,\nor illiquid assets at the long horizon. `Cash` is intentionally distinct from the Council's\nten asset classes. `OtherAssets` is the plan's own constant-real-value profile for possessions.","enum":["other_assets","government_and_mortgage_bonds","investment_grade_bonds","high_yield_bonds","emerging_markets_government_bonds","global_equities","emerging_markets_equities","private_equity","infrastructure","real_estate","hedge_funds_bond_mandate","hedge_funds_equity_mandate","hedge_funds_illiquid_mandate","cash"]},"IssueContext":{"type":"object","description":"Typed values commonly needed to explain a financial rule.\n\nKeeping this shape application-owned prevents a validation crate or Polars\ncolumn layout from becoming part of the public API contract.","properties":{"actual":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Quantity"}]},"applied":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Quantity"}]},"available":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Quantity"}]},"identifier":{"type":["string","null"]},"limit":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Quantity"}]},"planned":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Quantity"}]},"required":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Quantity"}]}}},"IssueDomain":{"type":"string","description":"Domain that produced an issue. Clients can use this for routing without parsing paths.","enum":["financer","plan","webhook"]},"IssueSeverity":{"type":"string","description":"Stable business-rule severity shared by the HTML platform and public APIs.","enum":["error","warning","info"]},"IssueSource":{"type":"string","description":"Boundary that produced an issue.","enum":["cast","contract","calculation","policy"]},"KapitalConversion":{"oneOf":[{"type":"object","required":["target"],"properties":{"target":{"type":"string","enum":["aldersopsparing"]}}},{"type":"object","required":["target"],"properties":{"target":{"type":"string","enum":["ratepension"]}}}],"description":"Conversion options for Kapitalpension.\n\nKapitalpension can be converted to Aldersopsparing (pay 40% now, tax-free later)\nor to Ratepension (defer tax, but causes folkepension modregning)."},"Lender":{"oneOf":[{"type":"string","enum":["Realkredit Danmark"],"example":"Realkredit Danmark"},{"type":"string","enum":["Nordea Kredit"],"example":"Nordea Kredit"},{"type":"string","enum":["Jyske Realkredit"],"example":"Jyske Realkredit"},{"type":"string","enum":["Totalkredit"],"example":"Totalkredit"},{"type":"object","required":["Other"],"properties":{"Other":{"type":"string"}}}]},"Loan":{"oneOf":[{"$ref":"#/components/schemas/ActualLoan"},{"type":"object","properties":{"loan_type":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/LoanType"}]}}}]},"LoanCostDetail":{"type":"object","description":"Per-loan cost breakdown for consumer recalculation.\n\n`cost_amount` recomputation rule (uniform across variants):\n- `fixed`:  `cost_amount = cost_value` (DKK)\n- any other variant: `cost_amount = principal × cost_value`\n\nFor `interest_on_margin_variable` and `redemption_rate`, `cost_value` is a\nblended factor that already bakes in the institute-specific formula\n(differencerente / kursværdi), so consumers don't need to redo it. The raw\nredemption rate (kursværdi) is available on the loan itself.","properties":{"cost_amount":{"type":["number","null"],"format":"double"},"cost_description":{"type":["string","null"]},"cost_type":{"type":["string","null"]},"cost_value":{"type":["number","null"],"format":"double"},"cost_variant":{"type":["string","null"]},"custom_cost_amount":{"type":["number","null"],"format":"double"},"custom_cost_value":{"type":["number","null"],"format":"double"},"max_value":{"type":["number","null"],"format":"double"},"min_value":{"type":["number","null"],"format":"double"}}},"LoanMonitoringSnapshot":{"type":"object","description":"Snapshot for conversion and refinance notifications.","required":["address","current_interest_rate","candidate_interest_rate","candidate_redemption_rate","current_redemption_rate","redemption_rate_improvement","current_isin"],"properties":{"address":{"type":"string"},"candidate_interest_rate":{"type":["number","null"],"format":"double"},"candidate_redemption_rate":{"type":["number","null"],"format":"double"},"current_interest_rate":{"type":["number","null"],"format":"double"},"current_isin":{"type":["string","null"]},"current_redemption_rate":{"type":["number","null"],"format":"double"},"redemption_rate_improvement":{"type":["number","null"],"format":"double"}}},"LoanType":{"oneOf":[{"type":"string","enum":["Realkredit lån"],"example":"Realkredit lån"},{"type":"string","enum":["Bank lån"],"example":"Bank lån"},{"type":"object","required":["other"],"properties":{"other":{"type":"string"}}}]},"LoanVariants":{"oneOf":[{"type":"string","enum":["FKort"],"example":"FKort"},{"type":"string","enum":["F1"],"example":"F1"},{"type":"string","enum":["F3"],"example":"F3"},{"type":"string","enum":["F5"],"example":"F5"},{"type":"string","enum":["Variabelt"],"example":"Variabelt"},{"type":"string","enum":["Fastforrentet"],"example":"Fastforrentet"},{"type":"string","enum":["Banklån"],"example":"Banklån"},{"type":"string","enum":["other"]},{"type":"object","required":["reference_rente"],"properties":{"reference_rente":{"type":"string"}}}]},"Location":{"type":"object","properties":{"access_address":{"type":["string","null"]},"address":{"type":["string","null"]},"address_id":{"type":["string","null"]},"address_slug":{"type":["string","null"]},"apartment_distribution_denominator":{"type":["integer","null"],"format":"int32"},"apartment_distribution_numerator":{"type":["integer","null"],"format":"int32"},"apartment_number":{"type":["integer","null"],"format":"int32"},"apartment_size":{"type":["integer","null"],"format":"int32"},"cadastral_district_name":{"type":["string","null"]},"cadastral_district_number":{"type":["string","null"]},"cadastre_area":{"type":["integer","null"],"format":"int32"},"cadastre_id":{"type":["string","null"]},"cadastre_number":{"type":["string","null"]},"cadastre_road_area":{"type":["integer","null"],"format":"int32"},"city":{"type":["string","null"]},"coast_distance":{"type":["number","null"],"format":"double"},"floor_designation":{"type":["string","null"]},"forest_distance":{"type":["number","null"],"format":"double"},"ground_drain_code":{"type":["integer","null"],"format":"int64"},"ground_water_supply_code":{"type":["integer","null"],"format":"int64"},"high_voltage_line_distance":{"type":["number","null"],"format":"double"},"house_number":{"type":["string","null"]},"house_number_id":{"type":["string","null"]},"household_count":{"type":["integer","null"],"format":"int32"},"is_on_leased_plot":{"type":["boolean","null"]},"lake_distance":{"type":["number","null"],"format":"double"},"low_rise_area_distance":{"type":["number","null"],"format":"double"},"motor_traffic_road_distance":{"type":["number","null"],"format":"double"},"motorway_distance":{"type":["number","null"],"format":"double"},"municipality_name":{"type":["string","null"]},"municipality_number":{"type":["string","null"]},"municipality_slug":{"type":["string","null"]},"parish_name":{"type":["string","null"]},"parish_number":{"type":["string","null"]},"point":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Point2d"}]},"population":{"type":["integer","null"],"format":"int32"},"postal_code":{"type":["string","null"]},"postal_code_id":{"type":["string","null"]},"property_address":{"type":["string","null"]},"property_address_slug":{"type":["string","null"]},"property_id":{"type":["integer","null"],"format":"int32"},"property_postal_code":{"type":["string","null"]},"property_street_name":{"type":["string","null"]},"property_street_name_slug":{"type":["string","null"]},"railway_distance":{"type":["number","null"],"format":"double"},"region_name":{"type":["string","null"]},"region_number":{"type":["string","null"]},"region_slug":{"type":["string","null"]},"settlement_area":{"type":["integer","null"],"format":"int64"},"settlement_code":{"type":["string","null"]},"settlement_id":{"type":["string","null"]},"settlement_name":{"type":["string","null"]},"settlement_type":{"type":["string","null"]},"street_name":{"type":["string","null"]},"street_name_id":{"type":["string","null"]},"street_name_slug":{"type":["string","null"]},"train_station_distance":{"type":["number","null"],"format":"double"},"transit_route_distance":{"type":["number","null"],"format":"double"},"unified_property_id":{"type":["string","null"]}}},"MortgageBalanceMonth":{"type":"object","description":"Mortgage principal state in constant 2026 DKK, effective in one calendar month. The first\nzero-cash row is the\nopening principal at the plan evaluation month (or shifted acquisition month); subsequent\nrows are the closing principal after that month's contractual payments.","required":["year","month","acquisition_loan_proceeds","principal_balance"],"properties":{"acquisition_loan_proceeds":{"type":"number","format":"double","description":"Loan cash made available in this month. Non-zero only on the opening acquisition row."},"month":{"type":"integer","format":"int32","minimum":0},"principal_balance":{"type":"number","format":"double"},"refinancing_payout":{"type":"number","format":"double","description":"Net refinancing payout made available in this event month. Negative values are the cash\ncontribution required to complete the refinancing."},"year":{"type":"integer","format":"int32"}}},"NewLoanRequest":{"allOf":[{"$ref":"#/components/schemas/CommonLoanRequest"},{"type":"object","required":["principal","loan_term"],"properties":{"date":{"type":["string","null"],"format":"date"},"loan_term":{"type":"number","format":"double"},"principal":{"type":"number","format":"double"}}}]},"NotificationEventRecord":{"type":"object","required":["PK","SK","association","notification_key","event_type","revision","payload","created_at"],"properties":{"PK":{"type":"string"},"SK":{"type":"string"},"association":{"type":"string"},"created_at":{"type":"string"},"event_type":{"$ref":"#/components/schemas/EventType"},"notification_key":{"type":"string"},"payload":{},"revision":{"type":"integer","format":"int32","minimum":0}}},"NotificationPayload":{"type":"object","description":"Notification state embedded in every outbound webhook event.","required":["notification_type","status","severity","source_key","revision","title","summary","fingerprint","material_score","snapshot"],"properties":{"fingerprint":{"type":"string","description":"Stable material-content fingerprint used for notification deduplication.","example":"conversion.down:property:1:Testvej 1"},"material_score":{"type":["number","null"],"format":"double","description":"Comparable business score used to decide whether a later observation is material."},"notification_type":{"$ref":"#/components/schemas/NotificationType","description":"Business signal represented by the notification."},"revision":{"type":"integer","format":"int32","description":"Monotonically increasing notification revision.","example":1,"minimum":0},"severity":{"$ref":"#/components/schemas/Severity","description":"Display priority assigned to the notification."},"snapshot":{"$ref":"#/components/schemas/NotificationSnapshot","description":"Type-specific source data. The shape is determined by `notification_type`:\nconversion and refinance events use `LoanMonitoringSnapshot`, bond alarms use\n`BondAlarmSnapshot`, price increases use `PriceIncreaseSnapshot`, and tinglysning\nevents use `TinglysningSnapshot`."},"source_key":{"type":"string","description":"Stable source identifier, such as a property or alarm key.","example":"property:0195c9e8-23e6-7be0-bc79-b8ba1ff34e37"},"status":{"$ref":"#/components/schemas/NotificationStatus","description":"Notification status after applying this event."},"summary":{"type":"string","description":"Human-readable notification summary.","example":"Der er en nedkonvertering på Testvej 1"},"title":{"type":"string","description":"Human-readable notification title.","example":"Nedkonvertering"}}},"NotificationRecord":{"type":"object","required":["PK","SK","association","notification_type","status","severity","source_key","revision","latest_snapshot","latest_fingerprint","title","summary","created_at","updated_at"],"properties":{"PK":{"type":"string"},"SK":{"type":"string"},"acknowledged_revision":{"type":["integer","null"],"format":"int32","minimum":0},"association":{"type":"string"},"created_at":{"type":"string"},"latest_fingerprint":{"type":"string"},"latest_snapshot":{},"material_score":{"type":["number","null"],"format":"double"},"notification_type":{"$ref":"#/components/schemas/NotificationType"},"revision":{"type":"integer","format":"int32","minimum":0},"severity":{"$ref":"#/components/schemas/Severity"},"source_key":{"type":"string"},"status":{"$ref":"#/components/schemas/NotificationStatus"},"summary":{"type":"string"},"title":{"type":"string"},"updated_at":{"type":"string"}}},"NotificationSnapshot":{"oneOf":[{"$ref":"#/components/schemas/LoanMonitoringSnapshot"},{"$ref":"#/components/schemas/BondAlarmSnapshot"},{"$ref":"#/components/schemas/PriceIncreaseSnapshot"},{"$ref":"#/components/schemas/TinglysningSnapshot"}],"description":"Type-specific notification snapshot embedded in `NotificationPayload`."},"NotificationStatus":{"type":"string","description":"Current notification lifecycle status after the event was applied.","enum":["open","acknowledged","resolved"]},"NotificationTarget":{"type":"object","required":["customer_key"],"properties":{"customer_key":{"type":"string"}}},"NotificationType":{"type":"string","description":"Business signal represented by a notification.","enum":["conversion.up","conversion.down","refinance","bond_alarm","price_increase","tinglysning"]},"PatchCustomer":{"type":"object","required":["column","sort_key","data"],"properties":{"column":{"$ref":"#/components/schemas/Column"},"data":{"$ref":"#/components/schemas/PatchCustomerData"},"primary_key":{"type":["string","null"]},"sort_key":{"type":"string"}}},"PatchCustomerData":{"oneOf":[{"type":"array","items":{"$ref":"#/components/schemas/Person"}},{"type":"array","items":{"$ref":"#/components/schemas/ProtectionCoverage"}},{"type":"array","items":{"$ref":"#/components/schemas/CustomerPerson"}},{"type":"array","items":{"$ref":"#/components/schemas/PropertyRef"}},{"type":"array","items":{"$ref":"#/components/schemas/TargetSpend"}},{"type":"array","items":{"$ref":"#/components/schemas/Loan"}},{"type":"string"},{"type":"number","format":"double"},{"type":["number","null"],"format":"double"},{"$ref":"#/components/schemas/CustomCost"}]},"PayoutType":{"type":"string","description":"Payout type for pension","enum":["lump_sum","periodic","lifelong"]},"Pension":{"type":"object","required":["pension_type","investment_category"],"properties":{"afterlon_other_public_support_reduced_pension_amount":{"type":["number","null"],"format":"double","description":"Gross pension amount paid after age 60 and before efterlønsalderen that has already\nreduced another public support benefit.\n\nUnder arbejdsløshedsforsikringsloven § 74 k, stk. 2, this documented amount is excluded\nfrom the prior-payout amount otherwise added to the efterløn pension valuation."},"aldersopsparing_high_cap_disqualifying_payout":{"type":["boolean","null"],"description":"Provider/advisor classification under PBL § 25 A, stk. 6-7. `None` uses the\nproduct-type lookup default; `false` records that the particular payout is covered\nby a statutory exception and therefore does not remove the high aldersopsparing cap."},"atp_lump_sum_amount":{"type":["number","null"],"format":"double","description":"ATP's official gross capitalized one-time payout. Required when the supplied annual ATP\nentitlement is at or below the year's capitalization threshold; the annual entitlement\nalone is not enough to reconstruct ATP's actuarial capital value."},"balance":{"type":["number","null"],"format":"double","description":"Opening balance at the plan boundary, before projected return, contributions, and payout.\nOmission means that no benefit-level balance is available,\nas can happen when one PensionsInfo agreement contains several separately forecast\nbenefits. In that case a usable payout forecast is required instead.\n\nFor ATP (including any accrued SUPP right) and tjenestemandspension this field instead\ncontains the official annual gross entitlement."},"earliest_payout_age":{"type":["number","null"],"format":"double","description":"Earliest payout age reported by the provider or an adviser.\n\nThis is the canonical access boundary for provider-reported pensions. It already\nreflects protected rights and product-specific minimum holding periods."},"forecast_sets":{"type":"array","items":{"$ref":"#/components/schemas/PensionForecastSet"},"description":"Alternative forecast anchor sets for this pension."},"investment_category":{"$ref":"#/components/schemas/InvestmentCategory","description":"Underlying investment category used to resolve Rådet's return assumptions."},"is_employer_paid":{"type":"boolean","description":"True when the annual contribution is employer-administered and AM-bidrag applies on the\ncontribution itself."},"is_under_payout":{"type":"boolean","description":"True when the provider reports the pension is already paying out."},"latest_payout_end_offset_years":{"type":["number","null"],"format":"double","description":"Provider/advisor latest legal end offset, measured in years after earliest payout."},"latest_payout_start_offset_years":{"type":["number","null"],"format":"double","description":"Provider/advisor latest legal start offset, measured in years after earliest payout."},"max_payment_years":{"type":["integer","null"],"format":"int32","description":"Advisor/import maximum payout period constraint.","minimum":0},"min_payment_years":{"type":["integer","null"],"format":"int32","description":"Advisor/import minimum payout period constraint.","minimum":0},"payment_years":{"type":["integer","null"],"format":"int32","description":"Agreed payout years. For PensionsInfo rows under payout this is remaining years.","minimum":0},"payout_form":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PayoutType","description":"Contractual payout form: lump sum, fixed period, or lifelong."}]},"payout_regulation":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PensionPayoutRegulation","description":"Explicit lifelong payment assumption when no within-scenario curve exists."}]},"pension_id":{"type":["string","null"],"description":"Stable identifier for this pension across imports/optimizer decisions"},"pension_type":{"type":"string","description":"Type of pension (ratepension, livsvarig_pension, kapitalpension, etc.)"},"pensions_info_source":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PensionsInfoSource","description":"PensionsInfo import identity when this pension was normalized from a provider benefit."}]},"plan":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PensionPayout","description":"Payout schedule for this pension (when and how much)\nEach pension owns its disbursement schedule, like assets own withdrawals"}]},"prior_sportspension_contributions":{"type":"number","format":"double","description":"Aggregate qualifying contributions already made to this § 15 B sportspension before the\ncurrent projection year. Only valid for the single sportspension account held by a person."},"selected_forecast_set_id":{"type":["string","null"],"description":"Forecast set selected for the main projection, e.g. imported or advisor base."},"strategy":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PensionStrategy","description":"Optional strategy for this pension (set by optimizer or advisor)\nThe strategy variant must match the pension_type"}]},"tax_free_payout":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PensionTaxFreePayout","description":"Documented tax-free payout amount under PBL § 20(3)-(4), § 47, or § 32(1).\n\nOmission means no documented special tax-free portion has been supplied. Periodic and\nlifelong arrangements use the provider/authority annual amount or fraction. A\nkapitalpension uses its remaining no-duty basis."}]}},"additionalProperties":false},"PensionForecastCalculation":{"type":"object","description":"One payout anchor for one forecast scenario.","required":["point"],"properties":{"payout_basis":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PensionForecastPayoutBasis"}]},"payout_periods":{"type":"array","items":{"$ref":"#/components/schemas/PensionForecastPeriod"},"description":"Complete component run-rates within this scenario. When supplied, these\nreplace the quoted total; they are never added to it (including OP/SUPP)."},"point":{"$ref":"#/components/schemas/PensionForecastPoint"}}},"PensionForecastPayoutBasis":{"type":"object","description":"Source facts for a supplied within-scenario payout curve. Separately reported\nfinite benefits must be separate pension records, not components of this right.","required":["commencement_date"],"properties":{"commencement_date":{"type":"string","format":"date"},"historical_payments_confirmed":{"type":"boolean","description":"Past parts of the periods have been reconciled to actual gross payments.\nA forecast alone is not evidence of money already paid."},"lifelong_annual_right":{"type":"boolean","description":"Provider-confirmed annual lifelong right (PERE method 1), including any\nbonus/price regulation. False means statutory classification is unresolved."}},"additionalProperties":false},"PensionForecastPeriod":{"type":"object","description":"A component's annual gross run-rate, in constant 2026 DKK, valid over an\nexplicit interval within ONE retirement-start scenario. These are not span\naverages or point observations: an importer must establish interval semantics.","required":["component_id","from_month","annual_gross_payout"],"properties":{"annual_gross_payout":{"type":"number","format":"double"},"component_id":{"type":"string"},"from_month":{"type":"integer","format":"int32","description":"Inclusive month offset from the scenario's quoted commencement.","minimum":0},"until_month":{"type":["integer","null"],"format":"int32","description":"Exclusive month offset; omission means this component continues for life.","minimum":0}},"additionalProperties":false},"PensionForecastPoint":{"type":"object","description":"One annual payout point in a pension forecast/prognosis, in constant 2026 DKK.","required":["annual_gross_payout"],"properties":{"age":{"type":["integer","null"],"format":"int32","minimum":0},"annual_gross_payout":{"type":"number","format":"double"},"year":{"type":["integer","null"],"format":"int32","minimum":0}}},"PensionForecastSet":{"type":"object","description":"A named prognosis set, such as a PensionsInfo forecast or advisor scenario.\nEvery payout point must be normalized to constant 2026 DKK before entering the plan.","required":["forecast_set_id"],"properties":{"calculations":{"type":"array","items":{"$ref":"#/components/schemas/PensionForecastCalculation"}},"forecast_set_id":{"type":"string"},"provenance":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ForecastProvenance"}]},"reference_payment_years":{"type":["integer","null"],"format":"int32","description":"Payout duration used by the provider to calculate finite forecast scenarios.\nThis calibrates an implied balance independently of the adviser-selected duration.","minimum":0}}},"PensionPayout":{"type":"object","description":"Payout schedule for a single pension.\n\nEach pension owns its disbursement schedule, similar to how assets own withdrawals.\n\n`end_year` is **exclusive**: a pension paying out for 15 years from 2032 has\n`start_year = 2032` and `end_year = 2047` (the payout stops before 2047).\nA lump-sum paid in 2032 has `end_year = Some(2033)`.\n\nAn optimized finite schedule must start inside the plan window, but its legal payout period may\ncontinue beyond the reporting horizon. `defer_within_plan_horizon` is the distinct instruction\nthat no payout should start in this plan. It deliberately carries no future start date: the\npension must be reassessed before its latest legal start.\n\nStored nested in each pension:\n```json\n{\n  \"pension_type\": \"ratepension\",\n  \"balance\": 2500000,\n  \"strategy\": { \"type\": \"rate\", \"principle\": \"serie\" },\n  \"plan\": {\n    \"start_year\": 2032,\n    \"end_year\": 2047\n  }\n}\n```","properties":{"defer_within_plan_horizon":{"type":"boolean","description":"Explicitly leave this pension unstarted throughout the current reporting horizon.\nNo start or end boundary may be supplied together with this instruction."},"end_month":{"type":["integer","null"],"format":"int32","description":"Calendar month payouts end (1-12), using the same exclusive convention as `end_year`.","minimum":0},"end_year":{"type":["integer","null"],"format":"int32","description":"Year payouts end, exclusive (None = lifelong). A payout from 2032 to 2047\ncovers years 2032..2046 (15 years). A lump-sum in 2032 has end_year = 2033.","minimum":0},"start_month":{"type":["integer","null"],"format":"int32","description":"Calendar month payouts begin (1-12).","minimum":0},"start_year":{"type":["integer","null"],"format":"int32","description":"Year payouts begin","minimum":0}}},"PensionPayoutInflationPeriod":{"type":"object","description":"A saved inflation assumption, not an observation of historical inflation.\nCalendar intervals are contiguous; the last interval continues indefinitely.","required":["from_year","annual_rate"],"properties":{"annual_rate":{"type":"number","format":"double"},"from_year":{"type":"integer","format":"int32","minimum":0},"until_year":{"type":["integer","null"],"format":"int32","description":"Exclusive calendar-year boundary.","minimum":0}},"additionalProperties":false},"PensionPayoutRegulation":{"type":"object","description":"Explicit planning assumption for a lifelong annual amount. It applies to a\nscalar provider quote or to the balance-based annuity conversion; this is not\nan insurer's repricing method. A supplied interval schedule already includes\nits expected regulation and cannot be combined with this assumption.","required":["kind"],"properties":{"inflation_periods":{"type":"array","items":{"$ref":"#/components/schemas/PensionPayoutInflationPeriod"},"description":"Dated assumptions retained when reopening a plan. Refreshing a provider\nquote requires explicitly replacing its amount, anchor and assumptions."},"kind":{"$ref":"#/components/schemas/PensionPayoutRegulationKind"},"reference_date":{"type":["string","null"],"format":"date","description":"A scalar quote or the balance-based first annual amount is valued at the\nstart of this calendar month. When omitted, the resolved first payout\nmonth is the anchor; unnecessary for real_level."}},"additionalProperties":false},"PensionPayoutRegulationKind":{"type":"string","enum":["real_level","nominal_level"]},"PensionStrategy":{"oneOf":[{"type":"object","description":"Strategy for Kapitalpension.\nMUST be paid out as full lump-sum OR converted to aldersopsparing/ratepension.\nPartial withdrawals are not allowed - kapitalpension is all-or-nothing.","required":["type"],"properties":{"convert_to":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/KapitalConversion","description":"Optional conversion to aldersopsparing or ratepension (instead of lump-sum payout)"}]},"type":{"type":"string","enum":["kapital"]}}},{"type":"object","description":"Strategy for Ratepension.\nPrinciple determines how payout is calculated; start_age and period come from the plan.","required":["principle","type"],"properties":{"principle":{"$ref":"#/components/schemas/RatepensionPrinciple","description":"Payout calculation principle (serie or amortisation)"},"type":{"type":"string","enum":["rate"]}}}],"description":"Strategy for a single pension.\n\nThe strategy variant MUST match the pension_type. For example,\na Ratepension must have a `PensionStrategy::Rate` variant.\n\nStored in DynamoDB as:\n```json\n{\n  \"pension_type\": \"ratepension\",\n  \"balance\": 2500000,\n  \"strategy\": {\n    \"type\": \"rate\",\n    \"principle\": \"serie\"\n  }\n}\n```"},"PensionTaxFreePayout":{"type":"object","required":["kind","value"],"properties":{"kind":{"$ref":"#/components/schemas/PensionTaxFreePayoutKind"},"through_year":{"type":["integer","null"],"format":"int32","description":"Last calendar year in which a fixed annual amount or fraction applies, inclusive.","minimum":0},"value":{"type":"number","format":"double"}}},"PensionTaxFreePayoutKind":{"type":"string","description":"Provider- or authority-documented tax-free treatment for one pension arrangement.\n\nThe value is interpreted according to `kind`: DKK per year, a fraction in `(0, 1]`, or the\nremaining DKK kapitalpension basis that is consumed before the ordinary payout duty applies.","enum":["fixed_annual_amount","fixed_fraction","remaining_capital_basis"]},"PensionsInfoSource":{"type":"object","description":"Stable identity of a normalized benefit from one PensionsInfo import.\n\nThe source keys preserve the provider -> agreement -> benefit relationship after the\ncalculation model splits one PensionsInfo agreement into separate pensions and protections.","required":["import_id","provider_id","scheme_reference"],"properties":{"benefit_type_number":{"type":["integer","null"],"format":"int32","minimum":0},"import_id":{"type":"string"},"provider_id":{"type":"integer","format":"int32","minimum":0},"scheme_reference":{"type":"string"},"scheme_type_number":{"type":["integer","null"],"format":"int32","minimum":0},"subscription_type_number":{"type":["integer","null"],"format":"int32","minimum":0},"valuation_date":{"type":["string","null"],"format":"date","description":"Original provider valuation date retained for import reconciliation."}}},"Person":{"type":"object","description":"Person in a plan. Financial entities owned by a person live directly\non that person.","required":["person_id","name","birth_date","green_check_tax_eligible","businesses"],"properties":{"assets":{"type":"array","items":{"$ref":"#/components/schemas/Asset"}},"benefits":{"type":"array","items":{"$ref":"#/components/schemas/Benefit"}},"birth_date":{"type":"string","format":"date"},"businesses":{"type":"array","items":{"$ref":"#/components/schemas/Business"}},"cessation_pension_qualifying_disposal_year":{"type":["integer","null"],"format":"int32","description":"Income year of the full or partial business disposal that produced the qualifying gain.\nThis is independent of whether a VSO tax envelope continues or ceases.","minimum":0},"cessation_pension_qualifying_gain":{"type":"number","format":"double","description":"Adviser-confirmed taxable business-sale gain that qualifies for a PBL § 15 A\ncessation-pension contribution, including the age, business-history, activity and Danish\ntax-liability conditions. This is a taxpayer-level fact because one gain may fund more than\none qualifying pension contract and may be attributed from a spouse under KSL § 25 A."},"gender":{"$ref":"#/components/schemas/Gender"},"green_check_tax_eligible":{"type":"boolean","description":"Whether the person satisfies the first-day full-tax-liability/cross-border conditions and\nis not excluded by a special tax regime."},"income_sources":{"type":"array","items":{"$ref":"#/components/schemas/IncomeSource"}},"municipality_code":{"type":"integer","format":"int32","minimum":0},"name":{"type":"string"},"pays_church_tax":{"type":"boolean"},"pensions":{"type":"array","items":{"$ref":"#/components/schemas/Pension"}},"person_id":{"type":"string"},"prior_cessation_pension_contributions":{"type":"number","format":"double","description":"Qualifying PBL § 15 A contributions made before the projection boundary."},"retirement_age":{"type":["number","null"],"format":"double"}}},"PersonShare":{"type":"object","description":"Fractional allocation of a property right or mortgage liability to one household person.","required":["person_id","share"],"properties":{"person_id":{"type":"string"},"share":{"type":"number","format":"double"}}},"Point2d":{"type":"object","properties":{"x":{"type":["number","null"],"format":"double"},"y":{"type":["number","null"],"format":"double"}}},"PriceIncreaseSnapshot":{"type":"object","description":"Snapshot for a municipal property-price increase notification.","required":["address","property_type","municipality_slug","baseline_loan_date","baseline_price_date","current_price_date","baseline_municipality_price","current_municipality_price","increase_pct","threshold_pct"],"properties":{"address":{"type":"string"},"baseline_loan_date":{"type":"string","format":"date"},"baseline_municipality_price":{"type":"number","format":"double"},"baseline_price_date":{"type":"string","format":"date"},"current_municipality_price":{"type":"number","format":"double"},"current_price_date":{"type":"string","format":"date"},"increase_pct":{"type":"number","format":"double"},"municipality_slug":{"type":"string"},"property_type":{"type":"string"},"threshold_pct":{"type":"integer","format":"int32"}}},"Property":{"type":"object","required":["address","value"],"properties":{"address":{"type":"string"},"debt_to_respect":{"type":["number","null"],"format":"double"},"property_type":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PropertyType"}]},"total_registered_debt":{"type":["number","null"],"format":"double"},"value":{"type":"number","format":"double"}}},"PropertyChangesetRequest":{"type":"object","required":["form_kind","customer_key","property_key"],"properties":{"comment":{"type":["string","null"]},"customer_key":{"type":"string"},"form_kind":{"$ref":"#/components/schemas/FinancerFormKind"},"loan_changes":{"type":["array","null"],"items":{"$ref":"#/components/schemas/ExistingLoanRebalanceChange"}},"loans":{"type":["array","null"],"items":{"$ref":"#/components/schemas/ExistingLoanRequest"}},"new_loans":{"type":["array","null"],"items":{"$ref":"#/components/schemas/NewLoanRequest"}},"payout":{"type":["number","null"],"format":"double"},"property_key":{"type":"string"},"redemption_date":{"type":["string","null"],"format":"date"}},"additionalProperties":false},"PropertyData":{"type":"object","required":["property_id","property_type","is_on_leased_plot","is_owner_apartment","units","addresses","cadastres","buildings","owners","transfers","valuations"],"properties":{"address":{"type":["string","null"]},"address_slug":{"type":["string","null"]},"addresses":{"type":"array","items":{"$ref":"#/components/schemas/Location"}},"apartment_distribution_denominator":{"type":["integer","null"],"format":"int32"},"apartment_distribution_numerator":{"type":["integer","null"],"format":"int32"},"apartment_number":{"type":["integer","null"],"format":"int32"},"apartment_size":{"type":["integer","null"],"format":"int32"},"buildings":{"type":"array","items":{"$ref":"#/components/schemas/Construction"}},"cadastres":{"type":"array","items":{"$ref":"#/components/schemas/Cadastre"}},"current_number_of_owners":{"type":["integer","null"],"format":"int32","minimum":0},"current_owner_type":{"type":["number","null"],"format":"double"},"current_purchase_date":{"type":["string","null"],"format":"date-time"},"current_purchase_price":{"type":["integer","null"],"format":"int64"},"current_transfer_date":{"type":["string","null"],"format":"date-time"},"current_transfer_type":{"type":["string","null"]},"ground_drain_code":{"type":["integer","null"],"format":"int64"},"ground_water_supply_code":{"type":["integer","null"],"format":"int64"},"is_on_leased_plot":{"type":"boolean"},"is_owner_apartment":{"type":"boolean"},"latest_plot_valuation":{"type":["integer","null"],"format":"int32"},"latest_property_valuation":{"type":["integer","null"],"format":"int32"},"latest_usage_code":{"type":["integer","null"],"format":"int32"},"latest_valuation_year":{"type":["integer","null"],"format":"int32"},"owners":{"type":"array","items":{"$ref":"#/components/schemas/PropertyOwner"}},"point":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Point2d"}]},"property_id":{"type":"integer","format":"int32"},"property_type":{"$ref":"#/components/schemas/PropertyType"},"transfer_count":{"type":["integer","null"],"format":"int32","minimum":0},"transfers":{"type":"array","items":{"$ref":"#/components/schemas/PropertyTransfer"}},"units":{"type":"array","items":{"$ref":"#/components/schemas/Apartment"}},"valuations":{"type":"array","items":{"$ref":"#/components/schemas/PropertyValuation"}},"weighted_area":{"type":["number","null"],"format":"double"}}},"PropertyInput":{"type":"object","description":"Full property input for creating new properties or calculations","required":["customer_key","address","loans","value"],"properties":{"address":{"type":"string"},"customer_key":{"type":"string"},"debt_to_respect":{"type":["number","null"],"format":"double"},"loans":{"type":"array","items":{"$ref":"#/components/schemas/Loan"}},"name":{"type":["string","null"]},"property_key":{"type":["string","null"],"description":"If set, creates a calculation under this property instead of a new property"},"property_type":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PropertyType"}]},"total_registered_debt":{"type":["number","null"],"format":"double"},"value":{"type":"number","format":"double"}}},"PropertyOwner":{"type":"object","properties":{"company_advertisement_protection":{"type":["string","null"]},"company_name":{"type":["string","null"]},"company_vat":{"type":["integer","null"],"format":"int32"},"owner_type":{"type":["number","null"],"format":"double"},"ownership_share":{"type":["number","null"],"format":"double"},"person_age":{"type":["number","null"],"format":"double"},"person_birthdate":{"type":["string","null"],"format":"date-time"},"person_gender":{"type":["string","null"]},"person_id":{"type":["string","null"]},"person_name":{"type":["string","null"]},"registered_share":{"type":["number","null"],"format":"double"}}},"PropertyRef":{"type":"object","description":"A property linked to a plan.\n\n`property_key` is the DynamoDB key of the property (`property:...`).\nThe optional year/month boundaries define the exact inclusive ownership window\n(e.g. sold in August 2031 means `year_to: 2031, month_to: 8`). Missing start and\nend boundaries mean the property is held for the entire plan duration.","required":["property_key","owner_shares","has_mortgage"],"properties":{"address":{"type":["string","null"],"description":"Human-readable address from financer. Populated at runtime by the platform layer."},"amortization":{"type":"array","items":{"$ref":"#/components/schemas/MortgageBalanceMonth"},"description":"Calendar-month mortgage balance schedule in constant 2026 DKK, injected by the platform.\nEmpty for mortgage-free properties."},"has_mortgage":{"type":"boolean","description":"Persisted mortgage state selected from the linked financer calculation.\n\nThis is the contractual source of truth for whether financer enrichment is required.\nThe runtime-only amortization schedule must never be used to infer mortgage state."},"month_from":{"type":["integer","null"],"format":"int32","description":"First active calendar month in `year_from` (None = January).","minimum":0},"month_to":{"type":["integer","null"],"format":"int32","description":"Last active calendar month in `year_to` (None = December).","minimum":0},"owner_shares":{"type":"array","items":{"$ref":"#/components/schemas/PersonShare"},"description":"Legal/economic ownership allocation. Shares are fractions in `(0, 1]` and must\ncollectively equal 1 for the property."},"property_key":{"type":"string"},"property_type":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PropertyType","description":"Physical property classification copied from the linked property record.\nSummerhouse allowance rules require `vacation_home`; selecting the allowance on the\nincome source certifies non-business/private-or-mixed use and the allowance method."}]},"sale_taxable":{"type":["boolean","null"],"description":"Whether a sale should be treated as taxable under ejendomsavance rules.\nDefaults to false so ordinary owner-occupied homes/sommerhuse remain tax-free unless\nan advisor explicitly marks the property as taxable."},"tax_basis":{"type":["number","null"],"format":"double","description":"Advisor-supplied nominal tax basis, fully adjusted under the Danish property-gain rules.\nRequired when `sale_taxable` is true. Unlike the projected real property value, this\nstatutory DKK basis is not increased with inflation."},"value":{"type":["number","null"],"format":"double","description":"Real property value at the plan's opening boundary on the first day of next month.\nPopulated at runtime by the platform layer for linked financer data."},"year_from":{"type":["integer","null"],"format":"int32","description":"First year this property is active in the plan (None = from plan start)","minimum":0},"year_to":{"type":["integer","null"],"format":"int32","description":"Last year this property is active in the plan (None = until plan end)","minimum":0}},"additionalProperties":false},"PropertyTransfer":{"type":"object","properties":{"given_share":{"type":["number","null"],"format":"double"},"ownership_share":{"type":["number","null"],"format":"double"},"purchase_date":{"type":["string","null"],"format":"date-time"},"purchase_price":{"type":["integer","null"],"format":"int64"},"received_share":{"type":["number","null"],"format":"double"},"registered_share":{"type":["number","null"],"format":"double"},"transfer_date":{"type":["string","null"],"format":"date-time"},"transfer_type":{"type":["string","null"]}}},"PropertyType":{"type":"string","enum":["house","owner_apartment","vacation_home","terraced_house","housing_cooperative","public_housing","plot","building_on_leased_plot","building"]},"PropertyValuation":{"type":"object","properties":{"plot_valuation":{"type":["integer","null"],"format":"int32"},"property_valuation":{"type":["integer","null"],"format":"int32"},"usage_code":{"type":["integer","null"],"format":"int32"},"valuation_year":{"type":["integer","null"],"format":"int32"}}},"ProtectionCoverage":{"type":"object","description":"Native event-triggered cover. It can be sourced from a standalone insurance\npolicy, a pension agreement, or a specific pension benefit.","required":["insured_person_id","coverage_kind","trigger_event_type"],"properties":{"annual_amount":{"type":["number","null"],"format":"double"},"beneficiary_links":{"type":"array","items":{"$ref":"#/components/schemas/BeneficiaryLink"}},"coverage_id":{"type":"string"},"coverage_kind":{"$ref":"#/components/schemas/ProtectionCoverageKind"},"end_age":{"type":["integer","null"],"format":"int32","minimum":0},"end_month":{"type":["integer","null"],"format":"int32","minimum":0},"end_year":{"type":["integer","null"],"format":"int32","minimum":0},"insured_person_id":{"type":"string"},"lump_sum_amount":{"type":["number","null"],"format":"double"},"payout_form":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ProtectionPayoutForm"}]},"payout_rate":{"type":["number","null"],"format":"double"},"pensions_info_source":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PensionsInfoSource","description":"PensionsInfo import identity when this coverage was normalized from a provider benefit."}]},"rules":{"type":"array","items":{"$ref":"#/components/schemas/ProtectionCoverageRule"}},"source_id":{"type":["string","null"]},"start_age":{"type":["integer","null"],"format":"int32","minimum":0},"start_month":{"type":["integer","null"],"format":"int32","minimum":0},"start_year":{"type":["integer","null"],"format":"int32","minimum":0},"trigger_event_type":{"$ref":"#/components/schemas/ContingencyEventType"}}},"ProtectionCoverageBasis":{"type":"string","description":"Base amount for phase-specific protection rules.","enum":["remaining_depot","full_yearly_payout","yearly_payout","fixed_annual_amount","fixed_lump_sum","remaining_guaranteed_period","salary","contribution","other"]},"ProtectionCoverageKind":{"type":"string","description":"Event-triggered cover type used across pensions and standalone insurance.","enum":["death_lump_sum","depot_protection","guaranteed_period","spouse_continuation","disability_income","premium_waiver","critical_illness_lump_sum","other"]},"ProtectionCoveragePhase":{"type":"string","description":"Phase where a coverage rule applies, e.g. livrente before/after payout start.","enum":["before_payout_start","after_payout_start","after_payout_end","after_guarantee_end","always","other"]},"ProtectionCoverageRule":{"type":"object","description":"Phase-specific rule for a protection coverage.","properties":{"annual_amount":{"type":["number","null"],"format":"double"},"beneficiary_links":{"type":"array","items":{"$ref":"#/components/schemas/BeneficiaryLink"}},"guaranteed_years":{"type":["integer","null"],"format":"int32","minimum":0},"lump_sum_amount":{"type":["number","null"],"format":"double"},"payout_basis":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ProtectionCoverageBasis"}]},"payout_form":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ProtectionPayoutForm"}]},"payout_rate":{"type":["number","null"],"format":"double"},"phase":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ProtectionCoveragePhase"}]},"rule_id":{"type":["string","null"]}}},"ProtectionPayoutForm":{"type":"string","description":"How a protection coverage pays into the projection.","enum":["lump_sum","annual","monthly","contribution_credit","other"]},"PublicWebhookRecord":{"type":"object","description":"Public webhook endpoint subscription.","required":["webhook_key","url","events","status","description","created_at","updated_at"],"properties":{"created_at":{"type":"string","format":"date-time","description":"RFC 3339 creation timestamp.","example":"2026-08-02T12:34:56Z"},"description":{"type":["string","null"],"description":"Optional operator-facing label."},"events":{"type":"array","items":{"$ref":"#/components/schemas/EventType"},"description":"Notification lifecycle events delivered to this endpoint.","maxItems":4,"minItems":1},"status":{"$ref":"#/components/schemas/WebhookStatus","description":"Disabled endpoints do not receive new deliveries or retries."},"updated_at":{"type":"string","format":"date-time","description":"RFC 3339 timestamp of the latest endpoint change.","example":"2026-08-02T12:34:56Z"},"url":{"type":"string","description":"Public HTTPS receiver URL.","example":"https://example.com/webhooks/raad-til-bolig"},"webhook_key":{"type":"string","description":"Stable webhook endpoint identifier used by all endpoint-management routes.","example":"webhook:0195c9e8-23e6-7be0-bc79-b8ba1ff34e37"}}},"PublicWebhookSettings":{"type":"object","description":"Company-wide webhook delivery settings. The signing secret is never returned by this endpoint.","required":["has_shared_secret","custom_headers","updated_at"],"properties":{"custom_headers":{"type":"array","items":{"$ref":"#/components/schemas/WebhookHeader"},"description":"Headers added to every webhook endpoint delivery for this company.","maxItems":10},"has_shared_secret":{"type":"boolean","description":"Whether a signing secret exists. Use the reset-secret endpoint to obtain a new secret."},"updated_at":{"type":"string","format":"date-time","description":"RFC 3339 timestamp of the latest settings change.","example":"2026-08-02T12:34:56Z"}}},"Quantity":{"type":"object","required":["value","unit","basis"],"properties":{"basis":{"$ref":"#/components/schemas/QuantityBasis"},"period":{"type":["string","null"]},"unit":{"$ref":"#/components/schemas/QuantityUnit"},"value":{"type":"number","format":"double"}}},"QuantityBasis":{"type":"string","enum":["point_in_time","annual_run_rate","calendar_year_total","active_period_total","dimensionless"]},"QuantityUnit":{"type":"string","enum":["dkk","dkk_per_year","fraction","percent","scalar","year","month"]},"RateQuote":{"type":"object","required":["institute","loan_type","loan_variant","available","principal","costs"],"properties":{"available":{"type":"boolean","description":"True when the pipeline produced an interest rate and at least one cost\nentry. Other optional quote fields can still be null."},"contribution_rate":{"type":["number","null"],"format":"double"},"costs":{"type":"array","items":{"$ref":"#/components/schemas/LoanCostDetail"}},"institute":{"$ref":"#/components/schemas/Lender"},"interest_rate":{"type":["number","null"],"format":"double"},"loan_to_value_range_end":{"type":["number","null"],"format":"double"},"loan_type":{"$ref":"#/components/schemas/LoanType"},"loan_variant":{"$ref":"#/components/schemas/LoanVariants"},"monthly_cost":{"type":["number","null"],"format":"double"},"monthly_cost_after_tax":{"type":["number","null"],"format":"double"},"principal":{"type":"number","format":"double"},"redemption_rate":{"type":["number","null"],"format":"double"},"total_costs":{"type":["number","null"],"format":"double"},"unavailable_reason":{"type":["string","null"],"description":"Populated when `available = false`."}}},"RateQuoteProperty":{"type":"object","required":["value"],"properties":{"debt_to_respect":{"type":["number","null"],"format":"double","description":"Existing senior debt to respect when computing LTV bands. Defaults to 0."},"property_type":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PropertyType","description":"Property type. Defaults to `house`. Drives owner vs. vacation\ncontribution-rate tables."}]},"total_registered_debt":{"type":["number","null"],"format":"double","description":"Total registered debt on the property. Defaults to 0."},"value":{"type":"number","format":"double","description":"Property market value (DKK).","example":4000000.0}}},"RateQuoteRequest":{"type":"object","description":"Request body for the rates quote endpoint.\n\nAll fields except `property.value` and `principal` are optional. The server\nfans out across the supplied (or default) institutes and loan variants and\nruns the full financer pipeline for each candidate, letting the pipeline\nauto-fill `interest_rate`, `redemption_rate`, `contribution_rate` and the\nLTV banding from current market data.","required":["property","principal"],"properties":{"institutes":{"type":["array","null"],"items":{"$ref":"#/components/schemas/Lender"},"description":"Restrict the comparison to specific institutes. Defaults to all\nsupported realkredit institutes."},"interest_only_terms":{"type":["number","null"],"format":"double","description":"Interest-only years (afdragsfrihed). Defaults to 0.","example":0.0},"loan_term":{"type":["number","null"],"format":"double","description":"Loan term in years. Defaults to 30.","example":30.0},"loan_variants":{"type":["array","null"],"items":{"$ref":"#/components/schemas/LoanVariants"},"description":"Restrict the comparison to specific loan variants. Defaults to all\nsupported realkredit variants."},"principal":{"type":"number","format":"double","description":"Total mortgage principal to price.","example":3000000.0},"property":{"$ref":"#/components/schemas/RateQuoteProperty"}}},"RateQuoteResponse":{"type":"object","required":["quotes"],"properties":{"quotes":{"type":"array","items":{"$ref":"#/components/schemas/RateQuote"}}}},"RatepensionPrinciple":{"type":"string","description":"Ratepension payout principles","enum":["serie","amortisation"]},"ResetSecretResponse":{"type":"object","description":"Newly generated company-wide webhook signing secret.","required":["shared_secret"],"properties":{"shared_secret":{"type":"string","description":"Standard Webhooks secret. Store it when returned; it cannot be retrieved later. Resetting\nreplaces the secret for every company webhook endpoint immediately.","example":"whsec_C2FVsBQIhrscChlQIMV+b5sSYspob7oD"}}},"Severity":{"type":"string","description":"Display priority assigned to a notification.","enum":["info","warning","critical"]},"TargetSpend":{"type":"object","description":"A spending target for a time period within a decumulation plan.\n\nPlan-level (household) concept: \"we want to spend X per year from year A to year B.\"\nMultiple entries allow stepped targets (e.g., 600k until 75, then 400k).","required":["annual_amount"],"properties":{"annual_amount":{"type":"number","format":"double","description":"Annual household consumption target paid from after-tax disposable income, stated in\nconstant 2026 DKK. A flat target therefore preserves purchasing power."},"end_month":{"type":["integer","null"],"format":"int32","description":"Inclusive final active month; December when omitted.","minimum":0},"end_year":{"type":["integer","null"],"format":"int32","description":"End year (None = until plan end)","minimum":0},"start_month":{"type":["integer","null"],"format":"int32","description":"Inclusive first active month; January when omitted.","minimum":0},"start_year":{"type":["integer","null"],"format":"int32","description":"Start year (None = from plan start)","minimum":0}}},"TaxableAmountRule":{"type":"string","description":"Rule key for reducing gross income to taxable amount.\n\nThe actual parameters (allowance, taxable_fraction) live in\n`taxable_amount_rules_dataframe()`. This enum provides a type-safe API\nfor setting the rule key on `IncomeSource`.","enum":["sommerhus_via_platform","sommerhus_privat"]},"TestWebhookResponse":{"type":"object","description":"Result of one synchronous test delivery.","required":["delivery_id","status","status_code","error"],"properties":{"delivery_id":{"type":"string","description":"Stable delivery identifier also sent in the `webhook-id` request header.","example":"webhook:0195c9e8#webhook_delivery:notification_event:0195c9e8"},"error":{"type":["string","null"],"description":"Delivery error when the receiver did not return 2xx."},"status":{"$ref":"#/components/schemas/DeliveryStatus","description":"`succeeded` for a receiver 2xx response, otherwise `failed`."},"status_code":{"type":["integer","null"],"format":"int32","description":"Receiver HTTP status when a response was received.","example":204,"minimum":0}}},"TinglysningSnapshot":{"type":"object","description":"Snapshot for a Danish land-registration change notification.","required":["address","change_date"],"properties":{"address":{"type":"string"},"change_date":{"type":"string","format":"date-time"}}},"UpdateWebhook":{"type":"object","description":"Patch fields on an existing webhook endpoint subscription.","properties":{"description":{"type":["string","null"],"description":"Operator-facing label. Omit to preserve it, send a string to replace it, or send `null` to\nclear it."},"events":{"type":["array","null"],"items":{"$ref":"#/components/schemas/EventType"},"description":"Complete replacement event list. Values must be unique.","maxItems":4,"minItems":1},"status":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/WebhookStatus","description":"Enable or disable deliveries to this endpoint."}]},"url":{"type":["string","null"],"description":"Replacement public HTTPS receiver URL.","example":"https://example.com/webhooks/raad-til-bolig"}},"additionalProperties":false},"UpdateWebhookSettings":{"type":"object","description":"Replace the company-wide custom headers sent to every webhook endpoint.","required":["custom_headers"],"properties":{"custom_headers":{"type":"array","items":{"$ref":"#/components/schemas/WebhookHeader"},"description":"Complete replacement list. Header names are case-insensitively unique. Standard Webhooks,\nrouting, framing, and content headers are reserved. At most 10 headers are allowed.","maxItems":10}},"additionalProperties":false},"ValidationErrorResponse":{"type":"object","required":["errors"],"properties":{"errors":{"type":"array","items":{"$ref":"#/components/schemas/AdvisorIssue"}}}},"VsoHorizonTreatment":{"type":"string","description":"Continuation or cessation treatment of a VSO tax position.","enum":["continue","cessation"]},"VsoTaxLayer":{"type":"object","description":"Net retained profit belonging to one statutory VSO tax period.","required":["tax_period","net_amount"],"properties":{"net_amount":{"type":"number","format":"double","description":"Balance on the retained-profit account after provisional business tax."},"tax_period":{"$ref":"#/components/schemas/VsoTaxPeriod"}}},"VsoTaxPeriod":{"type":"string","description":"Statutory VSO tax period for an opening retained-profit balance.\n\nThe periods follow changes in the provisional business-tax rate. Profit within one period is\ntax-equivalent for a private financial plan, so an advisor never has to enter annual history.","enum":["years_1987_to_1990","year_1991","years_1992_to_1998","years_1999_to_2000","years_2001_to_2004","years_2005_to_2006","years_2007_to_2013","year_2014","year_2015","from_2016"]},"WebhookDeliveryHeaders":{"type":"object","description":"Standard Webhooks headers included with every outbound delivery.","required":["webhook-id","webhook-timestamp","webhook-signature"],"properties":{"webhook-id":{"type":"string","description":"Stable delivery identifier. Use it as the idempotency key.","example":"webhook:0195c9e8#webhook_delivery:notification_event:0195c9e8"},"webhook-signature":{"type":"string","description":"Standard Webhooks signature in `v1,<base64-hmac-sha256>` format.","example":"v1,tZ1I4/hDygAJgO5TYxiSd6Sd0kDW6hPenDe+bTa3Kkw="},"webhook-timestamp":{"type":"string","description":"Unix timestamp in seconds used when computing the signature.","example":"1785674096"}}},"WebhookEvent":{"type":"object","description":"JSON body posted to a subscribed webhook endpoint.","required":["id","delivery_id","type","created_at","company_id","customer_id","notification_id","test","payload"],"properties":{"company_id":{"type":"string","description":"Company that owns the subscription and notification.","example":"company:raad-til-bolig"},"created_at":{"type":"string","format":"date-time","description":"RFC 3339 timestamp at which the notification event was created.","example":"2026-08-02T12:34:56Z"},"customer_id":{"type":"string","description":"Customer associated with the notification.","example":"customer:0195c9e8-23e6-7be0-bc79-b8ba1ff34e37"},"delivery_id":{"type":"string","description":"Stable identifier for this endpoint/event pair. It is also sent as the `webhook-id`\nheader and remains unchanged across retries.","example":"webhook:0195c9e8#webhook_delivery:notification_event:0195c9e8"},"id":{"type":"string","description":"Stable notification event identifier. The same event may be sent to multiple endpoints.","example":"notification_event:0195c9e8-23e6-7be0-bc79-b8ba1ff34e37"},"notification_id":{"type":"string","description":"Stable identifier of the current notification.","example":"notification:conversion:property_0195c9e8"},"payload":{"$ref":"#/components/schemas/NotificationPayload","description":"Notification state after applying the event."},"test":{"type":"boolean","description":"`true` only for a synchronous delivery requested through the test endpoint."},"type":{"$ref":"#/components/schemas/EventType","description":"Notification lifecycle event."}}},"WebhookHeader":{"type":"object","description":"Custom HTTP header added to every company webhook delivery.","required":["name","value"],"properties":{"name":{"type":"string","description":"HTTP header name. Names are case-insensitively unique. `webhook-*`, `x-webhook-*`, host,\ncontent, connection, and transfer-framing headers are reserved.","example":"X-Partner-Key"},"value":{"type":"string","description":"Header value, limited to 1024 bytes and prohibited from containing invalid HTTP characters.","example":"partner-secret","maxLength":1024}}},"WebhookStatus":{"type":"string","description":"Whether a webhook endpoint accepts deliveries.","enum":["enabled","disabled"]}},"securitySchemes":{"jwt_auth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token obtained from Cognito"},"oauth2_client_credentials":{"type":"oauth2","flows":{"clientCredentials":{"tokenUrl":"https://auth.raadtilbolig.dk/oauth2/token","scopes":{"api/*":"Full API access","api/property-data.read":"Read autocomplete and normalized property data"}}},"description":"Obtain a Bearer token with your Cognito client ID and client secret"}}},"tags":[{"name":"raad-til-bolig-api-v2","description":"Råd til Bolig API v2"},{"name":"data","description":"Normalized Danish property data"},{"name":"webhooks","description":"Webhook integration sequence:\n\n1. Call `reset_webhook_secret` and store the returned `whsec_...` secret. It is company-wide, is shown only in that response, and a later reset immediately replaces it for every endpoint.\n2. Expose a public HTTPS POST endpoint that preserves the raw request bytes.\n3. Call `create_webhook` with that URL and the required notification event subscriptions. The endpoint is enabled immediately.\n4. For every delivery, verify `webhook-signature` using the raw body, `webhook-id`, `webhook-timestamp`, and the saved secret according to the Standard Webhooks specification. Verify before parsing JSON.\n5. Deduplicate deliveries by `webhook-id`, process the `WebhookEvent` body, and return any 2xx response within five seconds. Non-2xx responses and network failures are retried with the same `webhook-id`.\n6. Call `test_webhook` and inspect its response to validate receiver connectivity. Test event bodies have `test: true` and are not retried.\n\nCustom headers configured through `update_webhook_settings` are company-wide. Endpoint URLs must use HTTPS, must resolve exclusively to public IP addresses, and redirects are not followed."}],"x-webhook-delivery":{"method":"POST","contentType":"application/json","bodySchema":{"$ref":"#/components/schemas/WebhookEvent"},"headerSchema":{"$ref":"#/components/schemas/WebhookDeliveryHeaders"},"successStatusCodes":"200-299","timeoutSeconds":5,"redirectsFollowed":false,"idempotencyHeader":"webhook-id","retriesReuseWebhookId":true,"signature":{"standard":"Standard Webhooks","specificationUrl":"https://www.standardwebhooks.com/","secretFormat":"whsec_<standard-base64>","signedContent":"<webhook-id>.<webhook-timestamp>.<raw-request-body>","signedContentEncoding":"UTF-8 bytes with the request body exactly as received","algorithm":"HMAC-SHA256","signatureFormat":"v1,<standard-base64>","timestampToleranceSeconds":300,"verifyBeforeJsonParsing":true,"verificationSteps":["Remove the whsec_ prefix and standard-base64 decode the remaining secret","Build the signed content from webhook-id, a period, webhook-timestamp, a period, and the raw request body","Compute HMAC-SHA256 over the UTF-8 signed-content bytes using the decoded secret","Standard-base64 encode the digest and compare v1,<digest> in constant time","Reject timestamps more than 300 seconds from the receiver clock"]},"managementOperationIds":{"initializeOrRotateSecret":"reset_webhook_secret","createEndpoint":"create_webhook","testEndpoint":"test_webhook","updateEndpoint":"update_webhook","deleteEndpoint":"delete_webhook","replaceCompanyHeaders":"update_webhook_settings"}}}