> Warning: > > Private metafields were removed in GraphQL Admin API version `2024-01`. While some apps retain access until version `2025-01`, we recommend migrating to public metafields as soon as possible. Before [ownership](/docs/apps/build/custom-data/ownership) and [permissions](/docs/apps/build/custom-data/permissions) were introduced for metafields, the only way developers could create metafields that were private to their app was through private metafields. However, they have the following limitations: - Only accessible from the GraphQL Admin API - Limitation of 10 private metafields per resource - Don't support [metafield definitions](/docs/apps/build/custom-data/metafields/definitions) The following queries and mutations have been deprecated from the GraphQL Admin API: - [Creating a private metafield](/docs/api/admin-graphql/latest/mutations/privateMetafieldUpsert) - [Deleting a private metafield](/docs/api/admin-graphql/latest/mutations/privateMetafieldDelete) - [Querying for private metafields](/docs/api/admin-graphql/latest/queries/privateMetafields) ## App-owned metafields Regular metafields are “app-owned” if the metafield’s namespace contains your app's reserved namespace. An [app reserved namespace](/docs/apps/build/custom-data/ownership#reserved-prefixes) is a metafield namespace that's prefixed with your app’s API client ID. This looks like the following: `app--{your-app-id}--{some-namespace}` Instead of requiring knowledge of your API client ID in the request, your app can use the `$app` shorthand in front of the desired namespace to add the prefix. For example, if your app wanted to declare a metafield with the namespace `client_name`, you would pass in `$app:client_name` as the namespace when creating the metafield through the [`metafieldsSet`](/docs/api/admin-graphql/latest/mutations/metafieldsSet) mutation. As a result, when reading this metafield back, the namespace looks like the following example: `app--12345--client_name` A metafield that uses a reserved prefix will be private by default and not accessible to anyone but your app. However, you may want to specify some granularity to how accessible your metafield can be. ## Updating your GraphQL queries As of the `2023-01` API version, `private metafields` have been migrated to app-owned metafields with [reserved namespaces](/docs/apps/build/custom-data/ownership#reserved-prefixes). The following sections show you how to migrate your app to use the new metafields system. ### Querying app-owned metafields In API version 2023-10 and lower, you could retrieve private metafields using the following queries:

As of the `2023-01` API version, you can use the following queries (for types implementing [`hasMetafields`](/docs/api/admin-graphql/latest/interfaces/HasMetafields)) with the `$app:` shorthand followed by your private metafield's namespace:

Both the [`metafield`](/docs/api/admin-graphql/latest/interfaces/HasMetafields#field-hasmetafields-metafield) and [`metafields`](/docs/api/admin-graphql/latest/interfaces/HasMetafields#connection-hasmetafields-metafields) query above under `product` (or other resource implementing [`hasMetafields`](/docs/api/admin-graphql/latest/interfaces/HasMetafields)) will filter through your app-owned metafields matching the specified namespace: `app-{your-app-id}--some_namespace` for that product. The [`metafields`](/docs/api/admin-graphql/latest/interfaces/HasMetafields#connection-hasmetafields-metafields) query returns a paginated list of metafields whilst [`metafield`](/docs/api/admin-graphql/latest/interfaces/HasMetafields#field-hasmetafields-metafield) returns a single metafield by also matching against a specified `key`. As of the `unstable` API version, you can also use the [`metafields`](/docs/api/admin-graphql/unstable/queries/metafields) query with the `$app:` shorthand:

### Creating and updating app-owned metafields In API version `2023-10` and lower, you could create or update private metafields using the following mutation:

As of the `2023-01` API version, you can create or update an app-owned metafield with the [`metafieldsSet`](/docs/api/admin-graphql/latest/mutations/metafieldsSet) mutation and use the `$app:` shorthand followed by the desired namespace:

The [`metafieldsSet`](/docs/api/admin-graphql/latest/mutations/metafieldsSet) mutation takes an array of [`MetafieldsSetInput`](/docs/api/admin-graphql/latest/input-objects/MetafieldsSetInput) allowing you to create or update more than one app-owned metafield. ### Deleting app-owned metafields In API version `2023-10` and lower, you could delete private metafields using the following mutation:

As of the `2023-01` API version, you can use the `id` of your app-owned metafield to delete it using the [`metafieldDelete`](/docs/api/admin-graphql/latest/mutations/metafieldDelete) mutation:

## Webhook subscription payloads with app-owned metafields If you previously had webhook subscriptions that returned the associated private metafields by namespace, then you can create new ones to return your app-owned metafields. The following mutations are available to create webhook subscriptions: - [`webhookSubscriptionCreate`](/docs/api/admin-graphql/latest/mutations/webhookSubscriptionCreate) - [`eventBridgeWebhookSubscriptionCreate`](/docs/api/admin-graphql/latest/mutations/eventBridgeWebhookSubscriptionCreate) - [`pubSubWebhookSubscriptionCreate`](/docs/api/admin-graphql/latest/mutations/pubSubWebhookSubscriptionCreate) In API version `2023-10` and lower, you could use the `privateMetafieldNamespaces` to filter by the private metafield namespace:

As of the `2023-01` API version, you can use the `$app:` shorthand followed by the namespace under `metafieldNamespaces`:

## Metafield access controls Since the `2021-10` API release, developers can define the metafields they want to use on a resource by creating a [metafield definition](/docs/apps/build/custom-data/metafields/definitions). Metafield definitions provide developers with validation options on the data stored, the ability to reserve a namespace/key on the resource it’s defined on and metadata about the field such as name and description. In the `2023-01` API release, metafield definitions now offer a new input field named `access` that allows developers to specify the granularity in which the data stored in the metafield is exposed. There are three levels of access:
Access Setting Description
PRIVATE Only the app that owns a metaobject of this type can read and write to it.
MERCHANT_READ Apps that act on behalf of the app user (such as the admin) can read metaobjects of this type.

Only the owning app can write to metaobjects of this type.
MERCHANT_READ_WRITE Both the owning app and apps that act on behalf of the appuser (such as the admin) can read or write to metaobjects of this type.

No other applications can read or write metaobjects.
By default, metafields defined by apps under [namespaces that they own](/docs/apps/build/custom-data/ownership) (meaning that the namespace contains the app's reserved prefix) will be private to the app. Similar to app-data metafields, after a metafield is defined, reading and writing to it is the same as other regular metafields.