apply_quote
ChatGPTCommit a Quote to the ledger. Pass the quote_id of the discover_price / price_quote_component result the chat agent has confirmed (single high-confidence quote, or the provider's selection when multiple quotes were presented) — the id, not the Quote object; the server holds the quote. This tool reads the quote's components and emits the right ledger entries — today that means an invoice per account (one event per account, grouping multi-attendee classes into household invoices). In the future this is also where drawdown / package consumption / other non-AR ledger movements will dispatch from based on the quote's shape; the chat agent doesn't need to know which. Finality — committing is permanent Once a Quote is applied, every one of its components is on the ledger PERMANENTLY. There is no edit, no replace, no re-apply. The ONLY way to undo a committed charge is create_reversal. This is unconditional — it holds for every Quote, every time. Consequence for amendments: when the provider adds something to an event you have ALREADY applied ("also charge $15 for materials", "add a late fee"), you MUST price and apply ONLY the new item — a fresh Quote containing just the added component(s). NEVER re-discover_price the whole event and re-apply_quote it: the original components are already committed, so re-applying double-books them. The amendment is a new, separate charge. If the provider instead wants to CHANGE or CANCEL an already- applied charge, that is a reversal (create_reversal) of the original event, optionally followed by applying a corrected Quote — never an in-place edit. Don't pass a low- or none-confidence quote. Confidence lives on the Quote — the chat agent is responsible for the disambiguation step (auto-apply single high, confirm single medium, present alternatives for multi-quote) BEFORE calling this tool. book_id will be the default book if not specified; in general, there's no need to specify it. posted_at if not specified defaults to the event's occurrence date (the posted_at the quote was discovered with) — a past date anchors to end-of-day in the org timezone; today keeps the live time. Pass it only to override that. due_date if not specified is taken from the due_date emitted by the pricer (carried on each component; computed there from the pricelist rule's payment timing and the event's posted_at, timezone-anchored). All components must share one due_date. Pass an explicit due_date only for manual corrections. If the pricer left due_date null (pricelist was silent on payment timing for the matched rule), this tool raises a missing_due_date ToolError instead of silently defaulting. On that error: ask the provider when the invoice is due. IMPORTANT: the answer is almost always a repeating org-level billing policy ("end of the month", "net 30", "due immediately", "first Monday of next month", etc.) — not a one-off date. Default behavior on the provider's answer: 1. Call update_pricelist to record the timing in the pricelist prose ("…due at the end of the month"). Read the current pricelist via get_pricelist, append the timing language to the matching rule, and write back. 2. Re-run discover_price so the pricer extracts the new timing into a concrete due_date on the resulting Quote. 3. Call apply_quote again. Only pass due_date as an explicit arg here if the provider says something that clearly signals a one-off — e.g. "just for this one, charge it tomorrow" or "this client prepays this specific session." Repeating patterns belong in the pricelist; future invoices need to inherit them without re-asking the provider every time. If in doubt about whether the provider's answer is a rule or a one-off, ASK them: "Should I make this a standing rule for this service, or just for today's invoice?" Don't guess. Recording a one-off as a rule pollutes the pricelist; treating a rule as a one-off forces the provider to answer the same question on every future invoice. On return, you'll get the full event payload(s). One event per account is emitted when multiple customers …
create_contact
ChatGPTHow to create a contact. At a minimum, provide a name -- either first or last -- and one or more tags. Contact tags depend on the current user's role: - For a user-provider: tag as 'attendee'/'client' (the client/customer who receives the service) or 'payer' (who pays on the account, typically a parent). - For a user-customer: tag as 'provider' (the service provider). A contact is one person. An account is a grouping of contacts (e.g., siblings, family members). To associate with an existing account, pass in an account_id (discoverable via get_contacts or a previous create_contact call). If the account_id is None, a new account will be created. NEVER invent an account_id — only use one returned from a previous call, or None. When creating a new account and the contact is not a payer, offer to either make the contact a payer or to add a billing contact to the account. Example: adding two contacts to the same account: 1. create_contact(name='June', tags=['attendee'], account_id=None) creates account 2. create_contact(name='Jerry', tags=['attendee'], account_id=<June's account_id>) Account naming: accounts are named after the attendee/client, not the payer. For example, if Jerry is the payer and June is the attendee, name the contact (and thus the account) after June. If there are multiple attendees, use the first attendee's name or a shared name if appropriate. In general, unless it's clear from context, we should ask what account the contact should be associated with. Prefer adding a contact to an existing account (as shown in the example above) rather than creating a new one and moving the contact later. We want to avoid gratuitously creating new accounts.
create_offered_service
ChatGPTHow to offer a new service. An offered service is added to the catalog of services the business provides to clients. Service names should be CATEGORY-LEVEL — e.g. "Consulting", "Tutoring", "Piano", "Ballet", "Soccer practice", "Art". DO NOT bake variants into the name (no "Piano - 30 minutes" or "Ballet - level 3" or "Friday soccer - older group"). Variants are a pricing concern; they live in the pricelist as rules ("Piano lessons are $55 for 30 min, $90 for 60 min"), not as separate offered services. Multi-quote disambiguation at pricing time handles the cases where the same service is priced differently depending on context. Don't include client names in the service name. The service is what's offered to the catalog, not a specific instance. Before creating a new service, check get_offered_services with similar_name to see if a category-level match already exists. If one does, use it. Do NOT create a new service just because the user mentioned a duration / level / format detail — pass those as event metadata when pricing. Services no longer carry price info, balance_type declarations, or payment terms. The pricing engine owns all of those — the pricer determines per-line price, balance_type, and due_date based on the org's pricelist. Services are just named handles for what's being offered. If the user mentions a price for this service while creating it (e.g. "create a piano class at $55/lesson"), you MUST also call update_pricelist to record the rule — create_offered_service only creates the name handle, not the price. Call get_pricelist first to read the current text, then pass the full new pricelist (existing rules + the new one) to update_pricelist.
create_org
ChatGPTCreate a personal organization for the current user. The user becomes the administrator with full access. A default ledger book is created automatically. The new org is automatically selected as the current context. Use this when list_my_orgs returns empty (user has no orgs).
create_payment
ChatGPTHow to create a payment entry in the ledger. While most of these parameters have a straight-forward meaning, the payer field deserves some explanation. A payer is a contact, typically tagged as a payer. It may be the user wants to use a contact that isn't tagged as a payer. It's probably worth it to be reluctant to use such a contact (unless it has no tags) and then tag the contact as such if used. When creating a payment event, ensure we know the method. Don't just assume it's a particular method unless it's very clear from context. Internally, the ledger will use a waterfall mechanism to apply the payment against open invoices. That means there's no need to ask the user how they want the payment applied: specifically, don't ask about invoices. In general, you'll never need to specify the book_id. As with apply_quote, the full event payload is returned. When inspecting the result, the account is the payer's account and the subaccount is the actual payer.
create_reversal
ChatGPTHow to reverse an entry in the ledger. The references field points to the event being reversed. Discover the event_id from the chat history, or by querying the ledger for the event to reverse. You normally do NOT pass effective_at. The reversal is POSTED now but is automatically back-dated to the original event's effective_at, so it nets out in the original's period rather than today's. Pass effective_at only to override that. If you can't find an event_id because it fell out the chat history and it hasn't yet been processed, let the user know to wait a bit for the event to process. Also, you can't reverse a reversal event. In that case, just re-create the event based on the event that was reversed. In general, you'll never need to specify the book_id. As with apply_quote, the full event payload is returned.
discover_price
ChatGPTDetermine the economic impact of a service event by consulting the org's pricelist. Reach for this as the FIRST step when the user reports a service event — don't interrogate the provider first. Resolve the inputs with tools, not questions: get_contacts to find the attendees, get_offered_services to find the contract. Do NOT ask whether a customer is on the list, what the service costs, or when it happened — the tools and the pricer surface every gap. Ask the provider only once something genuinely missing has been reported (e.g. an unpriced component). Describe the fact event: contract (the offered service / class), the list of attendees (customers), and optional event-level metadata (head_count, duration, qualifiers — anything the glossary flags as relevant). Do NOT enumerate activities or line items — the pricer decides those from the pricelist. Amending an already-applied event: if you have ALREADY called apply_quote for this event and the provider now wants to ADD a charge ("also add materials", "tack on a late fee"), describe ONLY the new thing here — not the whole event again. The original charges are already committed; re-discovering the full event and re-applying it double-books them. See apply_quote "Finality". provider defaults to the current user — DON'T ask who taught / ran the session. Only pass provider when the user explicitly says someone else ran it (e.g. a multi-instructor org where the user is recording on a colleague's behalf). posted_at is WHEN the event happened. One test decides whether to pass it: did the user's message contain a time cue? - YES — "today", "yesterday", "last Tuesday", "on the 3rd", an explicit date — you MUST pass posted_at, resolving their words to that date. A user who wrote "today" gave a cue: pass it. A stated date lets the pricer return a high-confidence quote that applies WITHOUT a separate date confirmation. - NO — the message said nothing about timing — OMIT posted_at. Do not ask the user for it, and do not pass a date you assumed (believing it was today is an assumption, not a cue the user gave). Omitting is deliberate signalling, not a fallback: the pricer then assumes today, caps the quote at medium confidence (never high), and notes the assumed date in the quote's reasoning. That medium is your signal to confirm the date with the provider before applying (see the Workflow section). Passing a date you assumed defeats this — the pricer cannot tell an assumed date from a stated one — and an applied charge is permanent, so a wrong date then needs a reversal to fix. Service-name lookup: when the user names a service (e.g. "piano session"), look it up in the catalog via get_offered_services(similar_name=...). If exactly one service matches, USE IT — don't ask the user to pick from invented variants. Variants (30 vs. 60 min, group vs. private) are pricelist rules keyed off event-level metadata, not separate services. Event metadata: pass everything that might be priced — the activities present, duration, head_count, format, qualifiers. If you suspect the pricelist depends on a fact the user didn't mention, ask naturally and pass it through. Returns a list of Quote — each is a coherent interpretation of the event (a complete set of line item components + an overall confidence + reasoning). Components may be: - Priced from the pricelist: amounts=[...], source=pricelist - Unpriced ('amount' in requires, no rule matched): amounts=[], source=pricelist, reasoning describes what's missing — fill in via price_quote_component or update_pricelist per prefer. - Ad-hoc (filled by you from provider input via price_quote_component): amounts=[...], source=adhoc - No-amount ('amount' in proscribes, bookkeeping activity like makeup_session that drains a prior hold): amounts=[], source=pricelist — FINAL, not unpriced. Don't fill it in; the apply layer raises proscribed_field if you do. The unpriced / no-amount split keys off requires / proscribes, NOT the activity name — read the fields; don't…
generate_payment_link
ChatGPTHow to generate a payment link a customer can open to pay an account's outstanding balance. This is the full customer-portal link (the same one used in unpaid-balance SMS reminders, but not shortened), valid for two weeks. Pass either an account id or a contact id: - an account id resolves to the payer-preferred contact on it - a contact id resolves to that contact's primary account Returns the link plus the account and contact it resolved to. Present the url as plain text exactly as returned — do NOT wrap it in a markdown link or anchor, so the provider can copy-paste the raw URL into an email.
get_account_aging
ChatGPTHow to query account aging. Use this query to discover things about accounts like the outstanding balance or days past due. In the result set, you'll see fields like balance_0_15 which indicate the day range for how long a balance has been on the books. As with the get_ledger query, you almost never need to specify the book_id (it's defaulted). provider restricts scope to particular members (providers) in your org. Two query styles, by what they produce: - Ordered by name similarity: similar_name ranks the results by how closely a contact name matches the given text. It orders, it does not restrict — every in-scope account is returned, just sorted by closeness. - Restricted to specific accounts or customers: account returns only the given account ids; customer returns only accounts associated with the given contact (customer) ids. Both take ids, not names. prefer_balance and prefer_days also order rather than restrict: prefer_balance ranks higher balances first, prefer_days ranks greater days past due first. Use join_customers=True to include customer information in the results. This will add a customers field to each result row containing an array of complete customer, i.e. contact, records. Each customer record includes id, data (names, phones, addresses), and tags. This ensures one row per account even when multiple customers are associated with it. Treat the default limit as a strong hint for how many items should be fetched at a time and depend more on pagination and/or query restrictions over returning everything.
get_account_notification_state
ChatGPTHow to get the current account notification state. About some of the returned fields: last_at indicates when the last notification was sent to that account next_at indicates when the next notification may be sent to that account reasons indicates the reasons why a notification will NOT be sent Note that these are account-level notifications. If at least one contact can receive a notification, then that account will get notified. Interpreting reasons can be confusing. Some reasons are in negative form like 'notify::not_today' and others are in positive form like 'contact:receiver:mobile' (meaning a contact doesn't have a mobile number); either way, they indicate why a notification will not be sent. If you are showing reasons, try to interpret them into a more natural language.
get_connectors
ChatGPTHow to fetch connector info. Connector status is important to determine whether or not a connector can be updated or created: 'new' == connector has been created and can be updated 'inactive' == connector is inactive but can be moved to 'new' 'ready' == connector is running 'verifying' == connector is doing setup/authorization to move to 'ready' 'failed' == connector failed to verify (terminal state) 'released' == connector is explicitly marked dead (terminal state) Use latest_mode to fetch latest connector for service: 'available' == prefers connectors with 'new' status 'running' == prefers connectors with 'ready' status 'recent' == prefers the most recently updated Pay attention to the usable field to determine if connector is, in fact, usable. The unready field will provide reasons the connector isn't usable.
get_contacts
ChatGPTReturns a list of contacts with names similar to similar_name. Results are paginated — use offset to fetch additional pages if needed. Use tags to filter only those tagged with at least one tags item. Use prefer_tags to boost score by each tag that overlaps. Use contact_ids to restrict search to only the specified contact_ids; otherwise None. Contact ids are not user facing, use only the name field to identify the contact. Contact tags depend on the current user's role: - For a user-provider: contacts are tagged 'attendee'/'client' (the client/customer who receives the service) or 'payer' (who pays on the account, typically a parent). When searching for a client, use both 'attendee' and 'client'. - For a user-customer: contacts are tagged 'provider' (the service provider). In our parlance, 'client' and 'customer' are interchangeable. Tags are not user facing; call them 'clients', 'billing contacts', and 'providers' respectively. An account is a grouping of contacts — there can be multiple contacts per account. When a user refers to an "account", they typically mean the group (e.g. a family), not an individual contact. Each contact returned will include its associated account. This is the preferred query for getting contact/account information. The list returned is sorted from highest to lowest 'score'. By default the limit is set to 5; if limit is None, all will be returned. Unless explicitly specified, restrict status to 'active'.
get_current_time
ChatGPTReturns the current local time in form of <hour>:<minute> <AM or PM> If no timezone is specified, the default timezone is used
get_glossary
ChatGPTReturn the org's glossary — both the platform-level layer (legal balance_type values and other ruleset-coupled vocabulary) and the per-org layer (custom contact attributes, custom metadata fields the org has defined). The platform layer is universal across all orgs and inherited automatically; it's exposed here as read-only reference data. The per-org layer starts empty and grows via update_glossary. Both are returned together so callers don't need a separate fetch to learn the platform vocabulary. Note: get_pricelist also returns this glossary as a nested field (so the common authoring flow needs only one read). Use this standalone tool when you just want the vocabulary without the pricelist context. Use when: - You need to look up what custom attributes / fields are defined. - You're about to call update_glossary and want to consolidate with existing definitions. - Displaying or auditing the org's vocabulary. Do NOT pass the platform field's content into update_glossary — it's frozen reference data, and writing it back would intern stale copies into the per-org row.
get_ledger
ChatGPTHow to query the ledger. Always use the production ledger. Leave the book_id field empty (use the main book on the current org). Note that the current org is determined outside of this function interface. For current balance inquiries, prefer get_account_aging. Use get_ledger only when you need transaction-level detail or to compute a balance at a specific past date. Always limit your query to no more than 20 results. Use offset to paginate for more results. Don't just increase the limit. Be sure to inform the user that there may be more pages of data after returning results. When you're aggregating on a window field, you must also specify the field filter value in the window arg. To match any (not null), use ''. When querying for a specific customer, find and specify the account id in the `account` field and always leave the `customer` field empty. Don't use any other window field, instead use the full account data to answer the specific question. Most of the information the user will be interested in is in the 'customers' journal, which is the main accounts receivable journal. Use this journal for most queries. You can read revenues from the 'revenues' journal. The 'payments' journal contains payments that were pulled from a feed but we don't yet know who made the payment. Regular payments will be credited to the 'customers' journal. The ledger consists of entries, which is a single debit or credit journal entry. The entries are grouped by transactions, represented by a single transaction_id. The ledger is double entry. A transaction will always balance, meaning that the sum of the debits and credits will always be zero. Generally, the user is interested in a single transaction so, you should almost always aggregate on 'transaction_id' and set transaction_id='' in the window. If you really need to know the underlying entries, you can add 'entries' to the include list to get all the detail (e.g. how a payment was allocated to different invoices and/or customers). That said, you should show one line per payment and only describe the allocation detail when asked about a specific payment. For payments, the 'subaccount' field in the results contains the contact id of the payer. To maintain chronological order when aggregating, also include 'posted_at' in the aggregations as the first element. The date you should reference is always the posted_at date. Never use the event_at date. Use posted_from and posted_to to filter transactions by their posted date range. Note that you'll need to provide the correct range between posted_from and posted_to. Specifically, if asking about, for example, today, ensure that from and to are a day apart. Use date_period to group results by time periods ('day', 'week', 'month', 'quarter', 'year'). When date_period is specified, it automatically groups by the time period using 'posted_at'. You don't need to include 'posted_at' in the aggregate list - it will be added automatically. When displaying a date to the user, it should be in human-readable format (e.g., 'August 1, 2025'). Tutor, teacher, coach, employee, staff, subcontractor are all synonyms for provider. Class, service, program, course, lesson are all synonyms for contract. Session is a synonym for an 'invoice' transaction_type. Do not show id values in the response, unless absolutely necessary. When describing the results, try to show each item as a sentence with names for customer, payer, provider and contract (aka service offering), if possible. When aggregating (balances, totals, sums, counts, etc), you should almost always set reversal=False in the window arg. Reversals are corrective entries that cancel out previous transactions. Only include reversals when the user specifically asks about reversed or voided transactions. Identify whether the request is about transactions (sessions, payments, invoices, etc) or balances. To get customer transactions, specify the filters in the `window` arg but don't use aggregations. To get balances, specify the filters in the window arg and also include them …
get_me
ChatGPTReturns current user's data + profile + identities (the first being primary). The profile.name field has the current user's name. The tags field indicates the user's role(s): a user-provider offers services (the typical user); a user-customer is a customer of one or more providers. A user can be both.
get_memberships
ChatGPTGet the list of members/providers associated with the current org. Typically, we only care about active members. The who fields are the memembers whereas the which fields are the org. Use who_ids to constrain to query to only those members. Results are paginated — use offset to fetch additional pages if needed. The roles field indicates what roles the org member has been provisioned for. The 'admin' role is an owner of the org. Do not show the provider id unless absolutely necessary.
get_offered_services
ChatGPTHow to get a list/catalog of services or classes currently being offered. The id (or service_id) is also the contract id. The price field in data is a default price. Unless explicitly specified, restrict status to 'active'.
get_org
ChatGPTReturns the current org and org setting (if available). An org and a business are synonymous. For a user-friendly org name, look in setting for a setting.profile.name. If that's unavailable, decode the org name field matching the form: 'classpay-<who_id>'. Then, fetch the member of the org for that who_id. This is the creator of the org. Use the creator's name or username as the user-friendly org name. The setting object has both the profile and customer configuration data.
get_pricelist
ChatGPTReturn the org's currently-active pricelist (the prose source of truth for pricing rules), bundled with the glossary needed to author rules. For a brand-new org that hasn't authored a pricelist yet, data is an empty string and version / status / updated_at are null — that's the normal initial state, not a failure. glossary is always populated (platform layer is universal; per-org layer may be empty). Pass version to read a specific prior version instead of the active one — e.g. to recover a rule that a later update_pricelist dropped, so you can re-author it back in. It accepts a relative offset too: -1 is the previous version, -2 two back. Omit for the current active version. The bundled glossary field is what makes this the one-stop fetch before authoring: when constructing a new pricelist via update_pricelist, you need to know what balance_type values are legal (from glossary.platform) and what custom terms the org has defined (from glossary.data). No separate get_glossary call needed. Use this whenever you need the current rules — before any update_pricelist, after discover_price returns confidence=low/none, when displaying the pricelist to the provider, when verifying a rule the user asks about. When showing the pricelist to the provider, present ONLY the data field (the prose) — optionally with a brief "last updated" note derived from updated_at. Do NOT mention version, status, or any field from glossary (neither platform nor data). Those are for YOUR reference when authoring or pricing, not for the provider to see. Mention version/status/glossary only when the user explicitly asks ("what version is this?", "what balance_types are available?", etc.). Do not surface internal field names (balance_type, due_date, etc.) when displaying the pricelist — the provider thinks in business terms ("a session", "billed after class"), not schema terms. If you need to explain a concept (the user asks "what happens when someone misses a class?"), use the natural-language meaning from the glossary, not the field-name vocabulary. Consolidation hint: over time the pricelist may accumulate redundant or contradictory rules. If it looks unwieldy (lots of rules, overlapping conditions, obsolete entries), surface that to the provider — they may want a consolidated rewrite. Apply by constructing a cleaned-up version and calling update_pricelist with the consolidated text. Consolidation is a deliberate, provider-initiated action, not automatic.
get_quotes
ChatGPTList this context's quotes, filtered by the given parameters. With no arguments, returns everything for the context. Plain filters (each None = no filter): - status — lifecycle: complete/incomplete (pending) or applied (committed). - quote_ids / batch_ids — by quote id, or by event batch. include surfaces normally-hidden quotes: - stale — quotes past their expiry or priced against a superseded pricelist are hidden by default; pass include=[stale] to see them. anchored_only filters by whether a quote's batch is decided (a sibling has been applied, settling the event): True keeps only quotes in decided batches; False applies no batch filter (all). Left unset it resolves from status: pending → open batches only (live options), applied → decided batches (the recorded decisions), all/mixed → no filter. So status=[complete], anchored_only=True lists the losing alternatives of already-decided events ("what we passed on"), while anchored_only=False widens a pending query to include them. A returned quote is not a guarantee it can be applied — apply_quote still rejects stale / re-billed / already-resolved quotes with a specific error; re-run discover_price to refresh when that happens. Rows whose stored payload no longer decodes are skipped.
get_today
ChatGPTReturns today's date and the day of week in the form of <day of week>, <month> <day>, <year> If no timezone is specified, the default timezone is used
list_my_orgs
ChatGPTList all organizations the current user belongs to. Returns a list of orgs with their IDs, names, and basic info. Results are sorted with non-personal orgs first, then personal orgs. May return a single org if user has an override org configured. ORG SELECTION FLOW: If you get an "org context required" error, follow this flow: 1. Call get_me - if profile.data.selected_org_id is set, call set_current_org with that ID to restore the selection 2. Otherwise, call this tool (list_my_orgs) to discover available orgs: - If 0 orgs: Use create_org (it auto-selects the new org) - If 1 org: Auto-select it with set_current_org - If multiple: Ask user which org, then set_current_org Once selected, it persists across sessions.
move_contact
ChatGPTMove a contact from current account to account to_account_id. If to_account_id is None, a new account is created and used. to_account_id may also be a contact id associated with an account; in that case it is resolved to that contact's account.
price_quote_component
ChatGPTFill in an ad-hoc amount on a quote's unpriced component. Use when discover_price returned a Quote with a component that has amounts=[] (no pricelist rule matched) and it should be a ONE-OFF charge — not a standing pricelist rule. This is the DEFAULT resolution for unpriced components whose prefer field is adhoc (materials, damage, adjustment) — pricing for those varies per occurrence. It's also correct for a prefer=pricelist component when the provider has explicitly said this specific charge is a one-off ("just this once"). For a standing/repeating charge — the default for prefer=pricelist activities — use update_pricelist + re-discover_price instead, so future events inherit the rule. The targeted component's amounts is set to [amount], its source becomes adhoc, and its reasoning is replaced with the provided text (typically a short description of why this one-off applies, e.g., "replacement music stand, broken in today's class"). Other components are passed through unchanged. due_date: leave OMITTED to keep the component's existing due_date. The pricer anchors an unpriced component's due_date to its sibling priced components when they share one cycle (e.g., a books charge inherits the session's end-of-month timing) — that inherited value is usually what you want. Pass due_date ONLY to override: - the provider wants different timing for this specific charge ("books are due now, even though the class is end-of-month") - the component's due_date is still null (the pricer couldn't anchor it — sibling charges had differing due_dates, or there were no priced siblings). In that case ask the provider when it's due and pass the answer here. If you leave the component's due_date null, apply_quote will reject the Quote with missing_due_date. Returns the augmented Quote — its id is unchanged. Pass that id to apply_quote to commit, OR back through discover_price(quote_id=...) first if you suspect the pricelist has dependent rules that might trigger now that this component has an amount (bundle discounts, tier thresholds, etc.).
set_current_org
ChatGPTSet the current organization context for subsequent operations. Most financial operations require an org context. This selection is persisted to your profile, so you only need to set it once. When to use: - After list_my_orgs returns exactly 1 org (auto-select it) - After the user chooses from multiple orgs - After create_org to set the newly created org as current
update_connector
ChatGPTHow to update a connector. The unready field in the results indicate the reasons for a connector to be unready for use. An empty unready means there's no reason it won't work.
update_contact
ChatGPTHow to update a contact. If arg is None, then no change will be made. Note that these fields will overwrite what's currently stored, so its important to fetch the contact first and patch the data that's changing. When updating name, be careful that you don't end up setting an empty string on the computed full name (composed of first, last, middle, title, suffix, & nick). In general, we want the first or last name to be non-empty. While you can never delete a contact, you can mark it inactive. This is the best way to hide a contact. Try not to make any other changes to the contact record automatically. meta is an org-defined freeform dict for organisation-specific annotations that don't fit the platform fields above. By convention, meta.labels (list[str]) is the canonical slot for tag-like terms the org defines for itself (e.g. "rmsb", "vip") — distinct from tags, which is the platform-controlled enum. Any term used in meta should be defined in the per-org glossary so its meaning and location are recorded; see get_glossary / update_glossary. Pass meta to overwrite the whole dict — fetch the contact first if you want to preserve existing keys.
update_glossary
ChatGPTWrite a new version of the org's per-org glossary. Pass the FULL new glossary text — this replaces the current one wholesale. The glossary is mutable in place (not versioned, unlike the pricelist). Call get_glossary first to read what's currently in place, then construct the new content (existing definitions + your additions / modifications / removals) and submit it here. Use when: - Defining a new contact attribute that pricelist rules will match against (e.g. a member tag the org will use for member pricing). - Documenting an org-specific term, custom balance_type, or per-event metadata field the platform glossary doesn't cover. - Naming a label the org wants to attach to entities via their freeform meta field (e.g. "rmsb" stored at contact.meta.labels). - Refining or removing a previously-defined term. When defining a term that gets stored on an entity, include where it lives in prose (e.g. "RMSB: members of the RMSB program; stored on contact meta.labels") so future lookups can route to the right field. Don't redefine platform-layer terms here (standard balance_types) — those are inherited from code and orgs can't override them.
update_my_profile
ChatGPTHow to update the current user's profile. Note that these fields will overwrite what's currently stored in the user profile. When doing an update, it's important to fetch the current profile and the patch in the data that's changing.
update_offered_service
ChatGPTHow to update an offered service or class. Note: services no longer carry pricing-relevant info (prices, balance_types, payment terms). The pricing engine owns all of that. Updates here only modify metadata (name, status); existing default_amount / payment_terms values stored on legacy service records are left in place (backwards compat) but no longer read or written.
update_org_setting_customer_notifications
ChatGPTHow to update customer notification in org settings. from_ uses the org setting profile name if not set; otherwise customize here unpaid_sms configuration for sending unpaid sms notifications
update_org_setting_profile
ChatGPTHow to update the org setting profile. To clear non-object fields, use an empty string. Returns None if there was no change or current user doesn't have permission to change. Note that these fields will overwrite what's currently there so it's important to fetch the org setting first and patch what's changing.
update_pricelist
ChatGPTCreate a new version of the org's pricelist. Versioned, not destructive: the new text becomes the active version and the prior one is retained (demoted to inactive, readable via get_pricelist(version= ...)); historical charges keep referencing whatever version they resolved against. So authoring pricing the provider just stated is a routine write — go ahead and submit, no confirmation needed. Pass the FULL new text — the new version is what becomes active, so include every existing rule you want to keep alongside your additions / modifications / removals. A rule you omit leaves the active version (and is recoverable from the prior version via get_pricelist if you drop one by mistake). Call get_pricelist first to read what's currently in place (the response also bundles the glossary you'll need to author against — legal balance_type values plus any custom per-org terms). Optional glossary arg: pass alongside data when the new rule references a custom term the per-org glossary doesn't yet define (e.g. introducing "member" pricing requires a glossary entry defining what "member" means). Both writes commit in the same transaction — atomically applied or atomically rolled back. The returned PricelistOut.glossary reflects the post-update state, so no follow-up get_glossary is needed. Pass ONLY the per-org part — do NOT include glossary.platform content from get_pricelist, that's frozen reference data. Use this whenever the pricelist changes: - Adding a new rule for a REPEATING charge — a charge that will apply consistently to future occurrences (a session price, a registration fee, a no-show policy, a member discount). - Modifying an existing rule (price change, condition refinement). - Removing an obsolete rule. - Consolidating redundant rules into more general ones. Do NOT use this to record a ONE-OFF charge. If discover_price returned an unpriced component whose prefer is adhoc (materials, damage, adjustment), the default is a one-off — fill it with price_quote_component, not a pricelist rule. Only encode an ad-hoc-prefer activity as a rule when the provider explicitly states a standing policy. Interning a one-off charge here pollutes the pricelist and mis-prices future events. Record only what the provider actually stated. In particular, do NOT append payment timing ("due at end of month", "due immediately", "net 30") the provider did not give — an invented due-date clause here silently becomes a standing billing policy that every future invoice inherits. If timing is unstated, leave it out of the prose entirely. Future discover_price calls see the new prose.
upsert_connector
ChatGPTHow to upsert a connector. If there's a connector with a 'new' status, it will be updated; otherwise it will be created. The unready field in the results indicate the reasons for a connector to be unready for use. An empty unready means there's no reason it won't work.