approvals_get
ChatGPTCheck what became of a tool call that was held for the user's approval. Pass the approval_id the held call returned. Reports whether the user is still deciding, approved it (with the result), declined it, or let it expire. It cannot approve anything; only the user can, in the Workspace369 app.
automations_disable
ChatGPTTurn a workflow off so it stops firing. This also stops anything already queued: a follow-up step waiting on a delay re-checks the live workflow before it runs and is skipped once it is off. Turning it back on is automations_activate, which needs its own automations:activate scope. Disabling an already-off workflow is reported, not an error.
automations_get
ChatGPTFetch one workflow's full definition: trigger, conditions, action, recipients and follow-up sequence. definition_warnings lists problems; conditions are ANDed and fail closed, so an unresolvable field or unknown operator makes a workflow log a run and deliver nothing. Recipient addresses are never returned, only the recipient type and name. Returns { automation: null } if the id does not exist in this workspace.
automations_list
ChatGPTList the workspace's workflows (the Workflows screen) with each one's trigger, action (its first step's action), category, step count, lifecycle (draft, active, paused), on/off state, last run's outcome and when it last fired. Each row carries can_fire and blocked_reasons, which show whether a workflow will actually run: a workflow can be stored and show as enabled yet never fire (its trigger has no live emitter, or its status was written as something other than a boolean).
automations_runs_list
ChatGPTThe workflow execution ledger. Filter by automation_id and/or outcome: delivered, failed, skipped (the workflow fired and delivered nothing: a failed condition, unresolved recipient, missing mailbox), stopped (hit a stopWorkflow condition) or waiting (a step is queued on a wait). Rows carry type (execution or testRun) when the log has one. Manual test runs are excluded unless include_test_runs: true. Message bodies, recipient addresses and phone numbers, and raw trigger entity data are never returned; automations_get shows the definition.
automations_test
ChatGPTDry-runs a workflow and sends nothing: every step reports what it would do (who it would email or text, what it would change), on sample data or on a real record (record_id). If the workflow has saved changes that are not live yet, those are what is tested, as in the Workflows screen. The run is logged as a test run, readable with automations_runs_list and include_test_runs: true.
booking_pages_list
ChatGPTList the workspace's booking pages. A page carries public_link only when it is active and has a slug; an inactive page has no usable link.
calendar_categories_list
ChatGPTList the calendar categories configured in the workspace. Only these names exist; a calendar event's category must be one of them.
calendar_check_availability
ChatGPTCheck which team members are available on a date, from their working hours and that day's calendar events. The day is bounded in the workspace time zone (a member's own time zone override wins for that member) and the answer states the zone. date is required; employee_ids (from team_members_list) narrows the check. Returns available members (with working hours) and unavailable members (booked, with conflicts, or not a working day).
calendar_create_event
ChatGPTCreate a calendar event (schedule). Needs title and starts_at (ISO date). ends_at defaults to one hour after starts_at and must be after it. For all-day events set is_all_day: true, pass YYYY-MM-DD dates (no timezone conversion) and time_zone; the default all-day end is the following date and explicit ends are exclusive (September 11 alone ends September 12). With a client_id the event inherits that client's visibility; a client-less event is visible to the whole team. category must be an existing name from calendar_categories_list. reminders are alarm metadata on the event (5_minutes_before, 15_minutes_before, 30_minutes_before, 1_hour_before, 2_hours_before, 1_day_before); they do not prove a notification was scheduled. recurrence is daily, weekly, biweekly or monthly, with recurrence_count or recurrence_end_date. assigned_user_ids are ids from team_members_list (omitted: assigned to the caller; []: unassigned). services (name + quantity) returns a catalog-priced estimate in the response (services_preview, estimate_total); nothing is stored on the event. A repeat call with the same idempotency_key returns the first event (deduped: true).
calendar_events_reschedule_batch
ChatGPTMove many calendar events at once as one reviewable action. Pass exactly one mode: from_date (ISO date) moves that day's client-linked events to to_date, or client_id moves that client's upcoming events to to_date. Days and times are local to time_zone (IANA; default the workspace time zone). Each event keeps its local time of day and duration; start_time ("HH:MM", 24h, local) re-anchors the day, preserving inter-event gaps unless preserve_order: false stacks them at the same time. At most 30 events move per call; if more match, the call refuses with the count. Recurring series and events someone has open in the app are skipped and counted. The result lists every move (old to new start).
calendar_list_events
ChatGPTList calendar events (schedules), paged (up to limit, default 25, max 100, plus nextCursor). from / to (ISO dates) filter by start time: a date-only value means the whole of that day in the workspace time zone (from = to = one date lists that day); a value with a time is an exact instant. All-day events (is_all_day) use UTC-midnight date markers: show the date, not a local clock time. employee_id keeps events assigned to that team member; client_id keeps one client's events. Cancelled events are excluded unless include_cancelled is true. Each row carries is_cancelled, location, meeting_url, description, notes and is_recurring. Scoped members see only their assigned clients' events plus team-wide ones; members restricted to assigned schedules see only events assigned to them.
calendar_update_event
ChatGPTEdit or reschedule an existing calendar event by event_id. Only the fields passed change. starts_at / ends_at are ISO dates; starts_at alone moves the event and keeps its duration. All-day moves use date strings with exclusive ends, and is_all_day is set when converting between timed and all-day. notes replaces the event's Notes block and description replaces the description ("" clears it). add_reminders appends to the existing reminders; clear_reminders: true removes them all. status: "cancelled" is a soft cancel that keeps the record. A recurring event (is_recurring) needs occurrence_scope: "all_events", which changes the whole series; other scopes are done on the Calendar screen. recurrence (none, daily, weekly, biweekly, monthly, with recurrence_count or recurrence_end_date) makes a one-off event repeat, re-patterns a series, or with none turns a series into one event; a pattern shorter than the event is refused, and a series on specific weekdays can only have its count changed. An event someone has open in the app is refused as being edited. Linked reminders fire for the first occurrence only on biweekly series, and changes says so.
clients_create
ChatGPTCreate a client, or a prospect when lifecycle_stage is "prospect". A name is required: name, first_name/last_name, or company. client_type defaults to business for a company-only client, otherwise individual. Phone numbers are stored as E.164: give the country code; a 10-digit number is read as US/Canada and an unreadable number is refused. Use addresses for several addresses (exactly one is primary; the first if none is flagged), or the flat address fields for one. Addresses are checked against Google Places: an agreeing match is stored geocoded, anything else is stored as written and reported as unverified. Scoped members' new clients are assigned to them. custom_fields ({key: value}) is validated against the workspace's definitions; an unknown field or a bad value refuses the create. A repeat call with the same idempotency_key returns the first client (deduped: true).
clients_get
ChatGPTFetch one client by id, with its custom_fields values (key to value). Returns { client: null } if it does not exist or is outside a scoped member's assigned clients. Archived clients are returned with archived: true.
clients_move_stage
ChatGPTMove a client to another stage of the workspace's client pipeline, by stage id or name (see workspace_schema clients.stages). A lead-group stage makes it a prospect; any other stage makes it a client. Moving to the stage it is already in changes nothing. Archived and out-of-scope clients are refused.
clients_search
ChatGPTList clients in the workspace, paged (up to limit, default 25, max 100, plus nextCursor). query matches name, email or company. lead_source, client_type, stage (a pipeline stage id or name) and tags (matches any) are applied after the page is cut, so a filtered page can be short while more matches exist; keep following nextCursor. Archived clients are excluded. Scoped members see only their assigned clients.
clients_update
ChatGPTUpdate a client by id. Only the fields passed change. lifecycle_stage "client" promotes a prospect, "prospect" demotes. phone: "" removes the primary phone. Email, phone and single-address updates replace only the primary entry and keep the others; addresses replaces the whole address list (the only way to remove or reorder one) and goes through the same Google Places check as on create. custom_fields ({key: value}, validated against the workspace's definitions; null clears a value) merges into the client's existing values. Returns the merged client, or an error if the client does not exist or is outside a scoped member's assigned clients.
contract_templates_list
ChatGPTList the workspace's contract templates with their roles (key, label, signer/approver/viewer/cc, routing order, and whether the client, a fixed person or the sender fills it). bulk_eligible: exactly one client role and every other role fixed. proposal_eligible: also, the client role signs or approves first. A template id goes to contracts_create_draft.
contracts_create_draft
ChatGPTCreate a DRAFT contract from a template. Pass template_id (see contract_templates_list) and client_id when the template has a client role; the client's primary email fills that role. Pass recipients for roles whose assignment is manual, as { role_key, name, email }; each email must be saved on the client (or its contacts), a team member or the workspace. Optional title, message (the invitation email subject and body) and reminders (the automatic reminder cadence: first after N days, then every N days, at most N). Nothing is sent: the draft can be reviewed in Workspace369 or sent with contracts_send. unassigned_role_keys lists roles that still need a person before the contract can be sent. Each call creates a new draft.
contracts_get
ChatGPTFetch one contract by contract_id: status, dates, verification and expiry settings, invitation subject, the sealed document's SHA-256 once completed, and each recipient's name, email, role, routing order and progress. Returns { contract: null } if the id does not exist or is outside a scoped member's book. send_request is the latest send started with contracts_send: queued while it runs, sent, or failed with the failure_code; null when there is none. Field values, signature images and signer IP addresses are never returned.
contracts_remind
ChatGPTEmail a reminder to recipients who were invited and have not finished. recipient_id selects one recipient; omit it to remind everyone who is due. A recipient reminded in the last 24 hours (too_soon) or not waiting on an emailed link, such as one signing in person (not_remindable), is listed under skipped. This sends real email and cannot be undone. Held until the user approves it in the Workspace369 app.
contracts_search
ChatGPTList contracts, newest first (up to limit, default 25, max 50). Filter by status_group (draft; waiting = sent, in progress or sealing; completed; declined; voided; expired), client_id, and archived ('exclude' by default, or 'only'). query matches words in the title. One call scans at most 200 contracts; scan_truncated: true means more may match, so pass next_cursor to continue. Field values, signatures and signer IP addresses are never returned. Members limited to specific clients see only contracts in their book.
contracts_send
ChatGPTSend a draft contract for signature: the document is frozen, the first routing group is emailed and the expiry clock starts. Needs contract_id. It uses one of the workspace's included monthly contracts if any remain; otherwise it costs Tokens, and nothing is sent if that charge fails. This emails real recipients and cannot be undone (voiding stops it). The send runs in the background (a contract from a text template is rendered first, which can take minutes), so the result status: "sending" confirms nothing yet: contracts_get shows whether it went out (status no longer draft; send_request.failure_code explains a failure). Calling again while a send is in progress starts nothing new (deduplicated: true). Held until the user approves it in the Workspace369 app.
contracts_void
ChatGPTVoid an open contract (sent or in progress) so nobody can sign it. Requires contract_id and a reason of 5 to 1000 characters, recorded on the contract. A contract that is sealing, completed, declined, voided or expired cannot be voided. Recipients are emailed that it was voided. This cannot be undone; duplicate the contract in Workspace369 to start again. Held until the user approves it in the Workspace369 app.
email_send_options
ChatGPTRead how an email would go out: the inboxes the user can send from (label, address, starter or own domain/mailbox, per-email recipient cap, which is the workspace default and which the default for automatic emails), what happens when from_inbox is omitted (when_from_inbox_omitted; 'ask_user' means the send tools will not choose), the default Cc and Bcc the send tools add (automatic ones are marked, and each says whether it counts toward the cap), each inbox's compliance archive Bcc (always added, cannot be removed), from-name and reply-to overrides, whether a signature is added, the default invoice email template, and the template library (id, name, context: 'general' templates work with inbox_send_message and invoices_send, 'invoice' with invoices_send, 'proposal' templates apply to proposals automatically). In the send tools cc / bcc replace the defaults. Recipient cap: at most 10 people per email from a starter inbox and 50 from the user's own domain or mailbox, counting To + Cc + Bcc; the workspace's own addresses do not count. Requires the Send Email permission.
expense_types_list
ChatGPTList the workspace's configured expense categories (id and name). expenses_create and expenses_update take a category name; an unknown category is allowed but flagged.
expenses_create
ChatGPTRecord a business expense. Needs a positive amount (dollars) and a category (a name from expense_types_list; an unknown one is recorded with a warning). date is "MM/dd/yyyy" or "yyyy-MM-dd" and defaults to today. receipt_number is folded into the description. project_id links the expense to a project (from projects_search); schedule_id links it to a calendar job. Dates inside a closed accounting period are refused. A repeat call with the same idempotency_key reports a replay with deduped.
expenses_list
ChatGPTList recorded expenses, paged by limit (default 25, max 100) and nextCursor. Filters: category (case-insensitive name), schedule_id, project_id, and period (today, this_week, this_month, this_year or all; UTC calendar, weeks start Monday). page_total_amount sums this page only.
expenses_update
ChatGPTCorrect an expense by expense_id (from expenses_list). Only the fields passed change: amount, category, description, date ("MM/dd/yyyy" or "yyyy-MM-dd") or project_id (empty string unlinks it). Refused when the expense is in a closed accounting period or would be moved into one. It cannot delete an expense or change who recorded it.
follow_ups_list
ChatGPTList the workspace's client follow-ups grouped as overdue, today, this_week (rest of the next 7 days) and suggested. Each item is the client's next follow-up as computed by the server: scheduled meetings, reminders and tasks first, then detected reasons (chase_payment = invoice past due, reply_owed = client message waiting over 24 h, onboarding_step = contract/deposit/kickoff missing, chase_proposal = proposal sent 3+ days and not accepted, renewal = retainer ends within 30 days), then suggested check-ins (quiet longer than the stage's cadence). amount + currency, when present, is the money behind the follow-up (only for callers who may see prices). best_time, when present, comes from the client's notes. scope: everyone (default) or mine (follow-ups naming the caller, or on clients they created or are assigned). group: overdue, today, this_week, suggested, or due (overdue + today). Scoped members see only their assigned clients; archived clients are excluded. Changes nothing.
inbox_send_bulk
ChatGPTSend one SMS to many of the workspace's own clients in a single action. recipients is [{client_id, to?, name?}] (find ids with clients_search) and body is the same text for every recipient. client_id is required: the text goes to that client's phone on file, or to to only if it is a number saved on that client. Numbers that are not on a client record cannot be texted. Clients who opted out of texts or are blacklisted are excluded as the campaigns screen does and returned as suppressed, not failed. One confirmation covers the exact recipient list, and confirmable tools are rejected inside batch_apply. At most 30 recipients per call. Duplicate phone numbers are sent once and reported as duplicates. Each recipient returns its own outcome (queued, failed, duplicate or suppressed) with its conversation id; a failure does not stop later recipients. Requires the inbox SMS send permission. Sends real messages and bills for each. Held until the user approves it in the Workspace369 app.
inbox_send_message
ChatGPTSend an email or SMS from the workspace inbox. Recipients (to, cc, bcc) must already be in the workspace: a client's or contact's saved address or number (pass client_id), a team member, the workspace's own address, or the contact of an existing conversation (conversation_id); others are refused, as is a client who opted out or is blacklisted. Email: to (one or more addresses, each with its own copy), subject and body (markdown), or a general library template (see email_send_options) whose subject and body apply unless overridden. If from_inbox is omitted, the workspace's AI & MCP inbox is used, else the only inbox the user can send from; with several and no default, nothing is sent and the result lists them. cc/bcc replace the default copies. At most 10 recipients per email from a starter inbox and 50 from the user's own domain or mailbox (To + Cc + Bcc, workspace addresses excluded); over the cap nothing is sent. SMS: to is one phone number and body the text; it needs client_id or conversation_id, and numbers that texted STOP or are blocked are refused. Sends a real, billed message. Held until the user approves it in the Workspace369 app.
invoice_payments_list
ChatGPTList the payments recorded against one invoice (amount, method, date, status, reference, notes) plus the invoice total, total paid and remaining balance. Identify the invoice by invoice_id or invoice_number (e.g. '26-6'). Proposals/estimates, unknown identifiers and invoices outside a scoped member's clients all return 'not found'. Monetary fields are null without the invoices.view_prices permission.
invoice_payments_summary
ChatGPTPayment summary for one invoice: total received, refunded, net, payment count, amounts by method, invoice total and remaining balance. Identify the invoice by invoice_id or invoice_number. Proposals/estimates, unknown identifiers and out-of-scope invoices return 'not found'. Monetary fields are null (and by-method omitted) without the invoices.view_prices permission.
invoices_batch_from_schedules
ChatGPTCreate invoices for every scheduled job in a date range in one action. from_date and optional to_date are ISO dates. Schedules are enumerated server-side; those that already have an invoice are skipped (exclude_already_invoiced: false includes them), as are cancelled appointments and schedules with no client. Each invoice gets the workspace's Invoice Defaults. At most 25 invoices are created per call (max_invoices lowers it), and the result reports how many eligible schedules remain. mark_as_paid (+ payment_type) records a real payment per invoice. notify_email and/or notify_sms send each client their invoice with its public pay link and bill for each message; emails go out as invoices_send sends them, with email_message replacing the body and sms_message adding a note to the text. from_inbox names the sending inbox; when omitted it is the workspace's inbox for AI & MCP emails or the only usable one, and with several and no default the call is refused before anything is created and the result lists them. A retry with the same idempotency_key reuses the invoices already created. Held until the user approves it in the Workspace369 app.
invoices_create_draft
ChatGPTCreate a draft invoice for a client. It is saved as a draft and never sent. Needs client_id and at least one line item: {product_id, quantity} (catalogue product; unit_price overrides its price) or {description, unit_price, quantity} (one-off). unit_price is the price of one unit; line totals are computed. details is the line's printed description and accepts simple markdown. A draft without a title is named "Invoice for <client>". The number comes from the workspace's invoice sequence (number sets one explicitly). due_in_days (0 = due on receipt) or due_date (MM/DD/YYYY) set the due date; payment_term is the printed label only. Tax is tax_percent or tax_amount, discount is discount_percent or discount_amount (not both of either); tax applies after the discount. notes prints on the invoice. The workspace's Invoice Defaults (due terms, tax, discount, deposit, notes) apply unless apply_workspace_defaults is false, and explicit values override them (see invoices_get_defaults). The invoice inherits its visibility from the client. A repeat call with the same idempotency_key returns the first invoice (deduped: true).
invoices_create_from_schedule
ChatGPTCreate an invoice for one scheduled job (calendar event). schedule_id comes from calendar_list_events. The invoice takes its client and line items from the schedule and its connected estimate/proposal when one exists (a declined optional add-on is left off); otherwise it starts empty and lines are added with invoices_update_draft. Dates, due date, payment term, notes and terms come from the workspace's Invoice Defaults (invoices_get_defaults). It is created as Pending with a public pay link, linked back to the schedule, and numbered from the workspace sequence. A schedule that already has an invoice is refused. mark_as_paid also records a payment for the full total, using payment_type (Cash, Check, Credit Card, Bank Transfer or Other). This tool does not edit statuses of existing invoices; payments_record records payments. Nothing is charged and nothing is sent to the client. A recorded payment sends the invoice.* webhook events to any webhook URLs the workspace has set up. A repeat call with the same idempotency_key returns the first invoice.
invoices_get
ChatGPTFetch one invoice by id or by invoice_number (e.g. '26-6'). Returns { invoice: null } if it does not exist, is a proposal/estimate, or is outside a scoped member's clients; if two invoices share a number it refuses and asks for the id. Includes line_items (up to 100, each with the line_number that invoices_update_draft's update_services addresses by position) and the true line_item_count. content_locked / content_lock_reason show whether the invoice is locked by a recorded payment or credit (its items and prices then change only through a change order in the app); change_orders_pending counts change orders awaiting the client. Monetary fields are null without the invoices.view_prices permission.
invoices_get_defaults
ChatGPTRead the workspace's Invoice Defaults, the terms applied to every new invoice: payment timing (due_in_days and the due_date_preview an invoice created today would get), the payment-term label, tax (none, a percentage, a fixed amount, or the default tax code), discount, whether a deposit is requested and at what percentage, and the default notes. invoices_create_draft applies these unless told otherwise. Existing invoices are not affected. Rates and amounts are null without the invoices.view_prices permission (prices_visible says which); the modes are still returned. tax_code_unresolved: true means tax-code mode is selected but no active default tax code is set, so new invoices are untaxed. Changing these settings is not possible from any tool.
invoices_search
ChatGPTList invoices, paged (up to limit, default 25, max 100, plus nextCursor). Proposals and estimates are excluded (use proposals_search). query matches number, title, client name or PO number; number looks up one invoice by its human number (e.g. '26-6') and returns its id. Archived invoices are excluded unless include_archived is true. There is no 'Overdue' status; use each row's is_overdue, due_date and balance_due. total and balance_due are null without the invoices.view_prices permission. Scoped members see only their assigned clients.
invoices_send
ChatGPTSend an invoice to its client by email or SMS, as the app's Send email sheet does. Identify it by invoice_id or invoice_number (e.g. '26-6'). channel is 'email' (default) or 'sms'; the recipient defaults to the client's email or phone. to, cc and bcc must be saved on the invoice's client or its contacts, or be a team member or the workspace; others are refused. Email uses the workspace's email settings and invoice template, with the invoice button and PDF attached; template (see email_send_options) replaces the template, and subject and message override its subject and body. If from_inbox is omitted, the workspace's AI & MCP inbox is used, else the only inbox the user can send from; with several and no default, nothing is sent and the result lists them. cc/bcc replace the default copies. At most 10 recipients per email from a starter inbox and 50 from the user's own domain or mailbox (To + Cc + Bcc, workspace addresses excluded); over the cap nothing is sent. SMS texts the invoice link, with message as a note. Refuses when the invoice has no link yet. Sends a real message and bills for it. Held until the user approves it in the Workspace369 app.
invoices_update_draft
ChatGPTEdit an existing invoice by invoice_id or invoice_number. Changeable: notes (replaced), discount (discount_percent or discount_amount; 0 removes it), tax (tax_percent or tax_amount), deposit_percent, invoice_date / due_date (MM/DD/YYYY) or due_in_days, payment_term (printed label only), and project_id / unlink_project. Lines: add_services (same shape as invoices_create_draft), remove_services (by line name) and update_services (edit a line in place by line_number: description, details, quantity, unit_price). Line numbers are positions from invoices_get, resolved after remove_services and before add_services; an out-of-range number is refused and nothing is written. Totals, tax and paid state are re-derived. It sends nothing. Only drafts are editable by default; a sent invoice is refused unless allow_non_draft: true, since the edit changes what the customer sees at the public invoice link. A locked invoice (a payment or credit recorded, or any sent invoice when the workspace locks on send) is always refused. Payment state (see payments_record), status, number, client and currency never change.
knowledge_search
ChatGPTRetrieve entries from the workspace's AI knowledge base (policies, procedures, pricing rules, FAQs). query runs a semantic search that falls back to keyword matching; category filters (e.g. "Pricing", "FAQ"); with neither it lists the newest entries. Returns up to limit entries (default 3, max 25), content truncated to 1500 characters.
lead_sources_list
ChatGPTList the workspace's lead sources (where new business comes from) and which one is the default for new clients.
lead_sources_set_default
ChatGPTSet which lead source new clients get when none is named (an empty lead_source_id clears it). This changes every client created afterwards. It does not create a lead source; pass an id from lead_sources_list.
media_attach
ChatGPTAttach an already-uploaded workspace-storage file (by storage_path) to a schedule, an invoice, or an expense (entity_type: "expense", id from expenses_list; this is how a receipt is attached). No file bytes are accepted: the app uploads and registers the file first. kind: "photo" (schedules only; is_before marks a before/after job photo) requires an image; kind: "document" attaches any registered file. Invoices resolve by entity_id or entity_number (ambiguous numbers are refused); proposals and estimates are not valid targets. Re-attaching the same path is a no-op (deduped).
media_list
ChatGPTList the files attached to a schedule, an invoice or an expense: photos and documents, with file name, description and before/after photo kind from the workspace storage registry. kind filters to photo, document or all (default). Returns storage paths, never file bytes.
notes_create
ChatGPTCreate a note. body accepts simple markdown (headings, - and 1. lists, bold, italic, underline, strikethrough, links), which becomes the editor's formatting. By default the note is a workspace note visible to the team; is_personal: true makes it private to the caller. client_ids takes ONE client id and writes the note into that client's Notes tab (needs permission to edit clients; it is then not in the workspace Notes screen). folder_id must be a folder in the target space; without it the note goes to that space's default folder. A repeat call with the same idempotency_key returns the first note (deduped: true).
notes_get
ChatGPTFetch one note by id. Returns { note: null } if it does not exist, is trashed, or is outside this workspace (another workspace or a user's personal notes).
notes_search
ChatGPTList notes, paged (up to limit, default 25, max 100, plus nextCursor). scope "workspace" (default) returns workspace and client notes; "personal" returns the caller's own personal notes. Filters: query (title/preview), folder_id, tag (case-insensitive substring), client_id (notes attached to that client). Filters apply after the page is cut, so keep following nextCursor.
notes_update
ChatGPTUpdate a note by id (from notes_search): title, body (replaces the whole body; markdown becomes the editor's formatting) or tags (REPLACES the tag list). Only the fields passed change. Works on workspace notes, client notes (needs permission to edit clients) and the caller's personal notes. Trashed notes are not editable, and a password-protected note's body cannot be changed here.
payments_record
ChatGPTRecord a payment against an invoice (full, general or partial); this is how an invoice becomes paid. Identify the invoice by invoice_id or invoice_number. Needs a positive amount and a payment_method (Cash, Check, Credit Card, Bank Transfer or Other). payment_date ("YYYY-MM-DD") defaults to now; a date inside a closed accounting period is refused. The amount cannot exceed the outstanding balance. partial: true requires an amount below the invoice total. installment_id settles a payment-plan installment. The invoice's paid state is re-derived server-side; invoice pricing is not edited. The payment lands in the invoice's activity log and sends the invoice.* webhook events to any webhook URLs the workspace has set up, as a payment recorded in the app does. Nothing is charged and nothing is sent to the client. A repeat call with the same idempotency_key does not record the payment twice.
products_create
ChatGPTproducts_list
ChatGPTList the services/products in the catalog with pricing, paged (up to limit, default 25, max 100, plus nextCursor). By default only active, non-deleted services are returned; status (draft, archived or all) includes inactive ones. Each row's id is the argument for products_update.
products_update
ChatGPTprojects_create
ChatGPTCreate a project. Needs project_name. status is planning, in_progress, on_hold, completed or cancelled (display names like "In Progress" are accepted). priority is low, medium, high or critical. start_date/end_date are ISO-8601; a date-only value is that day in the workspace time zone. With a client_id the project inherits that client's visibility; a client-less project is visible to full members only. A repeat call with the same idempotency_key returns the first project (deduped: true).
projects_get
ChatGPTFetch one project by id. Returns { project: null } if it does not exist or is outside a scoped member's assigned clients.
projects_search
ChatGPTList projects, paged (up to limit, default 25, max 100, plus nextCursor). query matches name or client id; status and clientId narrow the results. Scoped members see only their assigned clients' projects.
projects_update
ChatGPTUpdate a project by id (from projects_search): name, description, status (planning, in_progress, on_hold, completed, cancelled), budget, priority (low, medium, high, critical) or notes. Only the fields passed change. Progress cannot be set; it is derived from task completion. Moving to in_progress or completed stamps the actual start/end dates.
proposal_templates_apply
ChatGPTReplaces a draft proposal's sections, design and settings with a template's, and its line items too unless replace_line_items is false. Totals are re-derived; the client, title and notes are kept. Only drafts can be changed.
proposal_templates_delete
ChatGPTDeletes a proposal template or section template from the library. Proposals already made from it are not changed.
proposal_templates_duplicate
ChatGPTCopies a proposal template into a new template, named name or after the original.
proposal_templates_list
ChatGPTList the workspace's proposal templates (id, name, theme, accent, section and line counts) and saved sections (id, name, type). kind: proposal, section or all (default). Templates start a proposal via proposals_create_draft template_id, apply to a draft via proposal_templates_apply, and saved sections insert via proposals_edit_sections from_section_template_id.
proposal_templates_rename
ChatGPTRenames a proposal template or section template.
proposal_templates_save_from_proposal
ChatGPTSaves a proposal's sections, design, settings and line items as a new proposal template. The proposal itself is not changed.
proposal_templates_save_section
ChatGPTSaves one content section of a proposal as a reusable section template. The proposal itself is not changed.
proposals_create_draft
ChatGPTCreate a draft proposal for a client. It is saved as a draft and never sent. Needs client_id and title; numbered from the workspace's proposal sequence (PROP-NNN). template_id (see proposal_templates_list) starts from a saved template's sections, design, settings, line items and notes instead of the workspace defaults. Each line_items entry is {product_id, quantity} (catalogue product; name and price read server-side) or {description, unit_price, quantity} (one-off), where unit_price is the price of one unit; without line items the proposal quotes zero. optional: true marks a client-selectable add-on (only on product_id lines). details is a line's printed description and body seeds one text section; both accept simple markdown. notes is internal and not shown to the client. Totals and tax are computed. A discount cannot be set here; use proposals_update_draft. Signature, deposit and validity terms come from the proposal defaults (proposals_get_defaults). The proposal inherits its visibility from the client. A repeat call with the same idempotency_key returns the first proposal (deduped: true).
proposals_design_options
ChatGPTList what a proposal's design can be set to: themes (font, default accent), accent presets, page backgrounds, cover styles, title placements, gallery layouts, section types, the 18 cover banners (id, name, preview_url) and the photo gallery categories. gallery_category pages through that category's photos (12 at a time; pass cursor = next_cursor for more); a photo's path is the gallery_path for proposals_update_design or proposals_edit_sections.
proposals_edit_sections
ChatGPTAdd, change, reorder or remove a draft proposal's sections (the editor's blocks); proposals_get returns the section ids. Four fixed sections lead every proposal (cover, header, line_items = the pricing table, totals): they cannot be removed or moved, and only the pricing table's title changes. Content sections: text (markdown body), timeline (timeline rows of {date, description}), terms (plain-text body, or terms_id from the terms library) and images (images replaces the gallery; image_layout grid, masonry, hero, stacked, showcase, comparison or carousel). Each image is {source, caption?, alt?} with source banner, gallery, workspace_file or url (an https link, downloaded into the workspace). highlighted tints a content section with the accent. after_section_id places an added or moved section (a fixed section's id = top of the content). from_section_template_id inserts a saved section (proposal_templates_list). Up to 20 operations run in order, all or nothing. Only drafts can be changed. A call that fetches a web link is held until the user approves it in the Workspace369 app.
proposals_get
ChatGPTFetch one proposal or estimate by id. Returns { proposal: null } if it does not exist, is an invoice, or is outside a scoped member's clients. Includes line items (name, quantity, rate, total, whether client-selectable), design (theme, colours, cover, title placement) and sections (ids, types, titles, text, images, highlights); change them with proposals_update_design and proposals_edit_sections. content_locked / content_lock_reason show whether it is locked (accepted, or deposit paid) and so changes only through a change order in the app; change_orders_pending counts change orders awaiting the client. total is null without the invoices.view_prices permission.
proposals_get_defaults
ChatGPTRead the acceptance terms of proposals: whether accepting requires a signature, whether it requires a deposit (and how much), and when it expires. With no arguments it returns the workspace defaults that seed new proposals (including validity_days); with proposal_id it also returns that proposal's effective terms (valid_until is an actual date) and the deposit amount resolved against its current total. Monetary values are null without the invoices.view_prices permission; whether a deposit is required is always visible. Scoped members see only their assigned clients.
proposals_search
ChatGPTList proposals and estimates, paged (up to limit, default 25, max 100, plus nextCursor). Invoices are excluded (use invoices_search). query matches title, number or client name; archived proposals are excluded unless include_archived is true. Each row carries sent_at, viewed_at, view_count, accepted_at, declined_at and a derived opened_by_client; these, not status, show whether the client has seen it. total is null without the invoices.view_prices permission. Scoped members see only their assigned clients.
proposals_send
ChatGPTSend a proposal to its client. Enqueues the send; the proposals worker mints the client link, marks the proposal sent and emails the client with the workspace's proposal email template. Requires proposal_id. Invoices or estimates, missing ids and proposals outside a scoped member's clients return 'not found' and nothing is sent. If from_inbox is omitted, the workspace's AI & MCP inbox is used, else the only inbox the user can send from; with several and no default, nothing is sent and the result lists them. The email goes to the client alone, so there is no Cc/Bcc to set. At most 10 recipients per email from a starter inbox and 50 from the user's own domain or mailbox (To + Cc + Bcc, workspace addresses excluded); over the cap nothing is sent. The result names the inbox and recipient. A repeat call with the same idempotency_key collapses onto one send. Sends a real email and bills for it. Held until the user approves it in the Workspace369 app.
proposals_update_defaults
ChatGPTChange the workspace's proposal defaults (Settings > Proposals): theme and accent_color for new proposals, require_signature, the deposit (require_deposit, deposit_type percentage or amount, deposit_value), validity_days (0 = no expiry) and notes_footer (pre-fills a new proposal's internal notes). Defaults apply to new blank proposals only; existing proposals and templates are unchanged. proposals_get_defaults reads the current values.
proposals_update_design
ChatGPTChange how a draft proposal looks (the editor's Design tab). theme (modern, minimal, classic or bold) sets fonts and spacing. Changing theme without accent_color resets the accent to the theme's default, as the editor does; pass both to keep a custom colour. accent_color is hex. page_background_color is the backdrop around the paper (hex or "default"). title_placement: on_image puts the title over the cover; below_image shows the image as a banner with the title under it. cover_image: source: "banner" + banner_id, source: "gallery" + gallery_path, source: "workspace_file" + storage_path, source: "url" + an https image link (downloaded into the workspace), or source: "none" to remove it. cover_image_style: fill, inset or fade. cover_focal_point: 0 (top) to 100 (bottom). Options come from proposals_design_options. Only drafts can be changed. Returns previous_design, so a change can be undone by sending those values back. A call that fetches a web link is held until the user approves it in the Workspace369 app.
proposals_update_draft
ChatGPTEdit an existing draft proposal, identified by proposal_id. Changeable: title, internal notes (replaced), add_line_items (same grammar as proposals_create_draft), remove_line_items (by line name), discount (discount_percent or discount_amount; 0 removes it), signature requirement, deposit (deposit_percent or deposit_amount; require_deposit: false removes it) and expiry (valid_for_days; 0 removes it). When lines, discount or deposit change, subtotal, tax (at the proposal's rate, or a product's own rate, on the post-discount subtotal), total and deposit are re-derived server-side; other edits leave the money unchanged. Only drafts are editable: a sent, accepted or declined proposal is refused (recall a sent one to draft with proposals_update_status or in the app), and an accepted or deposit-paid proposal is locked and changes only through a change order in the app. It cannot send, accept, decline, convert, delete, change the client or tax rate, or edit layout, theme or sections. Repeating add_line_items adds the lines twice.
proposals_update_status
ChatGPTRecord what happened to a sent proposal: intent "accepted" or "declined" when the client answered off-system (call, meeting, email), or "draft" to recall it so it can be edited again. reason is recorded only on a decline. Only a sent proposal can change state, the same rule as the Proposals screen. Marking accepted does not store a signature or typed name; those are written only when the client accepts through the public link. A locked proposal (accepted, or deposit paid) cannot be recalled or declined. This does not send anything and does not create an invoice.
recurring_plans_get
ChatGPTFetch one recurring invoice plan by id (from recurring_plans_search): everything a search row has, plus sending (auto or review), payment terms, how it ends, any pause or upcoming amount change, pending one-off lines, and its last few autopay charge attempts (status, decline code, next retry). Returns { plan: null } if the id does not exist or is outside a scoped member's clients. Amounts are null without invoices.view_prices.
recurring_plans_search
ChatGPTList recurring invoice plans (an invoice that repeats on a schedule, paid by autopay or a pay link). Filters: query (plan or client name), status (draft, scheduled, active, paused, ended), client_id, needs_attention (failed payment, client must confirm with their bank, new card needed, draft to review, Stripe disconnected). Each plan has its cadence, next invoice date, autopay state and card, and any retry in progress. summary totals this page: active plans, plans needing attention and monthly recurring revenue (monthly_amount normalizes weekly/quarterly/yearly plans to a month). Amounts are null without the invoices.view_prices permission. Scoped members see only their clients.
recurring_plans_send_pay_link
ChatGPTEmail the client the pay link for a recurring plan's open invoice (the one a failed autopay is retrying or waiting on, else the plan's latest unpaid invoice), as invoices_send sends an invoice: the workspace's template, signature and PDF, with the Pay button. Useful when autopay failed or the client must confirm with their bank. plan_id is required; message (markdown) replaces the default line about the card; from_inbox and idempotency_key work as in invoices_send. Refuses when the plan has no unpaid invoice. Sends a real email and uses one email credit. Held until the user approves it in the Workspace369 app.
reminders_close
ChatGPTClose a reminder by id (from reminders_list) so it will not fire. intent complete ticks it off (shown under Completed); cancel dismisses it (shown under Canceled, struck through). For a recurring reminder, complete closes the whole series, not only the occurrence that is due.
reminders_create
ChatGPTCreate a reminder: a notification that fires at a single point in time. It is not a calendar event, which occupies a time range with a start and an end. A standalone reminder needs a title and an ISO-8601 remind_at. For a reminder tied to a calendar event, pass the event_id from calendar_create_event or calendar_list_events and minutes_before (1440 = 24 hours); this creates a workspace reminder linked to the event and notifies the caller by default. event_id with remind_at sets an absolute time instead. For all-day events, time_zone applies: 24 hours before means 24 elapsed hours before midnight at the start of that date in that zone, not 9 AM. reminder_type is personal (default, notifies only the caller) or workspace (notifies the team members in recipients via channels, default push). Recipients are team-member names or emails; a non-member email must be saved on the client named in client_id or be a workspace address, otherwise the reminder is refused. Unmatched recipients are listed in unresolved_recipients. recurrence repeats it daily, weekly, monthly or yearly. A repeat call with the same idempotency_key is safe.
reminders_list
ChatGPTList the caller's reminders, sorted by time (up to limit, default 25, max 100), with the ids reminders_close needs. filter: upcoming (default; pending, future), overdue (pending, past), completed (ticked off via reminders_close with intent complete) or all (everything not canceled). scope: personal, workspace or all (default). Each row has done; the status value sent means delivered, not done.
reminders_update
ChatGPTChange an existing reminder by id (from reminders_list): title, description, remind_at (ISO-8601), priority or channels. Only the fields passed change. There is no separate snooze action: moving remind_at postpones a reminder and re-arms delivery, so one that already fired fires again at the new time. Editing keeps the reminder's id and its link to the invoice or task it came from, whereas closing and re-creating gives a new id and files the original under Canceled. A completed or canceled reminder cannot be edited. Times are ISO-8601 only.
requests_convert_to_client
ChatGPTTurn an inbound request into a CRM client and mark the request converted. A new client lands in the Lead stage, owned by the request's assignee, with the lead source from the workspace's list ("Website form · <form name>" for a form request), the same as the Convert button in Requests. The client id is derived from the lead's email (or phone when there is no email), so converting the same person twice, or after the server auto-converted them, links to the same client (created: false). A request with neither an email nor a usable phone number is refused. Needs Clients write access as well as Requests write access. Creating the client can start workflow automations that run on new clients, which may message the prospect. Held until the user approves it in the Workspace369 app.
requests_forms_list
ChatGPTList the workspace's inbound intake forms, so a request's form_id can be resolved to a name: slug (document id and public URL segment), name, template, layout, active flag and timestamps. Active forms only unless include_inactive is true. Creating, editing, publishing or pausing a form is not available through this API; forms are edited in Workspace369 under Requests > Settings > Forms.
requests_get
ChatGPTFetch one inbound request by id, including the full message and the recorded consent (SMS consent, transactional/promotional consent, terms accepted, and the exact consent wording shown). Returns { request: null } if the id does not exist in this workspace. Funnel answer content, internal comments, IP address and user agent are never returned.
requests_respond
ChatGPTReply to an inbound prospect by email or SMS. The recipient is read from the request itself: the To address cannot be overridden, no files are attached, and only that prospect is messaged. Email goes out as the app's Reply via email does: from an inbox, with the workspace's from-name, reply-to, signature and default Cc/Bcc; subject defaults to 'Re: your request' and body is markdown, with {{client.*}} merge fields filled from the request's name, email and phone. If from_inbox is omitted, the workspace's AI & MCP inbox is used, else the only inbox the user can send from; with several and no default, nothing is sent and the result lists them. cc and bcc are email-only, must be team, workspace or the request's client addresses, and are final lists that replace the defaults (the inbox's archive Bcc is always added). One email goes to at most 10 recipients from a starter inbox and 50 from the user's own domain or mailbox. SMS goes only to a lead who recorded SMS consent. A request marked 'new' becomes 'reviewed' after a successful send. This sends a real message to the prospect and bills for it. Held until the user approves it in the Workspace369 app.
requests_search
ChatGPTList inbound prospect submissions (leads from contact forms, Pages, Funnels and the inbound lead API), newest first, paged (up to limit, default 25, max 100, plus nextCursor). status is new, reviewed, converted or archived (one at a time). form_id accepts an intake-form slug or a funnel id (funnel leads are identified by their source, 'funnel:{id}'). query matches name, email, phone and message. form_id and query are applied after paging, so a page can be short; keep following nextCursor.
requests_update_status
ChatGPTMark an inbound request 'reviewed' or archive it. Archiving is terminal: there is no delete and no way back to 'new'. A converted request can be archived but not moved back to 'reviewed'. Converting is done by requests_convert_to_client. Returns changed: false if the request was already in the requested status.
retainers_get
ChatGPTFetch one retainer by id (from retainers_search): everything a search row has, plus how extra hours are handled (bill_next or track_only), what happens to unused hours, the prepaid top-up settings, the prepaid balance with the amount reserved for a refund in progress and what is still available (prepaid_available = balance - reserved), and its last 10 ledger entries in plain words. Returns { retainer: null } if the id does not exist or is outside a scoped member's clients.
retainers_search
ChatGPTList retainers: hour banks (hours), fixed monthly (fixed) and prepaid deposits (prepaid). Filters: query (retainer or client name), status, client_id, type. Each row has this cycle's hours (included plus any rolled-in, approved, pending approval and left; negative = extra hours), the extra-hour rate, the prepaid balance, the next invoice date, and billing: automatic, review_needed (an older retainer waiting for the studio to confirm billing) or manual. Hours and money are separate figures. Money fields are null without invoices.view_prices. Scoped members see only their clients.
search_workspace_messages
ChatGPTFull-text search across individual inbox messages (email, SMS, call notes), e.g. to find the message where something was said. Returns up to limit matches (default 25, max 50), each with its conversation_id, plus nextCursor when more exist. Callers see only the channels and clients they have access to.
task_board_schema
ChatGPTGet the writable field schema for one task board: its statuses, built-in task fields, and board-specific custom fields with types, required state, ids and allowed dropdown options. Use it for estimated_hours and custom_fields on task writes. Board tags are listed by task_board_tags_list.
task_board_tags_list
ChatGPTSearch or page the tag catalog of one accessible task board, including tags not used by currently loaded tasks. Pass next_after_id as after_id with the same board and query to continue; pages are live, not a snapshot. If catalog_ready is false the catalog is not initialized.
task_boards_create
ChatGPTCreate a task board with the standard To-do / In Progress / Done columns. If the name is taken it is numbered ("Launch (1)") and the response says so. It cannot rename, delete or reconfigure a board.
task_boards_list
ChatGPTList the workspace's task boards with their ordered statuses. Task writes need a board_id and a status name that exists on that board. Scoped members see only boards they can access.
tasks_create
ChatGPTCreate ONE task on a board. Needs board_id (from task_boards_list) and title. status is validated against the board's statuses (names normalized) and defaults to the board default; priority is 1 low, 2 medium, 3 high. assignees are workspace member ids. subtasks are checklist titles created with the task. custom_fields is [{field, value}] using the field ids, types and dropdown options from task_board_schema (dates ISO 8601, checkboxes true/false, numbers numeric). start_date and due_date are epoch milliseconds or null; blocked_by lists ids of tasks on the same board that must finish first. A task whose exact title (case- and whitespace-insensitive) already exists on the board is skipped and returned with deduped: true. description is plain text (markdown renders literally). Scoped members can create only on boards they can access. A repeat call with the same idempotency_key returns the first task.
tasks_get
ChatGPTGet the details of one task on a board: description, status, priority, dates, tags, assignees, estimate, star state, custom-field values (resolved against the board schema) and subtasks. Needs board_id and the task id (or a title, accepted only when it identifies one unambiguous task; ambiguous titles are refused with the candidates).
tasks_search
ChatGPTList tasks across the workspace's boards, paged (up to limit, default 25, max 100, plus nextCursor). Filters: query (title), boardId (from task_boards_list), status (names normalized, todo matches To-do), priority (1 low, 2 medium, 3 high), assignee_id (a member id), due_before / due_after (ISO-8601 dates), tags (matches any) and is_starred. Overdue work is assignee_id with a due_before of now. Filters apply after the page is cut, so a filtered page can be short; keep following nextCursor. Rows carry an 80-character description preview; include_full_descriptions: true returns complete descriptions. Scoped members see only tasks on boards they can access.
tasks_subtasks_update
ChatGPTAdd checklist items to an existing task and/or complete or reopen existing ones. Locate the task by id + board_id. add appends titles (titles already on the checklist are skipped and reported). set_completion ({subtasks: [titles], completed}) is all-or-nothing: every title must match exactly one existing subtask, otherwise nothing changes. Returns the full checklist. To create a task with a checklist, pass subtasks to tasks_create.
tasks_update
ChatGPTUpdate a task by id on its board_id. Only the fields passed change. title, status (validated against the board's statuses), description (null clears), priority (1-3), estimated_hours (null clears) and is_starred are replaced as given. tags, assignees and blocked_by REPLACE the stored list ([] clears). custom_fields ([{field, value}], using ids from task_board_schema) is merged into the stored values; clear_custom_fields lists field ids or unique names to remove (a required field cannot be cleared). start_date/due_date are epoch milliseconds (null clears). A blocker must be an existing task on the same board, and an edge that would create a dependency cycle is rejected. Subtask changes go through tasks_subtasks_update. Returns the merged task, or an error if the task does not exist or is on a board outside a scoped member's access.
team_member_activity_list
ChatGPTList what a team member has recently done in the workspace (the audit trail behind Team > Member Activity): the action, the record touched, when, and the names of the fields that changed. It reports which fields changed, not what they changed to: old and new values are not served. Messages and Ask AI sessions are listed without a name, because their stored name is their content; use target_id to refer to them. since / until are epoch milliseconds; limit defaults to 25, max 100.
team_member_availability_get
ChatGPTRead a team member's weekly working hours. member_id selects one member; omit it for the whole roster. All seven days are always returned; active: false is a non-working day. Slot times are wall-clock times in time_zone; time_zone_source is member (their own zone), workspace, or default (a UTC fallback nobody set).
team_member_availability_set
ChatGPTChange a team member's working availability, one day at a time, and/or the time zone those hours are in. Only the days passed change (days you omit are left as they are); clearing a day means naming it with active: false or empty slots, and an active day needs at least one slot. Times are 12-hour strings like "8:00 AM" in the member's time zone. time_zone must be an IANA zone naming a place (e.g. "America/Denver"); fixed offsets like "+05:00" or "EST" are refused. Pass days, time_zone, or both. The member is not notified. Any caller holding team.edit_permissions may edit any member, including the workspace owner, as in the app. Held until the user approves it in the Workspace369 app.
team_members_list
ChatGPTList the workspace's team members: the assignable id, name, email, role, invitation status and active availability days of each. Ids are used for event assignees and calendar_check_availability.
team_members_search
ChatGPTFind a team member by name or email. An unambiguous match returns that member (id, name, email, role, status); an ambiguous query returns up to 5 candidates.
time_entries_create
ChatGPTLog a completed time entry for the calling user (entries for other members are not supported). Needs start_time (epoch ms, within the window the server enforces) and duration_minutes (0-10080). billable defaults to true. A billable entry resolves the workspace's own rate (project, then member, then workspace default) when hourly_rate is omitted; a rate other than the resolved one needs the billable-rates edit permission. A non-billable entry always has rate 0. total_amount is computed (duration / 60 x hourly_rate when billable, else 0). A repeat call with the same idempotency_key returns the first entry (deduped: true).
time_entries_list
ChatGPTList time entries, paged (up to limit, default 25, max 100, plus nextCursor). startTime/endTime (epoch ms) bound the entry start and are applied to each page, so keep following nextCursor. A scoped member sees only the entries they logged plus entries for their assigned clients.
time_entries_search
ChatGPTList time entries with filters, paged (up to limit, default 25, max 100, plus nextCursor). clientId, projectId, taskId and billable narrow the results; startTime/endTime (epoch ms) bound the entry start. Filters are applied to each page, so keep following nextCursor. A scoped member sees only the entries they logged plus entries for their assigned clients.
time_entries_update
ChatGPTUpdate one of the caller's own time entries by id. Only the fields passed change. Editable: start_time/end_time (epoch ms), duration_minutes, billable, hourly_rate, currency, description and client_id/project_id/task_id (re-pointing also re-resolves the stored display name). total_amount is recomputed when duration, rate or billable change. Marking an entry non-billable clears its rate; marking a $0 entry billable applies the workspace's resolved rate; a different hourly_rate needs the billable-rates edit permission. A timer started in the app can be stopped (is_running: false) or paused and resumed (is_paused, total_pause_ms, last_pause_at); a stopped entry cannot be restarted. Billing, timesheet-status and client-scope fields cannot be set. Invoiced, submitted, approved and period-locked entries, other members' entries, and restarting a stopped timer are refused.
timesheets_list
ChatGPTList weekly timesheet submissions, paged (up to limit, default 25, max 100, plus nextCursor). Each carries the member, the week's period, the approval status and server-computed totals (minutes, billable minutes, amount, entry count). status and memberId filters are applied to each page, so keep following nextCursor. Callers see only their own submissions unless they can approve timesheets.
workspace_business_summary
ChatGPTCounts only: the authoritative client count, invoice figures (total count, unpaid count, unpaid outstanding sum), today's scheduled event count and the number of catalog services. Invoice figures come from a Firestore aggregation over the invoices collection, which also stores proposals and legacy estimates. A figure that cannot be answered authoritatively is null ("unavailable"), not zero. Optional timezone (IANA, e.g. 'America/New_York') picks which day 'today' means; default UTC.
workspace_get_context
ChatGPTReturn the current workspace (id, name, plan), the caller's permissions and token balance. Takes no arguments.
workspace_schema
ChatGPTDescribe the record types you can work with through this server (clients, requests, proposals, contracts, invoices, projects, tasks, calendar events, products): the tools that read and write each, the fields their create tool accepts, and this workspace's client stages, client custom field definitions, calendar categories and lead sources (definitions only, never record values). Call it after workspace_get_context and before building screens. For task statuses and custom fields use task_board_schema.