Skip to main content
Glama

Invite members

well_invite_members

Invite one or more teammates into a workspace, or into a workspace group. Use it after well_list_member_candidates, on the people the user chose.

Pass invites — 1 to 20 entries, each { email, role } or { phone, role }, role admin or member — and a target: { kind: "workspace" } for this workspace, or { kind: "group", group_id } for a group you belong to. Only a workspace owner or admin may invite; a caller without that role is refused. A demo workspace refuses both targets with DEMO_WORKSPACE.

Returns one results entry per invite, named by its email or its phone: status sent (a new invitation), reissued (an already-pending address got a fresh link), or refused — with refusal_reason naming why, ALREADY_WORKSPACE_MEMBER when the address already has access, INSUFFICIENT_PERMISSIONS when the caller may not invite, and DEMO_WORKSPACE when the workspace holds sample data. invitation_email_sent is false when the invite persisted but the email did not leave, so offer a resend. Each successful result carries person_id for the invited address. Never invite an address already active in the workspace.

Pass notify: false (workspace target only) to create or reissue the pending membership WITHOUT emailing — for the assign-then-invite flow where an owner is assigned by a typed email now and the invitation is sent later from the invite card. Use the returned person_id to assign that person as an owner without a second lookup.

Pass delivery: "link" (workspace target only) when the user asks for an invitation link to forward instead of an email. Well creates the pending invitation for each named address and sends NO email. Each successful result then carries invite_url (the accept link) and role. The link works once and has no expiry. Every link call mints a NEW link: for an address that already holds a pending invitation (reissued), the new link replaces any earlier link or invitation email for it, which stops working. The invitation is made out to the named address, so always ask for the teammate's email first and never mint a link for a name alone.

Pass { phone, role } (workspace target only) to invite a teammate by mobile number, with no email address. Give the number as the user typed it or as a shared contact card gives it; Well normalises it, and reads a number with no country code as a French number. Never choose the role: pass the one the user named. On WhatsApp, when the user named none, leave role out of that one phone invite: Well's card asks the role, and the role the person picks is their yes. Elsewhere ask the user first; a phone invite with no role is refused with INVITE_ROLE_REQUIRED and creates no seat. Well creates the member's seat in the workspace, pending, for that number and that role, and sends NOTHING to the number itself: notify and delivery do not apply to it, and invitation_email_sent is false. The result carries phone (the number in international form), role, and invite_url, the invitation link that the user sends to that number by WhatsApp or SMS. The teammate opens the link, signs in and accepts, and is then in the workspace with that role: accepting is their consent, and nobody joins without it. Whoever opens the link can accept it, so it goes to that number only. The link works once and for seven days. reissued means the number already held a pending invitation here: the new invite_url replaces the earlier link, which stops working, the seven days start again, and the role stays the one the invitation already had. refused with INVITE_PHONE_NUMBER_INVALID means the text is not a valid phone number: ask the user for it again with its country code. ALREADY_WORKSPACE_MEMBER means a member of the workspace already verified that number.

Input Schema

NameRequiredDescriptionDefault
notifyNoSend the invitation email now. Defaults to true. Pass false to create or reissue the pending membership WITHOUT emailing, when a later explicit step sends it — e.g. assigning an owner by a typed email, then sending the invite from the invite card. Applies to a workspace target only; a group invite always notifies.
targetYesWhere the invites land: this workspace, or a workspace group.
invitesYesThe people to invite, 1 to 20 per call. Each entry names the teammate by exactly one key: `email` or `phone`.
deliveryNoHow the invitation reaches the teammate. Defaults to "email", which emails it. Pass "link" when the user asks for a link to forward: no email is sent, each result returns a new invite_url, and that link replaces any earlier link for the address. Applies to a workspace target only, and never with notify: true.
workspace_idNoTarget workspace. Omit when the token authorizes one workspace. Required when it authorizes several — a write lands in exactly one workspace and this call would not say which.
conversation_idNoThe conversation id returned by the previous Well result, in its meta under well/conversation_id, in its structuredContent, or in its JSON text block. Pass it back on every call in the same conversation, including a call a card makes, so the chosen workspace and the earlier answers still apply. It decides the conversation on its own: nothing the host states about the session replaces it. Omit it only on the first call of a conversation.
idempotency_keyNoOptional client-supplied key. A retried write with the same key returns the original result instead of re-applying the operation.
workspace_scopeNoRestrict this call to the family: the token's primary workspace plus the company workspaces under it that you are a member of. Unrelated authorized workspaces drop out of the read; row reads merge across the family, while a per-workspace figure still reports exactly one workspace.

Output Schema

NameRequiredDescriptionDefault
errorNo
resultsYesOne result per invite, in the order they were sent.
successYes
workspace_idNo
workspace_nameNoThe name of the workspace a workspace-target call invited into, to name it back to the user.
conversation_idNoThe conversation this result belongs to. Pass it back as the conversation_id argument on every later Well call in the same conversation.
newly_connectedNoTools that connected to this workspace since the last result and that no session was told about yet, oldest first. Present only when at least one tool is new. The connected-line rule says when to tell an entry; when it does, one line per entry, in the person's language: "<name> is connected.". Never fold several entries into one sentence. Each entry comes back once, on one result only: never repeat its line later.
conversation_id_noteNoPresent only when the server opened a fresh lane, stating that no choice recorded earlier was read.
conversation_id_sourceNoWhere the conversation id came from: the host's own request meta, the caller's argument, or a fresh lane the server opened.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed1 schema field changed
    • changedInput schema / properties / invites / items / anyOf
      Previous value: -[
      -  {
      -    "additionalProperties": false,
      -    "properties": {
      -      "email": {
      -        "description": "The teammate's email address.",
      -        "format": "email",
      -        "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
      -        "type": "string"
      -      },
      -      "role": {
      -        "description": "The role to grant on acceptance: admin or member.",
      -        "enum": [
      -          "admin",
      -          "member"
      -        ],
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "email",
      -      "role"
      -    ],
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "properties": {
      -      "phone": {
      -        "description": "The teammate's mobile number, as the user typed it or as a shared contact card gives it, with its country code when there is one (+33 6 00 00 00 01, 0033600000001). Well normalises it. A number with no country code is read as a French number.",
      -        "maxLength": 40,
      -        "minLength": 1,
      -        "type": "string"
      -      },
      -      "role": {
      -        "description": "The role the seat gives: admin or member, as the user named it; never choose it. Left out on WhatsApp when the user named none, so Well's card asks it; elsewhere ask the user first.",
      -        "enum": [
      -          "admin",
      -          "member"
      -        ],
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "phone"
      -    ],
      -    "type": "object"
      -  }
      -]New value: +[
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "email": {
      +        "description": "The teammate's email address.",
      +        "format": "email",
      +        "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
      +        "type": "string"
      +      },
      +      "role": {
      +        "description": "The role to grant on acceptance: admin or member.",
      +        "enum": [
      +          "admin",
      +          "member"
      +        ],
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "email",
      +      "role"
      +    ],
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "phone": {
      +        "description": "The teammate's mobile number, as the user typed it or as a shared contact card gives it, with its country code when there is one (+33 6 00 00 00 01, 0033600000001). Well normalises it. A number with no country code is read as a French number.",
      +        "maxLength": 40,
      +        "minLength": 1,
      +        "type": "string"
      +      },
      +      "role": {
      +        "description": "The role the seat gives: admin or member, as the user named it; never choose it. Left out on WhatsApp when the user named none, so Well's card asks it; elsewhere ask the user first.",
      +        "enum": [
      +          "admin",
      +          "member"
      +        ],
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "phone"
      +    ],
      +    "type": "object"
      +  }
      +]
  2. Changed2 schema fields changed
    • changedInput schema / properties / invites / items / anyOf
      Previous value: -[
      -  {
      -    "additionalProperties": false,
      -    "properties": {
      -      "email": {
      -        "description": "The teammate's email address.",
      -        "format": "email",
      -        "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
      -        "type": "string"
      -      },
      -      "role": {
      -        "description": "The role to grant on acceptance: admin or member.",
      -        "enum": [
      -          "admin",
      -          "member"
      -        ],
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "email",
      -      "role"
      -    ],
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "properties": {
      -      "phone": {
      -        "description": "The teammate's mobile number, as the user typed it or as a shared contact card gives it, with its country code when there is one (+33 6 00 00 00 01, 0033600000001). Well normalises it. A number with no country code is read as a French number.",
      -        "maxLength": 40,
      -        "minLength": 1,
      -        "type": "string"
      -      },
      -      "role": {
      -        "description": "The role the seat gives: admin or member. Required: ask the user, never choose it.",
      -        "enum": [
      -          "admin",
      -          "member"
      -        ],
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "phone",
      -      "role"
      -    ],
      -    "type": "object"
      -  }
      -]New value: +[
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "email": {
      +        "description": "The teammate's email address.",
      +        "format": "email",
      +        "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
      +        "type": "string"
      +      },
      +      "role": {
      +        "description": "The role to grant on acceptance: admin or member.",
      +        "enum": [
      +          "admin",
      +          "member"
      +        ],
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "email",
      +      "role"
      +    ],
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "phone": {
      +        "description": "The teammate's mobile number, as the user typed it or as a shared contact card gives it, with its country code when there is one (+33 6 00 00 00 01, 0033600000001). Well normalises it. A number with no country code is read as a French number.",
      +        "maxLength": 40,
      +        "minLength": 1,
      +        "type": "string"
      +      },
      +      "role": {
      +        "description": "The role the seat gives: admin or member, as the user named it; never choose it. Left out on WhatsApp when the user named none, so Well's card asks it; elsewhere ask the user first.",
      +        "enum": [
      +          "admin",
      +          "member"
      +        ],
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "phone"
      +    ],
      +    "type": "object"
      +  }
      +]
    • addedOutput schema / properties / workspace_name
      Added value: +{
      +  "description": "The name of the workspace a workspace-target call invited into, to name it back to the user.",
      +  "type": "string"
      +}
  3. Changed1 schema field changed
    • changedOutput schema / properties / newly_connected / description
      Previous value: -"Tools that connected to this workspace since the last result and that no session was told about yet, oldest first. Present only when at least one tool is new. Start the reply with one line per entry, in the person's language: \"<name> is connected.\". Never fold several entries into one sentence. Each entry comes back once, on one result only: never repeat its line later."New value: +"Tools that connected to this workspace since the last result and that no session was told about yet, oldest first. Present only when at least one tool is new. The connected-line rule says when to tell an entry; when it does, one line per entry, in the person's language: \"<name> is connected.\". Never fold several entries into one sentence. Each entry comes back once, on one result only: never repeat its line later."
  4. Changed13 schema fields changed
    • addedInput schema / properties / delivery
      Added value: +{
      +  "description": "How the invitation reaches the teammate. Defaults to \"email\", which emails it. Pass \"link\" when the user asks for a link to forward: no email is sent, each result returns a new invite_url, and that link replaces any earlier link for the address. Applies to a workspace target only, and never with notify: true.",
      +  "enum": [
      +    "email",
      +    "link"
      +  ],
      +  "type": "string"
      +}
    • changedInput schema / properties / invites / description
      Previous value: -"The people to invite, 1 to 20 per call."New value: +"The people to invite, 1 to 20 per call. Each entry names the teammate by exactly one key: `email` or `phone`."
    • removedInput schema / properties / invites / items / additionalProperties
      Removed value: -false
    • addedInput schema / properties / invites / items / anyOf
      Added value: +[
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "email": {
      +        "description": "The teammate's email address.",
      +        "format": "email",
      +        "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
      +        "type": "string"
      +      },
      +      "role": {
      +        "description": "The role to grant on acceptance: admin or member.",
      +        "enum": [
      +          "admin",
      +          "member"
      +        ],
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "email",
      +      "role"
      +    ],
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "properties": {
      +      "phone": {
      +        "description": "The teammate's mobile number, as the user typed it or as a shared contact card gives it, with its country code when there is one (+33 6 00 00 00 01, 0033600000001). Well normalises it. A number with no country code is read as a French number.",
      +        "maxLength": 40,
      +        "minLength": 1,
      +        "type": "string"
      +      },
      +      "role": {
      +        "description": "The role the seat gives: admin or member. Required: ask the user, never choose it.",
      +        "enum": [
      +          "admin",
      +          "member"
      +        ],
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "phone",
      +      "role"
      +    ],
      +    "type": "object"
      +  }
      +]
    • removedInput schema / properties / invites / items / properties
      Removed value: -{
      -  "email": {
      -    "description": "The teammate's email address.",
      -    "format": "email",
      -    "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
      -    "type": "string"
      -  },
      -  "role": {
      -    "description": "The role to grant on acceptance: admin or member.",
      -    "enum": [
      -      "admin",
      -      "member"
      -    ],
      -    "type": "string"
      -  }
      -}
    • removedInput schema / properties / invites / items / required
      Removed value: -[
      -  "email",
      -  "role"
      -]
    • removedInput schema / properties / invites / items / type
      Removed value: -"object"
    • addedInput schema / properties / workspace_scope
      Added value: +{
      +  "description": "Restrict this call to the family: the token's primary workspace plus the company workspaces under it that you are a member of. Unrelated authorized workspaces drop out of the read; row reads merge across the family, while a per-workspace figure still reports exactly one workspace.",
      +  "enum": [
      +    "family"
      +  ],
      +  "type": "string"
      +}
    • addedOutput schema / properties / results / items / properties / email / description
      Added value: +"The invited address. Present on an email invite."
    • addedOutput schema / properties / results / items / properties / invite_url
      Added value: +{
      +  "description": "Present on a successful delivery \"link\" invite and on a successful phone invite: the accept link of the invitation. Give it to the user exactly as returned so they send it to the teammate. It works once. On a delivery \"link\" invite it has no expiry and stops only when a new link is made for the address or the invitation is cancelled. On a phone invite it works for seven days, and a new ask for the number replaces it.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / results / items / properties / phone
      Added value: +{
      +  "description": "Present on a phone invite: the number the seat is made for, in international form (+33600000001). On a refusal for an invalid number it is the number as it was given.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / results / items / properties / role
      Added value: +{
      +  "description": "Present on a successful delivery \"link\" invite (the role the link grants) and on a successful phone invite (the role the seat gives; on `reissued` it is the role of the invitation that already stood, which this call did not change).",
      +  "enum": [
      +    "admin",
      +    "member"
      +  ],
      +  "type": "string"
      +}
    • changedOutput schema / properties / results / items / required
      Previous value: -[
      -  "email",
      -  "status",
      -  "invitation_email_sent"
      -]New value: +[
      +  "status",
      +  "invitation_email_sent"
      +]
  5. Changed1 schema field changed
    • addedOutput schema / properties / newly_connected
      Added value: +{
      +  "description": "Tools that connected to this workspace since the last result and that no session was told about yet, oldest first. Present only when at least one tool is new. Start the reply with one line per entry, in the person's language: \"<name> is connected.\". Never fold several entries into one sentence. Each entry comes back once, on one result only: never repeat its line later.",
      +  "items": {
      +    "additionalProperties": false,
      +    "properties": {
      +      "name": {
      +        "type": "string"
      +      },
      +      "service_id": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "name",
      +      "service_id"
      +    ],
      +    "type": "object"
      +  },
      +  "type": "array"
      +}
  6. Changed1 schema field changed
    • changedOutput schema / properties / results / items / properties / refusal_reason / description
      Previous value: -"Present only when status is refused. Named reasons include ALREADY_WORKSPACE_MEMBER (the address already has access) and INSUFFICIENT_PERMISSIONS (the caller is not an owner or admin)."New value: +"Present only when status is refused. Named reasons include ALREADY_WORKSPACE_MEMBER (the address already has access), INSUFFICIENT_PERMISSIONS (the caller is not an owner or admin), and DEMO_WORKSPACE (the workspace holds sample data, so invite the team in the user's own workspace)."
  7. Changed4 schema fields changed
    • addedInput schema / properties / conversation_id
      Added value: +{
      +  "description": "The conversation id returned by the previous Well result, in its meta under well/conversation_id, in its structuredContent, or in its JSON text block. Pass it back on every call in the same conversation, including a call a card makes, so the chosen workspace and the earlier answers still apply. It decides the conversation on its own: nothing the host states about the session replaces it. Omit it only on the first call of a conversation.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / conversation_id
      Added value: +{
      +  "description": "The conversation this result belongs to. Pass it back as the conversation_id argument on every later Well call in the same conversation.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / conversation_id_note
      Added value: +{
      +  "description": "Present only when the server opened a fresh lane, stating that no choice recorded earlier was read.",
      +  "type": "string"
      +}
    • addedOutput schema / properties / conversation_id_source
      Added value: +{
      +  "description": "Where the conversation id came from: the host's own request meta, the caller's argument, or a fresh lane the server opened.",
      +  "enum": [
      +    "host_meta",
      +    "argument",
      +    "minted"
      +  ],
      +  "type": "string"
      +}
  8. Changed2 schema fields changed
    • addedInput schema / properties / notify
      Added value: +{
      +  "description": "Send the invitation email now. Defaults to true. Pass false to create or reissue the pending membership WITHOUT emailing, when a later explicit step sends it — e.g. assigning an owner by a typed email, then sending the invite from the invite card. Applies to a workspace target only; a group invite always notifies.",
      +  "type": "boolean"
      +}
    • addedOutput schema / properties / results / items / properties / person_id
      Added value: +{
      +  "description": "The invitee's person id on a successful workspace invite (sent or reissued). Use it to assign the person as an owner right after inviting, with no second lookup. Absent on a refusal and on a group invite.",
      +  "type": "string"
      +}
  9. Added

TDQS

A4.8/5.0
Behavior5/5

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

The annotations only mark it non-readOnly/non-destructive; the description adds the real behavioral weight: owner/admin permission requirement, DEMO_WORKSPACE refusal, per-entry status semantics (sent/reissued/refused with refusal_reason codes), invitation_email_sent=false fallback, and that link invites send no email and replace prior links. This is far beyond what structured fields convey.

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?

Front-loaded with the core action and prerequisite, then organized by target/option. It is long but largely earns its length given the branching modes; there is mild redundancy (the 'link works once' behavior is stated twice) that could be trimmed.

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?

Despite an output schema existing, the description proactively explains result shapes and edge cases (person_id, invite_url, refusal reasons) so an agent knows how to react. For an 8-parameter mutation with multiple modes, nothing an agent needs to call it correctly is missing.

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 coverage is already 100%, and much of the description restates the schema's per-parameter docs. It does add cross-parameter meaning that the schema cannot (notify+delivery interactions, phone invites ignoring notify/delivery, seven-day link lifetime, role omitted on WhatsApp), but the redundancy keeps it from a full 5.

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?

Opens with a specific verb and resource ('Invite one or more teammates into a workspace, or into a workspace group') and names the prerequisite sibling well_list_member_candidates, so the agent can place it immediately in a flow and distinguish it from related write tools like create_agent_seat or get_workspace_invite_link.

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

Usage Guidelines5/5

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

Explicit sequencing ('Use it after well_list_member_candidates, on the people the user chose') plus concrete branch conditions: notify:false for the assign-then-invite flow, delivery:"link" when the user asks for a forwardable link, and phone invites for the mobile case. It even states constraints ('never mint a link for a name alone', 'never choose the role').

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