# Verify Client API Reference for AI Agents Purpose: help AI agents build against the Verify Client API without inferring behaviour from UI code or product PRDs. Canonical API docs: - docs/api/ai-build-reference.md - docs/api/api-integrations.openapi.yaml Public API docs: - /integrations/developer-docs - /integrations/developer-docs/llms.txt - /integrations/developer-docs/api-integrations.openapi.yaml Implementation source of truth: - api/routes/v1/microapps.ts - api/routes/v1/cases.ts - api/mcp/mcpRouter.ts - api/mcp/oauthMetadata.ts - api/auth/auth.ts - api/auth/types.ts - api/microapps/microappScopes.ts - api/microapps/sourceRestrictions.ts - shared/microapps.ts - shared/access-control.ts Current supported AI-build use case: - A firm admin creates an `api_integration` in Firm -> Apps & API access. - The external system exchanges `clientId` + one-time `clientSecret` for a scoped bearer token. - The external system can create an onboarding case, add parties, override the automatically selected primary client when needed, generate the configured Client Care Letter, and send invites when the token has the required scopes. - User-authenticated AI clients can connect to the Streamable HTTP MCP endpoint at /api/mcp and trigger OAuth via /.well-known/oauth-protected-resource/mcp. Recommended AI crawl order: 1. Read this file. 2. Read docs/api/ai-build-reference.md or /integrations/developer-docs. 3. Load docs/api/api-integrations.openapi.yaml or /integrations/developer-docs/api-integrations.openapi.yaml for schemas. 4. Inspect implementation source files only if a required endpoint or field is missing from the docs. Base URLs: - Canonical API base path: /api/v1 - Canonical MCP path: /api/mcp - MCP protected resource metadata: /.well-known/oauth-protected-resource/mcp - Production API is routed behind the deployed frontend domain; prefer relative /api/v1 from browser contexts. Auth summary: - Portal users use Cognito bearer tokens. - API integrations use POST /api/v1/microapps/system-tokens with client credentials. - Do not open /api/v1/microapps/system-tokens as a browser URL. It must be called as POST with JSON from server-side code. - Integration calls use Authorization: Bearer . - Only `tokenKind=system` MicroApp tokens can call first-party automation endpoints. - MCP clients should register /api/mcp without a static Authorization header when they should trigger user OAuth. - MCP unauthenticated requests return WWW-Authenticate with protected resource metadata; tool-level auth failures return _meta["mcp/www_authenticate"]. Important constraints: - Never store or expose `clientSecret` after creation; it is returned once. - Use the least scopes required. - Respect `allowedIpRanges` when configured. - Treat `caseId`, `partyId`, and `inviteId` as opaque strings. - Do not invent undocumented endpoints. If an endpoint is not in docs/api or api/routes/v1, inspect implementation before using it. - Generate backend/server code for client credentials. Never generate browser code that contains `clientSecret`. - Branch on HTTP status first. Treat `error.message` and `error.code` as optional. - MCP case tools return summarized records only and omit raw workflow payloads, evidence keys, and sensitive blobs. Scopes: - cases.create: POST /api/v1/cases - parties.manage: POST /api/v1/cases/{caseId}/parties, PUT /api/v1/cases/{caseId}/primary-client - client-care-letters.generate: POST /api/v1/cases/{caseId}/client-care-letter/generate - invites.send: POST /api/v1/cases/{caseId}/invites; POST /api/v1/cases/{caseId}/parties/{partyId}/reminders - cases.read: reserved for read endpoints as exposed - cases.list: reserved for list endpoints as exposed - cases.ids.list: GET /api/v1/microapps/api/cases/ids - users.list: GET /api/v1/microapps/api/users Endpoint contracts: - POST /api/mcp -> Streamable HTTP MCP server. Current tools: whoami, list_cases, search_cases, get_case. Requires authenticated user/system principal; list/search need case.list and internal users must provide orgId; get_case needs case.read and organisation scope. - POST /api/v1/microapps/system-tokens -> request clientId, clientSecret, optional scopes, optional expiresInSeconds; response accessToken, principal, expiresAt. - GET /api/v1/microapps/api/cases/ids -> requires cases.ids.list; request optional limit and cursor; response opaque case IDs and nextCursor for ledger sync/connectivity checks. - POST /api/v1/cases -> requires cases.create; request matterRef, matterType, optional tenure, propertyAddress, salePropertyAddress, purchasePropertyAddress, externalQuote, requireSof, demoMode, expiresAt, legacy fees, parties; response caseId, status, parties. For Sale & Purchase, send salePropertyAddress and purchasePropertyAddress separately; propertyAddress remains the compatibility primary address. If parties are supplied and none has isPrimaryContact=true, the first party becomes the primary client automatically. tenure can be freehold, leasehold, or unsure and is used for tenure-specific pack selection before matter-type fallback; legacy unknown/not sure/commonhold inputs normalize to unsure. Sale defaults include TA6, and leasehold Sale adds TA7 when missing from the pack. externalQuote is optional third-party/CRM quote metadata, not a Verify Client generated quote; it can include quote links/PDF references, Terms of Business links/PDF references, totals, and deposit values. Hosted quoteUrl and termsUrl appear to clients as non-blocking Quote and Terms of Business items in Forms and documents. New integrations should use externalQuote total/deposit fields rather than legacy fees. Source of funds follows the firm's saved default unless a matter-type or tenure onboarding pack row overrides it; matter types that do not support source of funds, such as Sale, omit SOF even when requireSof is true. Do not send Client Care Letter content or responsibility values by default; CCL generation uses matter data and firm defaults. Use clientCareLetter fee-earner/supervisor overrides only when status reports those values as missing for this matter. - POST /api/v1/cases/{caseId}/external-quote/presign -> requires cases.create for API integrations; request filename, contentType=application/pdf, kind=quote|terms; response uploadUrl, key, expiresIn. Upload the PDF to uploadUrl. - PUT /api/v1/cases/{caseId}/external-quote -> requires cases.create for API integrations; request externalQuote with quotePdfKey/termsPdfKey and quote/deposit metadata after uploading quote or Terms PDFs. - POST /api/v1/cases/{caseId}/parties -> requires parties.manage; request party fields with required name; mobile is optional but validated and normalized when supplied; response created party. - PUT /api/v1/cases/{caseId}/primary-client -> requires parties.manage; request partyId; marks exactly one existing case party as the primary client. Use this only when the first-party default is not the right Client Care Letter addressee. - GET /api/v1/cases/{caseId}/client-care-letter/status -> requires cases.read or client-care-letters.generate; response requirement, mode, configured template, missingFields, document, readinessStatus. - POST /api/v1/cases/{caseId}/client-care-letter/generate -> requires client-care-letters.generate; saves the generated HTML Client Care Letter. Blocks when required matter data is missing or template variables are unresolved. Generated letters are ready for invite after saving; uploaded letters still need firm review. - POST /api/v1/cases/{caseId}/invites -> requires invites.send; request partyId and optional channel/message fields; response invite metadata. If a generated Client Care Letter is required, generate it first or pass autoGenerateClientCareLetter=true with the client-care-letters.generate scope. - POST /api/v1/cases/{caseId}/parties/{partyId}/reminders -> requires invites.send; sends a one-off reminder using the firm's configured reminder templates. Only call after the original invite has been sent and while the party has not submitted/completed onboarding. - Client acknowledgement of HTML generated Client Care Letters requires a signed PDF snapshot upload via /api/v1/client/documents/{documentId}/presign-upload, followed by /api/v1/client/documents/{documentId}/ack with that snapshotKey. Unsigned legacy generated PDF keys do not satisfy acknowledgement. Duplicate safeguards: - Use a stable source-system matterRef. POST /api/v1/cases rejects duplicate matterRef values within the firm. - Use GET /api/v1/microapps/api/cases/ids only as a scoped case-ID sync/connectivity endpoint, not as full-text search. - 401 means credentials/token are missing, expired, malformed, or invalid; refresh/rotate credentials and do not treat it as duplicate uncertainty. - 403 means valid credentials lack scope, active integration status, or allowed source IP/origin configuration. - 5xx or network timeout means remote state is unknown. Block live creation unless an operator explicitly accepts local-ledger-only dedupe for that run.