Integration details
Description
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.
- Integration type
- Plugin
- Verification status
- Not applicable
- Platform
- ChatGPT
- Primary Subcategory
- Legal Practice & Matter Platforms
- Secondary Subcategories
- None listed
- Brand
- Quilia
- Access
- Account required
- First tracked
- 2026-08-12
- Tool count
- 59
- Geography
- US
The Primary Subcategory used for this profile’s headline score.
Other Subcategories where the Integration is listed.
Get alerts for Quilia
Get updates when Quilia’s Discoverability Score or category rank changes.
ChatGPT Plugin Discovery Score
ChatGPT Plugin discovery is coming soon
ChatGPT can surface a Plugin when it matches a user's request.Your Plugin Discovery Score measures how often yours appears.
No spam. Unsubscribe any time.
What discovery looks like

Competing in ChatGPT Legal Practice & Matter Platforms
View Category59 tools agents can invoke
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
api_call
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_org_location
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_firm_symptoms
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
add_team_member
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_appointments
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_contacts
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
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_documents
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_employment
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_notes
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_summary
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_type_symptoms
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
configure_case_types
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_appointment
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_audience
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_automation
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: "[email protected]"} -> 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_case
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_form_template
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_picked_list
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_automation
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.
delete_org_location
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
disable_team_member
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="[email protected]" -> 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
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_client_cases
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="[email protected]" -> 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
find_staff
Everything the firm has configured: profile, contact details, hours, branding, the full team with roles, office locations, and client FAQs. One call. 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_firm_settings
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 Two you cannot finish yourself — report them and tell the user where to go: - Client features -> Settings > Features in the portal. These toggles decide what a client can do in the app and are deliberately not writable through the API. - 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. Task lists have no tool yet either. 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 } ] }
get_setup_status
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_audience_options
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.
get_case_team
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. 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_integration_settings
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, "case_id": string, "invited_users": [{ "user_id": string, "case_id": string, "status": string, "role": string }], "message": string } Notes: - If the user is already a member of the case, no new invitation is sent (invited_users is empty). Error Handling: - Returns an error if the case is not found or you lack access - Returns an error if the invite fails
invite_client
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_endpoints
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_audiences
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. 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" "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_automations
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_type_symptoms
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, "inactive_reminders_enabled": boolean, "ssn_enabled": boolean, "journal_prompts_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_case_types
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_field_mapping_targets
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_org_locations
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_form_templates
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_mappable_fields
List the firm's fixed client lists. Use this to find an existing group_id before creating or updating an automation.
list_picked_lists
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). 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_current_user
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_article
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 }
remove_case_team_members
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)"}
report_quilia_mcp_bug
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
search_knowledge_base
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_message
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
send_request
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_org_faqs
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_branding
How do I improve a ChatGPT Plugin's discoverability?
The levers are the listing surface agents actually read: names, descriptions, keywords, tool metadata, and registry health. Which lever matters depends on where discovery breaks, which is what continuous measurement shows.
What are Quilia alternatives on ChatGPT?
As of 2026-09-26, Quilia competes with Aurora, Casepoint, Casepoint Gov, Chat Jurídico, Courtroom5, DocketDrafter, Everlaw, GC AI, HighQ, JUNE, LawVu, Mary, May or Shall, Relativity, Sandstone, مساعدي — IB Law in ChatGPT Legal Practice & Matter Platforms, ranked by public Discoverability Score.
Where is this profile measured?
This profile uses the geography attached to the latest public registry snapshot: US. Locale tags are intentionally omitted.