Valjean Specialty Public API

No key needed: the website's appetite check and class guides.

Operations 4

POST /api/appetite Public appetite check (no key) #
GET /api/appetite Public appetite check (no key), as a GET #
GET /api/guides Class guides (no key) #
GET /api/guides/{key} One class guide (no key) #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/valjeanspecialty:valjeanspecialty-public-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

valjeanspecialty-public-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Valjean Specialty agent Public API
  version: 1.0.0
  summary: Check appetite, send submissions, answer what's needed, follow files, get quotes and ask to bind.
  description: 'For retail agencies'' own software and AI assistants. Every key sees only its own agency''s files. A named Valjean broker works every file: chooses the markets, confirms anything you send, presents quotes and completes binding. Market names appear only on quotes your broker presents.'
  contact:
    name: Valjean Specialty
    email: submissions@valjeanspecialty.com
    url: https://valjeanspecialty.com
  license:
    name: 'Proprietary: for Valjean Specialty agencies'
    url: https://valjeanspecialty.com
  x-mcp:
    url: https://api.valjeanspecialty.com/api/mcp
    transport: streamable-http
    tools:
    - whoami
    - check_appetite
    - get_requirements
    - create_submission
    - list_submissions
    - get_submission
    - answer_questions
    - underwriter_questions
    - add_document
    - get_upload_link
    - finish_upload
    - send_documents
    - whats_new
    - list_documents
    - get_document_link
    - request_bind
    - list_bind_requests
    - withdraw_bind_request
    - ask_instant_quotes
    - list_policies
    - get_policy
    - list_invoices
    - list_quotes
servers:
- url: https://api.valjeanspecialty.com/api/agent/v1
  description: Live and demo (the key decides which book you see)
security:
- bearerAuth: []
- apiKeyHeader: []
- oauth2:
  - read
tags:
- name: Public
  description: 'No key needed: the website''s appetite check and class guides.'
paths:
  /api/appetite:
    post:
      operationId: publicCheckAppetite
      summary: Public appetite check (no key)
      description: 'The same appetite answer without a key, for websites. Rate-limited to 30 requests a minute per IP; answers cache at the edge for 5 minutes. GET with the same fields as query parameters (lines comma-separated) also works.


        No key needed.'
      tags:
      - Public
      x-valjean-scope: public
      servers:
      - url: https://api.valjeanspecialty.com
        description: Public endpoints (no key)
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                lines:
                  minItems: 1
                  maxItems: 6
                  type: array
                  items:
                    type: string
                    minLength: 1
                    maxLength: 60
                state:
                  type: string
                  minLength: 2
                  maxLength: 40
                naics:
                  type: string
                  pattern: ^[0-9]{2,6}$
                description:
                  type: string
                  maxLength: 300
                target_premium:
                  type: number
                  minimum: 0
                  maximum: 1000000000
                tiv:
                  type: number
                  minimum: 0
                  maximum: 100000000000
              required:
              - lines
              - state
              additionalProperties: false
            example:
              lines:
              - General Liability
              state: South Carolina
              description: restaurant with a bar
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicAppetiteAnswer'
        '400':
          $ref: '#/components/responses/BadRequest'
        '413':
          $ref: '#/components/responses/TooLarge'
        '429':
          $ref: '#/components/responses/RateLimited'
      x-codeSamples:
      - lang: shell
        label: curl
        source: "curl -X POST \"https://api.valjeanspecialty.com/api/appetite\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"lines\":[\"General Liability\"],\"state\":\"South Carolina\",\"description\":\"restaurant with a bar\"}'"
      - lang: javascript
        label: Node
        source: "const res = await fetch(\"https://api.valjeanspecialty.com/api/appetite\", {\n  method: \"POST\",\n  headers: { \"Content-Type\": \"application/json\" },\n  body: JSON.stringify({\n    \"lines\": [\n      \"General Liability\"\n    ],\n    \"state\": \"South Carolina\",\n    \"description\": \"restaurant with a bar\"\n  }),\n});\nconsole.log(await res.json());"
      - lang: python
        label: Python
        source: "import os, requests\n\nres = requests.post(\n    \"https://api.valjeanspecialty.com/api/appetite\",\n    json={\n      \"lines\": [\n        \"General Liability\"\n      ],\n      \"state\": \"South Carolina\",\n      \"description\": \"restaurant with a bar\"\n    },\n)\nprint(res.json())"
      x-agentic-access:
        action: read
        consequence: Reads only; changes nothing.
        human_in_the_loop: None needed.
        reversible: Nothing to undo.
        confirm_with_user: false
    get:
      operationId: publicCheckAppetiteGet
      summary: Public appetite check (no key), as a GET
      description: 'Same as the POST form, with fields as query parameters; cacheable. lines is comma-separated.


        No key needed.'
      tags:
      - Public
      x-valjean-scope: public
      servers:
      - url: https://api.valjeanspecialty.com
        description: Public endpoints (no key)
      security: []
      parameters:
      - name: lines
        in: query
        required: true
        schema:
          minItems: 1
          maxItems: 6
          type: array
          items:
            type: string
            minLength: 1
            maxLength: 60
        style: form
        explode: false
        description: Comma-separated.
      - name: state
        in: query
        required: true
        schema:
          type: string
          minLength: 2
          maxLength: 40
      - name: naics
        in: query
        required: false
        schema:
          type: string
          pattern: ^[0-9]{2,6}$
      - name: description
        in: query
        required: false
        schema:
          type: string
          maxLength: 300
      - name: target_premium
        in: query
        required: false
        schema:
          type: number
          minimum: 0
          maximum: 1000000000
      - name: tiv
        in: query
        required: false
        schema:
          type: number
          minimum: 0
          maximum: 100000000000
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PublicAppetiteAnswer'
        '400':
          $ref: '#/components/responses/BadRequest'
        '429':
          $ref: '#/components/responses/RateLimited'
      x-codeSamples:
      - lang: shell
        label: curl
        source: curl -X GET "https://api.valjeanspecialty.com/api/appetite"
      - lang: javascript
        label: Node
        source: "const res = await fetch(\"https://api.valjeanspecialty.com/api/appetite\", {\n  method: \"GET\",\n  headers: { },\n});\nconsole.log(await res.json());"
      - lang: python
        label: Python
        source: "import os, requests\n\nres = requests.get(\n    \"https://api.valjeanspecialty.com/api/appetite\",\n)\nprint(res.json())"
      x-agentic-access:
        action: read
        consequence: Reads only; changes nothing.
        human_in_the_loop: None needed.
        reversible: Nothing to undo.
        confirm_with_user: false
  /api/guides:
    get:
      operationId: listGuides
      summary: Class guides (no key)
      description: 'The classes Valjean publishes guidance for. Cached for an hour.


        No key needed.'
      tags:
      - Public
      x-valjean-scope: public
      servers:
      - url: https://api.valjeanspecialty.com
        description: Public endpoints (no key)
      security: []
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GuideList'
        '429':
          $ref: '#/components/responses/RateLimited'
      x-codeSamples:
      - lang: shell
        label: curl
        source: curl -X GET "https://api.valjeanspecialty.com/api/guides"
      - lang: javascript
        label: Node
        source: "const res = await fetch(\"https://api.valjeanspecialty.com/api/guides\", {\n  method: \"GET\",\n  headers: { },\n});\nconsole.log(await res.json());"
      - lang: python
        label: Python
        source: "import os, requests\n\nres = requests.get(\n    \"https://api.valjeanspecialty.com/api/guides\",\n)\nprint(res.json())"
      x-agentic-access:
        action: read
        consequence: Reads only; changes nothing.
        human_in_the_loop: None needed.
        reversible: Nothing to undo.
        confirm_with_user: false
  /api/guides/{key}:
    get:
      operationId: getGuide
      summary: One class guide (no key)
      description: 'What Valjean needs for the class, with answer types, whether each item is confirmed with an underwriter, and a live appetite read for SC and GA.


        No key needed.'
      tags:
      - Public
      x-valjean-scope: public
      servers:
      - url: https://api.valjeanspecialty.com
        description: Public endpoints (no key)
      security: []
      parameters:
      - name: key
        in: path
        required: true
        description: Guide key, e.g. gl_hospitality.
        schema:
          type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Guide'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
      x-codeSamples:
      - lang: shell
        label: curl
        source: curl -X GET "https://api.valjeanspecialty.com/api/guides/gl_hospitality"
      - lang: javascript
        label: Node
        source: "const res = await fetch(\"https://api.valjeanspecialty.com/api/guides/gl_hospitality\", {\n  method: \"GET\",\n  headers: { },\n});\nconsole.log(await res.json());"
      - lang: python
        label: Python
        source: "import os, requests\n\nres = requests.get(\n    \"https://api.valjeanspecialty.com/api/guides/gl_hospitality\",\n)\nprint(res.json())"
      x-agentic-access:
        action: read
        consequence: Reads only; changes nothing.
        human_in_the_loop: None needed.
        reversible: Nothing to undo.
        confirm_with_user: false
components:
  schemas:
    AnswerSpec:
      type: object
      properties:
        type:
          type: string
          description: How to answer. "file" means upload a document instead.
          enum:
          - text
          - number
          - money
          - percent
          - yes_no
          - choice
          - date
          - file
        options:
          type: array
          items:
            type: string
          description: 'For type "choice": the allowed answers.'
      required:
      - type
    AppetiteAnswer:
      type: object
      properties:
        fit:
          type: string
          description: '"unknown" means no appetite is on file for these lines yet: fall back to your own guidance.'
          enum:
          - good
          - possible
          - unlikely
          - unknown
        appetite_on_file:
          type: boolean
        headline:
          type: string
          description: One sentence you can show as is.
        market_count:
          type: integer
          description: Markets on file that could write it, capped at 5. Market names are never returned.
        market_count_label:
          type: string
          description: e.g. "2 markets", "5+ markets", "Not on file yet".
        lines:
          type: array
          items:
            type: object
            properties:
              line:
                type: string
                description: Line code.
              fit:
                type: string
                enum:
                - good
                - possible
                - unlikely
            required:
            - line
            - fit
        notes:
          type: array
          items:
            type: string
        needs:
          type: array
          items:
            type: string
          description: What Valjean will need for this risk, in plain words.
        class_matched:
          type:
          - string
          - 'null'
          description: The class Valjean matched the risk to, if any.
        state:
          type: string
          description: Two-letter state code.
        unknown_lines:
          type: array
          items:
            type: string
          description: Lines in the request Valjean didn't recognise.
        how_to_send:
          $ref: '#/components/schemas/HowToSend'
      required:
      - fit
      - appetite_on_file
      - headline
      - market_count
      - market_count_label
      - lines
      - notes
      - needs
      - class_matched
      - state
    GuideSummary:
      type: object
      properties:
        key:
          type: string
        name:
          type: string
        class:
          type: string
        line:
          type: string
        line_name:
          type: string
        naics_prefixes:
          type: array
          items:
            type: string
        keywords:
          type: array
          items:
            type: string
      required:
      - key
      - name
      - class
      - line
      - line_name
      - naics_prefixes
      - keywords
    HowToSend:
      type: object
      properties:
        email:
          type: string
          description: Where to email the risk instead.
          format: email
        include:
          type: array
          items:
            type: string
          description: What to include in that email.
        note:
          type: string
      required:
      - email
      - include
      - note
      description: 'Present whenever the fit isn''t "good": a broker reviews every submission, including ones outside the appetite on file.'
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              description: Machine-readable code, e.g. invalid_request, read_only, not_found, closed, rate_limited.
            message:
              type: string
              description: Plain-English explanation you can show a person.
            issues:
              type: array
              items:
                type: string
              description: 'Validation problems, as "field: problem" (400 invalid_request only).'
          required:
          - code
          - message
      required:
      - error
    Guide:
      allOf:
      - $ref: '#/components/schemas/GuideSummary'
      - type: object
        properties:
          updated_at:
            type: string
            format: date-time
          appetite:
            type: array
            items:
              type: object
              properties:
                state:
                  type: string
                fit:
                  type: string
                  enum:
                  - good
                  - possible
                  - unlikely
                  - unknown
                headline:
                  type: string
                market_count_label:
                  type: string
              required:
              - state
              - fit
              - headline
              - market_count_label
          needs:
            type: array
            items:
              type: object
              properties:
                key:
                  type: string
                label:
                  type: string
                level:
                  type: string
                  enum:
                  - required
                  - sometimes
                  - helpful
                kind:
                  type: string
                answer:
                  $ref: '#/components/schemas/AnswerSpec'
                help:
                  type:
                  - string
                  - 'null'
                confirmed:
                  type: boolean
                  description: 'False: Valjean''s typical list, not yet confirmed with an underwriter; say "typically".'
              required:
              - key
              - label
              - level
              - kind
              - answer
              - help
              - confirmed
          disclaimer:
            type: string
        required:
        - updated_at
        - appetite
        - needs
        - disclaimer
    PublicAppetiteAnswer:
      allOf:
      - $ref: '#/components/schemas/AppetiteAnswer'
      - type: object
        properties:
          disclaimer:
            type: string
        required:
        - disclaimer
    GuideList:
      type: object
      properties:
        guides:
          type: array
          items:
            $ref: '#/components/schemas/GuideSummary'
      required:
      - guides
  responses:
    NotFound:
      description: Not found, or not your agency's (not_found). The API never says which.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: not_found
              message: Submission not found.
    BadRequest:
      description: Fix the request and send it again (invalid_request, invalid_json).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: invalid_request
              message: Check the request.
              issues:
              - 'insured.state: Too small: expected string to have >=2 characters'
    TooLarge:
      description: 'Body or file too large (too_large): JSON bodies up to 64 KB, direct uploads 4 MB, upload links 25 MB.'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: too_large
              message: Files over 4 MB need an upload link.
    RateLimited:
      description: 'Too many requests (rate_limited): 120 a minute per key, 30 a minute per IP on public endpoints, counted across all of Valjean''s servers. Wait Retry-After seconds.'
      headers:
        Retry-After:
          description: Seconds to wait.
          schema:
            type: integer
            example: 60
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: rate_limited
              message: Too many requests. Try again in a minute.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: vjk_live_… / vjk_demo_… API key, or an OAuth access token (vjat_…)
      description: 'Send the key as `Authorization: Bearer vjk_…`. Keys come from your agency portal or your Valjean broker.'
    apiKeyHeader:
      type: apiKey
      in: header
      name: X-Api-Key
      description: The same API key in an X-Api-Key header, for tools that can't set Authorization.
    oauth2:
      type: oauth2
      description: 'For AI apps that connect by signing in. Authorization code flow with PKCE (S256 only), public clients (no secret), dynamic client registration at https://api.valjeanspecialty.com/oauth/register. Discovery: https://api.valjeanspecialty.com/.well-known/oauth-authorization-server. Access tokens last 1 hour; refresh tokens 30 days and rotate on use. Tokens stop when the API key or portal link that approved them is revoked.'
      flows:
        authorizationCode:
          authorizationUrl: https://api.valjeanspecialty.com/oauth/authorize
          tokenUrl: https://api.valjeanspecialty.com/oauth/token
          refreshUrl: https://api.valjeanspecialty.com/oauth/token
          scopes:
            read: Appetite, requirements, status, quotes, documents, policies
            submit: Also send files, answers, documents, bind requests and instant-quote asks
x-event-types:
- submission.received
- submission.status_changed
- needs.requested
- markets.updated
- quote.presented
- bind.status_changed
- policy.bound
- policy.status_changed
- document.available
- invoice.sent
- invoice.paid
- invoice.overdue