> ## Documentation Index
> Fetch the complete documentation index at: https://docs.go.gbgplc.com/llms.txt
> Use this file to discover all available pages before exploring further.

# User Invite

export const ModuleToc = () => {
  const ref = useRef(null);
  const [items, setItems] = useState([]);
  const [inVariant, setInVariant] = useState(false);
  useEffect(() => {
    const toc = ref.current;
    if (!toc) return;
    const sections = [{
      label: "Capabilities",
      match: "capabilities",
      suffix: true
    }, {
      label: "Default outcomes",
      match: "default outcomes"
    }, {
      label: "Input payload",
      match: "input payload"
    }, {
      label: "Sample response",
      match: "sample response"
    }];
    const related = {
      label: "Related guides",
      match: "related guides and tutorials"
    };
    const selector = "h2[id], h3[id], h4[id], h5[id], h6[id]";
    const read = heading => ({
      heading,
      level: Number(heading.tagName.slice(1)),
      text: heading.textContent.replace(/​/g, "").trim()
    });
    const matches = (entry, section) => {
      const text = entry.text.toLowerCase();
      return text === section.match || section.suffix && text.endsWith(` ${section.match}`);
    };
    const isSection = entry => sections.some(section => matches(entry, section));
    const after = entry => toc.compareDocumentPosition(entry.heading) & Node.DOCUMENT_POSITION_FOLLOWING;
    const link = (entry, label) => ({
      label: label || entry.text,
      id: entry.heading.id
    });
    const sectionLinks = pool => sections.map(section => {
      const entry = pool.find(candidate => matches(candidate, section));
      return entry ? link(entry, section.label) : null;
    }).filter(Boolean);
    const root = toc.closest("#content-area") || document.body;
    const key = window.location.pathname;
    if (!root.moduleTocPage || root.moduleTocPage.key !== key) {
      root.moduleTocPage = {
        key,
        headings: Array.from(root.querySelectorAll(selector)).filter(heading => !heading.closest("details")).map(read)
      };
    }
    const page = root.moduleTocPage.headings.filter(after);
    const relatedEntry = page.find(entry => matches(entry, related));
    const relatedLink = relatedEntry ? [link(relatedEntry, related.label)] : [];
    const variant = toc.closest("details");
    if (variant) {
      const own = Array.from(variant.querySelectorAll(selector)).map(read).filter(after);
      const ownLinks = sectionLinks(own);
      const shared = sectionLinks(page).filter(fallback => !ownLinks.some(existing => existing.label === fallback.label));
      const order = sections.map(section => section.label);
      const merged = [...ownLinks, ...shared].sort((a, b) => order.indexOf(a.label) - order.indexOf(b.label));
      setInVariant(true);
      setItems([...merged, ...relatedLink]);
      return;
    }
    const isVariant = (entry, index) => {
      if (isSection(entry)) return false;
      for (const next of page.slice(index + 1)) {
        if (next.level <= entry.level) return false;
        if (isSection(next)) return true;
      }
      return false;
    };
    const loose = [];
    const groups = [];
    page.forEach((entry, index) => {
      if (isVariant(entry, index)) groups.push({
        entry,
        pool: []
      }); else if (isSection(entry)) (groups.length ? groups[groups.length - 1].pool : loose).push(entry);
    });
    const variants = groups.map(group => ({
      ...link(group.entry),
      children: sectionLinks(group.pool)
    })).filter(group => group.children.length);
    setInVariant(false);
    setItems(variants.length > 1 ? [...sectionLinks(loose), ...variants, ...relatedLink] : [...sectionLinks(page), ...relatedLink]);
  }, []);
  const linkClass = "block -ml-px border-l border-transparent pl-4 text-gray-600 dark:text-gray-400 hover:border-gray-400 hover:text-gray-900 dark:hover:text-gray-200";
  return <nav ref={ref} aria-label={inVariant ? "In this variant" : "On this page"} data-module-toc="" className="module-toc not-prose my-6 text-sm">
      {items.length > 0 && <p className="mb-2 font-medium text-gray-900 dark:text-gray-200">
          {inVariant ? "In this variant" : "On this page"}
        </p>}
      {items.length > 0 && <ul className="space-y-1.5 border-l border-gray-200 dark:border-white/10">
          {items.map(item => <li key={item.id}>
              <a href={`#${item.id}`} className={item.children ? `${linkClass} font-medium text-gray-800 dark:text-gray-200` : linkClass}>
                {item.label}
              </a>
              {item.children && <ul className="mt-1.5 space-y-1.5 pl-4">
                  {item.children.map(child => <li key={child.id}>
                      <a href={`#${child.id}`} className={linkClass}>
                        {child.label}
                      </a>
                    </li>)}
                </ul>}
            </li>)}
        </ul>}
    </nav>;
};

Invite a user to complete a journey via SMS or email

This page documents the **User Invite** module, including its variants, capabilities, and the result values it returns.

<ModuleToc />

## User Invite: Single User Invite

Invites a user to complete a journey by sending an invite URL via SMS or email.

### Capabilities

The module returns the following capabilities.

#### Recipient supplied

True when the subject provided a mobile phone number or personal email address to send the invite to.

| Detail | Description |
| - | - |
| Type | Boolean |
| Default | `isFalse` |

#### Invite sent

True when at least one channel's invite message was accepted for delivery by its messaging provider.

| Detail | Description |
| - | - |
| Type | Boolean |
| Default | `isFalse` |

#### SMS status

The status of the SMS invite. Delivery status is refreshed by polling the provider for the configured poll duration; NONE when no mobile number was supplied, ERRORED when the send request got no provider response.

| Code | Label | Description |
| - | - | - |
| `NONE` | None | No SMS was sent because no mobile number was supplied. |
| `ACCEPTED` | Accepted | The message was accepted by the messaging provider. |
| `QUEUED` | Queued | The message is queued for delivery by the messaging provider. |
| `SENT` | Sent | The message was sent to the carrier. |
| `DELIVERED` | Delivered | The carrier confirmed delivery of the message to the recipient's handset. |
| `UNDELIVERED` | Undelivered | The message could not be delivered to the recipient. |
| `FAILED` | Failed | The messaging provider rejected or failed to send the message. |
| `ERRORED` | Errored | The send request could not be completed - no response was received from the messaging provider. |

#### Email status

The status of the email invite as reported by the email provider at the time of sending; NONE when no personal email address was supplied, ERRORED when the send request got no provider response.

| Code | Label | Description |
| - | - | - |
| `NONE` | None | No email was sent because no personal email address was supplied. |
| `ACCEPTED` | Accepted | The email was accepted for delivery by the email provider. |
| `FAILED` | Failed | The email provider rejected or failed to send the email. |
| `ERRORED` | Errored | The send request could not be completed - no response was received from the email provider. |

### Default outcomes

The module is pre-configured with the following default outcomes, which can be used in evaluation and routing logic within the journey designer.

| Outcome | Condition | Description |
| - | - | - |
| `Skipped` | Recipient Supplied is `false` | No invite was sent because the subject supplied neither a mobile phone number nor a personal email address. |
| `Sent` | Invite Sent is `true` | At least one invite message was accepted for delivery by its messaging provider. |
| `Failed` | SMS Status is `FAILED` or SMS Status is `UNDELIVERED` or Email Status is `FAILED` | An invite was sent but the messaging provider rejected it or couldn't deliver it. |
| `Error` | default, when no conditions matched | The module couldn't return a result. |

### Input payload

The following is a sample payload used to submit data to the **Single User Invite** module for processing.

```json JSON theme={null}
{
  "context": {
    "subject": {
      "identity": {
        "phones": [
          {
            "type": "mobile",
            "number": "+1234567"
          }
        ]
      }
    }
  }
}
```

| Field | Required | Description |
| - | - | - |
| `PersonalEmail` | No | The personal email address to send the invite to. Supply this, a mobile phone number, or both. |
| `MobilePhone` | No | The mobile phone number to send the invite to. Supply this, a personal email address, or both. |

### Sample response

The following is a sample response returned by the module.

```json JSON theme={null}
{
  "response": {
    "advice": {
      "recipientSupplied": true,
      "inviteSent": false,
      "smsStatus": "FAILED",
      "emailStatus": "NONE"
    },
    "outcome": "Failed"
  }
}
```
