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

# August 2026

> Elementum platform releases for August 2026.

export const EmailSubscriptionPicker = ({webhookUrl, logUrl = "https://script.google.com/macros/s/AKfycbwYKNyp9YTtV7fhwZOKwePB-0_cOz8jOD1kBLEprmvbTD5LBPn_iSuYagvaWlxbmtg/exec", heading = "Subscribe to email updates", description, buttonLabel = "Subscribe", lists = [{
  key: "ga-releases",
  label: "General Availability Release notifications",
  description: "Get an email when a new release ships — about twice a month."
}, {
  key: "upcoming-features",
  label: "Upcoming Feature updates",
  description: "Hear about new Beta features being tested — expect a few updates each week."
}]}) => {
  const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  const MAX_TEXT_LENGTH = 120;
  const logSubmissionAttempt = payload => {
    if (!logUrl) return;
    try {
      fetch(logUrl, {
        method: "POST",
        mode: "no-cors",
        keepalive: true,
        headers: {
          "Content-Type": "text/plain;charset=UTF-8"
        },
        body: JSON.stringify(payload)
      }).catch(() => {});
    } catch (e) {}
  };
  const [firstName, setFirstName] = useState("");
  const [lastName, setLastName] = useState("");
  const [company, setCompany] = useState("");
  const [email, setEmail] = useState("");
  const [website, setWebsite] = useState("");
  const [selectedLists, setSelectedLists] = useState(() => lists.map(l => l.key));
  const [status, setStatus] = useState("idle");
  const [errorMessage, setErrorMessage] = useState("");
  const isSubmitting = status === "loading";
  const [uid] = useState(() => `esp-${Math.random().toString(36).slice(2, 9)}`);
  const firstNameId = `${uid}-first-name`;
  const lastNameId = `${uid}-last-name`;
  const companyId = `${uid}-company`;
  const emailId = `${uid}-email`;
  const websiteId = `${uid}-website`;
  const statusId = `${uid}-status`;
  const toggleList = key => {
    setSelectedLists(prev => prev.includes(key) ? prev.filter(k => k !== key) : [...prev, key]);
  };
  const handleSubmit = async event => {
    event.preventDefault();
    setErrorMessage("");
    if (website.trim().length > 0) {
      setStatus("success");
      setFirstName("");
      setLastName("");
      setCompany("");
      setEmail("");
      return;
    }
    const trimmedFirstName = firstName.trim();
    const trimmedLastName = lastName.trim();
    const trimmedCompany = company.trim();
    const trimmedEmail = email.trim();
    if (!trimmedFirstName || trimmedFirstName.length > MAX_TEXT_LENGTH) {
      setStatus("error");
      setErrorMessage("Please enter your first name (up to 120 characters).");
      return;
    }
    if (!trimmedLastName || trimmedLastName.length > MAX_TEXT_LENGTH) {
      setStatus("error");
      setErrorMessage("Please enter your last name (up to 120 characters).");
      return;
    }
    if (!trimmedCompany || trimmedCompany.length > MAX_TEXT_LENGTH) {
      setStatus("error");
      setErrorMessage("Please enter your company (up to 120 characters).");
      return;
    }
    if (!EMAIL_REGEX.test(trimmedEmail)) {
      setStatus("error");
      setErrorMessage("Please enter a valid email address.");
      return;
    }
    if (selectedLists.length === 0) {
      setStatus("error");
      setErrorMessage("Select at least one list to subscribe to.");
      return;
    }
    if (!webhookUrl) {
      setStatus("error");
      setErrorMessage("This form is not configured yet. Please try again later.");
      return;
    }
    setStatus("loading");
    const payload = {
      firstName: trimmedFirstName,
      lastName: trimmedLastName,
      company: trimmedCompany,
      email: trimmedEmail,
      lists: selectedLists,
      action: "subscribe",
      source: typeof window !== "undefined" ? window.location.pathname : "",
      userAgent: typeof navigator !== "undefined" ? navigator.userAgent : "",
      submittedAt: new Date().toISOString()
    };
    logSubmissionAttempt(payload);
    try {
      const response = await fetch(webhookUrl, {
        method: "POST",
        mode: "no-cors",
        headers: {
          "Content-Type": "text/plain;charset=UTF-8"
        },
        body: JSON.stringify(payload)
      });
      if (response.type !== "opaque" && !response.ok) {
        throw new Error(`Request failed with status ${response.status}`);
      }
      setStatus("success");
      setFirstName("");
      setLastName("");
      setCompany("");
      setEmail("");
      setSelectedLists(lists.map(l => l.key));
    } catch (err) {
      setStatus("error");
      setErrorMessage("We couldn't complete your subscription right now. Please try again in a moment.");
    }
  };
  const inputClass = "w-full rounded-md border border-zinc-300 bg-white px-3 py-2 text-sm text-zinc-900 placeholder:text-zinc-400 focus:border-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-600/30 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100 dark:placeholder:text-zinc-500";
  const labelClass = "block text-sm font-medium text-zinc-700 dark:text-zinc-200 mb-1";
  const checkboxLabelClass = "flex items-start gap-2 text-sm text-zinc-700 dark:text-zinc-200";
  const checkboxClass = "mt-0.5 h-4 w-4 rounded border-zinc-300 text-blue-600 focus:ring-blue-600/30 dark:border-zinc-700 dark:bg-zinc-900";
  return <div className="email-form not-prose my-4 rounded-xl border border-zinc-200 bg-zinc-50 p-5 dark:border-zinc-800 dark:bg-zinc-900/40">
      <h3 className="m-0 text-base font-semibold text-zinc-900 dark:text-zinc-50">
        {heading}
      </h3>
      {description ? <p className="mt-1 mb-0 text-sm text-zinc-600 dark:text-zinc-300">{description}</p> : null}

      <form onSubmit={handleSubmit} noValidate className="mt-4 flex flex-col gap-3">
        <div className="grid gap-3 md:grid-cols-2">
          <div>
            <label htmlFor={firstNameId} className={labelClass}>
              First name
            </label>
            <input id={firstNameId} type="text" name="firstName" autoComplete="given-name" required maxLength={MAX_TEXT_LENGTH} value={firstName} onChange={e => setFirstName(e.target.value)} disabled={isSubmitting} className={inputClass} placeholder="Jane" />
          </div>
          <div>
            <label htmlFor={lastNameId} className={labelClass}>
              Last name
            </label>
            <input id={lastNameId} type="text" name="lastName" autoComplete="family-name" required maxLength={MAX_TEXT_LENGTH} value={lastName} onChange={e => setLastName(e.target.value)} disabled={isSubmitting} className={inputClass} placeholder="Doe" />
          </div>
        </div>

        <div className="grid gap-3 md:grid-cols-2">
          <div>
            <label htmlFor={companyId} className={labelClass}>
              Company
            </label>
            <input id={companyId} type="text" name="company" autoComplete="organization" required maxLength={MAX_TEXT_LENGTH} value={company} onChange={e => setCompany(e.target.value)} disabled={isSubmitting} className={inputClass} placeholder="Acme Corp" />
          </div>
          <div>
            <label htmlFor={emailId} className={labelClass}>
              Work email
            </label>
            <input id={emailId} type="email" name="email" autoComplete="email" required value={email} onChange={e => setEmail(e.target.value)} disabled={isSubmitting} className={inputClass} placeholder="jane@acme.com" />
          </div>
        </div>

        <fieldset className="mt-1 flex flex-col gap-2">
          <legend className={labelClass}>Subscribe me to:</legend>
          {lists.map(list => {
    const checkboxId = `${uid}-list-${list.key}`;
    return <label key={list.key} htmlFor={checkboxId} className={checkboxLabelClass}>
                <input id={checkboxId} type="checkbox" name="lists" value={list.key} checked={selectedLists.includes(list.key)} onChange={() => toggleList(list.key)} disabled={isSubmitting} className={checkboxClass} />
                <span className="flex flex-col">
                  <span className="font-medium">{list.label}</span>
                  {list.description ? <span className="text-zinc-500 dark:text-zinc-400">{list.description}</span> : null}
                </span>
              </label>;
  })}
        </fieldset>

        <div aria-hidden="true" style={{
    position: "absolute",
    left: "-10000px",
    top: "auto",
    width: "1px",
    height: "1px",
    overflow: "hidden"
  }}>
          <label htmlFor={websiteId}>Website (leave blank)</label>
          <input id={websiteId} type="text" name="website" tabIndex={-1} autoComplete="off" value={website} onChange={e => setWebsite(e.target.value)} />
        </div>

        <button type="submit" disabled={isSubmitting} className="mt-1 inline-flex w-full items-center justify-center rounded-md bg-blue-600 px-4 py-2 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-60 dark:bg-blue-500 dark:hover:bg-blue-400">
          {isSubmitting ? "Subscribing…" : buttonLabel}
        </button>

        <div id={statusId} role="status" aria-live="polite" className="min-h-[1.25rem] text-sm">
          {status === "success" ? <span className="text-emerald-700 dark:text-emerald-400">
              Thanks — your subscription was submitted.
            </span> : null}
          {status === "error" ? <span className="text-red-700 dark:text-red-400">{errorMessage}</span> : null}
        </div>
      </form>

      <p className="mt-3 mb-0 text-xs text-zinc-500 dark:text-zinc-400">
        Already subscribed?{" "}
        <a href="/release-notes/unsubscribe" className="underline hover:text-zinc-700 dark:hover:text-zinc-200">
          Unsubscribe
        </a>
        .
      </p>
    </div>;
};

export const ReleaseDocButton = ({href, label}) => {
  return <a href={href} className="not-prose no-underline inline-flex items-center gap-2 px-3 py-1.5 mt-1 mb-3 rounded-md bg-emerald-600 hover:bg-emerald-700 text-white hover:text-white text-sm font-medium shadow-sm transition-colors dark:bg-emerald-500 dark:hover:bg-emerald-400" style={{
    textDecoration: "none"
  }}>
      <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
        <path d="M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H20v20H6.5a2.5 2.5 0 0 1 0-5H20" />
      </svg>
      <span>{label}</span>
      <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
        <line x1="5" y1="12" x2="19" y2="12" />
        <polyline points="12 5 19 12 12 19" />
      </svg>
    </a>;
};

<Note>
  The colored badges indicate the area of Elementum where you will see the update (for example, Apps, Tables, Intelligence, or Automations).
</Note>

<Tabs>
  <Tab title="General Availability">
    ## August 6, 2026

    ### Agents & AI

    **Custom Inputs & Outputs on Skill Automation Tools** <Badge color="blue" size="sm">Intelligence</Badge> - Skill builders get the same control over automation tool interfaces that agent builders already have — closing a key parity gap in the Skills framework.

    * Define custom inputs and outputs on automation-type Skill tools, just like you already can on agent automation tools.
    * When configured, the agent uses your custom inputs as its interface for the tool, guiding it to the right values for more accurate, predictable results.
    * Adopt it at your own pace: automation tools without custom inputs keep their existing behavior.

    <ReleaseDocButton href="/ai-agents/agents-skills#custom-inputs-and-outputs-on-automation-tools" label="See more about Skill tool inputs and outputs" />

    **Permission Options on Skill Tools** <Badge color="blue" size="sm">Intelligence</Badge> - Share Skills across your organization without compromising on security — every tool runs under exactly the identity you intend.

    * Match each Skill tool to your governance model by running it as the Agent default, Publisher, Current user, or Service account.
    * Consolidate one-off agent tools into shared, reusable Skills with confidence, keeping the same fine-grained access control you had before.
    * Adopt it with zero rework: existing tools keep running unchanged and new tools default to Agent default.

    <ReleaseDocButton href="/ai-agents/agents-skills#skill-tool-execution-permissions" label="See more about Skill tool permissions" />

    ### Apps

    **Multiple Closed Statuses** <Badge color="orange" size="sm">Apps</Badge> - Model the way your team really closes work — mark every ending you need as closed, not just one, so reporting and automations stay accurate.

    * Tag any number of status options as closed, like Closed-Good, Closed-Bad, Canceled, and Complete, instead of being boxed into a single closed status.
    * Trust your numbers everywhere, since every closed value carries the same indicator across reports, filters, dashboards, and automations.

    <ReleaseDocButton href="/workflows/layouts#mark-statuses-as-closed" label="See more about closed statuses" />

    **Side-by-Side Attachment Viewer** <Badge color="orange" size="sm">Record Details</Badge> - Read a record's attachments without losing your place — files now open in a side-by-side preview panel right next to the record instead of a modal that covers it.

    * Keep the record and its attachment on screen together, so you can reference both while you work.
    * Expand the preview to the full viewport with one click when you need a closer look.
    * Works with the file types Elementum already supports for preview, so it fits right into your existing workflow.

    <ReleaseDocButton href="/workflows/file-uploads-attachments" label="See more about attachments on a record" />

    ### Automations

    **"Is In" Filters for Automations** <Badge color="yellow" size="sm">Automations</Badge> - Match a whole list of values in one filter — pull every record you care about in a single Search Records condition instead of stacking OR conditions or hardcoding values.

    * Return every record whose field value is in a list you supply, using the new **is in** operator on Text, HTML, Number, and Decimal fields.
    * Build the list at runtime by feeding an **Execute Script** output straight into the filter, so your search adapts to each run.
    * Replace long chains of OR conditions with one clean condition that's easier to read and maintain.

    <ReleaseDocButton href="/workflows/automation-actions-reference#search-records" label="See more about the Search Records action" />

    **Post Update Note** <Badge color="yellow" size="sm">Automations</Badge> - Keep everyone in the loop automatically — let your workflows post updates to a record's timeline the moment something changes, so your team never has to chase down status.

    * Surface automated updates in the same Updates feed your team already watches, so important changes never slip through the cracks.
    * Turn generic notifications into context-rich messages with dynamic values that resolve when the automation runs.
    * Add a clear, human-readable trail to any process by triggering the action from any automation you already run.

    <ReleaseDocButton href="/workflows/automation-actions-reference#post-update-note" label="See more about the Post Update Note action" />

    ### Experience

    **Customizable Side Nav** <Badge color="teal" size="sm">Navigation</Badge> - Make Elementum feel like yours — put the apps, agents, and views you use every day one click away and clear out everything else.

    * Keep your most-used work within reach by pinning Apps, Agents, Conversations, Views, Elements, Tables, and Tasks to a dedicated section.
    * Cut the clutter by hiding the items and sections you don't need and reordering the rest to match how you actually work.
    * Set it once and move faster every day — your personalized layout follows you across sessions, pages, and logins.

    <ReleaseDocButton href="/getting-started/home-page#pin-and-organize-navigation-items" label="See more about customizing the navigation menu" />

    ### Platform

    **Environments Secret Vault** <Badge color="purple" size="sm">Org Settings</Badge> - Stop hardcoding secrets and rewriting configuration every time you promote work — store your keys once and let each environment resolve to the right value automatically.

    * Keep API keys and other sensitive values out of your configuration with write-only secrets that can't be read back once set.
    * Reference saved vault entries as values in Automations. At runtime, the value resolves from whichever environment the automation runs in.
    * Control exactly who can view or manage every entry with per-entry access policies.

    <ReleaseDocButton href="/administration/environments-secret-vault" label="See more about the Environments Secret Vault" />

    **RSA Key Rotation** <Badge color="purple" size="sm">Org Settings</Badge> - Stay on top of your security posture on your own terms — rotate the RSA signing key on a Snowflake cloudlink whenever you need to, without opening a support ticket.

    * Rotate keys on your own schedule, right from Organization Settings, to keep pace with your security policies.
    * Swap with confidence: test the new key against a live connection before you apply it, so you never break an active connection.
    * See exactly what's changing, with the current and new keys displayed side by side during the rotation.

    <ReleaseDocButton href="/administration/connect-snowflake-to-elementum#key-rotation" label="See more about RSA key rotation" />
  </Tab>
</Tabs>

## Stay in the loop

<EmailSubscriptionPicker webhookUrl="https://elementum.elementum.io/api/v1/webhooks/38fad8b3-0323-44ba-be52-d622b1a29289" heading="Subscribe to release email updates" description="Choose what you'd like to hear about. Both lists are selected by default." />
