Skip to main content
Glama

Save client

save_client

Create or update a customer. Pass an id to update that customer; omit id to create a new one. Returns the new client id on create. Updating an existing customer with an opening date later than its own earliest invoice or receipt is refused, unwritten, with code confirm-required until the same call is repeated with confirm true. To create/update many at once, send an items array — one all-or-nothing transaction with a single ledger recompute; each item with an id updates, each without creates.

Input Schema

NameRequiredDescriptionDefault
idNoClient id. Present → update that client; omit → create a new one.
cityNoCity.
nameNoDisplay name / description of the client.
emailNoClient email address.
itemsNoVector of client objects to create/update in one all-or-nothing batch. Preferred over repeated calls. Each item with an id updates; each without creates.
hiddenNoWhether this client is hidden from lists.
numberNoMain telephone (landline) number of the client, beside the contact person's contactnumber. Trimmed; a blank value on update clears it.
suburbNoSuburb or district.
addressNoStreet address line 1.
confirmNoPROD-1950: required only to save an opening date that is LATER than the customer's own earliest invoice or receipt. Without it that one case is refused, unwritten, with code confirm-required plus earliesttransactiondate and transactionsbefore, so the caller can show what the date would exclude from the debtors balance and let the user override it.
countryNoClient country (ISO code or name).
starredNoWhether this client is marked as a favourite.
websiteNoClient website, e.g. www.example.co.za. Trimmed; a blank string clears it, null leaves it unchanged.
address2NoStreet address line 2.
archivedNoWhether this client is archived. Archiving is a status flag only: the server stores it and still returns archived clients on every list, report and total. The apps filter on it.
contactsNoThe full list of contact people. It replaces the stored list: a person left out is removed. Leave the key out to keep the stored list; send [] to clear it.
addressesNoThe full list of addresses. It replaces the stored list: an address left out is removed. Leave the key out to keep the stored list; send [] to clear it.
vatnumberNoVAT / tax registration number.
postalcityNoPostal address city.
postalcodeNoPostal or ZIP code.
contactnameNoName of the primary contact person.
creditlimitNoMost this customer may owe, in base currency. Changing or clearing a stored limit needs the Extend credit over limit permission.
openingdateNoDate the opening balance applies from (ISO 8601).
contactemailNoEmail of the primary contact person.
customercodeNoYour internal customer/account code for this client.
postalsuburbNoPostal address suburb.
contactnumberNoPhone number of the primary contact person.
postaladdressNoPostal address line 1, e.g. a PO Box.
openingbalanceNoOpening balance amount carried for this client at openingdate.
postaladdress2NoPostal address line 2.
documentaddressNoWhich address documents print: physical (default) or postal.
clearcreditlimitNoWhen true, removes the stored credit limit.
postalpostalcodeNoPostal address postal code.
registrationnumberNoBusiness registration number.
clearopeningbalanceNoReserved, mirrors update_bank_account: when true the opening-date confirmation below is not asked.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed4 schema fields changed
    • addedInput schema / properties / addresses
      Added value: +{
      +  "description": "The full list of addresses. It replaces the stored list: an address left out is removed. Leave the key out to keep the stored list; send [] to clear it.",
      +  "items": {
      +    "properties": {
      +      "address": {
      +        "type": "string"
      +      },
      +      "address2": {
      +        "type": "string"
      +      },
      +      "city": {
      +        "type": "string"
      +      },
      +      "country": {
      +        "type": "string"
      +      },
      +      "id": {
      +        "description": "The address id. Send it to keep and update that address; leave it out to add a new one.",
      +        "type": "string"
      +      },
      +      "label": {
      +        "description": "A name for the address, e.g. Head office.",
      +        "type": "string"
      +      },
      +      "postalcode": {
      +        "type": "string"
      +      },
      +      "showoninvoices": {
      +        "description": "True on the ONE address that documents print. More than one is refused.",
      +        "type": "boolean"
      +      },
      +      "suburb": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [],
      +    "type": "object"
      +  },
      +  "maxItems": 50,
      +  "type": "array"
      +}
    • addedInput schema / properties / contacts
      Added value: +{
      +  "description": "The full list of contact people. It replaces the stored list: a person left out is removed. Leave the key out to keep the stored list; send [] to clear it.",
      +  "items": {
      +    "properties": {
      +      "email": {
      +        "type": "string"
      +      },
      +      "id": {
      +        "description": "The contact person id. Send it to keep and update that person; leave it out to add a new one.",
      +        "type": "string"
      +      },
      +      "name": {
      +        "type": "string"
      +      },
      +      "ondocuments": {
      +        "description": "True to send documents to this person.",
      +        "type": "boolean"
      +      },
      +      "phone": {
      +        "type": "string"
      +      },
      +      "role": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [],
      +    "type": "object"
      +  },
      +  "maxItems": 50,
      +  "type": "array"
      +}
    • addedInput schema / properties / items / items / properties / addresses
      Added value: +{
      +  "description": "The full list of addresses. It replaces the stored list: an address left out is removed. Leave the key out to keep the stored list; send [] to clear it.",
      +  "items": {
      +    "properties": {
      +      "address": {
      +        "type": "string"
      +      },
      +      "address2": {
      +        "type": "string"
      +      },
      +      "city": {
      +        "type": "string"
      +      },
      +      "country": {
      +        "type": "string"
      +      },
      +      "id": {
      +        "description": "The address id. Send it to keep and update that address; leave it out to add a new one.",
      +        "type": "string"
      +      },
      +      "label": {
      +        "description": "A name for the address, e.g. Head office.",
      +        "type": "string"
      +      },
      +      "postalcode": {
      +        "type": "string"
      +      },
      +      "showoninvoices": {
      +        "description": "True on the ONE address that documents print. More than one is refused.",
      +        "type": "boolean"
      +      },
      +      "suburb": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [],
      +    "type": "object"
      +  },
      +  "maxItems": 50,
      +  "type": "array"
      +}
    • addedInput schema / properties / items / items / properties / contacts
      Added value: +{
      +  "description": "The full list of contact people. It replaces the stored list: a person left out is removed. Leave the key out to keep the stored list; send [] to clear it.",
      +  "items": {
      +    "properties": {
      +      "email": {
      +        "type": "string"
      +      },
      +      "id": {
      +        "description": "The contact person id. Send it to keep and update that person; leave it out to add a new one.",
      +        "type": "string"
      +      },
      +      "name": {
      +        "type": "string"
      +      },
      +      "ondocuments": {
      +        "description": "True to send documents to this person.",
      +        "type": "boolean"
      +      },
      +      "phone": {
      +        "type": "string"
      +      },
      +      "role": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [],
      +    "type": "object"
      +  },
      +  "maxItems": 50,
      +  "type": "array"
      +}
  2. Changed2 schema fields changed
    • addedInput schema / properties / items / items / properties / website
      Added value: +{
      +  "description": "Client website, e.g. www.example.co.za. Trimmed; a blank string clears it, null leaves it unchanged.",
      +  "type": "string"
      +}
    • addedInput schema / properties / website
      Added value: +{
      +  "description": "Client website, e.g. www.example.co.za. Trimmed; a blank string clears it, null leaves it unchanged.",
      +  "type": "string"
      +}
  3. Changed2 schema fields changed
    • addedInput schema / properties / items / items / properties / number
      Added value: +{
      +  "description": "Main telephone (landline) number of the client, beside the contact person's contactnumber. Trimmed; a blank value on update clears it.",
      +  "type": "string"
      +}
    • addedInput schema / properties / number
      Added value: +{
      +  "description": "Main telephone (landline) number of the client, beside the contact person's contactnumber. Trimmed; a blank value on update clears it.",
      +  "type": "string"
      +}
  4. Changed16 schema fields changed
    • addedInput schema / properties / clearcreditlimit
      Added value: +{
      +  "description": "When true, removes the stored credit limit.",
      +  "type": "boolean"
      +}
    • addedInput schema / properties / creditlimit
      Added value: +{
      +  "description": "Most this customer may owe, in base currency. Changing or clearing a stored limit needs the Extend credit over limit permission.",
      +  "type": "number"
      +}
    • addedInput schema / properties / documentaddress
      Added value: +{
      +  "description": "Which address documents print: physical (default) or postal.",
      +  "enum": [
      +    "physical",
      +    "postal"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / items / items / properties / clearcreditlimit
      Added value: +{
      +  "description": "When true, removes the stored credit limit.",
      +  "type": "boolean"
      +}
    • addedInput schema / properties / items / items / properties / creditlimit
      Added value: +{
      +  "description": "Most this customer may owe, in base currency. Changing or clearing a stored limit needs the Extend credit over limit permission.",
      +  "type": "number"
      +}
    • addedInput schema / properties / items / items / properties / documentaddress
      Added value: +{
      +  "description": "Which address documents print: physical (default) or postal.",
      +  "enum": [
      +    "physical",
      +    "postal"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / items / items / properties / postaladdress
      Added value: +{
      +  "description": "Postal address line 1, e.g. a PO Box.",
      +  "type": "string"
      +}
    • addedInput schema / properties / items / items / properties / postaladdress2
      Added value: +{
      +  "description": "Postal address line 2.",
      +  "type": "string"
      +}
    • addedInput schema / properties / items / items / properties / postalcity
      Added value: +{
      +  "description": "Postal address city.",
      +  "type": "string"
      +}
    • addedInput schema / properties / items / items / properties / postalpostalcode
      Added value: +{
      +  "description": "Postal address postal code.",
      +  "type": "string"
      +}
    • addedInput schema / properties / items / items / properties / postalsuburb
      Added value: +{
      +  "description": "Postal address suburb.",
      +  "type": "string"
      +}
    • addedInput schema / properties / postaladdress
      Added value: +{
      +  "description": "Postal address line 1, e.g. a PO Box.",
      +  "type": "string"
      +}
    • addedInput schema / properties / postaladdress2
      Added value: +{
      +  "description": "Postal address line 2.",
      +  "type": "string"
      +}
    • addedInput schema / properties / postalcity
      Added value: +{
      +  "description": "Postal address city.",
      +  "type": "string"
      +}
    • addedInput schema / properties / postalpostalcode
      Added value: +{
      +  "description": "Postal address postal code.",
      +  "type": "string"
      +}
    • addedInput schema / properties / postalsuburb
      Added value: +{
      +  "description": "Postal address suburb.",
      +  "type": "string"
      +}
  5. Changed4 schema fields changed
    • changedInput schema / properties / archived / description
      Previous value: -"Whether this client is archived (hidden from active lists)."New value: +"Whether this client is archived. Archiving is a status flag only: the server stores it and still returns archived clients on every list, report and total. The apps filter on it."
    • addedInput schema / properties / clearopeningbalance
      Added value: +{
      +  "description": "Reserved, mirrors update_bank_account: when true the opening-date confirmation below is not asked.",
      +  "type": "boolean"
      +}
    • addedInput schema / properties / confirm
      Added value: +{
      +  "description": "PROD-1950: required only to save an opening date that is LATER than the customer's own earliest invoice or receipt. Without it that one case is refused, unwritten, with code confirm-required plus earliesttransactiondate and transactionsbefore, so the caller can show what the date would exclude from the debtors balance and let the user override it.",
      +  "type": "boolean"
      +}
    • changedInput schema / properties / items / items / properties / archived / description
      Previous value: -"Whether this client is archived (hidden from active lists)."New value: +"Whether this client is archived. Archiving is a status flag only: the server stores it and still returns archived clients on every list, report and total. The apps filter on it."
  6. First observed

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare non-read-only and non-destructive; the description adds substantial context beyond that — the returned id on create, the confirm-required refusal code for late opening dates, and the all-or-nothing single-ledger-recompute semantics for `items`. It omits auth/permission implications (e.g. the Extend credit over limit permission) and the fact that `contacts`/`addresses` replace and remove stored entries, both of which live only in the schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four dense sentences, front-loaded with the core create/update contract, then the edge case and the batch mode. Slightly redundant with schema text ('each item with an id updates, each without creates' duplicates the `items` description), but nothing is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 35-parameter, zero-required upsert tool with no output schema, the description covers the decision logic (create vs update vs batch), the one notable failure mode and its retry protocol, and the create return value. The remaining field-level detail is fully specified in the schema, so an agent has enough to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so every parameter is already documented in the schema — including the id-vs-create rule and the items array. The description largely restates those same rules rather than adding new semantics (formats, defaults, limits), so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Create or update a customer') and immediately disambiguates the two modes: id present means update, absent means create. No sibling tool (save_supplier, save_employee) operates on this resource, so no explicit differentiation is required for an agent to pick it.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives clear conditions for each mode: pass id to update, omit to create, and use `items` for bulk 'preferred over repeated calls'. It also explains the confirm-retry path. It stops short of naming exclusions or alternatives (e.g. list_clients to find an id, delete_client for removal), so not a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources