MCP App Store
by KeenEthics

Overview

Quilia lets law firm users work with their private Quilia case data from ChatGPT. Users can review setup status and firm settings, manage team members, locations, branding, FAQs, case types, audiences, automations, request templates, cases, appointments, messages, requests, documents, notes, contacts, employment records, and Quilia help content using the same access they have in Quilia.

Tools

add_case_team_members

ChatGPT
Add one or more staff members to a case's team. Use this to ensure specific staff members receive case notifications or are visible as contacts for the case. This tool requires staff user IDs, which you can find using find_staff or get_firm_settings. Args: - case_id (string, required): The case ID to modify - user_ids (array of strings, required): The staff user IDs to add - contactable (boolean, optional): Whether these staff members should be visible to clients as contacts - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "success": boolean, "case_id": string, "added_count": number, "skipped_count": number, "message": string }

add_firm_symptoms

ChatGPT
Add firm-owned custom symptoms to the firm's symptom vocabulary. These are private to the firm. This does not edit Quilia's shared global symptoms. Re-adding a previously removed firm symptom reactivates it. After adding symptoms, call configure_case_type_symptoms if they should be shown only for a specific case type. Args: - symptoms: array of { label, category }, where category is physical, cognitive, emotional, or ptsd - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: { "requested": number, "added": number, "symptoms": [ ...the firm symptoms actually added or reactivated... ] }

add_org_location

ChatGPT
Add an office location to the firm. Locations are the firm's offices as clients see them; each can carry its own address, phone, hours-adjacent links and social profiles. Note that this creates secondary offices. The primary office (id 0) is already present and should be modified with update_org_location instead. Set is_virtual=true for a location with no physical address (the address fields are cleared). List existing locations first with list_org_locations so you don't create a duplicate. Pass a branding object to open the office with its own name, colors, logo and icon shown to its clients instead of the firm-level branding. That is a second request under the hood, so if it fails the location still exists — fix the branding with update_org_location rather than adding the location again. Requires an admin role.

add_team_member

ChatGPT
Add a staff member (team member) to your organization. Use this to onboard an attorney, admin, or legal staff member. Creation is idempotent on the email address. By default ('auto') an invitation email is sent only to net-new users or those who have never signed in, making repeated calls safe. Args: - name (string, required): The full name of the staff member - email (string, required): The email address of the staff member - role (enum, required): 'admin', 'attorney', or 'legal staff' - name_first (string, optional): First name - name_last (string, optional): Last name - phone (string, optional): Phone number - job_title (string, optional): Job title - location_id (number, optional): Office location ID to assign, from list_org_locations. 0 is the firm's primary office, and the only location a single-office firm has. - permission_id (string, optional): Permission set UUID to assign - invite (enum, optional): 'send' | 'skip' | 'auto' (default 'auto') — controls the invitation email - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "success": boolean, "user_id": string, "member_id": string, "message": string } Error Handling: - Returns a conflict error if a user with this email already exists - Returns an error if staff creation fails

api_call

ChatGPT
Make authenticated GET requests to Quilia API endpoints to fetch data. Use this tool to execute API requests after finding relevant endpoints via list_endpoints. Your OAuth bearer token is automatically forwarded with each request. Full endpoint reference: https://api.quilia.dev/v2 (or call list_endpoints). NOTE: This tool is read-only - only GET requests are supported. To create or change data, use the dedicated tools (create_case, send_message, update_message, send_request, create_appointment, invite_client). Args: - endpoint (string, required): API path relative to /v2/ with no leading slash and no inline query string (e.g., "cases", "clients/123") - method (enum, optional): HTTP method - GET only (default: GET) - query_params (object, optional): Query parameters as key-value pairs, used instead of putting ?key=value in endpoint - headers (object, optional): Additional headers (cannot override Authorization, Host, Content-Length) - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "success": boolean, // true if status 2xx "status": number, // HTTP status code "statusText": string, // HTTP status text "data": any, // Response body (parsed JSON or text) "rate_limit": { // Present if rate limit headers returned "limit": string, "remaining": string, "reset": string }, "error": string // Present if request failed } Examples: - endpoint="cases", method="GET" -> List all cases - endpoint="cases/123", method="GET" -> Get specific case - endpoint="appointments", query_params={"limit": "10", "status": "scheduled"} -> List appointments with filters Security: - Path traversal (..) and absolute URLs are blocked - Protected headers (Authorization, Host) cannot be overridden - Request timeout: 30 seconds Error Handling: - Returns structured error with status code and message - Network errors return status 0 - Timeouts return status 408

apply_staff_rule

ChatGPT
Re-run the integration's staff-on-case rule on the firm's existing cases now, instead of waiting for the daily check (which reaches each open case about once a week). Use it right after changing staff_assignments with update_integration_settings. ALWAYS PREVIEW FIRST. dry_run defaults to true: every case's CMS staff sync runs with its writes skipped, and the result lists per case who would be added (name, CMS role, visible to the client or not), removed, made visible or hidden, and which CMS staff would get a new Quilia account and team invite. Show the user that preview, confirm with them, and only then call again with dry_run false to apply. Never apply without that confirmation. Covers cases that are not closed. A firm with many open cases is previewed on its newest ones (the result says how many of how many); applying covers all of them. Pass case_ids to check a few specific cases first. Both runs go through the same sync the daily reconcile and every import use. Applying adds and removes staff on cases (removing only people a CMS sync placed, never anyone added by hand) and can email a team invite to CMS staff who have no Quilia account. It never messages clients. The run can take longer than one call. If the result says it is still running, call again with the run_id it gives instead of starting another. Requires an admin role at the firm. Args: - cms_type, cms_id (string, required): From get_integration_settings. - case_ids (string[], optional): Only these Quilia cases. - dry_run (boolean, default true): Preview only. - run_id (string, optional): Read a run started earlier.

attach_provider_listing

ChatGPT
Attach an already-claimed listing to an existing verified provider practice organization. Quilia wizard accounts only. This moves a claimed directory listing from its isolated provider org into an existing verified practice org (merging the orgs). This is useful when a practice is claimed and its affiliated sub-listings (e.g. doctors in the practice) need to join the same provider org. Not firm-scoped: it writes to the provider directory, not to the firm currently in context, so it takes no org_id. Args: - listing (string, required): The listing id, or its directory URL (directory.quilia.com/p/<id>) or admin URL. - target_org_id (string, required): The ID of the verified provider practice organization. Returns: For JSON format: { "success": boolean }

configure_case_type_symptoms

ChatGPT
Set the complete symptom list clients should see for one case type. Call list_case_type_symptoms first to get exact keys. This replaces the case type's explicit selection with the symptom_keys you provide. Send an empty array to clear the explicit case-type list; the app will then use the firm's normal symptom list. Args: - case_type (string): Case type identifier, e.g. 'carbon monoxide poisoning' - symptom_keys (string[]): Complete list of selected symptom keys - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: { "case_type": string, "selected_count": number, "selected_symptom_keys": string[], "hidden_symptom_keys": string[], "unknown_symptom_keys": string[] } Hidden symptom keys are ignored and returned in hidden_symptom_keys. Unknown symptom keys are ignored and returned in unknown_symptom_keys.

configure_case_types

ChatGPT
Turn case types on or off for the firm, and set their per-type options. Call list_case_types first — you need the exact case_type strings, and you should see what is already on before changing anything. Turning a case type OFF stops it being selectable on new cases; it does not touch cases that already use it. Target one case type, several, or a whole category. Trimming a firm down to what they actually file is one call per category, not one per case type. Only the options you pass are changed; everything else keeps its current value. Args: - case_types (string[], optional): Case types to configure. Give this or category, not both. - category (string, optional): Configure every case type in an Areas of Law category, e.g. 'Immigration'. Give this or case_types, not both. - enabled (boolean, optional): Whether the firm uses these case types - is_default (boolean, optional): Use as the fallback config for case types without their own. Only valid when targeting exactly one case type — a firm has one fallback. - inactive_reminders_enabled (boolean, optional): Remind clients on these case types who haven't opened the app in 7 days - ssn_enabled (boolean, optional): Show these clients an SSN field in their profile - journal_prompts_enabled (boolean, optional): Show these clients AI follow-up prompts on the journal screen - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "success": boolean, "updated": number, "case_types": [ ...the resulting configuration for each case type... ], "message": string } Not covered here: required documents, request templates, and which app screens clients see. Those are portal-only — hand the user the settings_url from the response. Error Handling: - Returns a forbidden error unless you hold an admin role at the firm - Returns an error if both or neither of case_types and category are given - Returns an error naming any case type that is not in the Quilia catalog - Returns an error if is_default is set on more than one case type

create_appointment

ChatGPT
Schedule a new appointment on a case (a medical visit or a legal calendar event). Use this to book an appointment for a case. The creator is derived automatically from your authenticated identity — you do not set it. Provide the assigned staff member via user_id if the appointment should be owned by a specific person. Args: - case_id (string, required): The case ID to associate the appointment with - start (string, required): ISO 8601 start timestamp (e.g. "2026-06-10T15:00:00.000Z") - end (string, optional): ISO 8601 end timestamp. Required when type is 'legal'. - title (string, optional): Title/name of the appointment (recommended for legal) - notes (string, optional): Additional notes or details - type (enum, optional): 'medical' (default) or 'legal' - user_id (string, optional): The user ID of the staff member to assign the appointment to - contact_id (string, optional): The contact/people ID involved (e.g. the medical provider) - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "success": boolean, "appointment_id": string, "start": string, "message": string } Error Handling: - Returns an error if end is missing for a 'legal' appointment - Returns an error if appointment creation fails

create_audience

ChatGPT
Create a reusable audience — a named definition of who a message goes to. An audience is not a list of people. It is a set of conditions resolved at send time, so a client who qualifies next month is included next month without anyone editing anything. Read the valid values FIRST with get_audience_options. Case phases are whatever this firm's own data contains; case types are the ones the firm has switched on in Settings → Case Types, which is NOT the same as the ones it currently has cases in. A guessed value is not an error — the audience just matches nobody. Use list_audiences to see what already exists and reuse one rather than creating a near-duplicate. Leaving case_type out matches every case type. Only add a case_type condition to NARROW to a few. Shape: a person matches if they match ANY group, and within a group ALL conditions must match. Multiple values in one condition are OR. Groups cannot nest. Got the conditions wrong? Use update_audience — do not delete and recreate.

create_automation

ChatGPT
Create an automation — a standing rule that turns a date into a message. Who (an audience or picked list), when (a trigger date plus an offset), how often, what to say, and how it reaches them. IT IS CREATED PAUSED AND SENDS NOTHING. The result includes a link to the firm's Automations list, where a human reviews it and switches it on. Always give the user that link — an automation nobody turns on is an automation that does nothing. Once switched on it only reaches people whose trigger date is still ahead of them. It never messages the firm's history: turning on "when the case closes" does not message every already-closed case. For a specific person, use find_client to get their user_id, create_picked_list with that one id, then pass the returned list id as group_id. The list is fixed until edited. IT SENDS AS THE FIRM unless you pass send_as. That is almost always right for an onboarding or check-in sequence — clients know the firm, not the individual who built the automation. Pass a staff id only when the firm asks for a named person on it, and say whose name will be on it when you report back. It must be someone who WORKS AT THE FIRM: clients, staff at other firms, and Quilia support (wizard) accounts are all rejected — a Quilia person's name must never appear on a firm's message to its own client. IT SENDS ON ALL THREE CHANNELS BY DEFAULT — push, SMS and email. Push is always on and cannot be turned off, because the app is the only client surface Quilia has. Use channels only to NARROW that: {"sms":"fallback"} so clients with the app do not also get a text, or {"email":false}. The result states what it will actually send on, so you never have to guess. The message body is MARKDOWN — bold, italic, links, and lists that nest if you indent them. Headings, images, tables and raw HTML are rejected rather than shown to a client as literal characters. A YouTube link alone on its own line embeds a player, and Quilia publishes six phase explainers in English and Spanish worth reaching for when the message is about a case moving phase. See the message_body argument for those, the merge variables and the in-app links you can use. If the firm has clients who read another language, write the translations yourself and pass them in translations. A language you leave out is not a skipped client: they receive the English.

create_case

ChatGPT
Create a new case with an existing or new client. Creates a case in the system with support for creating a new client inline. This is useful for intake workflows where you need to create both a case and client simultaneously. Args: - type (string, required): The type of case (e.g., "motor vehicle collision", "slip and fall") - client_id (string, optional): ID of an existing client to associate. Required when new_client is omitted. - new_client (object, optional): New client details. Required when client_id is omitted. When provided, this object must include name and phone. - name (string, required inside new_client): Full name of the client - phone (string, required inside new_client): Phone number of the client - email (string, optional): Email address - name_first (string, optional): First name - name_last (string, optional): Last name - language_code (string, optional): Language preference (e.g., "en", "es") - phase (string, optional): Initial phase of the case - status (enum, optional): Case status - "pending", "open", or "closed" - opened_at (string, optional): ISO 8601 timestamp for when case was opened - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Note: Either client_id or new_client must be provided. Returns: For JSON format: { "success": boolean, "case_id": string, "status": string, "type": string, "phase": string, // Optional "members": [{ "user_id": string, "status": string, "member_id": string }], "opened_at": string, // Optional "created_at": string, // Optional "message": string } Examples: - type="motor vehicle collision", client_id="uuid" -> Create case for existing client - type="slip and fall", new_client={name: "John Doe", phone: "+1234567890", email: "john@example.com"} -> Create case with new client - type="dog bite", new_client={name: "Jane Smith", phone: "+1234567890"}, phase="intake" -> Create case with phase Error Handling: - Returns error if neither client_id nor new_client is provided - Returns error if case creation fails

create_draft_broadcast

ChatGPT
Create a broadcast message as a draft that DOES NOT SEND. Use this to draft a broadcast message to a saved audience or picked list, so a human can review, edit, and send it from the Quilia portal. This is the correct tool for drafting announcements, newsletters, or any message targeting a group of clients. IT SENDS NOTHING. The message is saved as a draft. The response includes a link to the draft in the portal. Hand that link to the user and tell them it is ready for review and will not send until they click Send in the portal. The message body is MARKDOWN and supports merge variables (e.g., {{client_first_name}}), the same as automations. Args: - audience_id (string, optional): The audience ID to target (from list_audiences) - group_id (string, optional): The picked list ID to target (from list_picked_lists) - subject (string, required): Subject line of the broadcast - body (string, required): Body content of the broadcast (markdown format) - channels (object, optional): Delivery preferences: sms ('always' | 'fallback' | 'never') and email (boolean). - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Note: Provide exactly one of audience_id or group_id. Returns: For JSON format: { "success": boolean, "message_id": string, "portal_url": string, "message": string }

create_form_template

ChatGPT
Build a form the firm can send to clients — an intake questionnaire, a symptom diary, a treatment history. Saved to the firm's template library and sent with send_request. Write the questions the way the client will read them: plain language, one thing per question. Call list_form_templates first so you don't rebuild something the firm already has. Choosing a field type: - text one line (a name, a medication) - textarea several lines (describe what happened) - select a fixed set of choices; you must give options - date / time / datetime - file the client uploads something (a photo, a bill) - signature captures a signature - richtext formatted text - ssn a Social Security number Never ask for a Social Security number with 'text'. Use 'ssn': it encrypts the answer onto the client's own record and keeps it off the submission, the completion PDF, the exports and everything the form feeds. A 'text' field would store the number in all of them. Only one 'ssn' field per form is captured, and it always belongs to the client filling the form — there is no way to collect a spouse's or a third party's. Mark a question required only when the form is useless without it — a required question the client cannot answer blocks the whole submission. Args: - title (string, required): The form title the client sees - description (string, optional): Instructions shown above the questions - fields (array, required): The questions, in order. Each: { type, label, description?, placeholder?, required?, options? } - generate_pdf_on_completion (boolean, optional): Produce a PDF of the answers on submit - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "success": boolean, "template": { ...the saved form, with field ids... }, "message": string } Error Handling: - Returns an error naming any select field given without options - Returns an error if options are given on a field type that has none

create_picked_list

ChatGPT
Create a fixed list of one or more clients. Use this when the firm names specific people rather than a rule such as case status or case type. First use find_client to get the client's user_id, then create the list and pass the returned id as group_id to create_automation. The list does not change until someone edits it. The list is created immediately, but any automation created from it starts PAUSED and still requires a human to review and activate it.

create_task

ChatGPT
Create and send a freeform one-off action task to a client — sent from you, the authenticated user. This tool atomically resolves your identity as the sender and creates an ad-hoc task with an inline title (and optional description). It does NOT require a pre-existing request template. Use it when you need to ask a client to do something specific that isn't covered by the firm's saved templates. Args: - title (string, required): The task title shown to the client - description (string, optional): Optional instructions shown with the task - case_id (string, required): Case ID to associate the request with - recipient_id (string, required): User ID of the client who will receive the request - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "success": boolean, "request_id": string, "status": string, "task_title": string, "message": string } Examples: - title="Please call the office", case_id="uuid", recipient_id="client-uuid" -> Send a simple task - title="Review settlement offer", description="Check your email for the PDF", case_id="uuid", recipient_id="client-uuid" -> Send a task with details Error Handling: - Returns error if authentication fails - Returns error if task creation fails

delete_automation

ChatGPT
Delete an automation and cancel every send it still had queued. GET THE USER TO SAY SO FIRST. confirmed must be true, and it is not a formality: this removes the rule and cancels its pending sends, and neither comes back. If you are working from "we don't want that one", ask plainly before calling this. Messages it has ALREADY SENT are untouched. Those are a record of what a client received, and deleting the rule does not unsend them — say so, because "delete the automation" is easily heard as undoing its history. Prefer update_automation with pause: true when the user might want it back. Pausing stops it sending and keeps the copy; deleting does not. Args: - automation_id (string, required): The automation to delete - confirmed (true, required): The user has confirmed Returns: { "id": string, "deleted": true, "cancelled_messages": number }

delete_org_location

ChatGPT
Permanently remove one of the firm's office locations. This cannot be undone — confirm with the user first, and prefer correcting a location with update_org_location when the office still exists. Deleting a location also silently unassigns every staff member assigned to it (their office becomes empty — the database sets those references to null), so check team assignments first and expect to re-assign people if the office was in use. Get location_id from list_org_locations. The primary office (id 0) cannot be deleted. Requires an admin role.

detach_provider_listing

ChatGPT
Detach a mistakenly affiliated listing from its provider org, dropping it back to an unclaimed state. Quilia wizard accounts only. Not firm-scoped: it writes to the provider directory, not to the firm currently in context, so it takes no org_id. Args: - listing (string, required): The listing id, or its directory URL (directory.quilia.com/p/<id>) or admin URL. Returns: For JSON format: { "success": boolean }

disable_team_member

ChatGPT
Deactivate a staff member, keeping their record. They can be reactivated later. This sets the staff member to inactive (active = false) — it does NOT hard-delete them, so their history and associations are preserved. Use this when someone leaves the team or should lose access without erasing the record. Args: - user_id (string, required): The user ID of the staff member to deactivate - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "success": boolean, "user_id": string, "member_id": string, "active": false, "message": string } Error Handling: - Returns a not_found error if the staff member doesn't exist or you lack access - Returns an error if the update fails

discover_contact_types

ChatGPT
Read the words the firm's own CMS uses for the contacts on its cases, with how many contacts carry each and example contact names, so they can be routed with update_integration_settings.contact_type_mapping. The same reading as the Import button on the integration page. Read-only on the CMS; it stores the reading on the integration and never changes the firm's mapping. Filevine returns three kinds of word: project roles, person types and hashtags. A Filevine contact matches a mapped word when its project role, one of its person types, or one of its hashtags is that word. Neos returns its involvement types. A word that is mapped nowhere is not imported. The examples are the point: words read misleadingly cold (a firm's "Info Req" can be entirely hospitals), so judge each by its example names before mapping it. Can take up to a minute. If the result says it is still running, call again with the run_id it gives. Requires an admin role at the firm.

find_client

ChatGPT
Find a client by name or email to get their user_id. Searches your firm's clients (the people on cases — not staff). Use this when you only know a client's name or email and need their user_id for another operation, such as inviting them to a case or sending them a message. Args: - name (string, optional): Name to search (fuzzy match) - email (string, optional): Email to search (case-insensitive, partial match) - limit (number, optional): Maximum results to return, 1-50 (default: 10) - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Note: Either name or email must be provided. Returns: For JSON format: { "clients": [ { "user_id": string, // Unique user identifier "name": string, // Client's display name "email": string, // Client's email (if available) "phone": string // Client's phone (if available) } ], "count": number // Number of results returned } Examples: - name="John" -> Find clients named John - email="john@example.com" -> Find the client with that exact email - name="Smith", limit=5 -> Find up to 5 clients whose name contains Smith Error Handling: - Returns an error if neither name nor email is provided - Returns empty results if no clients match the search criteria

find_client_cases

ChatGPT
Resolve a client by name (or email) and return their case(s) — the fastest way to answer "what's going on with X's case?". PREFER THIS over find_client + api_call/list_endpoints for any case lookup that starts from a person. Behavior (driven by the 'result' field so you know what to do next): - 'no_client_match' -> no client matched; tell the user. - 'multiple_client_matches'-> several people matched; ask which (see 'candidates'). - 'no_cases' -> the client has no cases on file. - 'one_case' -> the client's single case PLUS its summary is included inline ('summary'); just answer. - 'multiple_cases' -> ask which case (see 'cases' with type/status/phase/opened); then call get_case_summary with the chosen case_id. Args: - name (string, optional): Client name (case-insensitive, partial match) - email (string, optional): Client email (case-insensitive, partial match) - client_id (string, optional): Known client user_id (skips the name lookup) Provide at least one of name / email / client_id. Returns (JSON): { "result": string, // one of the values above "client": { "user_id", "name", "email", "phone" } | null, "candidates": [ ...client objects ], // when multiple_client_matches "cases": [ { "case_id", "type", "status", "phase", "opened_at" } ], "summary": string | null // the case summary, only on 'one_case' } Examples: - name="Thidarat Pickne" -> her single case + summary in one call - name="Smith" -> likely 'multiple_client_matches' or 'multiple_cases' to disambiguate

find_staff

ChatGPT
Find a team member (staff) by name or email to get their user_id. Searches your firm's staff — attorneys, admins, and legal staff (not clients). Use this when you only know a team member's name or email and need their user_id, e.g. to assign them to an appointment or deactivate them. This is a LOOKUP, not a listing: a name or an email is required. To see the whole team, call get_firm_settings. Args: - name (string, optional): Name to search (fuzzy match) - email (string, optional): Email to search (case-insensitive, partial match) - limit (number, optional): Maximum results to return, 1-50 (default: 10) - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Note: Either name or email must be provided. Returns: For JSON format: { "staff": [ { "user_id": string, // Unique user identifier "name": string, // Team member's display name "email": string, // Team member's email (if available) "phone": string // Team member's phone (if available) } ], "count": number // Number of results returned } Examples: - name="Jane" -> Find team members named Jane - email="jane@firm.com" -> Find the team member with that exact email - name="Smith", limit=5 -> Find up to 5 team members whose name contains Smith Error Handling: - Returns an error if neither name nor email is provided - Returns empty results if no team members match the search criteria

get_article

ChatGPT
Fetch the full content of a specific knowledge base article by its slug. Use this tool after search_knowledge_base to retrieve the complete article body when you need detailed steps or information to answer the user's question. Args: - slug (string, required): The URL slug of the article (returned by search_knowledge_base) Returns: Full article content including title, body text, metadata, and URL. Examples: - slug="how-to-set-up-filevine" -> Get the full Filevine setup guide - slug="billing-faq" -> Get the complete billing FAQ article

get_audience_options

ChatGPT
The values this firm can build audience conditions on. Read this BEFORE creating or updating an audience: - Case phases are per-firm strings taken from the firm's own data, so a guessed value simply matches nobody. - Case types are the ones the firm has switched on in Settings → Case Types. - Case statuses are the full set rather than the ones in use. Args: - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "case_status": ["open", "closed", ...], "case_phase": ["Intake", "Treatment", ...], "case_type": ["Auto Accident", "Slip and Fall", ...] }

get_case

ChatGPT
Get a case's structured detail — status, type, phase, members — plus its incident record (date of injury, location) in one call. Use this for exact facts (member statuses, phase, injury date) when the narrative summary isn't enough; use get_case_summary for the "what's going on" read. Args: - case_id (string, required): The case id (UUID) Returns (JSON): { "found": boolean, "case": { "id", "type", "status", "case_phase", "opened_at", "created_at", "members": [ { "user_id", "status", "case_phase" } ] } | null, "incident": object | null // accidents row: date_of_injury, location, narrative (null if none) } Error Handling: - Throws if the case does not exist or is not in your organization.

get_case_appointments

ChatGPT
List a case's appointments — the treatment / visit timeline (scheduled, attended, status). The "is the client treating, what's coming up" read. To add an appointment, use create_appointment. Args: - case_id (string, required): The case id (UUID) Returns (JSON): { "case_id": string, "total": number, "appointments": [ { "id", "start", "end", "status", "notes", "created_at" } ] }

get_case_contacts

ChatGPT
List the people and companies on a case — medical providers (with their treatment status: "treating" / "complete"), witnesses, adjusters, emergency contacts, plus insurance companies and body shops. The "who's on this case / who do I call / who's treating the client" read. Args: - case_id (string, required): The case id (UUID) - kind ('people' | 'company', optional): limit to one bucket - contact_type (string, optional): filter by category (e.g. "provider", "witness") Returns (JSON): { "case_id": string, "total": number, "people": [ { "id", "name", "type", "relationship", "treatment_status", "phone", "email", "city", "state" } ], "companies": [ { "id", "name", "type", "phone", "city", "state", "police_report_number" } ] } Error Handling: throws if the case does not exist or is not in your organization.

get_case_documents

ChatGPT
List a case's documents with each one's "case-manager memo" — a 1–2 sentence note of what matters in that document (a lab result's finding, a record's key facts), auto-generated from the document's text. Use this to load granular per-document context for a case (e.g. before drafting, or to cross-check the case summary against the underlying records). Args: - case_id (string, required): The case id (UUID) Returns (JSON): { "case_id": string, "total": number, // documents on the case "with_memo": number, // how many have a memo "documents": [ { "id": string, "filename": string | null, "label": string | null, // e.g. "lab result", "medical record" "description": string | null, "analysis_status": string | null, "memo": string | null, // the per-document case-manager note (null if not analyzed yet) "created_at": string | null } ] } Error Handling: - Throws if the case does not exist or is not in your organization.

get_case_employment

ChatGPT
List a case's employment records — the basis for a lost-wages / lost-earning-capacity claim (employer, job title, supervisor, which is primary). Args: - case_id (string, required): The case id (UUID) Returns (JSON): { "case_id": string, "total": number, "employments": [ { "id", "job_title", "is_primary", "company": { "name" }, "supervisor": { "name" } } ] }

get_case_notes

ChatGPT
List a case's notes — the client's own running account of the case ("attorney updates"), newest first. Read-only: clients author these, the firm reads them. Read this for the client's narrative alongside the firm-side summary. Args: - case_id (string, required): The case id (UUID) Returns (JSON): { "case_id": string, "total": number, "notes": [ { "id", "notes", "prompt_topic", "created_by", "created_at" } ] }

get_case_summary

ChatGPT
Get a case's living AI summary (the "case memory" digest): a concise, always-current read of the matter — status, the Liability / Coverage / Damages picture, what's been done and what it still needs. This is the right tool for "what's going on with this case?" when you already have a case_id. Use find_client_cases first if you only have the client's name. Args: - case_id (string, required): The case id (UUID) Returns (JSON): { "found": boolean, // false when the case has no summary generated yet "case_id": string | null, "summary": string | null, // the human-readable digest "structured": object|null, // machine digest: phase, assessment[], client_actions, firm_actions, open_questions, risk_flags, engagement "model": string | null, "updated_at": string | null } Error Handling: - Throws if the case does not exist or is not in your organization. - found:false (not an error) when the case exists but hasn't been summarized yet.

get_case_team

ChatGPT
The case team: the staff assigned to a specific case, not the clients on it. Use this to see who is receiving case notifications, and who is visible to the client as a contact, before changing the team. Everyone on the team gets the case's notifications; visible_to_client is a separate question, whether the client sees them in the app as a contact on the case (contactable is the same flag under its old name). Each member shows their organization role and, when they were imported from the firm's CMS, their role on the case there (cms_role, e.g. Responsible Attorney).

get_current_user

ChatGPT
Get the authenticated user's full context including user_id. Returns the current user's identity and organization context. Use this tool to get your own user_id for operations that require identifying yourself (e.g., sending messages as yourself). The organization is the firm the next call will act on. Pass its org_id to every write tool. For a Quilia wizard one connection works across every firm, and a switch_org made anywhere else (another assistant session, the portal) moves this connection too; a write whose org_id no longer matches is refused until you switch back. Args: - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "user_id": string, // Unique user identifier "name": string, // User's display name "email": string, // User's email address "org_id": string, // Organization identifier "organization": string // Organization display name } Examples: - No args -> Get current user info in markdown format - response_format="json" -> Get current user info as structured JSON Error Handling: - Returns "Authentication required" if no valid OAuth token is provided - Returns an error if required identity claims are missing from the token

get_firm_settings

ChatGPT
Everything the firm has configured: profile, contact details, hours, branding, the full team with roles, office locations, and client FAQs. One call. Each team member carries auto_add_to_cases and auto_add_visible_to_client. TWO DIFFERENT WAYS staff get on cases; do not confuse them: - update_team_member auto_add_to_cases ("Add to every case" in Settings > Team) FORCES the person onto every case, regardless of any CMS project assignment. Use it for a firm with no CMS, or for someone who is not on the CMS project team but needs every case's notifications (an office manager, say). auto_add_visible_to_client decides whether clients see them. - update_integration_settings staff_assignments.overrides only applies on a case where the CMS lists that person on the project team; it never adds anyone the CMS did not list. To put someone on every case regardless, use update_team_member auto_add_to_cases. USE THIS whenever you are asked to go over, review, or check a firm's settings — including "team members", "who is on the team", or any question about the firm's own configuration rather than a case. It is the only way to LIST the team: find_staff requires a name or an email and cannot enumerate. It reports what is NOT set as well as what is, because the blanks are usually the point. Pairs with get_setup_status: that one says which areas are incomplete, this one says what the values actually are. Reach for both when onboarding a firm. Three areas are deliberately not here, because they have their own tools: - Case types -> list_case_types - Intake forms -> list_form_templates - Integrations -> get_integration_settings Do not reconstruct any of this with api_call. If a section reads "Could not read", that is a real failure — say so rather than reporting the section as empty. Args: - response_format ('markdown' | 'json', optional): Output format (default: 'markdown'). Use 'json' when you need the FAQ answers, which markdown omits. Returns: For JSON format: { "profile": { "name": string, "phone": string | null, ... } | null, "team": [ { "user_id": string, "name": string | null, "email": string | null, "phone": string | null, "job_title": string | null, "role": string | null, "active": boolean | null, "location_id": number | null } ], "team_total": number | null, "locations": [ { "id": number, "name": string | null, ... } ], "faqs": [ { "id": string, "order": number, "question": string, "answer": string } ], "unavailable": { "<section>": "<why it could not be read>" } }

get_integration_settings

ChatGPT
The firm's complete, non-secret integration configuration: connected CMS and other services, connection health, auto-invite and phase-message behavior, feed settings, and the raw service-specific config used for field mappings, staff selection, imports, syncs, and automations. USE THIS during onboarding or integration troubleshooting. Integration settings are often the most detailed part of a firm's setup, and get_setup_status only says whether a CMS is connected. This tool shows the actual values. Credentials, sessions, access tokens, API keys, and encrypted values are never returned. staff_assignments holds the staff-on-case rules, including per-person overrides. TWO DIFFERENT WAYS staff get on cases; do not confuse them: - update_team_member auto_add_to_cases ("Add to every case" in Settings > Team) FORCES the person onto every case, regardless of any CMS project assignment. Use it for a firm with no CMS, or for someone who is not on the CMS project team but needs every case's notifications (an office manager, say). auto_add_visible_to_client decides whether clients see them. - update_integration_settings staff_assignments.overrides only applies on a case where the CMS lists that person on the project team; it never adds anyone the CMS did not list. To put someone on every case regardless, use update_team_member auto_add_to_cases. Pass cms_type when the user names a service. Omit it to review every configured integration. The response includes a settings_url for changes that still require the portal. Args: - cms_type (string, optional): Integration service identifier, e.g. filevine, clio, or mycase. - response_format ('markdown' | 'json', optional): Output format (default: 'markdown'). Returns: For JSON format: { "integrations": [ { "cms_id": string, "cms_type": string, "name": string | null, "enabled": boolean, "connection_status": string | null, "auto_invite": boolean | null, "feed": unknown, "config": unknown, "settings_url": string, ... } ] }

get_sample_client

ChatGPT
The firm's sample client: a fake client the firm's staff sign in to the Quilia app as, to see exactly what their clients see. Returns the sign-in number, the 6-digit code (no text is sent; it is typed into the app), whether it is enabled, and its cases. Every onboarding call needs this: give the user the number and code so they can open the app on their own phone. If the firm has none, set one up with set_up_sample_client. Requires an admin role at the firm.

get_setup_status

ChatGPT
The firm's onboarding checklist — what is configured and what is still missing. LEAD ONBOARDING WITH THIS. It is the cheapest way to find out where a firm actually is, and it names the next action for every area that is incomplete. This reports each area as complete or not; it never reports the VALUES. When you need those — "go over their settings", "who is on the team" — call get_firm_settings. Each item that you can act on has a tool behind it: - Firm profile -> update_org_settings - Team members -> add_team_member, update_team_member - Locations -> list_org_locations, add_org_location, update_org_location - Case types -> configure_case_types - Client FAQs -> set_org_faqs - Intake forms -> create_form_template - Task lists -> create_task_list_template (a reusable template, e.g. "New MVA case"). Not create_task_list: that drafts a list on one case and does not complete this item. - Client features -> update_org_settings.client_features (admins and wizards). These toggles decide what a client can do in the app, so confirm with the firm before changing one. One you cannot finish yourself — report it and tell the user where to go: - CMS connection -> Inspect existing values with get_integration_settings, then send the user to its settings_url for the OAuth sign-in or any portal-only changes. Do not treat an incomplete item as a problem to fix silently — ask. A firm may have turned something off on purpose. Args: - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "checklist": [ { "key": string, "label": string, "complete": boolean, "summary": string, "hint": string | null } ] }

import_by_phase

ChatGPT
Import every open CMS project in the chosen phases that Quilia does not already have, as clients and cases, optionally inviting each client. The same import as the portal's Import by phase window. Filevine only; phase ids come from list_cms_phases. ALWAYS PREVIEW FIRST and get the user's explicit go-ahead. Without confirm true nothing happens and the result is the preview: how many clients would be imported, how many are already in Quilia and skipped, whether clients will be invited, and roughly how long it takes. Tell the user the count and whether their clients will be texted, and wait for them to say go before calling again with confirm true. send_invite true texts or emails every imported client an invitation to Quilia. Invitations only go out between 8am and 8pm in each client's local time, so some arrive the next morning. send_invite applies to this import only; it never changes the firm's invite-on-import setting. A confirmed import needs a stored count (list_cms_phases with recount true). One import per firm runs at a time. Requires an admin role at the firm.

invite_client

ChatGPT
Invite a client (or staff member) to a case. THIS SENDS A TEXT MESSAGE to the invited person. Before calling, state exactly which client will be invited and the phone number the text will go to (look it up via find_client / find_staff or the case details), and get the user's explicit go-ahead. Never invite silently. The invited user gets an SMS prompting them to join the case in the Quilia app. Args: - case_id (string, required): The case ID to invite the user to - user_id (string, required): The user ID of the client/staff member to invite (resolve this first) - role (enum, optional): 'client' (default) or 'staff' - contactable (boolean, optional): Whether the invited user's contact info is visible to clients - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "success": boolean, // true only if an invite was sent, scheduled or queued "case_id": string, "invited_users": [{ "user_id": string, "case_id": string, "status": string, "role": string, "scheduled_at": string }], "skipped": [{ "user_id": string, "reason": string, "invited_at": string, "detail": string }], "message": string // what happened to each person, in words } Notes: - Works right after create_case: a client create_case added (a "pending" member, never invited) gets the invite. - A client already invited is NOT re-sent; they come back in skipped as "already_invited" with the date. - status "scheduled" means the text is held to the client's local daytime window and goes out at scheduled_at. - success false means nobody was texted. Say so; never tell the user an invite went out when success is false. - Staff are added to the case but are not texted. Error Handling: - Returns an error if the case is not found or you lack access - Returns an error if the invite fails

list_audiences

ChatGPT
The firm's audiences — the saved groups of clients an automation sends to. Use this before create_automation to reuse an audience the firm already has rather than building a second one that means the same thing. It returns each audience's matching rules in plain English, which is how you tell two similarly-named ones apart. "Built in" audiences ship with Quilia and belong to every firm; the rest were made by this one. Args: - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "audiences": [ { "id": string, "name": string, "description": string | null, "is_system": boolean, "rules": string[] // one per condition group; groups are ORed, conditions within are ANDed } ], "count": number }

list_automations

ChatGPT
Every automation the firm has, what triggers it, and whether it is running. START HERE for anything about a firm's automations — reviewing them, changing one, turning one off. It carries the id that update_automation and delete_automation need, so reaching for api_call to find an automation first is never necessary. RUNNING means it is messaging clients now. Paused means it exists and does nothing. Say which when you report one: it is the difference a firm actually cares about. READ THIS BEFORE CHANGING ANY TRANSLATION. Every automation carries its full copy and every language it has been translated into. update_automation's translations REPLACES the whole set, so fixing one language means sending all of them back — and the ones you do not send are deleted, taking a firm's hand-written copy with them. This tool is where you get the rest. The copy comes back as markdown, which is exactly what update_automation accepts, so a language you are not changing can be passed straight through unedited. A PHASE automation (anchor case_phase_changed) stores no phase of its own. It fires on every phase change, and its audience's case_phase is <phase> condition decides which phase reaches it. phases and the trigger sentence are read from that audience; an empty phases means it really does fire on any phase change. To retarget one, point it at a different audience. STALE means the English was edited after that translation was written, so the two now say different things. It is not an error and the firm still receives the translation — it is a prompt to rewrite it. null means nobody knows: it was written before this was tracked. Args: - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "automations": [ { "id": string, "name": string, "trigger": string, // plain English, e.g. "30 days after the case closes" // or "1 day after the case enters phase 'PI - Treating'" "phases": string[] | null, // case_phase_changed only: the phases it fires on. // [] = every phase change. null on other anchors. "audience_id": string | null, "audience_name": string | null, // often enough on its own; list_audiences has the conditions "group_id": string | null, // a picked list instead of an audience "send_time": string | null, // null for one that fires on the event itself "message_subject": string | null, "message_body": string, // markdown, ready to send back to update_automation "channels": { // effective, not stored: nothing stored reads back as all three "sms": "always" | "fallback" | "never", "email": boolean }, "translations": [ { "language": string, // e.g. "es", "ht", "vi", "zh_CN" "message_subject": string | null, "message_body": string, // markdown, same as above "stale": boolean | null // true = the English moved on; null = unknown } ], "active": boolean } ], "count": number, "active_count": number } A language absent from translations is not a gap to fill in silently — those clients receive the base copy. Ask before writing a language a firm never asked for.

list_case_type_symptoms

ChatGPT
List the firm's symptom vocabulary, and optionally show which symptoms are selected for one case type. Use this before configuring case-type symptoms so you have the exact symptom keys. The list includes shared Quilia symptoms plus firm-owned custom symptoms. Hidden symptoms are omitted unless include_hidden is true. Args: - case_type (string, optional): Case type identifier, e.g. 'carbon monoxide poisoning' - include_hidden (boolean, optional): Include symptoms hidden by this firm - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: { "custom_symptoms_enabled": boolean, "case_type": string | null, "selected_count": number, "symptoms": [ { "key": string, "label": string, "category": string, "source": "quilia" | "firm", "hidden": boolean, "case_types": string[], "selected_for_case_type": boolean | undefined } ] } If Custom Symptoms is off for the firm, the API returns an error; enable that firm feature before editing symptom vocabulary or case-type symptom lists.

list_case_types

ChatGPT
List the case types the firm uses, grouped by Areas of Law category. A firm's case types drive what a client sees in the app — which symptom lists, which screens. Most firms are set up with all of them and actually file a handful, so start here before suggesting any case-type change: it shows what is on, what is off, and what has never been configured either way. A case type with configured: false has no saved setting at this firm. It is ON — case types are enabled unless somebody switches them off — but nobody has made that choice deliberately. Args: - enabled_only (boolean, optional): Only return case types that are currently enabled - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "enabled_count": number, "total": number, "categories": [ { "category": string, "enabled_count": number, "total": number, "case_types": [ { "case_type": string, "name": string, "category": string, "enabled": boolean, "configured": boolean, "is_default": boolean, "ssn_enabled": boolean, "settings_url": string } ] } ] } Each case type carries a settings_url — the portal page for the settings this tool does not cover: required documents, request templates, and which app screens clients see.

list_cms_phases

ChatGPT
The CMS phases a firm can import existing clients from, grouped by case type, with how many open projects each holds and how many of those are already in Quilia. Only phases of case types mapped to a Quilia case type are listed: a project of an unmapped type cannot be imported. Filevine only. Counts come from the last stored count, and the result says how old it is. Pass recount true for a fresh one: it walks every open project in the CMS (seconds to a minute). If the recount is still running when this returns, call again with the run_id it gives. Then import with import_by_phase. Requires an admin role at the firm.

list_endpoints

ChatGPT
List the Quilia API endpoints that api_call can actually reach. Only endpoints you can call are listed. While api_call is GET-only — the normal configuration — the write routes are withheld and reported as a count, because listing a PATCH you cannot issue costs you a call to find that out. REACH FOR A TOOL FIRST. This plus api_call is the fallback for reads nothing else covers, not the way in. A question about a firm's own setup is get_firm_settings; onboarding gaps are get_setup_status; a person is find_client / find_staff. Falling through to here for something a tool already answers is how one question turns into fifteen calls. There is no api_call path for a write. If the thing you want needs one, find the dedicated tool for it; if there is no such tool, say so plainly rather than attempting api_call or reporting the action as done. Args: - category (string, optional): Filter by resource (e.g., "appointments", "cases", "clients") - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "endpoints": [ { "method": string, // HTTP method — GET while api_call is GET-only "path": string, // API path (e.g., "/v2/cases") "description": string, // Brief description of the endpoint "category": string // Resource category } ], "count": number, // Endpoints listed "categories": string[], // Categories among the listed endpoints "unreachable_writes": number // Write routes that exist but api_call cannot issue } A category that is entirely writes will not appear at all — that is not a bug, it is nothing you can call. Examples: - No args -> List every reachable endpoint - category="cases" -> Only case-related endpoints - response_format="json" -> Structured endpoint list

list_field_mapping_targets

ChatGPT
The Quilia fields a CMS field can be mapped to, and the record types a repeating CMS collection can fill. CALL THIS before setting a mapping with update_integration_settings. It gives the exact target_key and record_key values that tool accepts, what kind of value each takes, and — where a column constrains its values — the only words allowed in it. Firms label things their own way: BI, UM, UIM, "3rd party". Clients do not know those terms, so a target with a vocabulary must be given one of Quilia's words, and the firm's own label is mapped onto it. A value outside the vocabulary is left unset rather than saved. Each target also says how it is scoped. Most are set per project type and need a project_type_id. An integration-scoped one is set once and applies to every project type, so passing a project_type_id with it is rejected rather than quietly ignored. The CMS half of a mapping is not here. Sections and fields differ per firm and per project type and are read from the firm's own CMS; get_integration_settings shows what they have mapped already. Args: - response_format ('markdown' | 'json', optional): Output format (default: 'markdown'). Returns the case-field targets and the record types with their parts.

list_form_templates

ChatGPT
List the firm's saved request templates — the forms it sends to clients. Call this before editing a form: it returns each template's id and its full question list, which is what update_form_template needs. Also worth calling before creating one, so you don't build a second copy of a form the firm already has. Args: - type ('form' | 'signature' | 'task', optional): Only return one kind. 'form' is a built form; 'signature' and 'task' are edited in the portal, not here. - limit (number, optional): How many to return (default 50, max 100) - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "count": number, "templates": [ { "id": string, "title": string, "description": string, "type": string, "generate_pdf_on_completion": boolean, "fields": [ { "id": string, "type": string, "label": string, "description": string, "placeholder": string, "required": boolean, "options": string[] } ], "created_at": string, "builder_url": string } ] }

list_mappable_fields

ChatGPT
The firm's own CMS sections and fields for one project type, with the kind of value each can fill and the firm's dropdown values where it has them. CALL THIS with list_field_mapping_targets before setting a mapping. This is the firm's half; that tool is Quilia's half. Field names differ between firms and between project types of the same firm, so never guess a selector — two firms on the same CMS will name the same idea differently. Only fields that hold a value are listed. Layout fields are excluded. This is a record of the last read, not a live one; discovered_at says when. Quilia records it when someone opens the mapping screen for that project type. If a project type returns nothing, ask the firm to open that screen or press Refresh fields. Args: - cms_type (string, required): Service id from get_integration_settings. - cms_id (string, required): Account / tenant id from get_integration_settings. - project_type_id (string, required): CMS project type. - response_format ('markdown' | 'json', optional): Output format (default: 'markdown'). Returns the sections, whether each repeats, and the fields in each.

list_org_locations

ChatGPT
The firm's office locations, with the id every other location tool needs. The first entry (id 0) is the firm's primary office. It is editable via update_org_location, but it cannot be deleted, and add_org_location creates secondary offices instead. START HERE before add_org_location, so you don't create a second row for an office that already exists — add takes no location_id, it creates one. update_org_location and delete_org_location DO each take a location_id, and this is where it comes from. "Own branding: on" means that office overrides the firm-level logo, icon and colors for its own clients — worth reporting, since it changes what those clients see. Args: - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "locations": [ { "id": number, "name": string | null, "city": string | null, ... // address, phone, email, timezone, links, branding } ] }

list_picked_lists

ChatGPT
List the firm's fixed client lists. Use this to find an existing group_id before creating or updating an automation.

remove_case_team_members

ChatGPT
Remove one or more staff members from a case's team. Use this to remove staff members from receiving case notifications. Note: This only removes staff members. If you pass a client's user ID, they will not be removed. Args: - case_id (string, required): The case ID to modify - user_ids (array of strings, required): The staff user IDs to remove - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "success": boolean, "case_id": string, "removed_count": number, "failed_count": number, "message": string }

report_quilia_mcp_bug

ChatGPT
Report a Quilia tool that returned the wrong result, so the Quilia team can fix it. Use this when a Quilia tool gave an answer the user tells you is wrong, or when you can see the output is wrong — a field that should be populated coming back empty, a lookup finding the wrong record, a count that doesn't match. Most Quilia bugs return a normal-looking response with wrong content, so a human noticing is the only way they get caught. Offer to file a report when you see one; don't file silently or repeatedly for the same problem. YOU fill this in, not the user. The valuable part is which tool, what you passed it, and how the output differed from what it should have been — the user can't supply that, and it's what makes the bug findable. NEVER include client or case data. The reporting system must not hold it. Describe the defect generically and refer to people by role: "the client", "a staff member", "the firm". Say "searching by a client's last name returned no match", not "searching for Jane Doe returned no match". For arguments_used, pass value SHAPES, not values. A report containing an email, phone number, SSN or date of birth is rejected — if that happens, restate it generically and try once more. Args: - tool_name (string, required): The tool that misbehaved, e.g. "find_staff" - expected (string, required): What it should have returned or done - actual (string, required): What it returned or did instead - arguments_used (object, optional): Argument names to value shapes, e.g. {"name": "string(5)"} Returns: { "filed": boolean // Whether the report was sent to the Quilia team } Example: tool_name="find_staff", expected="the team member's name and email", actual="every record came back with name 'Unknown' and no email field", arguments_used={"name": "string(5)"}

resync_case

ChatGPT
Re-pull a case from the connected CMS to fetch the latest changes, including the client's contact info, phone, email, and case phase. Use this when a user reports fixing something in the CMS (e.g. a phone number) and wants Quilia to see it. Returns the list of changes applied, and whether the client has contact info to be invited.

search_knowledge_base

ChatGPT
Search Quilia's public knowledge base for help articles, tutorials, FAQs, and troubleshooting guides. Returns matching articles with links, summaries, and metadata. Use to find support content for law firm users, CMS integration guides, or troubleshooting steps. Args: - query (string, required): Search term (1-200 characters) - category (enum, optional): Filter by audience - 'firms' or 'clients' - content_type (enum, optional): Filter by type - 'portalTutorial', 'cmsIntegration', 'faq', 'troubleshooting', 'appTutorial' - cms (enum, optional): Filter by CMS - 'filevine', 'clio', 'casepeer', 'mycase', 'neos', 'smokeball', 'smartadvocate', 'litify', 'salesforce', 'allCMS' - limit (number, optional): Maximum results, 1-50 (default: 10) - offset (number, optional): Pagination offset (default: 0) - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "articles": [{ "id": string, "title": string, "url": string, "excerpt": string, "mainCategory": string, "contentType": string, "compatibleCMS": string[], "featureTags": string[] }], "total": number, "count": number, "offset": number, "has_more": boolean, "next_offset": number (if has_more is true) } Examples: - query="filevine setup" -> Find Filevine integration setup guides - query="import cases", cms="clio" -> Find case import docs for Clio - query="billing", content_type="faq" -> Find billing FAQs - query="notifications", category="firms" -> Find notification guides for firms

send_message

ChatGPT
Send a message to a client or case — sent from you, the authenticated user. This tool atomically resolves your identity as the sender and sends a message. Use it to message a client directly or post to a case without needing to fetch your own user_id first. Args: - recipient_id (string, optional): User ID of the recipient (required if no case_id) - case_id (string, optional): Case ID to send the message to (required if no recipient_id) - subject (string, required): Subject line of the message - body (string, required): Body content of the message (supports HTML) - scheduled_date (string, optional): ISO 8601 timestamp for scheduled delivery - channels (object, optional): Delivery preferences: sms ('always' | 'fallback' | 'never') and email (boolean). Push is always enabled. - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Note: Either recipient_id or case_id must be provided. Returns: For JSON format: { "success": boolean, // true if message was sent "message_id": string, // Unique identifier of the created message "message": string, // Success message from API "scheduled": boolean // true if message was scheduled for future delivery } Examples: - recipient_id="uuid", subject="Hello", body="<p>Hi there!</p>" -> Send immediate message to user - case_id="uuid", subject="Update", body="<p>Case update...</p>" -> Send message to case - recipient_id="uuid", subject="Reminder", body="...", scheduled_date="2024-12-01T09:00:00Z" -> Schedule message - recipient_id="uuid", subject="Reminder", body="...", channels={sms:"fallback", email:false} -> Use push, with SMS only as fallback, and no email Error Handling: - Returns error if neither recipient_id nor case_id is provided - Returns error if authentication fails - Returns API error details if message creation fails

send_request

ChatGPT
Send a request template to a client — sent from you, the authenticated user. This tool atomically resolves your identity as the sender, finds a request template by name, and sends it to the specified recipient. Use it when you need to send intake forms, medical records requests, or other templated requests to clients. Args: - template_name (string, required): Name of the request template to send (fuzzy match on title) - case_id (string, required): Case ID to associate the request with - recipient_id (string, required): User ID of the client who will receive the request - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "success": boolean, "request_id": string, "status": string, "template_id": string, "template_title": string, "message": string } Examples: - template_name="Medical Records", case_id="uuid", recipient_id="client-uuid" -> Send medical records request - template_name="Intake Form", case_id="uuid", recipient_id="client-uuid" -> Send intake form - template_name="Authorization", case_id="uuid", recipient_id="client-uuid" -> Send authorization request Error Handling: - Returns error if template not found (includes list of available templates) - Returns error if authentication fails - Returns error if request send fails

set_org_branding

ChatGPT
Set the firm's logo and icon from publicly reachable image URLs (for example the logo already published on the firm's own website), OR omit the URLs to change nothing and read back the firm's current logo and icon along with a direct link to the portal where the user can manually upload their local image files. Logo: ~2.6:1 aspect ratio, 1200x460 px, scaled to 64pt tall. Icon: Square canvas (1:1), 512x512 px minimum. Rendered as a circle in the app — keep the mark inside the inscribed circle with margin, corners are cropped. Needs to be legible at 32pt (monogram/brandmark, not a wordmark). Both: Dark or full-color on transparent background. No reverse/knockout variants and no baked-in background. (Logo acts as fallback in circular slots when icon is missing). When URLs are provided, Quilia downloads the image and hosts its own copy (PNG, JPG, GIF or WebP, 10MB max, resized to 1600px if larger). Pass null for a field to remove that image. This tool sets IMAGES only. The two button colors are separate and live on update_org_settings (button_icon_color / button_background_color); they color the action buttons on the Organization screen and are not a brand palette. Requires an admin role.

set_org_faqs

ChatGPT
Set the firm's client-facing FAQs — the questions and answers clients see in the Quilia app. This REPLACES the entire FAQ list. Always read the current FAQs first with api_call endpoint="org/faqs" and resend the ones you want to keep alongside any new ones, or they will be lost. Array order is display order; an empty array removes all FAQs. FAQs also need to be switched on for clients to see them — that's faqs_enabled in update_org_settings. Requires an admin role.

set_treatment_status

ChatGPT
Set a provider contact's treatment status on a case. "complete" marks the client released from that provider (stamps released_at to now); "treating" marks active care. Use this to track where the client is in treatment — e.g. mark a client released from a provider once care is done. Get the contact_id from get_case_contacts. Args: - case_id (string, required): The case id (UUID) - contact_id (string, required): The provider contact (people row) id - treatment_status ('treating' | 'complete', required) Returns the updated contact: { id, name, type, treatment_status, released_at }.

set_up_sample_client

ChatGPT
Create the firm's sample client: a fake client the firm's staff sign in to the Quilia app as, to see what their clients see. It gets one case of case_type, made the way a real new case is (the firm's default case team and that case type's setup), filled with sample activity over the next minute. Returns the sign-in number and 6-digit code to give the user. One per firm: if the firm already has one, it is returned unchanged. It is excluded from billing, client counts and automations, and is never texted. case_type must be one the firm has enabled (list_case_types). Requires an admin role at the firm.

switch_org

ChatGPT
Switch which firm you are acting as. Quilia wizard accounts only. Every other tool reads and writes within ONE firm. This moves that context to a different firm, so a subsequent get_case / find_client / send_message applies there instead. It does not copy or move any data. Use it when the user names a firm other than the one currently in context. Call get_current_user first if you are unsure which firm that is. Args: - organization (string, required): The firm's name, or its organization id. Names match case-insensitively, exact before partial. If several firms match, the error lists them with ids so you can retry unambiguously. - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "id": string, // The organization now in context "name": string, // Its display name "active": boolean // false for a deactivated firm (still reachable by a wizard) } Note: the switch takes effect on the very next call. One connection works across every firm: the firm lives on your one wizard account, not on this connection, so a switch made anywhere else (another assistant session, the portal, or another connected app) moves this connection too. That is why every write tool requires org_id, the firm you mean to act on: if this connection was moved, the write is refused with nothing changed, naming both firms, until you switch back. Call get_current_user to see which firm the next call will act on, and pass that id as org_id.

update_audience

ChatGPT
Change an audience — its name, its note, or who it matches. CONDITIONS ARE REPLACED, NOT MERGED. Sending groups makes it the entire definition; there is no way to add a single condition. Read the audience with list_audiences, change what you need in the shape it gives you, and send the whole thing back. THIS CHANGES WHO RUNNING AUTOMATIONS REACH. Membership is worked out at send time, so every automation pointing at this audience uses the new definition from its next run. Messages already queued keep the recipients they were queued with. Say which automations use it before changing one — list_automations shows that. Seeded audiences (Active Cases, and the ones behind the automatic NPS and welcome automations) can be edited, because they are what a firm's automations already point at. They cannot be deleted. Read get_audience_options for the values conditions can use. Case types are the ones the firm has switched on in Settings → Case Types; a value outside that list is not rejected, it just matches nobody.

update_automation

ChatGPT
Change what an automation says, which channels it sends on, or pause it. Anything you leave out stays as it is. USE THIS to fix wording, a subject line, a translation, or the delivery channels on an automation that already exists — including one a firm has already switched on. IF IT IS RUNNING, THE EDIT REACHES CLIENTS WHO ARE ALREADY QUEUED. Both the copy and the channels are stamped onto each send when it is queued, so this rewrites those pending sends as well as the rule. That is the point, and it is worth telling the user: correcting a typo fixes the messages about to go out, and turning SMS off stops texts that were already scheduled. THE AUDIENCE CAN BE FIXED WHILE IT IS PAUSED. audience_id (or group_id) is accepted as long as the automation is paused and has queued nothing — which is true of every automation this tool creates, right up until a human turns it on. So an automation built against the wrong audience is a one-call fix, not a delete-and-rewrite. Once it has queued sends the rebuild is real and the portal owns it; you will get an error saying how many are queued. It still cannot change WHEN an automation fires — trigger, offset and repeat are not editable here, because changing them on a running automation rebuilds its queue. Say that and point at the portal. It cannot turn an automation ON. pause: true is the only direction; activation queues sends to every matching client and stays a human decision in the portal. Do not claim to have activated anything. translations REPLACES the whole set — send every language, not just the one that changed. So CALL list_automations FIRST and send the untouched languages back verbatim; the copy it returns is markdown and needs no editing to be resent. A language you leave out is deleted, along with whatever the firm wrote in it, and nothing anywhere reports that it is gone. Leave the field out entirely to keep every translation as it is. Editing message_body does not re-translate: the existing translations stay and go stale, and you should say so. channels MERGES — sending just {"sms":"never"} leaves email as it was. The result reports the resulting choice, so report that rather than what you asked for. Args: - automation_id (string, required): The automation to change - name (string, optional): What the firm calls it - message_subject (string, optional): Subject clients see - message_body (string, optional): The message, in markdown - audience_id (string, optional): A different audience — paused, unqueued automations only - group_id (string, optional): A picked list instead; same restriction - send_as (string, optional): "firm", or the id of someone who works at the firm — whose name clients see it from. Clients and Quilia support (wizard) accounts are rejected. - channels (object, optional): { sms: 'always'|'fallback'|'never', email: boolean } - translations (array, optional): Replaces every translation - pause (true, optional): Pause it Returns: { "id": string, "name": string, "active": boolean, "channels": { "sms": "always" | "fallback" | "never", "email": boolean }, "message_subject": string | null }

update_case_team_member

ChatGPT
Update a staff member's settings on a specific case. Use this to change whether a staff member is contactable (visible to clients) or if they allow messages. Args: - case_id (string, required): The case ID - user_id (string, required): The staff user ID to update - contactable (boolean, optional): Whether the staff member is visible to clients - allow_messages (boolean, optional): Whether the staff member can be messaged - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "success": boolean, "case_id": string, "user_id": string, "message": string }

update_client

ChatGPT
Update a client's contact information (phone, email, or name) directly in Quilia. Use this for firms without a CMS or when the CMS is wrong and the firm instructs a direct edit. Refuses duplicate phones or emails and says who has it. Returns whether the client can be invited.

update_form_template

ChatGPT
Edit a form template the firm already has. To ADD a question, use append_fields. It leaves every existing question alone, so this is the safe option and the one you almost always want. To REORDER or REMOVE questions, use fields — but it REPLACES the entire list. Call list_form_templates first and send back every question the form should end up with, in order. Anything you leave out is deleted. Passing both is refused: they disagree about what the form should end up as. Editing a template does not rewrite forms already sent to clients — those keep the version they were sent with, so nobody loses answers mid-submission. Args: - template_id (string, required): The template to edit, from list_form_templates - title (string, optional): A new title - description (string, optional): New instructions - append_fields (array, optional): Questions to add to the end - fields (array, optional): The complete replacement question list - generate_pdf_on_completion (boolean, optional): Whether to produce a PDF on submit - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "success": boolean, "template": { ...the saved form... }, "message": string } Error Handling: - Returns an error if both fields and append_fields are given - Returns a not_found error if the template isn't at this firm, or was deleted - Refuses anything that is not a form: signature templates hold placements on an uploaded document and task templates hold their behaviour elsewhere, so both are edited in the portal

update_integration_settings

ChatGPT
Configure an existing CMS or other firm integration. Covers the settings that make integration setup complicated: staff-on-case assignment rules, CMS-to-Quilia case-type mappings, service-specific config, auto-invite behavior, phase messages, client feed, and custom case handling. ALWAYS call get_integration_settings first. It supplies the exact cms_type, cms_id, current staff rules, valid integration_case_type_id values, existing mappings, and the config you must preserve. Staff assignments REPLACE the complete saved staff rule. Include every rule the firm wants to keep, including existing overrides. staff_assignments.overrides sets one person's outcome, keyed by user id from get_firm_settings: "visible" (on the case, visible to the client), "hidden" (on the case, not visible) or "exclude" (kept off the case; a sync removes them if an earlier one added them). An override only applies when the CMS lists that person on the project team, and it beats the role rules in every mode. Use it when a CMS lists most of the firm on every case and a role rule cannot tell people apart. The API rejects ids that are not active staff at this firm. TWO DIFFERENT WAYS staff get on cases; do not confuse them: - update_team_member auto_add_to_cases ("Add to every case" in Settings > Team) FORCES the person onto every case, regardless of any CMS project assignment. Use it for a firm with no CMS, or for someone who is not on the CMS project team but needs every case's notifications (an office manager, say). auto_add_visible_to_client decides whether clients see them. - update_integration_settings staff_assignments.overrides only applies on a case where the CMS lists that person on the project team; it never adds anyone the CMS did not list. To put someone on every case regardless, use update_team_member auto_add_to_cases. A saved rule reaches existing cases only on the daily check, about once a week per case. To apply it now, preview and then apply it with apply_staff_rule. Get the user's confirmation before replacing staff assignments, clearing a case-type mapping with null, or removing a config key with null. Clearly name the values that will be replaced or cleared. Case-type mappings are partial: send only rows to change. integration_case_type_id is the id from get_integration_settings; quilia_case_type is the Quilia case type id, or null to clear it. The API rejects unknown case types and rows from another integration. For Filevine subtype_mapping, call list_mappable_fields for that project type and choose a non-repeating dropdown or multi-select field with dropdown values. This sets only that project type's subtype field; other project types and the organization-wide fallback are preserved. A null source clears only the project-type override. For Filevine language_mapping, choose where each client's language is read on import and sync: {source: "tags"} reads only the contact's hashtags; {source: "contactField", contact_field_selector} reads only that custom contact field. null restores the default chain (the contact's Details-tab language, then hashtags, then the project form field). A language the client chose in the app is never overwritten. Use this, not config_patch: it keeps the project form field settings beside it. config_patch is a SHALLOW merge into the top-level config. Omitted keys remain unchanged and null removes a key. If a value is a nested object, send that whole nested value—not one leaf—because that top-level key is replaced. This cannot create or connect an integration and cannot change credentials, sessions, account identity, connection state, or health timestamps. Those remain portal/OAuth-only. Requires an admin role at the firm. This tool changes configuration only; it does not send a message during the call. Enabling auto-invite can invite every eligible client at the firm; phase messages can schedule messages for every opted-in client on matching cases; feed delivery can send activity for every configured case to the firm's CMS. State the applicable worst-case audience and get the user's confirmation before turni…

update_message

ChatGPT
Correct a message that is scheduled but has not gone out yet. The scheduled sender reads the message row when it fires, so an edit made before then is what the client actually receives. Use this to fix a typo, swap a named screen for a link, or rewrite a paragraph in something you or a colleague already queued — instead of scheduling a duplicate and asking someone to delete the original in the portal. Args: - message_id (string, required): The message to correct. send_message returns this as message_id; api_call GET /messages lists it for messages queued earlier. - subject (string, optional): Replacement subject line - body (string, optional): Replacement body, in the same HTML send_message takes. REPLACES the whole body — send the complete new text, not a fragment. - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') At least one of subject or body is required. What it will NOT do: - Edit a message that has already been sent. It is in the client's inbox and its body was personalized in place when it went out. Send a follow-up instead. - Edit a message an automation generated. The automation rewrites it from its own template on the next cycle, so the edit would be silently reverted — use update_automation, which also rewrites the sends already queued. - Reschedule or cancel. This changes the words, not the timing. Cancelling is a portal action. Timing: the sender wakes up every 15 minutes, so an edit lands only if it commits before the run that picks the message up. When the send time is close, the response says so rather than implying the change is safely in. Returns: For JSON format: { "success": boolean, "message_id": string, "message_subject": string | null, "status": string | null, // unchanged by this tool — an edit never sends or unsends "scheduled_date": string | null, // when it goes out; null on a draft "message": string } Examples: - message_id="uuid", body="<p>Corrected text.</p>" -> Replace the body, keep the subject - message_id="uuid", subject="New subject", body="<p>...</p>" -> Replace both Error Handling: - Returns error if neither subject nor body is provided - Returns the API's own reason when the message is already sent or belongs to an automation - Returns error if the message does not exist or belongs to another firm

update_org_location

ChatGPT
Update one of the firm's office locations. Only the fields you send change. Get location_id from list_org_locations. Location id 0 is the primary office. Pass a branding object to give this location its own name, colors, logo and icon shown to its clients instead of the firm-level branding; set branding.enabled=false to fall back to firm branding without discarding the values. Requires an admin role.

update_org_settings

ChatGPT
Update the firm's own profile and settings — the same fields the firm fills in under Settings > Organization in the Quilia portal. Use this to set up or correct a firm's details: name, address, phone, email, website, time zone, office hours, review and payment links, social profiles, brand colors, referral email, the client-facing FAQ toggle, custom invite SMS wording, and the client alert banner. Read the current values first with api_call endpoint="org" so you only send what actually changes. Omitted fields are left alone; passing null clears a field. Two fields REPLACE rather than merge: invite_message_templates (send every language you want to keep) and organization_alert (send the whole object). Requires an admin role at the firm; a non-admin gets a 403 explaining that.

update_team_member

ChatGPT
Update a staff member's details, or your own profile. Omit user_id to edit YOURSELF — any staff role can change their own profile fields, the same ones the portal offers at Settings → Profile. Pass user_id (from find_staff) to edit someone else: that requires an admin role at the firm, as do role, active, location_id, permission_id and email on anyone including yourself. Only the fields you pass are changed. Pass null to clear a nullable field. The call is atomic: if any field fails validation (e.g. an oversized avatar_url), the entire request is rejected and nothing is written to the database. Args: - user_id (string, optional): The staff member to update. Omit for your own profile. - name / name_first / name_last (string, optional): Names - phone (string, optional): Phone number - job_title (string, optional): Job title - city / state / country (string, optional): Location fields - timezone (string, optional): IANA timezone, e.g. 'America/Denver' - bio (string, optional): The "About" text shown in the portal - linkedin / twitter / facebook / instagram (string, optional): Social profiles - email (string, optional, ADMIN): Email address. Note this updates the profile/notification address; it does not change the login address, which needs a confirmed change in the portal. - role (enum, optional, ADMIN): 'admin', 'attorney', or 'legal staff' - active (boolean, optional, ADMIN): Set true to reactivate a disabled member - location_id (number, optional, ADMIN): Office location ID - permission_id (string, optional, ADMIN): Permission set UUID - response_format ('markdown' | 'json', optional): Output format (default: 'markdown') Returns: For JSON format: { "success": boolean, "user_id": string, "member_id": string, "updated_fields": string[], "message": string } Error Handling: - Returns a forbidden error if you are not an admin and are editing someone else, or any admin-only field - Returns a not_found error if the staff member doesn't exist at your firm - Returns an error if no fields were provided

Capabilities

Writes

Example Prompts

Click any prompt to copy it.

App Stats

74

Tools

3

Prompts

ChatGPT

Platforms

Category

CRM Records

Works with

ChatGPT

Data refreshed daily