Skip to main content
Glama

Create or update a contact

upsert_contact
DestructiveIdempotent

Creates a contact, or updates the one that already exists for that phone number or BSUID (the business-scoped user id Meta sends; a contact may have one and no phone). On a contact that exists only the fields you send change, and nothing is removed: tags are added to its tags, attributes are merged into its attributes, changing the name needs an owner or admin, and profile_name is only filled when WhatsApp has not given one; a field sent as null is left as it is. Writes only to Cosend: it sends nothing and the contact is not notified. Who can be messaged stays the same: a send needs no opt-in, only an opt-out stops one, and opt-outs are recorded on their own ledger.

Tool call template

Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "connection_id": "<connection_id>"
    },
    "name": "upsert_contact"
  }
}

Input Schema

NameRequiredDescriptionDefault
nameNo
tagsNoA new contact gets these tags. A contact that already exists gets them added to its own, and keeps every tag it has.
bsuidNo
localeNo
timezoneNo
attributesNoFree-form values. PATCH replaces them whole; a POST for a contact that already exists writes the keys sent over its values and removes none. A key registered in /v1/contact-fields must hold a value of its type (text: a string; number: a number; date: YYYY-MM-DD; boolean: true or false; choice: one of its choices) or null; after a PATCH that sets attributes, or a POST that writes them to a contact that already exists, every required field must have a value. A key that is not registered is kept as it comes.
phone_e164No
profile_nameNo
connection_idYes

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed2 schema fields changed
    • changedInput schema / properties / attributes / description
      Previous value: -"Free-form values, replaced whole on PATCH. A key registered in /v1/contact-fields must hold a value of its type (text: a string; number: a number; date: YYYY-MM-DD; boolean: true or false; choice: one of its choices) or null; a PATCH that sets attributes must give every required field a value. A key that is not registered is kept as it comes."New value: +"Free-form values. PATCH replaces them whole; a POST for a contact that already exists writes the keys sent over its values and removes none. A key registered in /v1/contact-fields must hold a value of its type (text: a string; number: a number; date: YYYY-MM-DD; boolean: true or false; choice: one of its choices) or null; after a PATCH that sets attributes, or a POST that writes them to a contact that already exists, every required field must have a value. A key that is not registered is kept as it comes."
    • addedInput schema / properties / tags / description
      Added value: +"A new contact gets these tags. A contact that already exists gets them added to its own, and keeps every tag it has."
  2. Changed1 schema field changed
    • addedInput schema / properties / name
      Added value: +{
      +  "anyOf": [
      +    {
      +      "maxLength": 200,
      +      "minLength": 1,
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ]
      +}
  3. First observed

TDQS

A4.1/5.0
Behavior4/5

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

With annotations already declaring readOnlyHint=false, idempotentHint=true and destructiveHint=true, the description still adds real value: merge-not-replace semantics, that a null field is a no-op, that changing the name requires an owner or admin, that profile_name only fills when WhatsApp has not supplied one, and that no message is sent and the contact is not notified. The consent paragraph explains that an opt-out is tracked on a separate ledger, which is useful context for anyone worried the upsert affects messaging eligibility. The only shortfall is that overwriting existing attribute values (implicitly destructive and consistent with destructiveHint=true) is described more fully in the schema than here.

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?

The definition deliberately front-loads identity and match keys before merge rules and side effects, so the most decision-relevant information lands first. Sentences are dense and clause-heavy, and the trailing consent/opt-out paragraph is close to tangential, but each clause carries distinct behavioral information rather than restating the schema.

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

Completeness4/5

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

For a nine-parameter, nested-object mutation with no output schema and low schema description coverage, this covers match logic, merge behavior, permissions, null handling and side effects thoroughly. The one gap is that nothing is said about what the call returns (whether the response distinguishes a create from an update), which matters precisely because there is no output schema to fall back on.

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

Parameters4/5

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

Schema description coverage is only 22%, so the description must compensate, and it does for the important fields: name (owner/admin requirement), tags (added, existing tags kept), attributes (merged, keys written over values), profile_name (fill-only-if-empty), and bsuid/phone_e164 as the match keys. It says nothing about locale, timezone, or connection_id, and the attributes replacement-vs-merge distinction is partly delegated to the schema, so it stops short of full coverage.

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?

The description opens with a specific verb pair and resource ("Creates a contact, or updates the one that already exists") and names the exact matching keys: phone number or BSUID, clarifying that a contact may have a BSUID and no phone. That key-level detail lets an agent distinguish this from generic create/update siblings without opening the schema.

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

Usage Guidelines3/5

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

The upsert condition (match on phone_e164 or bsuid, otherwise create) is stated clearly, which is genuine when-to-use guidance. However, the sibling list includes update_contact, tag_contact, merge_contacts and delete_contact, and the description never says when to prefer this tool over update_contact or when to use tag_contact instead of passing tags here. Usage is implied by the merge semantics rather than explicitly routed.

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