{
  "openapi": "3.0.4",
  "info": {
    "title": "Optiom Portal API",
    "description": "Partner sales channel for Optiom vehicle protection products.\n\n**Sequence:** authenticate -> resolve the vehicle -> quote -> bind -> download the declaration.\nEvery call other than authenticate needs the bearer token in the `Authorization` header.\n\n**Series:** `Prime` and `Plus`. `Plus` is a dealer sale and additionally requires\n`DealerId`.\n\n**Payment:** `REF`, `FIN` and `PW` settle before the bind and carry your own\n`PaymentReference`. `PFF` finances the premium instead: price the plans with\n`POST /quote/financing`, then bind with the chosen plan and the customer's bank account\nin the `Financing` block. The bind writes the loan and the policy together. `CC` charges\na card: open a payment with `POST /payment/card`, render Moneris Checkout with the\nticket, confirm the outcome with `POST /payment/card/confirm`, then bind with the\n`CardPayment` block. Confirming is required, not a check you can skip. Card details go\nfrom the customer to Moneris and never reach either of us, and the form renders inside\nyour own page, so you need no Moneris account and the customer is never redirected.\n\n**Errors:** every failure returns the same envelope - `{ isValid, errors[] }`, where each\nerror carries a stable `code`, the `field` it applies to, and a displayable `message`.\nMatch on `code`, never on `message`. A 422 means the request was understood and refused;\na 400 means it was malformed.\n\n**Rate limiting:** requests are throttled per partner. A 429 carries `Retry-After` in\nseconds.\n\n**Authentication is provisional in v1.** The current hybrid - `client_id` and\n`client_secret` headers alongside a username and password body - is superseded by a\nstandard OAuth2 client-credentials grant in a later version. Isolate your token\nacquisition behind one function so the change is contained.",
    "contact": {
      "name": "Optiom",
      "email": "support@optiom.com"
    },
    "version": "1.0"
  },
  "paths": {
    "/v1/authenticate": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "summary": "Exchanges partner credentials for a bearer token.",
        "description": "Four credentials are required: `client_id` and `client_secret` identify the integration\r\nand travel as headers; the username and password identify the seller the quotes are\r\nwritten against and travel in the body. Both pairs are issued by Optiom during onboarding.\r\n            \r\nCache the token and reuse it until `expiryTime`. Reauthenticating on every call will\r\nconsume the rate-limit allowance.\r\n            \r\n**This contract is provisional.** A later version replaces it with a standard OAuth2\r\nclient-credentials grant. Keep token acquisition behind a single function.",
        "parameters": [
          {
            "name": "client_id",
            "in": "header",
            "description": "Partner client id, issued by Optiom.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "client_secret",
            "in": "header",
            "description": "Partner client secret, issued by Optiom.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "Seller username and password.",
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/AuthenticateRequest"
                  }
                ],
                "description": "Seller credentials. Sent alongside the client_id and client_secret headers, which identify\r\nthe integration rather than the seller."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/AuthenticateRequest"
                  }
                ],
                "description": "Seller credentials. Sent alongside the client_id and client_secret headers, which identify\r\nthe integration rather than the seller."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/AuthenticateRequest"
                  }
                ],
                "description": "Seller credentials. Sent alongside the client_id and client_secret headers, which identify\r\nthe integration rather than the seller."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token issued.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResponse"
                }
              }
            }
          },
          "401": {
            "description": "Any of the four credentials is wrong, or the account is disabled. Not distinguished, deliberately."
          },
          "422": {
            "description": "The request body could not be read.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          }
        },
        "security": [ ]
      }
    },
    "/v1/bind": {
      "post": {
        "tags": [
          "Bind"
        ],
        "summary": "Binds a quote into a policy and issues the declaration document.",
        "description": "Takes one coverage category and one term from the quote response, plus the product\r\ncodes selected within them. Every non-optional product of that category must be\r\nincluded.\r\n            \r\n**Binding is the point of no return.** It creates a policy, charges against the chosen\r\npayment method and generates documents. There is no partner-facing endpoint to reverse\r\nit — cancellation goes through Optiom.\r\n            \r\n**Retrying is safe.** A repeated bind for an application that already produced a policy\r\nreturns 200 with that same policy and declaration, rather than creating a second one.\r\nA bind still in flight returns 422 `BindInProgress`; wait and retry, do not treat it as\r\na failure. Because of that, a timed-out or connection-dropped bind should always be\r\nretried before being reported as failed — the first attempt may well have succeeded.\r\n            \r\nTake `documentDownloadId` from the response and fetch the PDF from\r\n`GET /document/{downloadId}`.\r\n            \r\n**Financing (`PFF`)** creates the loan and the policy in this one call. Send the\r\n`financing` block with a plan taken from `POST /quote/financing` and the customer's bank\r\naccount, and leave `paymentReference` empty; the response carries the loan under\r\n`financing`, and the lender's letter as `financingLetterDownloadId`. If the loan is\r\nwritten but the policy then fails, the bind returns `BindFailedAfterFinancing` and the\r\nloan is reversed: contact support with the loan number rather than retrying.\r\n            \r\n**Card (`CC`)** settles a payment the customer already made through Moneris Checkout.\r\nOpen it with `POST /payment/card`, confirm it, then send its `paymentId` in the\r\n`cardPayment` block and leave `paymentReference` empty; the card approval code becomes\r\nthe policy's reference. The response carries the card under `cardPayment`, including the\r\ncustomer's receipt as `cardPayment.receiptDownloadId` when the charge was taken during\r\nthe bind; fetch it from `GET /document/{downloadId}` like any other document. A charge due\r\ntoday is taken during this call, so a `BindFailedAfterCardCapture` means the customer was\r\ncharged, the policy was not, and the charge is being reversed: contact support rather\r\nthan retrying. `CardPaymentUnavailable` and `BindIncomplete` are the opposite — nothing\r\nwas charged, or a charge that went through will be reused, so retry with the same\r\npayment id.",
        "requestBody": {
          "description": "Application id, term, coverage category, selected products, payment details and, for PFF, the financing plan.",
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/BindRequest"
                  }
                ],
                "description": "Turns a priced quote into a policy. Carries the chosen term, the selected products, the\r\npayment method and the reference for the payment already collected."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/BindRequest"
                  }
                ],
                "description": "Turns a priced quote into a policy. Carries the chosen term, the selected products, the\r\npayment method and the reference for the payment already collected."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/BindRequest"
                  }
                ],
                "description": "Turns a priced quote into a policy. Carries the chosen term, the selected products, the\r\npayment method and the reference for the payment already collected."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Policy bound, or already bound by an earlier attempt. `policyNumber` and `documentDownloadId` identify it.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/BindResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BindResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/BindResponse"
                }
              }
            }
          },
          "400": {
            "description": "The bind produced no result."
          },
          "401": {
            "description": "Missing, expired or invalid bearer token."
          },
          "422": {
            "description": "Refused. `BindInProgress`, `FinancingUnavailable`, `CardPaymentUnavailable` and `BindIncomplete` are retryable; `QuoteAlreadyBound`, an ineligible product set, payment-method errors, `FinancingDeclined`, `PolicyNumberInUseByLender`, `BindFailedAfterFinancing`, `CardPaymentDeclined`, `CardPaymentExpired`, `CardPaymentAmountMismatch`, `CardPaymentAlreadyUsed` and `BindFailedAfterCardCapture` are not.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/document/{downloadId}": {
      "get": {
        "tags": [
          "Document"
        ],
        "summary": "Downloads the policy declaration document produced by a bind.",
        "description": "`downloadId` is the `documentDownloadId` from the bind response. The response is the\r\nfile itself, normally `application/pdf`, with the filename in `Content-Disposition` —\r\nnot a JSON envelope.\r\n            \r\nThe document is available immediately after bind and stays available; it can be\r\nfetched more than once. Each fetch is recorded as a reprint for audit.\r\n            \r\nA document belonging to another seller and a document that does not exist both return\r\nthe same `DocumentNotFound`, deliberately — the id space is not probeable.",
        "parameters": [
          {
            "name": "downloadId",
            "in": "path",
            "description": "The `documentDownloadId` returned by POST /bind.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The document. Binary, with its own content type."
          },
          "401": {
            "description": "Missing, expired or invalid bearer token."
          },
          "422": {
            "description": "`DocumentNotFound` — unknown id, or not owned by the authenticated seller.",
            "content": {
              "application/pdf": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/octet-stream": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header.",
            "content": {
              "application/pdf": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/octet-stream": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "application/pdf": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/octet-stream": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/payment/card": {
      "post": {
        "tags": [
          "Payment"
        ],
        "summary": "Opens a card payment for a quote and returns a Moneris Checkout ticket to render.",
        "description": "Four steps take a card payment: start here, render Moneris's checkout in the customer's\r\nbrowser, confirm, then bind with the `paymentId`. Confirming is required, not optional:\r\na bind before it returns `CardPaymentNotAuthorized`.\r\n            \r\nThe partner needs no Moneris account and never handles card data. The form renders inside\r\ntheir own page, so the customer is never redirected.\r\n            \r\nSend the same `coverageCategory`, `selectedProducts` and `paymentTermInYears` intended\r\nfor the bind, so the amount matches. Changing them later invalidates the payment and the\r\nbind returns `CardPaymentAmountMismatch`.\r\n            \r\nRender the checkout with the `ticket`, `checkoutJsUrl` and `environment` returned:\r\n            \r\n```html\r\n<script src=\"{checkoutJsUrl}\"></script>\r\n<div id=\"monerisCheckout\"></div>\r\n<script>\r\n  const checkout = new monerisCheckout();\r\n  checkout.setMode(\"{environment}\");\r\n  checkout.setCheckoutDiv(\"monerisCheckout\");\r\n  checkout.setCallback(\"payment_complete\", () => confirmWithOptiom());\r\n  checkout.setCallback(\"cancel_transaction\", () => startAgain());\r\n  checkout.startCheckout(\"{ticket}\");\r\n</script>\r\n```\r\n            \r\nThe browser callbacks are a hint, not an outcome. Only `POST /payment/card/confirm`\r\nsays whether the card was accepted. The ticket is a secret and expires: never log it,\r\nnever put it in a URL, and start again if it lapses.\r\n            \r\nBy default the card is charged on the policy effective date, which for a policy starting\r\ntoday means during the bind. Send `chargeDate` as today to take the money up front on a\r\nfuture-dated policy. A future charge date places no hold: the card is verified, saved,\r\nand charged on the day.",
        "requestBody": {
          "description": "Quote, the coverage selection being paid for, and optionally when to charge.",
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CardPaymentStartRequest"
                  }
                ],
                "description": "Opens a card payment for a quote. No card details are accepted here or anywhere else in this\r\nAPI: the customer types the card into Moneris's own hosted checkout."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CardPaymentStartRequest"
                  }
                ],
                "description": "Opens a card payment for a quote. No card details are accepted here or anywhere else in this\r\nAPI: the customer types the card into Moneris's own hosted checkout."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CardPaymentStartRequest"
                  }
                ],
                "description": "Opens a card payment for a quote. No card details are accepted here or anywhere else in this\r\nAPI: the customer types the card into Moneris's own hosted checkout."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Checkout opened. Render it with the ticket, then confirm.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/CardPaymentStartResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CardPaymentStartResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/CardPaymentStartResponse"
                }
              }
            }
          },
          "400": {
            "description": "No result was produced."
          },
          "401": {
            "description": "Missing, expired or invalid bearer token."
          },
          "422": {
            "description": "Refused. `CardPaymentUnavailable` is retryable; `ChargeDateOutOfRange`, `PaymentMethodNotAvailableForSeller` and the coverage errors are not.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/payment/card/confirm": {
      "post": {
        "tags": [
          "Payment"
        ],
        "summary": "Reports what Moneris says happened in the checkout.",
        "description": "Required before the bind, not a check that can be skipped. The ticket is deliberately not\r\nstored, so this is the only moment Moneris can be asked what happened; binding first\r\nreturns `CardPaymentNotAuthorized`.\r\n            \r\nCall this after the checkout signals it is finished, and read `status`:\r\n            \r\n- `Authorized` — bind with this `paymentId`.\r\n- `Pending` — the customer has not finished. Ask again.\r\n- `Declined` or `Cancelled` — start a new card payment; the quote is untouched.\r\n            \r\nSafe to repeat, and it charges nothing: the money moves during the bind, or on the\r\nscheduled charge date. A declined card is a 200 with a status, not an error.",
        "requestBody": {
          "description": "The quote, the payment id from start, and the checkout ticket.",
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CardPaymentConfirmRequest"
                  }
                ],
                "description": "Asks Optiom to check with Moneris how the checkout went. Safe to repeat: it reports the\r\noutcome rather than causing one."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CardPaymentConfirmRequest"
                  }
                ],
                "description": "Asks Optiom to check with Moneris how the checkout went. Safe to repeat: it reports the\r\noutcome rather than causing one."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CardPaymentConfirmRequest"
                  }
                ],
                "description": "Asks Optiom to check with Moneris how the checkout went. Safe to repeat: it reports the\r\noutcome rather than causing one."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Outcome read from Moneris. Inspect `status`.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/CardPaymentConfirmResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CardPaymentConfirmResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/CardPaymentConfirmResponse"
                }
              }
            }
          },
          "400": {
            "description": "No result was produced."
          },
          "401": {
            "description": "Missing, expired or invalid bearer token."
          },
          "422": {
            "description": "Refused. `CardPaymentUnavailable` is retryable; `CardPaymentNotFound` and `PaymentIdRequired` are not.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/quote": {
      "post": {
        "tags": [
          "Quote"
        ],
        "summary": "Creates a quote, or updates one already created, and returns priced options for every\r\navailable term.",
        "description": "Call `GET /quote/vehicle-configurations` first: a VIN decodes to several trims, and\r\n`vehicleConfigurationId` picks the one being insured.\r\n            \r\nOmit `applicationId` to create a quote. Send the `applicationId` from a previous\r\nresponse to revise one — a revision reprices from scratch, so send the full request\r\nagain, not just the changed fields. A quote belonging to another seller is rejected\r\nas not found.\r\n            \r\nThe response carries the option lists `replacementOptions` and\r\n`limitedIndemnityOptions`. Each is a list of terms, and each term holds the products\r\npriced for it. The pair chosen from that matrix, one coverage category and one term,\r\nis what `POST /bind` takes.\r\n            \r\n`Plus` additionally requires `dealerId` from `GET /quote/dealers`, and a\r\n`dealerContact`. Some sellers require `producerCode`; Optiom will tell you at\r\nonboarding whether yours is one of them.",
        "requestBody": {
          "description": "Vehicle, applicant and coverage inputs.",
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CreateQuoteRequest"
                  }
                ],
                "description": "Inputs for pricing a policy. One call replaces what Portal collects across three pages,\r\nso everything the quote depends on is sent together."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CreateQuoteRequest"
                  }
                ],
                "description": "Inputs for pricing a policy. One call replaces what Portal collects across three pages,\r\nso everything the quote depends on is sent together."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CreateQuoteRequest"
                  }
                ],
                "description": "Inputs for pricing a policy. One call replaces what Portal collects across three pages,\r\nso everything the quote depends on is sent together."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Quote priced. Inspect the option lists to choose a term and coverage category.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/CreateQuoteResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateQuoteResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateQuoteResponse"
                }
              }
            }
          },
          "400": {
            "description": "The quote could not be produced at all."
          },
          "401": {
            "description": "Missing, expired or invalid bearer token."
          },
          "422": {
            "description": "The request was understood and refused. Read `errors[].code` for the reason and `errors[].field` for where.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/quote/financing": {
      "post": {
        "tags": [
          "Quote"
        ],
        "summary": "Returns the monthly payment options for financing a quote through Optiom's lender.",
        "description": "Financing spreads the premium over monthly pre-authorised debits. This call prices the\r\nplans for one coverage category, term and product selection, and creates nothing:\r\ncall it as often as needed while the customer settles on a plan.\r\n            \r\nSend the same `coverageCategory`, `selectedProducts` and `paymentTermInYears` intended\r\nfor the bind, plus the `downPayment` and the `firstInstallmentDate` requested. The\r\nfirst installment must be at least a day after the policy effective date and no more\r\nthan a month out; the error names the window when it is not.\r\n            \r\nEach option carries the `loanTermInMonths` to send back in the bind's `financing`\r\nblock. Figures here are indicative, because the lender prices the contract when it is\r\ncreated. The bind response repeats the final ones.\r\n            \r\nFinancing is available on Prime and Plus replacement coverage. It is not offered on\r\nLimitedIndemnity, and the seller must be set up for it.",
        "requestBody": {
          "description": "Quote, coverage selection, down payment and requested first installment date.",
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/FinancingQuoteRequest"
                  }
                ],
                "description": "Asks what the monthly payments would be for a quote. Nothing is created or committed, so a\r\npartner can call this as often as needed while the customer settles on a plan."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/FinancingQuoteRequest"
                  }
                ],
                "description": "Asks what the monthly payments would be for a quote. Nothing is created or committed, so a\r\npartner can call this as often as needed while the customer settles on a plan."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/FinancingQuoteRequest"
                  }
                ],
                "description": "Asks what the monthly payments would be for a quote. Nothing is created or committed, so a\r\npartner can call this as often as needed while the customer settles on a plan."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Options priced. Each entry is a loan the customer could take.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/FinancingQuoteResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FinancingQuoteResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/FinancingQuoteResponse"
                }
              }
            }
          },
          "400": {
            "description": "No result was produced."
          },
          "401": {
            "description": "Missing, expired or invalid bearer token."
          },
          "422": {
            "description": "Refused. `FinancingUnavailable` is retryable; `FinancingDeclined`, `FinancingNotAvailableForCoverageCategory` and the plan validation errors are not.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/quote/vehicle-configurations": {
      "get": {
        "tags": [
          "Quote"
        ],
        "summary": "Decodes a VIN into the vehicle configurations it could be, so the caller can pick one.",
        "description": "A VIN identifies a model and year but rarely a single trim, so this returns every\r\nconfiguration the VIN decodes to. Pass the chosen `id` as `vehicleConfigurationId` on\r\n`POST /quote`. A single result still has to be chosen explicitly.\r\n            \r\n`effectiveDate` and `odometer` are part of the request because eligibility is evaluated\r\nagainst them, not only against the VIN.",
        "parameters": [
          {
            "name": "Vin",
            "in": "query",
            "description": "17-character vehicle identification number.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Odometer",
            "in": "query",
            "description": "Current odometer reading in kilometres.",
            "schema": {
              "maximum": 2147483647,
              "minimum": 0,
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "EffectiveDate",
            "in": "query",
            "description": "Date coverage would start. Send the same value on the subsequent quote.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "PurchaseMethod",
            "in": "query",
            "description": "How the customer acquired the vehicle.",
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/PurchaseMethod"
                }
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One or more configurations. Empty `vehicles` means the VIN decoded but nothing is eligible.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/VehicleConfigurationsResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VehicleConfigurationsResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/VehicleConfigurationsResponse"
                }
              }
            }
          },
          "400": {
            "description": "The VIN could not be resolved."
          },
          "401": {
            "description": "Missing, expired or invalid bearer token."
          },
          "422": {
            "description": "The query string was invalid — see `errors[]`.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/quote/lienholders": {
      "get": {
        "tags": [
          "Quote"
        ],
        "summary": "Finds lienholders by name so the caller can send an id rather than an address.",
        "description": "Send the matched `id` as `lienHolderId` on `POST /quote`; the name and postal code are\r\nresolved from it. A leased or financed vehicle needs one.\r\n            \r\nThe search term must be at least three characters.",
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "description": "Partial lienholder name, three characters or more.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matches, possibly empty.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/LienholderSearchResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LienholderSearchResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/LienholderSearchResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or invalid bearer token."
          },
          "422": {
            "description": "`SearchTooShort` when the term is under three characters.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/quote/dealers": {
      "get": {
        "tags": [
          "Quote"
        ],
        "summary": "Lists the dealers the authenticated seller is actively linked to.",
        "description": "Required for `Plus`, which is a dealer sale: pass the chosen `id` as `dealerId` on\r\n`POST /quote`. The list is scoped to the authenticated seller, so a dealer absent from\r\nit will be rejected as `DealerNotLinkedToSeller` at quote time.\r\n            \r\nThe set changes rarely. Cache it rather than calling it per quote.",
        "responses": {
          "200": {
            "description": "Linked dealers. Empty means the seller has no active dealer links and cannot sell Plus.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/DealerSearchResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DealerSearchResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/DealerSearchResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or invalid bearer token."
          },
          "422": {
            "description": "Unprocessable Content",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/test-vin": {
      "get": {
        "tags": [
          "TestVin"
        ],
        "summary": "Returns a real, unused VIN of the requested year for integration testing.",
        "description": "Non-production only. Quoting twice against the same VIN trips duplicate-policy\r\nchecks, so this supplies a fresh one per test run rather than having you keep a list.\r\n            \r\nNot available in production, and not part of the integration a partner ships.",
        "parameters": [
          {
            "name": "year",
            "in": "query",
            "description": "Model year the test VIN should decode to.",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A usable VIN and the year it decodes to.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/TestVinResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TestVinResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/TestVinResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, expired or invalid bearer token."
          },
          "422": {
            "description": "`TestVinProductionDisabled` in production, or no unused VIN available for that year.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry after the number of seconds in the Retry-After header.",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Internal Server Error",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExternalErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Applicant": {
        "type": "object",
        "properties": {
          "isCompany": {
            "type": "boolean",
            "description": "True when the insured is a business. LastName is then not required and\r\nFirstOrCompanyName carries the registered name."
          },
          "firstOrCompanyName": {
            "type": "string",
            "description": "Given name, or the company name when IsCompany is true.",
            "nullable": true
          },
          "lastName": {
            "type": "string",
            "description": "Family name. Not required when IsCompany is true.",
            "nullable": true
          },
          "addressLine1": {
            "type": "string",
            "description": "Street address.",
            "nullable": true
          },
          "addressLine2": {
            "type": "string",
            "description": "Unit or suite. Optional.",
            "nullable": true
          },
          "city": {
            "type": "string",
            "description": "City. Must be in the seller province - province is not sent, it is derived.",
            "nullable": true
          },
          "postalCode": {
            "type": "string",
            "description": "Canadian postal code.",
            "nullable": true
          },
          "phoneNumber": {
            "type": "string",
            "description": "Primary contact number.",
            "nullable": true
          },
          "businessPhone": {
            "type": "string",
            "description": "Alternate contact number. Optional.",
            "nullable": true
          },
          "emailAddress": {
            "type": "string",
            "description": "Required. Policy documents are delivered here, so a wrong address means no documents.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "The named insured. Province is not sent: it is derived from the authenticated seller, and\r\nthe applicant's address must be in the same province. Email is required, so there is no\r\nemail-declined option."
      },
      "AuthenticateRequest": {
        "type": "object",
        "properties": {
          "username": {
            "type": "string",
            "description": "Seller username issued by Optiom. Determines which seller quotes are written against.",
            "nullable": true
          },
          "password": {
            "type": "string",
            "description": "Seller password issued by Optiom.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Seller credentials. Sent alongside the client_id and client_secret headers, which identify\r\nthe integration rather than the seller."
      },
      "BankAccountDetails": {
        "type": "object",
        "properties": {
          "institutionNumber": {
            "type": "string",
            "description": "Three-digit institution number.",
            "nullable": true
          },
          "transitNumber": {
            "type": "string",
            "description": "Five-digit branch transit number.",
            "nullable": true
          },
          "accountNumber": {
            "type": "string",
            "description": "Account number, 7 to 12 digits.",
            "nullable": true
          },
          "accountHolderName": {
            "type": "string",
            "description": "Name on the account. It does not have to match the policy holder.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Canadian chequing account for the pre-authorised debits."
      },
      "BindRequest": {
        "type": "object",
        "properties": {
          "applicationId": {
            "type": "string",
            "description": "Identifies the quote being bound. From the CreateQuote response.",
            "format": "uuid"
          },
          "paymentMethod": {
            "type": "string",
            "description": "Payment method code. REF (referral), FIN (agency finance) and PW settle before the\r\nbind. PFF finances the premium through Optiom's lender and needs the Financing block.\r\nCC settles a card payment taken beforehand and needs the CardPayment block.",
            "nullable": true
          },
          "paymentReference": {
            "type": "string",
            "description": "The partner reference for the payment already taken - a cheque number, a receipt id.\r\nStored against the policy for reconciliation; not validated against any provider.\r\nLeave empty for PFF and CC: the loan number, or the card approval code, becomes the\r\nreference instead.",
            "nullable": true
          },
          "paymentTermInYears": {
            "type": "integer",
            "description": "Coverage term. Must be one of the PaymentTermInYears values returned in the chosen\r\ncoverage category on the quote, not an arbitrary number of years.",
            "format": "int32"
          },
          "coverageCategory": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CoverageCategory"
              }
            ],
            "description": "Which coverage family to bind on. Required. Replacement for products from\r\nreplacementOptions, LimitedIndemnity for limitedIndemnityOptions.",
            "nullable": true
          },
          "selectedProducts": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Product type codes being bound. All must come from the coverage category named above,\r\nand every product in that category with isOptional false must be included.",
            "nullable": true
          },
          "financing": {
            "allOf": [
              {
                "$ref": "#/components/schemas/FinancingDetails"
              }
            ],
            "description": "The financing plan and bank account. Required when PaymentMethod is PFF and rejected\r\nfor every other method. Take the plan from a POST /quote/financing option.",
            "nullable": true
          },
          "cardPayment": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CardPaymentDetails"
              }
            ],
            "description": "The card payment settling this policy. Required when PaymentMethod is CC and rejected for\r\nevery other method. Take the payment id from POST /payment/card once it is authorized.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Turns a priced quote into a policy. Carries the chosen term, the selected products, the\r\npayment method and the reference for the payment already collected."
      },
      "BindResponse": {
        "type": "object",
        "properties": {
          "isValid": {
            "type": "boolean",
            "description": "False when the bind was refused. Read Errors for why."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QuoteValidationError"
            },
            "description": "Empty on success. Each entry carries a stable Code, the Field it applies to, and a displayable Message.",
            "nullable": true
          },
          "policyId": {
            "type": "integer",
            "description": "Optiom's own identifier for the policy. Useful when contacting support; it is not an input to any endpoint.",
            "format": "int32"
          },
          "policyNumber": {
            "type": "string",
            "description": "The customer-facing policy number. This is the reference to store and display.",
            "nullable": true
          },
          "documentDownloadId": {
            "type": "string",
            "description": "Identifies the generated policy declaration. Pass it to GET /document/{downloadId} to\r\nretrieve the PDF.",
            "format": "uuid"
          },
          "financingLetterDownloadId": {
            "type": "string",
            "description": "Identifies the lender's financing letter on a PFF policy. Pass it to\r\nGET /document/{downloadId} the same way as the declaration. Null on every other\r\npayment method, and on a financed policy whose letter could not be produced, in which\r\ncase Errors says so.",
            "format": "uuid",
            "nullable": true
          },
          "financing": {
            "allOf": [
              {
                "$ref": "#/components/schemas/FinancingSummary"
              }
            ],
            "description": "The loan written for a PFF policy, with the contract figures. Null on every other\r\npayment method.",
            "nullable": true
          },
          "cardPayment": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CardPaymentSummary"
              }
            ],
            "description": "The card that paid for a CC policy, either charged during this bind or scheduled for its\r\ncharge date. Null on every other payment method.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "The bound policy. Returned both by the bind that created it and by any later retry of the\r\nsame bind, so receiving this twice does not mean two policies exist."
      },
      "CardPaymentConfirmRequest": {
        "type": "object",
        "properties": {
          "applicationId": {
            "type": "string",
            "description": "Identifies the quote the payment belongs to.",
            "format": "uuid"
          },
          "paymentId": {
            "type": "string",
            "description": "paymentId from POST /payment/card.",
            "nullable": true
          },
          "ticket": {
            "type": "string",
            "description": "The ticket the start call returned and the checkout ran on. Optiom does not keep it, so\r\nsend it back here. Treat it as a secret: never log it or put it in a URL.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Asks Optiom to check with Moneris how the checkout went. Safe to repeat: it reports the\r\noutcome rather than causing one."
      },
      "CardPaymentConfirmResponse": {
        "type": "object",
        "properties": {
          "isValid": {
            "type": "boolean",
            "description": "False when the payment could not be checked at all. A declined card is a valid result, not an error."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QuoteValidationError"
            },
            "description": "Empty on success. Each entry carries a stable Code, the Field it applies to, and a displayable Message.",
            "nullable": true
          },
          "paymentId": {
            "type": "string",
            "description": "Echo of the card payment being checked.",
            "nullable": true
          },
          "status": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CardPaymentStatus"
              }
            ],
            "description": "Bind only on Authorized. Pending means the customer has not finished, so ask again.\r\nDeclined and Cancelled both mean starting a new card payment."
          },
          "amount": {
            "type": "number",
            "description": "What will be charged. Zero for a scheduled charge, which holds nothing now.",
            "format": "double"
          },
          "chargeDate": {
            "type": "string",
            "description": "The day the card is charged.",
            "format": "date"
          },
          "maskedCardNumber": {
            "type": "string",
            "description": "Last four digits with the rest masked, for showing the customer which card was used.",
            "nullable": true
          },
          "cardType": {
            "type": "string",
            "description": "Card brand, for example Visa.",
            "nullable": true
          },
          "expiresAt": {
            "type": "string",
            "description": "When the authorization stops being bindable, UTC. Null for a scheduled charge, which does\r\nnot hold funds. Bind before this.",
            "format": "date-time",
            "nullable": true
          },
          "message": {
            "type": "string",
            "description": "Displayable detail when the card was refused. Empty otherwise.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "How the checkout went, according to Moneris rather than the browser. This is the only\r\ntrustworthy answer: the checkout's own callbacks are a hint."
      },
      "CardPaymentDetails": {
        "type": "object",
        "properties": {
          "paymentId": {
            "type": "string",
            "description": "paymentId from POST /payment/card, once POST /payment/card/confirm has reported it as\r\nAuthorized.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "The authorized card payment to settle the policy with. Required when PaymentMethod is CC and\r\nrejected for every other method."
      },
      "CardPaymentStartRequest": {
        "type": "object",
        "properties": {
          "applicationId": {
            "type": "string",
            "description": "Identifies the quote being paid for. From the CreateQuote response.",
            "format": "uuid"
          },
          "coverageCategory": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CoverageCategory"
              }
            ],
            "description": "Which coverage family the customer is buying. Must match the bind that follows.",
            "nullable": true
          },
          "selectedProducts": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Product type codes being bought. Same rules as the bind request.",
            "nullable": true
          },
          "paymentTermInYears": {
            "type": "integer",
            "description": "Coverage term in years. Must be one of the terms on the quote.",
            "format": "int32"
          },
          "chargeDate": {
            "type": "string",
            "description": "When to take the money. Omit it and the card is charged on the policy effective date,\r\nor immediately at bind when that date is today. Send today's date to charge at bind\r\ninstead. Cannot be earlier than today or later than the effective date.",
            "format": "date",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Opens a card payment for a quote. No card details are accepted here or anywhere else in this\r\nAPI: the customer types the card into Moneris's own hosted checkout."
      },
      "CardPaymentStartResponse": {
        "type": "object",
        "properties": {
          "isValid": {
            "type": "boolean",
            "description": "False when the payment could not be opened. Read Errors for why."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QuoteValidationError"
            },
            "description": "Empty on success. Each entry carries a stable Code, the Field it applies to, and a displayable Message.",
            "nullable": true
          },
          "paymentId": {
            "type": "string",
            "description": "Identifies this card payment. Send it to confirm, and then to bind.",
            "nullable": true
          },
          "applicationId": {
            "type": "string",
            "description": "Echo of the quote being paid for.",
            "format": "uuid"
          },
          "ticket": {
            "type": "string",
            "description": "Pass to monerisCheckout.startCheckout(). Valid for a short time only, and usable once.\r\nTreat it as a secret: never log it or put it in a URL.",
            "nullable": true
          },
          "status": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CardPaymentStatus"
              }
            ],
            "description": "Status of the payment. Pending means the ticket is ready and waiting for the customer.\r\nAuthorized means an earlier payment on this quote is still good and no ticket was issued."
          },
          "amount": {
            "type": "number",
            "description": "What the card will be charged. Zero when the charge is scheduled for a later date, since\r\nnothing is held on the card until then.",
            "format": "double"
          },
          "currency": {
            "type": "string",
            "description": "Currency of Amount. Always CAD.",
            "nullable": true
          },
          "chargeDate": {
            "type": "string",
            "description": "The day the card is charged: the bind day, or a later scheduled date.",
            "format": "date"
          },
          "checkoutJsUrl": {
            "type": "string",
            "description": "Script to load before starting the checkout. Load it from here rather than hard-coding it.",
            "nullable": true
          },
          "environment": {
            "type": "string",
            "description": "Pass to monerisCheckout.setMode(). Either qa or prod.",
            "nullable": true
          },
          "expiresAt": {
            "type": "string",
            "description": "When the ticket stops working, UTC. Start again after that.",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "A Moneris Checkout session to render in your page. Everything the customer types goes\r\nstraight to Moneris."
      },
      "CardPaymentStatus": {
        "enum": [
          "Pending",
          "Authorized",
          "Declined",
          "Cancelled",
          "Expired",
          "Captured",
          "Voided",
          "Scheduled"
        ],
        "type": "string",
        "description": "Where a card payment has got to. Only Authorized may be bound against."
      },
      "CardPaymentSummary": {
        "type": "object",
        "properties": {
          "paymentId": {
            "type": "string",
            "description": "The card payment that settled this policy.",
            "nullable": true
          },
          "status": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CardPaymentStatus"
              }
            ],
            "description": "Captured for a charge taken during the bind, Scheduled for one taken on a later date."
          },
          "maskedCardNumber": {
            "type": "string",
            "description": "Last four digits with the rest masked.",
            "nullable": true
          },
          "cardType": {
            "type": "string",
            "description": "Card brand, for example Visa.",
            "nullable": true
          },
          "amountCharged": {
            "type": "number",
            "description": "What was charged. Zero when the charge is still to come on ChargeDate.",
            "format": "double"
          },
          "chargeDate": {
            "type": "string",
            "description": "The day the card is charged.",
            "format": "date"
          },
          "authorizationNumber": {
            "type": "string",
            "description": "The issuer's approval code, which is also stored as the policy's payment reference. Empty\r\nuntil a scheduled charge goes through.",
            "nullable": true
          },
          "transactionDate": {
            "type": "string",
            "description": "When the charge was taken, UTC. Default when it is still to come.",
            "format": "date-time"
          },
          "receiptDownloadId": {
            "type": "string",
            "description": "Identifies the credit card receipt, to retrieve from GET /v1/document/{downloadId} like\r\nany other document. Null when the charge is still to come on ChargeDate.",
            "format": "uuid",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "The card payment settled against a bound policy."
      },
      "CoApplicant": {
        "type": "object",
        "properties": {
          "firstName": {
            "type": "string",
            "description": "Given name of the second person on the policy.",
            "nullable": true
          },
          "lastName": {
            "type": "string",
            "description": "Family name of the second person on the policy.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Optional secondary applicant on a quote. When null, no co-applicant is recorded."
      },
      "CoverageCategory": {
        "enum": [
          "Replacement",
          "LimitedIndemnity"
        ],
        "type": "string",
        "description": "The coverage families a quote can be priced under. Each one maps to a list on the\r\nCreateQuote response: Replacement to replacementOptions, LimitedIndemnity to\r\nlimitedIndemnityOptions. Bind takes the category whose list the chosen products came from."
      },
      "CoverageSummary": {
        "type": "object",
        "properties": {
          "productType": {
            "type": "string",
            "description": "Product code. This is what goes into BindRequest.SelectedProducts.",
            "nullable": true
          },
          "description": {
            "type": "string",
            "description": "Short display name for the product.",
            "nullable": true
          },
          "productDetailDescription": {
            "type": "string",
            "description": "Longer description of what the product covers, suitable for showing the customer.",
            "nullable": true
          },
          "coverageTermInYears": {
            "type": "integer",
            "description": "Years of coverage this product provides. Can differ from the term it is priced under.",
            "format": "int32"
          },
          "retailPremium": {
            "type": "number",
            "description": "Premium before fees and tax.",
            "format": "double",
            "nullable": true
          },
          "retailPrice": {
            "type": "number",
            "description": "Premium plus fees, before tax.",
            "format": "double",
            "nullable": true
          },
          "gstAmount": {
            "type": "number",
            "description": "Federal GST. Populated only in provinces that charge it separately.",
            "format": "double",
            "nullable": true
          },
          "pstAmount": {
            "type": "number",
            "description": "Provincial PST. Populated only in provinces that charge it separately.",
            "format": "double",
            "nullable": true
          },
          "hstAmount": {
            "type": "number",
            "description": "Harmonised HST. Populated instead of GST and PST in HST provinces.",
            "format": "double",
            "nullable": true
          },
          "effectiveDate": {
            "type": "string",
            "description": "When this product coverage starts.",
            "format": "date-time",
            "nullable": true
          },
          "expiryDate": {
            "type": "string",
            "description": "When this product coverage ends.",
            "format": "date-time",
            "nullable": true
          },
          "isOptional": {
            "type": "boolean",
            "description": "True when the product can be left out of the bind. Every product where this is false\r\nmust appear in SelectedProducts.",
            "nullable": true
          },
          "totalWithTax": {
            "type": "number",
            "description": "The amount actually charged for this product. This is the figure to show the customer.",
            "format": "double",
            "nullable": true
          },
          "feeTotalWithTax": {
            "type": "number",
            "description": "The portion of TotalWithTax that is fees rather than premium.",
            "format": "double",
            "nullable": true
          },
          "coverageCategory": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CoverageCategory"
              }
            ],
            "description": "Which option list this product came from, and therefore what to send as CoverageCategory."
          }
        },
        "additionalProperties": false,
        "description": "One product priced for one term. Amounts are in Canadian dollars."
      },
      "CreateQuoteRequest": {
        "type": "object",
        "properties": {
          "applicationId": {
            "type": "string",
            "description": "Optional. When omitted, PortalAPI generates a new quote. When provided, the\r\nexisting quote is updated, but only if it belongs to the authenticated seller;\r\ncross-seller access is rejected.",
            "format": "uuid",
            "nullable": true
          },
          "series": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PortalApiSeries"
              }
            ],
            "description": "Product line. Prime is a direct sale. Plus is a dealer sale and additionally needs\r\nDealerId and DealerContact."
          },
          "dealerId": {
            "type": "integer",
            "description": "Required for Plus, ignored otherwise. Must be a dealer the authenticated seller is\r\nactively linked to - take it from GET /quote/dealers.",
            "format": "int32",
            "nullable": true
          },
          "effectiveDate": {
            "type": "string",
            "description": "Date coverage starts. Drives pricing and eligibility, so a quote taken today for a\r\nlater start prices as of that start.",
            "format": "date-time"
          },
          "vin": {
            "type": "string",
            "description": "17-character vehicle identification number.",
            "nullable": true
          },
          "vehicleConfigurationId": {
            "type": "integer",
            "description": "Id of the vehicle configuration returned by /quote/vehicle-configurations. A VIN\r\ndecodes to several trims and this picks the one being insured, so it cannot be guessed.",
            "format": "int32"
          },
          "odometer": {
            "type": "integer",
            "description": "Current odometer reading in kilometres.",
            "format": "int32"
          },
          "purchaseMethod": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PurchaseMethod"
              }
            ],
            "description": "How the customer acquired the vehicle. Lease and finance values make LienHolderId\r\nmandatory. Note that the zero value, DealerPurchase, is indistinguishable from an\r\nomitted field, so send it explicitly."
          },
          "applicant": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Applicant"
              }
            ],
            "description": "Named insured. Their address must be in the seller province.",
            "nullable": true
          },
          "coApplicant": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CoApplicant"
              }
            ],
            "description": "Optional second name on the policy.",
            "nullable": true
          },
          "allEligibilityConditionsApply": {
            "type": "boolean",
            "description": "Attestation that the customer meets the eligibility conditions of the product. Must be\r\ntrue to bind, and it is the partner asserting it on the customer behalf."
          },
          "primaryInsurance": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PrimaryInsurance"
              }
            ],
            "description": "The existing primary auto policy. Optional.",
            "nullable": true
          },
          "producerCode": {
            "type": "string",
            "description": "Producer code. Mandatory for some sellers and rejected as missing for those;\r\nOptiom confirms at onboarding whether yours requires it.",
            "nullable": true
          },
          "dealerContact": {
            "type": "string",
            "description": "Name of the person at the dealership handling the sale. Required for Plus.",
            "nullable": true
          },
          "notes": {
            "type": "string",
            "description": "Free text kept on the application for Optiom staff. Not shown to the customer.",
            "nullable": true
          },
          "lienHolderId": {
            "type": "integer",
            "description": "Lienholder on the vehicle, from GET /quote/lienholders. Required when PurchaseMethod is\r\na lease or a finance. Sending the id is enough - name and postal code resolve from it.",
            "format": "int32",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Inputs for pricing a policy. One call replaces what Portal collects across three pages,\r\nso everything the quote depends on is sent together."
      },
      "CreateQuoteResponse": {
        "type": "object",
        "properties": {
          "applicationId": {
            "type": "string",
            "description": "Identifies this quote. Send it back to revise the quote, and to bind it. Keep it -\r\nthere is no endpoint to look a quote up again.",
            "format": "uuid"
          },
          "isValid": {
            "type": "boolean",
            "description": "False when the quote could not be priced. Read Errors for why."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QuoteValidationError"
            },
            "description": "Empty on success. Each entry carries a stable Code, the Field it applies to, and a displayable Message.",
            "nullable": true
          },
          "series": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PortalApiSeries"
              }
            ],
            "description": "Echoes the requested product line.",
            "nullable": true
          },
          "dealerId": {
            "type": "integer",
            "description": "Echoes the dealer the quote was written under. Plus only.",
            "format": "int32",
            "nullable": true
          },
          "dealerName": {
            "type": "string",
            "description": "Resolved dealer name, so the partner can display it without a second lookup.",
            "nullable": true
          },
          "effectiveDate": {
            "type": "string",
            "description": "Echoes the coverage start date the pricing was calculated against.",
            "format": "date-time",
            "nullable": true
          },
          "province": {
            "type": "string",
            "description": "Province the policy is written in. Derived from the seller, not from the request.",
            "nullable": true
          },
          "vehicleValue": {
            "type": "number",
            "description": "Vehicle value the pricing used. Black Book retail; not the purchase price.",
            "format": "double",
            "nullable": true
          },
          "odometer": {
            "type": "integer",
            "description": "Echoes the odometer reading used.",
            "format": "int32",
            "nullable": true
          },
          "year": {
            "type": "integer",
            "description": "Model year resolved from the VIN.",
            "format": "int32",
            "nullable": true
          },
          "vehicleCategory": {
            "allOf": [
              {
                "$ref": "#/components/schemas/RiskFactor"
              }
            ],
            "description": "Rating band the vehicle fell into. Explains why two similar vehicles price differently.",
            "nullable": true
          },
          "replacementOptions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TermPricingSummary"
            },
            "description": "Replacement coverage priced per term. Bind against these with CoverageCategory = Replacement.",
            "nullable": true
          },
          "limitedIndemnityOptions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TermPricingSummary"
            },
            "description": "Limited indemnity coverage priced per term. Bind with CoverageCategory = LimitedIndemnity.",
            "nullable": true
          },
          "indemnityLimits": {
            "type": "object",
            "additionalProperties": {
              "type": "number",
              "format": "double"
            },
            "description": "Payout ceilings by product code, for display alongside the limited indemnity options.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A priced quote. The three option lists are the pricing matrix: pick one coverage category\r\nand one term from it, and pass that pair to POST /bind. Everything except ApplicationId,\r\nIsValid and Errors is omitted when the quote did not price."
      },
      "DealerResult": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Send as DealerId on a Plus quote.",
            "format": "int32"
          },
          "name": {
            "type": "string",
            "description": "Dealership name.",
            "nullable": true
          },
          "city": {
            "type": "string",
            "description": "Dealership city, for telling apart same-named locations.",
            "nullable": true
          },
          "province": {
            "type": "string",
            "description": "Dealership province.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "One linked dealer."
      },
      "DealerSearchResponse": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DealerResult"
            },
            "description": "Empty when the seller has no active dealer links, which means it cannot sell Plus.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Dealers the authenticated seller is actively linked to."
      },
      "ExternalErrorResponse": {
        "type": "object",
        "properties": {
          "isValid": {
            "type": "boolean",
            "description": "Always false on this response."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QuoteValidationError"
            },
            "description": "One entry per reason the request was refused. Never empty.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "The failure envelope every endpoint returns. Successful quote and bind responses carry the\r\nsame two properties, so one branch on IsValid covers both."
      },
      "FinancingDetails": {
        "type": "object",
        "properties": {
          "loanTermInMonths": {
            "type": "integer",
            "description": "Length of the loan in months, taken from a loanTermInMonths in the\r\nPOST /quote/financing response. Not the policy term.",
            "format": "int32"
          },
          "downPayment": {
            "type": "number",
            "description": "Amount paid up front. Must be the same figure the chosen option was quoted with.",
            "format": "double"
          },
          "firstInstallmentDate": {
            "type": "string",
            "description": "Date of the first installment. Also sets the day of month for every later one.",
            "format": "date-time"
          },
          "bankAccount": {
            "allOf": [
              {
                "$ref": "#/components/schemas/BankAccountDetails"
              }
            ],
            "description": "Chequing account the lender debits. Never stored by Optiom; it is passed to the lender and discarded.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "The financing plan the customer chose and the account the installments come out of.\r\nRequired when PaymentMethod is PFF, rejected otherwise."
      },
      "FinancingOptionSummary": {
        "type": "object",
        "properties": {
          "loanTermInMonths": {
            "type": "integer",
            "description": "Length of the loan in months. This is the value the bind request expects.",
            "format": "int32"
          },
          "numberOfInstallments": {
            "type": "integer",
            "description": "How many installments are debited. One fewer than the term, because the down payment covers the first month.",
            "format": "int32"
          },
          "installmentAmount": {
            "type": "number",
            "description": "Amount of each installment.",
            "format": "double"
          },
          "financeCharge": {
            "type": "number",
            "description": "Total interest over the life of the loan.",
            "format": "double"
          },
          "applicationFee": {
            "type": "number",
            "description": "One-time fee for setting up the financing, included in the total.",
            "format": "double"
          },
          "annualPercentageRate": {
            "type": "number",
            "description": "Annual percentage rate, for example 12.99.",
            "format": "double"
          },
          "totalAmountPayable": {
            "type": "number",
            "description": "Everything the customer pays across the loan, including the finance charge and application fee.",
            "format": "double"
          },
          "firstPaymentDate": {
            "type": "string",
            "description": "Date of the first installment for this option.",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "One loan the customer could take. Pass the chosen loanTermInMonths back on the bind."
      },
      "FinancingQuoteRequest": {
        "type": "object",
        "properties": {
          "applicationId": {
            "type": "string",
            "description": "Identifies the quote being financed. From the CreateQuote response.",
            "format": "uuid"
          },
          "coverageCategory": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CoverageCategory"
              }
            ],
            "description": "Which coverage family the customer is buying. Financing is not available on LimitedIndemnity.",
            "nullable": true
          },
          "selectedProducts": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Product type codes being financed. Same rules as the bind request.",
            "nullable": true
          },
          "paymentTermInYears": {
            "type": "integer",
            "description": "Coverage term in years. Must be one of the terms on the quote.",
            "format": "int32"
          },
          "downPayment": {
            "type": "number",
            "description": "Amount paid up front, reducing what is financed. Zero is valid.",
            "format": "double"
          },
          "firstInstallmentDate": {
            "type": "string",
            "description": "Requested date of the first installment. Must be at least one day after the policy\r\neffective date and no more than a month out; the response repeats the date actually used.",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "Asks what the monthly payments would be for a quote. Nothing is created or committed, so a\r\npartner can call this as often as needed while the customer settles on a plan."
      },
      "FinancingQuoteResponse": {
        "type": "object",
        "properties": {
          "isValid": {
            "type": "boolean",
            "description": "False when financing was refused. Read Errors for why."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QuoteValidationError"
            },
            "description": "Empty on success. Each entry carries a stable Code, the Field it applies to, and a displayable Message.",
            "nullable": true
          },
          "applicationId": {
            "type": "string",
            "description": "Echo of the quote these options were priced for.",
            "format": "uuid"
          },
          "paymentTermInYears": {
            "type": "integer",
            "description": "Echo of the coverage term the options were priced for, in years.",
            "format": "int32"
          },
          "amountFinanced": {
            "type": "number",
            "description": "Premium including tax, before the down payment comes off.",
            "format": "double"
          },
          "downPayment": {
            "type": "number",
            "description": "Echo of the down payment the options were priced with.",
            "format": "double"
          },
          "firstInstallmentDate": {
            "type": "string",
            "description": "Echo of the first installment date the options were priced with.",
            "format": "date-time"
          },
          "options": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FinancingOptionSummary"
            },
            "description": "One entry per loan term the lender would write. Empty when financing is unavailable.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "What financing would cost for a quote. These figures are indicative; the binding ones come\r\nback on the bind response, because the lender prices the contract when it is created."
      },
      "FinancingSummary": {
        "type": "object",
        "properties": {
          "loanNumber": {
            "type": "string",
            "description": "The lender's loan number. Also stored on the policy as its payment reference.",
            "nullable": true
          },
          "loanTermInMonths": {
            "type": "integer",
            "description": "Length of the loan in months.",
            "format": "int32"
          },
          "numberOfInstallments": {
            "type": "integer",
            "description": "How many installments are debited.",
            "format": "int32"
          },
          "installmentAmount": {
            "type": "number",
            "description": "Amount of each installment.",
            "format": "double"
          },
          "financeCharge": {
            "type": "number",
            "description": "Total interest over the life of the loan.",
            "format": "double"
          },
          "applicationFee": {
            "type": "number",
            "description": "One-time fee for setting up the financing, included in the total.",
            "format": "double"
          },
          "annualPercentageRate": {
            "type": "number",
            "description": "Annual percentage rate, for example 12.99.",
            "format": "double"
          },
          "totalAmountPayable": {
            "type": "number",
            "description": "Everything the customer pays across the loan.",
            "format": "double"
          },
          "firstPaymentDate": {
            "type": "string",
            "description": "Date of the first installment.",
            "format": "date-time"
          },
          "lastPaymentDate": {
            "type": "string",
            "description": "Date of the last installment.",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "The loan that was written for a financed policy. These are the contract figures, which can\r\ndiffer slightly from the indicative ones on POST /quote/financing."
      },
      "LienholderResult": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Send as LienHolderId on the quote. Name and postal code resolve from it.",
            "format": "int32"
          },
          "name": {
            "type": "string",
            "description": "Registered lienholder name.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "One matched lienholder."
      },
      "LienholderSearchResponse": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LienholderResult"
            },
            "description": "Empty when nothing matched. Contact Optiom if the lienholder is genuinely absent.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Lienholders matching the search term."
      },
      "PortalApiSeries": {
        "enum": [
          "Prime",
          "Plus"
        ],
        "type": "string",
        "description": "The product lines a quote can be written for."
      },
      "PrimaryInsurance": {
        "type": "object",
        "properties": {
          "insurerName": {
            "type": "string",
            "description": "Name of the primary auto insurer.",
            "nullable": true
          },
          "policyNumber": {
            "type": "string",
            "description": "Policy number with that insurer.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "The applicant's existing primary auto-insurance policy. Optional: null when the\r\napplicant has no primary coverage to declare."
      },
      "PurchaseMethod": {
        "enum": [
          "DealerPurchase",
          "DealerLease",
          "Private",
          "DealerFinance",
          "DealerCash",
          "LeaseBuyout",
          "ManufacturerFinance",
          "ManufacturerLease",
          "NonManufacturerFinance",
          "NonManufacturerLease",
          "None"
        ],
        "type": "string"
      },
      "QuoteValidationError": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "Stable identifier for the failure. Branch on this; it does not change with wording or locale.",
            "nullable": true
          },
          "field": {
            "type": "string",
            "description": "Request property the failure belongs to, for attaching the message to a form field. Empty when it applies to the request as a whole.",
            "nullable": true
          },
          "message": {
            "type": "string",
            "description": "Human-readable explanation, safe to show a user. Wording is not part of the contract.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "One reason a request was refused."
      },
      "RiskFactor": {
        "enum": [
          "Undefined",
          "Standard",
          "Surcharge",
          "HighRisk",
          "Low",
          "Medium",
          "Ineligible"
        ],
        "type": "string"
      },
      "TermPricingSummary": {
        "type": "object",
        "properties": {
          "paymentTermInYears": {
            "type": "integer",
            "description": "Length of the term in years. Send this value on bind, not an arbitrary one.",
            "format": "int32"
          },
          "coverage": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CoverageSummary"
            },
            "description": "Products priced at this term. Sum TotalWithTax across the selected ones for the customer total.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "The products available at one term length, priced. The term is what goes into\r\nBindRequest.PaymentTermInYears."
      },
      "TestVinResponse": {
        "type": "object",
        "properties": {
          "vin": {
            "type": "string",
            "description": "A real, unused 17-character VIN.",
            "nullable": true
          },
          "year": {
            "type": "integer",
            "description": "Model year the VIN decodes to.",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "A VIN that has not been quoted before, for use in non-production testing."
      },
      "TokenResponse": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "description": "Send as: Authorization: Bearer {token}.",
            "nullable": true
          },
          "expiryTime": {
            "type": "string",
            "description": "UTC instant the token stops being accepted. Reauthenticate before this, not on every call.",
            "format": "date-time"
          },
          "expiryMinutes": {
            "type": "integer",
            "description": "Lifetime in minutes, for callers that would rather compute their own expiry.",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "A bearer token and how long it lasts."
      },
      "VehicleConfiguration": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "description": "Send as VehicleConfigurationId on the quote.",
            "format": "int32"
          },
          "make": {
            "type": "string",
            "description": "Manufacturer.",
            "nullable": true
          },
          "model": {
            "type": "string",
            "description": "Model name.",
            "nullable": true
          },
          "year": {
            "type": "integer",
            "description": "Model year.",
            "format": "int32"
          },
          "bodyStyle": {
            "type": "string",
            "description": "Body style, for example Sedan or SUV.",
            "nullable": true
          },
          "modelSeries": {
            "type": "string",
            "description": "Trim level. Usually the only thing separating two otherwise identical candidates.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "One trim the VIN could be."
      },
      "VehicleConfigurationsResponse": {
        "type": "object",
        "properties": {
          "vin": {
            "type": "string",
            "description": "Echoes the VIN queried.",
            "nullable": true
          },
          "vehicles": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/VehicleConfiguration"
            },
            "description": "Candidate configurations. Empty means the VIN decoded but nothing is eligible.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "What a VIN decodes to. Usually more than one trim."
      }
    },
    "securitySchemes": {
      "Bearer": {
        "type": "http",
        "description": "Bearer token returned by POST /v1/authenticate. Send it as: Authorization: Bearer {token}",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      }
    }
  },
  "security": [
    {
      "Bearer": [ ]
    }
  ],
  "tags": [
    {
      "name": "Authentication",
      "description": "External authentication entry point. Anonymous, since it mints the bearer token every other endpoint needs.\r\nExpects body: {Username, Password} and headers: client_id, client_secret."
    },
    {
      "name": "Bind",
      "description": "Turns a priced quote into a policy."
    },
    {
      "name": "Document",
      "description": "Returns the policy declaration PDF for a bound policy. The downloadId is the\r\ndocumentDownloadId returned by POST /bind."
    },
    {
      "name": "Payment",
      "description": "Collects a card payment for a quote through Moneris Checkout. Card details go from the\r\ncustomer to Moneris and never touch Optiom or your servers."
    },
    {
      "name": "Quote",
      "description": "Prices a policy. POST /quote returns the full pricing matrix in one call. The\r\n/vehicle-configurations, /lienholders and /dealers helpers resolve the ids that call needs."
    },
    {
      "name": "TestVin",
      "description": "Test helper for non-production environments. Returns a real, unused 17-character VIN for\r\nexercising the quote and bind flow end to end. Not available in production."
    }
  ]
}