Invite members
well_invite_membersInvite 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. The role is required: when the user named none, ask, and never choose it. 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
| Name | Required | Description | Default |
|---|---|---|---|
| notify | No | 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. | |
| target | Yes | Where the invites land: this workspace, or a workspace group. | |
| invites | Yes | The people to invite, 1 to 20 per call. Each entry names the teammate by exactly one key: `email` or `phone`. | |
| delivery | No | 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. | |
| workspace_id | No | Target 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_id | No | 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. | |
| idempotency_key | No | Optional client-supplied key. A retried write with the same key returns the original result instead of re-applying the operation. | |
| workspace_scope | No | 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. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| error | No | ||
| results | Yes | One result per invite, in the order they were sent. | |
| success | Yes | ||
| workspace_id | No | ||
| conversation_id | No | The conversation this result belongs to. Pass it back as the conversation_id argument on every later Well call in the same conversation. | |
| newly_connected | No | 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. | |
| conversation_id_note | No | Present only when the server opened a fresh lane, stating that no choice recorded earlier was read. | |
| conversation_id_source | No | Where the conversation id came from: the host's own request meta, the caller's argument, or a fresh lane the server opened. |