> ## Documentation Index
> Fetch the complete documentation index at: https://auth0-chore-events-autoupdate.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# How to Manage User Profile Metadata

> Access and set user and app metadata in user profiles using the Auth0 Dashboard, Management API, or a `post-login` Action.

export const AuthCodeGroup = ({children, dropdown}) => {
  const [processedChildren, setProcessedChildren] = useState(children);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      unsubscribe = window.autorun(() => {
        const processChildren = node => {
          if (typeof node === "string") {
            let processedNode = node;
            for (const [key, value] of window.rootStore.variableStore.values.entries()) {
              const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
              processedNode = processedNode.replaceAll(new RegExp(escapedKey, "g"), value);
            }
            return processedNode;
          } else if (Array.isArray(node)) {
            return node.map(processChildren);
          } else if (node && node.props && node.props.children) {
            return {
              ...node,
              props: {
                ...node.props,
                children: processChildren(node.props.children)
              }
            };
          }
          return node;
        };
        setProcessedChildren(processChildren(children));
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  return <CodeGroup dropdown={dropdown}>{processedChildren}</CodeGroup>;
};

export const AuthCodeBlock = ({filename, icon, language, highlight, children}) => {
  const [displayText, setDisplayText] = useState(children);
  const [copyText, setCopyText] = useState(children);
  const wrapperRef = React.useRef(null);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      if (!window.autorun || !window.rootStore) {
        return;
      }
      unsubscribe = window.autorun(() => {
        let processedChildrenForDisplay = children;
        let processedChildrenForCopy = children;
        for (const [key, value] of window.rootStore.variableStore.values.entries()) {
          const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
          let displayValue = value;
          if (key === "{yourClientSecret}" && value !== "{yourClientSecret}") {
            displayValue = value.substring(0, 3) + "*****MASKED*****";
          }
          processedChildrenForDisplay = processedChildrenForDisplay.replaceAll(new RegExp(escapedKey, "g"), displayValue);
          processedChildrenForCopy = processedChildrenForCopy.replaceAll(new RegExp(escapedKey, "g"), value);
        }
        setDisplayText(processedChildrenForDisplay);
        setCopyText(processedChildrenForCopy);
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  useEffect(() => {
    if (!wrapperRef.current) return;
    const originalWriteText = navigator.clipboard.writeText.bind(navigator.clipboard);
    let isOverriding = false;
    const handleClick = e => {
      const button = e.target.closest('[data-testid="copy-code-button"]');
      if (!button || !wrapperRef.current.contains(button)) return;
      isOverriding = true;
      navigator.clipboard.writeText = text => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
          return originalWriteText(copyText);
        }
        return originalWriteText(text);
      };
      setTimeout(() => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
        }
      }, 100);
    };
    const wrapper = wrapperRef.current;
    wrapper.addEventListener('click', handleClick, true);
    return () => {
      wrapper.removeEventListener('click', handleClick, true);
      if (navigator.clipboard.writeText !== originalWriteText) {
        navigator.clipboard.writeText = originalWriteText;
      }
    };
  }, [copyText]);
  return <div ref={wrapperRef}>
      <CodeBlock filename={filename} icon={icon} language={language} lines highlight={highlight}>
        {displayText}
      </CodeBlock>
    </div>;
};

You can access and set `user_metadata` and `app_metadata` in user profiles using a variety of methods depending on your use case.

<Tabs>
  <Tab title="Management API">
    When you create a new user with the [Management API's Create a User endpoint](/docs/api/management/v2/users/post-users) (`POST /users`), you can set the new user's metadata by specifying the body parameters for `user_metadata` and `app_metadata`.

    <Accordion title="Example: create new user with metadata">
      To create a user with the following profile details:

      ```json theme={null}
      {
          "email": "jane.doe@example.com",
          "user_metadata": {
              "hobby": "surfing"
          },
          "app_metadata": {
              "plan": "full"
          }
      }
      ```

      Make the following `POST` call to the <Tooltip tip="Management API: A product to allow customers to perform administrative tasks." cta="View Glossary" href="/docs/glossary?term=Management+API">Management API</Tooltip> [`/post_users`](https://auth0.com/docs/api/management/v2#!/Users/post_users) endpoint to create the user and set the property values:

      <Callout icon="file-lines" color="#0EA5E9" iconType="regular">Using the Auth0 CLI? If you haven't already, [set up and authenticate your CLI session](/docs/deploy-monitor/auth0-cli) before running this command.</Callout>

      <AuthCodeGroup>
        ```bash Auth0 CLI theme={null}
        auth0 api post "users" \
          --data '{"email": "jane.doe@example.com", "user_metadata": {"hobby": "surfing"}, "app_metadata": {"plan": "full"}}'
        ```

        ```bash cURL theme={null}
        curl --request POST \
          --url 'https://{yourDomain}/api/v2/users' \
          --header 'authorization: Bearer MGMT_API_ACCESS_TOKEN' \
          --header 'content-type: application/json' \
          --data '{"email": "jane.doe@example.com", "user_metadata": {"hobby": "surfing"}, "app_metadata": {"plan": "full"}}'
        ```

        ```csharp C# theme={null}
        var client = new RestClient("https://{yourDomain}/api/v2/users");
        var request = new RestRequest(Method.POST);
        request.AddHeader("authorization", "Bearer MGMT_API_ACCESS_TOKEN");
        request.AddHeader("content-type", "application/json");
        request.AddParameter("application/json", "{"email": "jane.doe@example.com", "user_metadata": {"hobby": "surfing"}, "app_metadata": {"plan": "full"}}", ParameterType.RequestBody);
        IRestResponse response = client.Execute(request);
        ```

        ```go Go theme={null}
        package main

        import (
            "fmt"
            "strings"
            "net/http"
            "io/ioutil"
        )

        func main() {

            url := "https://{yourDomain}/api/v2/users"

            payload := strings.NewReader("{"email": "jane.doe@example.com", "user_metadata": {"hobby": "surfing"}, "app_metadata": {"plan": "full"}}")

            req, _ := http.NewRequest("POST", url, payload)

            req.Header.Add("authorization", "Bearer MGMT_API_ACCESS_TOKEN")
            req.Header.Add("content-type", "application/json")

            res, _ := http.DefaultClient.Do(req)

            defer res.Body.Close()
            body, _ := ioutil.ReadAll(res.Body)

            fmt.Println(res)
            fmt.Println(string(body))

        }
        ```

        ```java Java theme={null}
        HttpResponse<String> response = Unirest.post("https://{yourDomain}/api/v2/users")
          .header("authorization", "Bearer MGMT_API_ACCESS_TOKEN")
          .header("content-type", "application/json")
          .body("{"email": "jane.doe@example.com", "user_metadata": {"hobby": "surfing"}, "app_metadata": {"plan": "full"}}")
          .asString();
        ```

        ```javascript Node.JS theme={null}
        var axios = require("axios").default;

        var options = {
          method: 'POST',
          url: 'https://{yourDomain}/api/v2/users',
          headers: {
            authorization: 'Bearer MGMT_API_ACCESS_TOKEN',
            'content-type': 'application/json'
          },
          data: {
            email: 'jane.doe@example.com',
            user_metadata: {hobby: 'surfing'},
            app_metadata: {plan: 'full'}
          }
        };

        axios.request(options).then(function (response) {
          console.log(response.data);
        }).catch(function (error) {
          console.error(error);
        });
        ```

        ```php PHP theme={null}
        $curl = curl_init();

        curl_setopt_array($curl, [
          CURLOPT_URL => "https://{yourDomain}/api/v2/users",
          CURLOPT_RETURNTRANSFER => true,
          CURLOPT_ENCODING => "",
          CURLOPT_MAXREDIRS => 10,
          CURLOPT_TIMEOUT => 30,
          CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
          CURLOPT_CUSTOMREQUEST => "POST",
          CURLOPT_POSTFIELDS => "{"email": "jane.doe@example.com", "user_metadata": {"hobby": "surfing"}, "app_metadata": {"plan": "full"}}",
          CURLOPT_HTTPHEADER => [
            "authorization: Bearer MGMT_API_ACCESS_TOKEN",
            "content-type: application/json"
          ],
        ]);

        $response = curl_exec($curl);
        $err = curl_error($curl);

        curl_close($curl);

        if ($err) {
          echo "cURL Error #:" . $err;
        } else {
          echo $response;
        }
        ```

        ```python Python theme={null}
        import http.client

        conn = http.client.HTTPSConnection("")

        payload = "{"email": "jane.doe@example.com", "user_metadata": {"hobby": "surfing"}, "app_metadata": {"plan": "full"}}"

        headers = {
            'authorization': "Bearer MGMT_API_ACCESS_TOKEN",
            'content-type': "application/json"
            }

        conn.request("POST", "/{yourDomain}/api/v2/users", payload, headers)

        res = conn.getresponse()
        data = res.read()

        print(data.decode("utf-8"))
        ```

        ```ruby Ruby theme={null}
        require 'uri'
        require 'net/http'
        require 'openssl'

        url = URI("https://{yourDomain}/api/v2/users")

        http = Net::HTTP.new(url.host, url.port)
        http.use_ssl = true
        http.verify_mode = OpenSSL::SSL::VERIFY_PEER

        request = Net::HTTP::Post.new(url)
        request["authorization"] = 'Bearer MGMT_API_ACCESS_TOKEN'
        request["content-type"] = 'application/json'
        request.body = "{"email": "jane.doe@example.com", "user_metadata": {"hobby": "surfing"}, "app_metadata": {"plan": "full"}}"

        response = http.request(request)
        puts response.read_body
        ```
      </AuthCodeGroup>
    </Accordion>

    You can set or update an existing user's metadata with the [Update a User endpoint](/docs/api/management/v2/users/patch-users-by-id) (`PATCH /users/{id}`) by similarly specifying the body parameters for `user_metadata` and `app_metadta`.

    <Accordion title="Example: update an existing user's metadata">
      Assuming you created a user with the following metadata values:

      ```json theme={null}
      {
          "email": "jane.doe@example.com",
          "user_metadata": {
              "hobby": "surfing"
          },
          "app_metadata": {
              "plan": "full"
          }
      }
      ```

      To update `user_metadata` and add the user's home address as a second-level property:

      ```json theme={null}
      {
          "addresses": {
              "home": "123 Main Street, Anytown, ST 12345"
          }
      }
      ```

      You would make the following `PATCH` call:

      <AuthCodeGroup>
        ```bash Auth0 CLI theme={null}
        auth0 api patch "users/user_id" \
          --data '{"user_metadata": {"addresses": {"home": "123 Main Street, Anytown, ST 12345"}}}'
        ```

        ```bash cURL theme={null}
        curl --request PATCH \
          --url 'https://{yourDomain}/api/v2/users/user_id' \
          --header 'authorization: Bearer MGMT_API_ACCESS_TOKEN' \
          --header 'content-type: application/json' \
          --data '{"user_metadata": {"addresses": {"home": "123 Main Street, Anytown, ST 12345"}}}'
        ```

        ```csharp C# theme={null}
        var client = new RestClient("https://{yourDomain}/api/v2/users/user_id");
        var request = new RestRequest(Method.PATCH);
        request.AddHeader("authorization", "Bearer MGMT_API_ACCESS_TOKEN");
        request.AddHeader("content-type", "application/json");
        request.AddParameter("application/json", "{"user_metadata": {"addresses": {"home": "123 Main Street, Anytown, ST 12345"}}}", ParameterType.RequestBody);
        IRestResponse response = client.Execute(request);
        ```

        ```go Go theme={null}
        package main

        import (
            "fmt"
            "strings"
            "net/http"
            "io/ioutil"
        )

        func main() {

            url := "https://{yourDomain}/api/v2/users/user_id"

            payload := strings.NewReader("{"user_metadata": {"addresses": {"home": "123 Main Street, Anytown, ST 12345"}}}")

            req, _ := http.NewRequest("PATCH", url, payload)

            req.Header.Add("authorization", "Bearer MGMT_API_ACCESS_TOKEN")
            req.Header.Add("content-type", "application/json")

            res, _ := http.DefaultClient.Do(req)

            defer res.Body.Close()
            body, _ := ioutil.ReadAll(res.Body)

            fmt.Println(res)
            fmt.Println(string(body))

        }
        ```

        ```java Java theme={null}
        HttpResponse<String> response = Unirest.patch("https://{yourDomain}/api/v2/users/user_id")
          .header("authorization", "Bearer MGMT_API_ACCESS_TOKEN")
          .header("content-type", "application/json")
          .body("{"user_metadata": {"addresses": {"home": "123 Main Street, Anytown, ST 12345"}}}")
          .asString();
        ```

        ```javascript Node.JS theme={null}
        var axios = require("axios").default;

        var options = {
          method: 'PATCH',
          url: 'https://{yourDomain}/api/v2/users/user_id',
          headers: {
            authorization: 'Bearer MGMT_API_ACCESS_TOKEN',
            'content-type': 'application/json'
          },
          data: {user_metadata: {addresses: {home: '123 Main Street, Anytown, ST 12345'}}}
        };

        axios.request(options).then(function (response) {
          console.log(response.data);
        }).catch(function (error) {
          console.error(error);
        });
        ```

        ```php PHP theme={null}
        $curl = curl_init();

        curl_setopt_array($curl, [
          CURLOPT_URL => "https://{yourDomain}/api/v2/users/user_id",
          CURLOPT_RETURNTRANSFER => true,
          CURLOPT_ENCODING => "",
          CURLOPT_MAXREDIRS => 10,
          CURLOPT_TIMEOUT => 30,
          CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
          CURLOPT_CUSTOMREQUEST => "PATCH",
          CURLOPT_POSTFIELDS => "{"user_metadata": {"addresses": {"home": "123 Main Street, Anytown, ST 12345"}}}",
          CURLOPT_HTTPHEADER => [
            "authorization: Bearer MGMT_API_ACCESS_TOKEN",
            "content-type: application/json"
          ],
        ]);

        $response = curl_exec($curl);
        $err = curl_error($curl);

        curl_close($curl);

        if ($err) {
          echo "cURL Error #:" . $err;
        } else {
          echo $response;
        }
        ```

        ```python Python theme={null}
        import http.client

        conn = http.client.HTTPSConnection("")

        payload = "{"user_metadata": {"addresses": {"home": "123 Main Street, Anytown, ST 12345"}}}"

        headers = {
            'authorization': "Bearer MGMT_API_ACCESS_TOKEN",
            'content-type': "application/json"
            }

        conn.request("PATCH", "/{yourDomain}/api/v2/users/user_id", payload, headers)

        res = conn.getresponse()
        data = res.read()

        print(data.decode("utf-8"))
        ```

        ```ruby Ruby theme={null}
        require 'uri'
        require 'net/http'
        require 'openssl'

        url = URI("https://{yourDomain}/api/v2/users/user_id")

        http = Net::HTTP.new(url.host, url.port)
        http.use_ssl = true
        http.verify_mode = OpenSSL::SSL::VERIFY_PEER

        request = Net::HTTP::Patch.new(url)
        request["authorization"] = 'Bearer MGMT_API_ACCESS_TOKEN'
        request["content-type"] = 'application/json'
        request.body = "{"user_metadata": {"addresses": {"home": "123 Main Street, Anytown, ST 12345"}}}"

        response = http.request(request)
        puts response.read_body
        ```
      </AuthCodeGroup>

      The user's profile will now appear as follows:

      ```json theme={null}
      {
          "email": "jane.doe@example.com",
          "user_metadata": {
              "hobby": "surfing",
              "addresses": {
                  "home": "123 Main Street, Anytown, ST 12345"
              }
          },
          "app_metadata": {
              "plan": "full"
          }
      }
      ```
    </Accordion>

    When you update an existing user's metadata, only properties at the root level are merged into the object. All lower-level properties will be replaced.

    <Accordion title="Example: update metadata sub-properties">
      For example, to add a user's work address as an additional inner property, you would have to include the complete contents of the `addresses` property. Since the `addresses` object is a root-level property, it will be merged into the final JSON object representing the user, but its sub-properties will not.

      ```json theme={null}
      {
        "user_metadata": {
          "addresses": {
            "home": "123 Main Street, Anytown, ST 12345",
            "work": "100 Industrial Way, Anytown, ST 12345"
          }
        }
      }
      ```

      Therefore, the corresponding `PATCH` call to the API would be:

      <AuthCodeGroup>
        ```bash Auth0 CLI theme={null}
        auth0 api patch "users/user_id" \
          --data '{"user_metadata": {"addresses": {"home": "123 Main Street, Anytown, ST 12345", "work": "100 Industrial Way, Anytown, ST 12345"}}}'
        ```

        ```bash cURL theme={null}
        curl --request PATCH \
          --url 'https://{yourDomain}/api/v2/users/user_id' \
          --header 'authorization: Bearer MGMT_API_ACCESS_TOKEN' \
          --header 'content-type: application/json' \
          --data '{"user_metadata": {"addresses": {"home": "123 Main Street, Anytown, ST 12345", "work": "100 Industrial Way, Anytown, ST 12345"}}}'
        ```

        ```csharp C# theme={null}
        var client = new RestClient("https://{yourDomain}/api/v2/users/user_id");
        var request = new RestRequest(Method.PATCH);
        request.AddHeader("authorization", "Bearer MGMT_API_ACCESS_TOKEN");
        request.AddHeader("content-type", "application/json");
        request.AddParameter("application/json", "{"user_metadata": {"addresses": {"home": "123 Main Street, Anytown, ST 12345", "work": "100 Industrial Way, Anytown, ST 12345"}}}", ParameterType.RequestBody);
        IRestResponse response = client.Execute(request);
        ```

        ```go Go theme={null}
        package main

        import (
            "fmt"
            "strings"
            "net/http"
            "io/ioutil"
        )

        func main() {

            url := "https://{yourDomain}/api/v2/users/user_id"

            payload := strings.NewReader("{"user_metadata": {"addresses": {"home": "123 Main Street, Anytown, ST 12345", "work": "100 Industrial Way, Anytown, ST 12345"}}}")

            req, _ := http.NewRequest("PATCH", url, payload)

            req.Header.Add("authorization", "Bearer MGMT_API_ACCESS_TOKEN")
            req.Header.Add("content-type", "application/json")

            res, _ := http.DefaultClient.Do(req)

            defer res.Body.Close()
            body, _ := ioutil.ReadAll(res.Body)

            fmt.Println(res)
            fmt.Println(string(body))

        }
        ```

        ```java Java theme={null}
        HttpResponse<String> response = Unirest.patch("https://{yourDomain}/api/v2/users/user_id")
          .header("authorization", "Bearer MGMT_API_ACCESS_TOKEN")
          .header("content-type", "application/json")
          .body("{"user_metadata": {"addresses": {"home": "123 Main Street, Anytown, ST 12345", "work": "100 Industrial Way, Anytown, ST 12345"}}}")
          .asString();
        ```

        ```javascript Node.JS theme={null}
        var axios = require("axios").default;

        var options = {
          method: 'PATCH',
          url: 'https://{yourDomain}/api/v2/users/user_id',
          headers: {
            authorization: 'Bearer MGMT_API_ACCESS_TOKEN',
            'content-type': 'application/json'
          },
          data: {
            user_metadata: {
              addresses: {
                home: '123 Main Street, Anytown, ST 12345',
                work: '100 Industrial Way, Anytown, ST 12345'
              }
            }
          }
        };

        axios.request(options).then(function (response) {
          console.log(response.data);
        }).catch(function (error) {
          console.error(error);
        });
        ```

        ```php PHP theme={null}
        $curl = curl_init();

        curl_setopt_array($curl, [
          CURLOPT_URL => "https://{yourDomain}/api/v2/users/user_id",
          CURLOPT_RETURNTRANSFER => true,
          CURLOPT_ENCODING => "",
          CURLOPT_MAXREDIRS => 10,
          CURLOPT_TIMEOUT => 30,
          CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
          CURLOPT_CUSTOMREQUEST => "PATCH",
          CURLOPT_POSTFIELDS => "{"user_metadata": {"addresses": {"home": "123 Main Street, Anytown, ST 12345", "work": "100 Industrial Way, Anytown, ST 12345"}}}",
          CURLOPT_HTTPHEADER => [
            "authorization: Bearer MGMT_API_ACCESS_TOKEN",
            "content-type: application/json"
          ],
        ]);

        $response = curl_exec($curl);
        $err = curl_error($curl);

        curl_close($curl);

        if ($err) {
          echo "cURL Error #:" . $err;
        } else {
          echo $response;
        }
        ```

        ```python Python theme={null}
        import http.client

        conn = http.client.HTTPSConnection("")

        payload = "{"user_metadata": {"addresses": {"home": "123 Main Street, Anytown, ST 12345", "work": "100 Industrial Way, Anytown, ST 12345"}}}"

        headers = {
            'authorization': "Bearer MGMT_API_ACCESS_TOKEN",
            'content-type': "application/json"
            }

        conn.request("PATCH", "/{yourDomain}/api/v2/users/user_id", payload, headers)

        res = conn.getresponse()
        data = res.read()

        print(data.decode("utf-8"))
        ```

        ```ruby Ruby theme={null}
        require 'uri'
        require 'net/http'
        require 'openssl'

        url = URI("https://{yourDomain}/api/v2/users/user_id")

        http = Net::HTTP.new(url.host, url.port)
        http.use_ssl = true
        http.verify_mode = OpenSSL::SSL::VERIFY_PEER

        request = Net::HTTP::Patch.new(url)
        request["authorization"] = 'Bearer MGMT_API_ACCESS_TOKEN'
        request["content-type"] = 'application/json'
        request.body = "{"user_metadata": {"addresses": {"home": "123 Main Street, Anytown, ST 12345", "work": "100 Industrial Way, Anytown, ST 12345"}}}"

        response = http.request(request)
        puts response.read_body
        ```
      </AuthCodeGroup>
    </Accordion>

    To delete a metadata key, make a `PATCH` call and set the metadata key's value to `null` (for example, `{ "user_metadata": {"color": null}}`).

    To delete user or app metadata entirely, make a `PATCH` call and set the metadata itself to an empty object (for example, `{ "user_metadata": {} }`).
  </Tab>

  <Tab title="`post-login` Action">
    You can configure a `post-login` trigger to modify `user_metadata` and `app_metadata` as part of a user’s login flow.

    Post-login triggers are useful for tasks such as storing application-specific data on the user profile, capturing user operation logs, mapping <Tooltip tip="Security Assertion Markup Language (SAML): Standardized protocol allowing two parties to exchange authentication information without a password." cta="View Glossary" href="/docs/glossary?term=SAML">SAML</Tooltip> attributes to the metadata field, or caching expensive operation values on the User profile for re-used in future logins.

    The `post-login` `api` object provides common operations that can be performed in this trigger. To manage user metadata, we want to use the `api.user.setAppMetadata` and `api.user.setUserMetadata` methods. For example, to guard against some behavior running more than once for a specific user, consider an Action that looks like this:

    ```js theme={null}
    exports.onExecutePostLogin = async (event, api) => {
      if (event.user.app_metadata.didAnExpensiveTask) {
        console.log(`Skipping the expensive task because it already occurred for ${event.user.email}.`);

        return;
      }
      // do and expensive task
      api.user.setAppMetadata("didAnExpensiveTask", true);
    };
    ```

    Here, we added a check at the start of the Action to see if we have already performed the expensive task for this user. If the metadata field exists, then we return from the function.

    At the end of the Action, we call `api.user.setAppMetadata` to signal that we would like to store some metadata on the user object. At the end of each trigger’s execution, Actions will update the user profile as a single operation. If several calls are made to `setUserMetadata` actions, even if they are made in different actions as part of the same flow, Actions will only update the user profile a single time--at the end of the trigger’s execution.

    <Callout icon="file-lines" color="#0EA5E9" iconType="regular">
      Multiple `setUserMetadata` or `setAppMetadata` calls will be batched together into a single user profile update at the end of the trigger’s execution, even if they are made by different Actions.
    </Callout>

    In the event of a [Redirect invoked with `api.redirect.sendUserTo()`](/docs/customize/actions/redirect-with-actions), any pending user or app metadata updates will be applied to the user profile before the user is redirected to the external site.
  </Tab>

  <Tab title="Lock">
    You can use the [Lock library](/docs/libraries/lock) to define, add, read, and update the `user_metadata.` You can read the user's `user_metadata` properties the same way you would read any other user profile property. For example, the following code snippet retrieves the value associated with `user_metadata.hobby` and assigns it to an element on the page:

    ```javascript theme={null}
    // Use the accessToken acquired upon authentication to call getUserInfo
    lock.getUserInfo(accessToken, function(error, profile) {
      if (!error) {
        document.getElementById('hobby').textContent = profile.user_metadata.hobby;
      }
    });
    ```

    You can use `additionalSignUpFields` to add custom fields to user sign-up forms. When a user adds data in a custom field, Auth0 stores entered values in that user's `user_metadata`. To learn more about adding `user_metadata` on signup, read [Additional Signup Fields](/docs/libraries/lock/lock-configuration#additionalsignupfields-array-).
  </Tab>

  <Tab title="Rules">
    <Warning>
      The End of Life (EOL) date of Rules and Hooks will be **November 18, 2026**, and they are no longer available to new tenants created as of **October 16, 2023**. Existing tenants with active Hooks will retain Hooks product access through end of life.

      We highly recommend that you use Actions to extend Auth0. With Actions, you have access to rich type information, inline documentation, and public `npm` packages, and can connect external integrations that enhance your overall extensibility experience. To learn more about what Actions offer, read [Understand How Auth0 Actions Work](/docs/customize/actions/actions-overview).

      To help with your migration, we offer guides that will help you [migrate from Rules to Actions](/docs/customize/actions/migrate/migrate-from-rules-to-actions) and [migrate from Hooks to Actions](/docs/customize/actions/migrate/migrate-from-hooks-to-actions). We also have a dedicated [Move to Actions](https://auth0.com/extensibility/movetoactions) page that highlights feature comparisons, [an Actions demo](https://www.youtube.com/watch?v=UesFSY1klrI), and other resources to help you on your migration journey.

      To read more about the Rules and Hooks deprecation, read our blog post: [Preparing for Rules and Hooks End of Life](https://auth0.com/blog/preparing-for-rules-and-hooks-end-of-life/).
    </Warning>

    You can read, update, and delete metadata using [Auth0 Rules](/docs/customize/rules).  The following sections refer to this example where the user and their information is represented by the following JSON snippet:

    ```json theme={null}
    {
      "user_id": "jdoe",
      "email": "john.doe@example.com",
      "app_metadata": {
        "roles": [ "writer" ]
      },
      "user_metadata": {
        "preferences": {
          "color": "blue"
        }
      }
    }
    ```

    ## Read metadata

    You can read metadata using rules with the <Tooltip tip="Management API: A product to allow customers to perform administrative tasks." cta="View Glossary" href="/docs/glossary?term=Management+API">Management API</Tooltip>. You can also search for profile-related information in `user_metadata`, such as:

    * `name`
    * `nickname`
    * `given_name`
    * `family_name`

    As an example, assume the following metadata is stored for a user with the email address `jane.doe@example.com`:

    ```json theme={null}
    {
        "email": "jane.doe@example.com",
        "user_metadata": {
            "hobby": "surfing"
        },
        "app_metadata": {
            "plan": "full"
        }
    }
    ```

    Using the example metadata above, you can refer to specific items from the dataset in [Auth0 Rules](/docs/customize/rules) or via a call to the [Management API](/docs/manage-users/user-accounts/metadata) as follows:

    ```js theme={null}
    console.log(user.email); // "jane.doe@example.com"
    console.log(user.user_metadata.hobby); // "surfing"
    console.log(user.app_metadata.plan); // "full"
    ```

    Any valid JSON snippet can be used as metadata. `user.app_metadata` is `Undefined` by default.

    <Accordion title="Example: conditional on metadata">
      You can make a decision based on the user's roles:

      ```javascript theme={null}
      function(user, context, callback){
        user.app_metadata = user.app_metadata || {};
        if (user.app_metadata.roles.indexOf('writer')){
          // code to be executed
        }
      }
      ```

      You can base decisions on specific preferences, such as a color preference:

      ```javascript theme={null}
      function(user, context, callback){
        user.user_metadata = user.user_metadata || {};
        if (user.user_metadata.preferences.color === 'black'){
          // code to be executed
        }
        ...
      }
      ```
    </Accordion>

    ### Read application metadata (clientMetadata)

    Application metadata (`clientMetadata`) is an optional, top-level property of the `context` object. Existing applications will have no value for this property.

    ```javascript theme={null}
    function(user, context, callback){
      context.clientMetadata = context.clientMetadata || {};
      if (context.clientMetadata.usersuppliedkey1 === 'black'){
        // this code would not be executed for the user
      }
      ...
    }
    ```

    ## Update metadata

    You can use Rules to map SAML attributes that Auth0 receives from the IdP into `user_metadata` or `app_metadata`.

    ### Update app metadata

    To add an administrative role to the user:

    ```javascript theme={null}
    function(user, context, callback){
      user.app_metadata = user.app_metadata || {};
      // update the app_metadata that will be part of the response
      user.app_metadata.roles = user.app_metadata.roles || [];
      user.app_metadata.roles.push('administrator');

      // persist the app_metadata update
      auth0.users.updateAppMetadata(user.user_id, user.app_metadata)
        .then(function(){
          callback(null, user, context);
        })
        .catch(function(err){
          callback(err);
        });
    }
    ```

    This results in the following JSON representation of the user profile details:

    ```json theme={null}
    {
      "user_id": "jdoe",
      "email": "john.doe@example.com",
      "app_metadata": {
        "roles": [ "writer", "administrator" ]
      },
      "user_metadata": {
        "preferences": {
          "color": "blue"
        }
      }
    }
    ```

    ### Update user metadata

    To add the user's `fontSize` preference to the user profile:

    ```javascript theme={null}
    function(user, context, callback){
      user.user_metadata = user.user_metadata || {};
      // update the user_metadata that will be part of the response
      user.user_metadata.preferences = user.user_metadata.preferences || {};
      user.user_metadata.preferences.fontSize = 12;

      // persist the user_metadata update
      auth0.users.updateUserMetadata(user.user_id, user.user_metadata)
        .then(function(){
          callback(null, user, context);
        })
        .catch(function(err){
          callback(err);
        });
    }
    ```

    This results in the following JSON representation of the user profile details:

    ```json theme={null}
    {
      "user_id": "jdoe",
      "email": "john.doe@example.com",
      "app_metadata": {
        "roles": [ "writer" ]
      },
      "user_metadata": {
        "preferences": {
          "color": "blue",
          "fontSize": 12
        }
      }
    }
    ```

    ### Update app and user metadata simultaneously

    To reduce the rule's processing time, you may update both the `app_metadata` and `user_metadata` in the same rule:

    ```javascript expandable theme={null}
    function(user, context, callback){

      var q = require('q');

      user.app_metadata = user.app_metadata || {};
      user.user_metadata = user.user_metadata || {};
      // update the user_metadata that will be part of the response
      user.user_metadata.preferences = user.user_metadata.preferences || {};
      user.user_metadata.preferences.fontSize = 12;

      // update the app_metadata that will be part of the response
      user.app_metadata.roles = user.app_metadata.roles || [];
      user.app_metadata.roles.push('admin');

      // persist the app_metadata update
      var appMetadataPromise  = auth0.users.updateAppMetadata(user.user_id, user.app_metadata);

      // persist the user_metadata update
      var userMetadataPromise = auth0.users.updateUserMetadata(user.user_id, user.user_metadata);

      // using q library to wait for all promises to complete
      q.all([userMetadataPromise, appMetadataPromise])
        .then(function(){
          callback(null, user, context);
        })
        .catch(function(err){
          callback(err);
        });
    }
    ```

    This results in the following JSON representation of the user profile details:

    ```json theme={null}
    {
      "user_id": "jdoe",
      "email": "john.doe@example.com",
      "app_metadata": {
        "roles": [ "writer", "admin" ]
      },
      "user_metadata": {
        "preferences": {
          "color": "blue",
          "fontSize": 12
        }
      }
    }
    ```

    ## Delete metadata

    ### Delete app metadata properties and values

    To delete a property, set the property's value to `null`.

    #### Delete user's roles example

    To delete the user's roles, use the following sample rule:

    ```javascript theme={null}
    function(user, context, callback){
      user.app_metadata = user.app_metadata || {};
      // update the app_metadata that will be part of the response
      user.app_metadata.roles = null;

      // persist the app_metadata update
      auth0.users.updateAppMetadata(user.user_id, user.app_metadata)
        .then(function(){
          callback(null, user, context);
        })
        .catch(function(err){
          callback(err);
        });
    }
    ```

    This results in the following JSON representation of the user profile:

    ```json theme={null}
    {
      "user_id": "jdoe",
      "email": "john.doe@example.com",
      "app_metadata": { },
      "user_metadata": {
        "preferences": {
          "color": "blue"
        }
      }
    }
    ```

    #### Delete single property value example

    To delete a single value of a property, remove that specific value. For example, to remove the `writer` role from the user profile:

    ```javascript theme={null}
    function(user, context, callback){
      user.app_metadata = user.app_metadata || {};
      user.app_metadata.roles = user.app_metadata.roles || [];

      var index = user.app_metadata.roles.indexOf('writer');

      if (index !== -1){
        // update the app_metadata that will be part of the response
        user.app_metadata.roles.splice(index, 1);
      }

      // persist the app_metadata update
      auth0.users.updateAppMetadata(user.user_id, user.app_metadata)
        .then(function(){
          callback(null, user, context);
        })
        .catch(function(err){
          callback(err);
        });
    }
    ```

    This results in the following JSON representation of the user profile:

    ```json theme={null}
    {
      "user_id": "google-oauth2|1234",
      "email": "john.doe@gmail.com",
      "app_metadata": {
        "roles": [ ]
      },
      "user_metadata": {
        "preferences": {
          "color": "blue"
        }
      }
    }
    ```

    The `roles` property still exists but does not contain any value.

    ### Delete user metadata properties and values

    To delete the user's color preference:

    ```javascript theme={null}
    function(user, context, callback){
      user.user_metadata = user.user_metadata || {};
      // update the user_metadata that will be part of the response
      user.user_metadata.preferences = user.user_metadata.preferences || {};
      delete user.user_metadata.preferences.color;

      // persist the user_metadata update
      auth0.users.updateUserMetadata(user.user_id, user.user_metadata)
        .then(function(){
          callback(null, user, context);
        })
        .catch(function(err){
          callback(err);
      });
    }
    ```

    This results in the following JSON representation of the user profile details:

    ```json theme={null}
    {
      "user_id": "jdoe",
      "email": "john.doe@example.com",
      "app_metadata": {
        "roles": [ "writer" ]
      },
      "user_metadata": {
        "preferences": { }
      }
    }
    ```
  </Tab>
</Tabs>
