Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: Valjean Specialty agent Bind 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: Bind
description: Your written instruction to bind, and its progress.
paths:
/submissions/{id}/quotes/{quoteId}/bind-request:
post:
operationId: requestBind
summary: Ask to bind a quote
description: 'Your written instruction to bind a presented quote. It never binds by itself: your broker confirms with the market and sends the binder, and you get bind.status_changed events.
Needs a **read-and-submit** key and an agency with a signed broker agreement.'
tags:
- Bind
x-valjean-scope: submit
security:
- bearerAuth: []
- apiKeyHeader: []
- oauth2:
- submit
parameters:
- name: id
in: path
required: true
description: Submission id.
schema:
type: string
format: uuid
- name: quoteId
in: path
required: true
description: A presented quote's id.
schema:
type: string
format: uuid
- name: dry_run
in: query
required: false
description: 1 to check the request and see what would happen, without saving or sending anything.
schema:
type: string
enum:
- '1'
- 'true'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
instruction:
type: string
minLength: 10
maxLength: 2000
requested_effective_date:
type: string
format: date
contact_name:
type: string
minLength: 2
maxLength: 120
contact_email:
type: string
format: email
pattern: ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
accept_subjectivities:
type: boolean
const: true
required:
- instruction
- contact_name
- accept_subjectivities
additionalProperties: false
example:
instruction: Please bind as quoted, effective 12/15/2026
contact_name: Dana Whitfield
contact_email: dana@agency.example
accept_subjectivities: true
responses:
'200':
description: 'With ?dry_run=1: every check passed and nothing was saved or sent.'
content:
application/json:
schema:
$ref: '#/components/schemas/DryRun'
headers:
RateLimit-Policy:
$ref: '#/components/headers/RateLimitPolicy'
'201':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/BindRequestCreated'
example:
bind_request_id: 9c8b7a6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d
status: preparing
status_text: 'Received: your broker is preparing it'
quote:
id: 6f1d2c3b-8a9e-4b7c-9d10-1e2f3a4b5c6d
carrier: Magnolia Underwriting Managers
premium: 17075
subjectivities: Signed application; currently valued loss runs
note: Not bound yet. Your broker confirms it with the market and sends the binder; you'll get bind.status_changed events.
headers:
RateLimit-Policy:
$ref: '#/components/headers/RateLimitPolicy'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/Conflict'
'413':
$ref: '#/components/responses/TooLarge'
'429':
$ref: '#/components/responses/RateLimited'
'500':
$ref: '#/components/responses/ServerError'
x-codeSamples:
- lang: shell
label: curl
source: "curl -X POST \"https://api.valjeanspecialty.com/api/agent/v1/submissions/SUBMISSION_ID/quotes/QUOTE_ID/bind-request\" \\\n -H \"Authorization: Bearer $VALJEAN_KEY\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"instruction\":\"Please bind as quoted, effective 12/15/2026\",\"contact_name\":\"Dana Whitfield\",\"contact_email\":\"dana@agency.example\",\"accept_subjectivities\":true}'"
- lang: javascript
label: Node
source: "const res = await fetch(\"https://api.valjeanspecialty.com/api/agent/v1/submissions/SUBMISSION_ID/quotes/QUOTE_ID/bind-request\", {\n method: \"POST\",\n headers: { Authorization: `Bearer ${process.env.VALJEAN_KEY}`, \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n \"instruction\": \"Please bind as quoted, effective 12/15/2026\",\n \"contact_name\": \"Dana Whitfield\",\n \"contact_email\": \"dana@agency.example\",\n \"accept_subjectivities\": true\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/agent/v1/submissions/SUBMISSION_ID/quotes/QUOTE_ID/bind-request\",\n headers={\"Authorization\": f\"Bearer {os.environ['VALJEAN_KEY']}\"},\n json={\n \"instruction\": \"Please bind as quoted, effective 12/15/2026\",\n \"contact_name\": \"Dana Whitfield\",\n \"contact_email\": \"dana@agency.example\",\n \"accept_subjectivities\": True\n },\n)\nprint(res.json())"
x-agentic-access:
action: create
consequence: The agent's written instruction to bind a presented quote. It does not bind.
human_in_the_loop: Your broker confirms with the market and sends the binder.
reversible: withdrawBindRequest while it's still being prepared; after that, call your broker.
confirm_with_user: true
dry_run: dry_run=1
/submissions/{id}/bind-requests:
get:
operationId: listBindRequests
summary: Bind progress
description: 'Bind requests on a submission and where each stands.
Any key (read or read-and-submit).'
tags:
- Bind
x-valjean-scope: read
security:
- bearerAuth: []
- apiKeyHeader: []
- oauth2:
- read
parameters:
- name: id
in: path
required: true
description: Submission id.
schema:
type: string
format: uuid
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/BindRequestList'
headers:
RateLimit-Policy:
$ref: '#/components/headers/RateLimitPolicy'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/RateLimited'
'500':
$ref: '#/components/responses/ServerError'
x-codeSamples:
- lang: shell
label: curl
source: "curl -X GET \"https://api.valjeanspecialty.com/api/agent/v1/submissions/SUBMISSION_ID/bind-requests\" \\\n -H \"Authorization: Bearer $VALJEAN_KEY\""
- lang: javascript
label: Node
source: "const res = await fetch(\"https://api.valjeanspecialty.com/api/agent/v1/submissions/SUBMISSION_ID/bind-requests\", {\n method: \"GET\",\n headers: { Authorization: `Bearer ${process.env.VALJEAN_KEY}`, },\n});\nconsole.log(await res.json());"
- lang: python
label: Python
source: "import os, requests\n\nres = requests.get(\n \"https://api.valjeanspecialty.com/api/agent/v1/submissions/SUBMISSION_ID/bind-requests\",\n headers={\"Authorization\": f\"Bearer {os.environ['VALJEAN_KEY']}\"},\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
/submissions/{id}/bind-requests/{bindRequestId}:
delete:
operationId: withdrawBindRequest
summary: Withdraw a bind request
description: 'Withdraws your bind request while your broker is still preparing it (status preparing), before anything goes to the market. Once it''s been sent to the market (requested), this returns 409 not_withdrawable: call your broker. Withdrawing twice is fine and returns the same result.
Needs a **read-and-submit** key and an agency with a signed broker agreement.'
tags:
- Bind
x-valjean-scope: submit
security:
- bearerAuth: []
- apiKeyHeader: []
- oauth2:
- submit
parameters:
- name: id
in: path
required: true
description: Submission id.
schema:
type: string
format: uuid
- name: bindRequestId
in: path
required: true
description: The bind request's id (from the bind request or bind progress).
schema:
type: string
format: uuid
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/BindWithdrawn'
example:
bind_request_id: 9c8b7a6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d
status: cancelled
status_text: Cancelled
note: Withdrawn. Nothing was sent to the market.
headers:
RateLimit-Policy:
$ref: '#/components/headers/RateLimitPolicy'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/Conflict'
'429':
$ref: '#/components/responses/RateLimited'
'500':
$ref: '#/components/responses/ServerError'
x-codeSamples:
- lang: shell
label: curl
source: "curl -X DELETE \"https://api.valjeanspecialty.com/api/agent/v1/submissions/SUBMISSION_ID/bind-requests/{bindRequestId}\" \\\n -H \"Authorization: Bearer $VALJEAN_KEY\""
- lang: javascript
label: Node
source: "const res = await fetch(\"https://api.valjeanspecialty.com/api/agent/v1/submissions/SUBMISSION_ID/bind-requests/{bindRequestId}\", {\n method: \"DELETE\",\n headers: { Authorization: `Bearer ${process.env.VALJEAN_KEY}`, },\n});\nconsole.log(await res.json());"
- lang: python
label: Python
source: "import os, requests\n\nres = requests.delete(\n \"https://api.valjeanspecialty.com/api/agent/v1/submissions/SUBMISSION_ID/bind-requests/{bindRequestId}\",\n headers={\"Authorization\": f\"Bearer {os.environ['VALJEAN_KEY']}\"},\n)\nprint(res.json())"
x-agentic-access:
action: delete
consequence: Withdraws a bind request before it reaches the market.
human_in_the_loop: None needed.
reversible: Ask to bind again.
confirm_with_user: true
components:
responses:
Forbidden:
description: 'Not allowed: read_only (a read key tried to submit), agency_not_active (no signed broker agreement), not_offered (instant quotes are off), forbidden (API access off for the agency). Don''t retry.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: read_only
message: This key can read only. Ask your Valjean broker for a key that can submit.
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.
Unauthorized:
description: 'Key or token missing, wrong, expired or revoked (unauthorized). Don''t retry: get a new key or sign in again.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: unauthorized
message: This API key isn't valid. Ask your Valjean broker for a new one.
Conflict:
description: 'The file''s state doesn''t allow it: closed, not_open, quote_expired, already_requested, not_withdrawable (the bind request already went to the market: call your broker), no_instant_market, limit, or ai_off (plain-sentence answers aren''t switched on; send answers by key). Don''t retry the same request.'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: closed
message: This submission is closed.
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'
ServerError:
description: Something went wrong on Valjean's side (server_error). Safe to retry later; creates are idempotent with client_reference or an Idempotency-Key header.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: server_error
message: Something went wrong. Try again shortly.
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.
schemas:
BindRequest:
type: object
properties:
id:
type: string
format: uuid
quote_id:
type: string
format: uuid
status:
type: string
enum:
- preparing
- requested
- binder_received
- bound
- cancelled
status_text:
type: string
created_at:
type: string
format: date-time
requested_at:
type:
- string
- 'null'
format: date-time
binder_received_at:
type:
- string
- 'null'
format: date-time
required:
- id
- quote_id
- status
- status_text
- created_at
- requested_at
- binder_received_at
BindRequestList:
type: object
properties:
bind_requests:
type: array
items:
$ref: '#/components/schemas/BindRequest'
required:
- bind_requests
BindWithdrawn:
type: object
properties:
bind_request_id:
type: string
format: uuid
status:
type: string
enum:
- cancelled
status_text:
type: string
note:
type: string
required:
- bind_request_id
- status
- status_text
- note
DryRun:
type: object
properties:
dry_run:
type: boolean
const: true
valid:
type: boolean
const: true
description: The request passed every check. A request that fails a check returns the same error it would without dry_run.
would:
type: string
description: What sending it for real would do.
enum:
- create
- return_existing
- request_bind
note:
type: string
required:
- dry_run
- valid
- would
- note
description: 'With ?dry_run=1: checked, nothing saved or sent. Other fields describe what would happen (the file''s insured and lines, an existing file''s id, or the quote and its subjectivities).'
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
BindRequestCreated:
type: object
properties:
bind_request_id:
type: string
format: uuid
status:
type: string
const: preparing
status_text:
type: string
quote:
type: object
properties:
id:
type: string
format: uuid
carrier:
type: string
premium:
type: number
required:
- id
- carrier
- premium
subjectivities:
type:
- string
- 'null'
note:
type: string
required:
- bind_request_id
- status
- status_text
- quote
- subjectivities
- note
headers:
RateLimitPolicy:
description: 'The rate limit for this key, as an IETF RateLimit-Policy: 120 requests (q) per 60-second window (w), counted across all of Valjean''s servers. Over it, you get 429 with Retry-After.'
schema:
type: string
example: '"per-key";q=120;w=60'
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