---
title: Manage metaobject definitions
description: 'Learn how to create, read, update, and delete metaobject definitions.'
source_url:
  html: >-
    https://shopify.dev/docs/apps/build/metaobjects/manage-metaobject-definitions
  md: >-
    https://shopify.dev/docs/apps/build/metaobjects/manage-metaobject-definitions.md
api_name: admin
---

# Manage metaobject definitions

Metaobject definitions are schemas that specify the structure, fields, and rules for metaobject types. This guide shows you how to create, read, update, and delete metaobject definitions using TOML configuration or the GraphQL Admin API.

**Tip:**

Not sure how to structure your data? See [Data modeling with metafields and metaobjects](https://shopify.dev/docs/apps/build/metaobjects/data-modeling-with-metafields-and-metaobjects) for a translation guide from SQL concepts to Shopify's architecture.

***

## Requirements

* Your app can make [authenticated requests](https://shopify.dev/docs/api/usage/authentication) to the GraphQL Admin API.
* Your app has the `read_metaobject_definitions` and `write_metaobject_definitions` [access scopes](https://shopify.dev/docs/api/usage/access-scopes#authenticated-access-scopes).

***

## Creating definitions

There are two ways to set up metaobject definitions:

* **TOML**: TOML configurations in `shopify.app.toml` create app-owned definitions. Your app maintains control while optionally allowing edits in the Shopify admin.
* **GraphQL**: The GraphQL Admin API provides programmatic control for creating merchant-owned metaobjects (editable in the Shopify admin and accessible to all installed apps) and dynamically generating definitions based on user configuration.

### TOML (app-owned) example

This example creates an app-owned metaobject definition for author profiles. Because the app controls the author data structure, it uses the app's TOML configuration file to ensure that the definition is consistently deployed across all installations.

## File

shopify.app.toml

```toml
[metaobjects.app.author]
name = "Author"
access.admin = "merchant_read_write"
access.storefront = "public_read"


[metaobjects.app.author.fields.full_name]
name = "Full Name"
type = "single_line_text_field"


[metaobjects.app.author.fields.bio]
name = "Biography"
type = "multi_line_text_field"


[metaobjects.app.author.fields.email]
name = "Email"
type = "single_line_text_field"


[metaobjects.app.author.fields.photo]
name = "Profile Photo"
type = "file_reference"
```

Deploy the changes with your app:

## Terminal

```bash
shopify app deploy
```

**Benefits of TOML:**

* Definitions are version-controlled as part of your app.
* Automatic creation and updates on deploy.
* Works with `shopify app dev` to safely test out changes.
* Consistent across all shops - when you update your app's data structure, it deploys to every installation automatically.
* The app maintains ownership.

### Graph​QL Admin API example

These examples show how to create metaobject definitions using GraphQL. The first creates a merchant-owned definition that all apps can access. The second creates an app-owned definition that only your app controls.

##### Merchant-owned (editable in Shopify admin)

```graphql
# POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

mutation CreateSizeChartDefinition {
  metaobjectDefinitionCreate(definition: {
    type: "size_chart"
    name: "Size Chart"
    description: "Product sizing information"
    access: {
      storefront: PUBLIC_READ
    }
    fieldDefinitions: [
      {
        key: "size"
        name: "Size"
        type: "single_line_text_field"
        required: true
      }
      {
        key: "chest_inches"
        name: "Chest (inches)"
        type: "number_decimal"
      }
      {
        key: "waist_inches"
        name: "Waist (inches)"
        type: "number_decimal"
      }
    ]
  }) {
    metaobjectDefinition {
      id
      type
      name
      fieldDefinitions {
        key
        name
        type { name }
      }
    }
    userErrors {
      field
      message
    }
  }
}
```

##### App-owned (app controlled)

```graphql
# POST https://{shop}.myshopify.com/api/{api_version}/graphql.json

mutation CreateAppOwnedDefinition {
  metaobjectDefinitionCreate(definition: {
    type: "$app:warranty_info"
    name: "Warranty Information"
    description: "Extended warranty details"
    access: {
      admin: MERCHANT_READ_WRITE
      storefront: PUBLIC_READ
    }
    fieldDefinitions: [
      {
        key: "coverage_period"
        name: "Coverage Period"
        type: "single_line_text_field"
      }
      {
        key: "terms"
        name: "Terms and Conditions"
        type: "multi_line_text_field"
      }
    ]
  }) {
    metaobjectDefinition {
      id
      type
      name
    }
    userErrors {
      field
      message
    }
  }
}
```

Key differences:

* **Merchant-owned**: Use a simple type name without any prefix (like `size_chart` or `author`). This provides full control over both the definition and entries in the Shopify admin. `access.admin` isn't required. Only `access.storefront` is used to control customer visibility.
* **App-owned**: Use the reserved `$app:` prefix in the type. The app controls the definition. Use `access.admin` to grant merchant write permissions for the entries.

**Use GraphQL when:**

* Merchants define their own custom metaobjects through your app's UI.
* Field structure varies per merchant or changes frequently.
* Building form builders, CMS-like tools, or content managers.
* You're creating merchant-owned metaobjects that other apps can access.

### Dynamic definition creation example

This example shows how to programmatically create definitions based on user input, such as in a content manager app where custom metaobject types are configured through your app's UI.

Your app would collect metaobject configuration (using a form or UI), validate the input, construct the variables object, and then execute the mutation. This enables the creation of custom metaobjects through your app's interface without editing code or configuration files.

## POST https://{shop}.myshopify.com/api/{api\_version}/graphql.json

## GraphQL mutation

```graphql
mutation CreateDynamicMetaobject($input: MetaobjectDefinitionCreateInput!) {
  metaobjectDefinitionCreate(definition: $input) {
    metaobjectDefinition {
      id
      name
      type
      fieldDefinitions {
        key
        name
        type { name }
      }
    }
    userErrors {
      field
      message
    }
  }
}
```

## Variables

```json
{
  "input": {
    "type": "custom_content_block",
    "name": "Custom Content Block",
    "description": "User-defined content block",
    "fieldDefinitions": [
      {
        "key": "title",
        "name": "Title",
        "type": "single_line_text_field"
      },
      {
        "key": "content",
        "name": "Content",
        "type": "rich_text_field"
      }
    ]
  }
}
```

***

## Reading definitions

Query metaobject definitions to retrieve their structure and configuration.

### Querying all definitions

Retrieve all metaobject definitions to discover what types exist and their configurations. Use this to find available type identifiers when building admin interfaces or before creating entries.

## GraphQL

POST https://{shop}.myshopify.com/api/{api\_version}/graphql.json

```graphql
query GetAllDefinitions {
  metaobjectDefinitions(first: 50) {
    edges {
      node {
        id
        type
        name
        description
        access {
          admin
          storefront
        }
        fieldDefinitions {
          key
          name
          type { name }
          required
        }
      }
    }
  }
}
```

### Querying a specific definition by type

Retrieve a single definition using its type identifier. This is useful when you need to check a definition's structure before creating entries or to verify field configurations.

## GraphQL

POST https://{shop}.myshopify.com/api/{api\_version}/graphql.json

```graphql
query GetDefinitionByType {
  metaobjectDefinitionByType(type: "size_chart") {
    id
    type
    name
    description
    fieldDefinitions {
      key
      name
      type { name }
      required
      validations {
        name
        value
      }
    }
    access {
      admin
      storefront
    }
  }
}
```

***

## Updating definitions

Modify existing definitions to add fields, update access, or change configuration. To update definitions declared in TOML, simply update the configuration file, test with `shopify app dev` and deploy a new version of your app.

### Updating name and description

Update the display name and description of a metaobject definition. This is useful for improving clarity or correcting information without affecting the underlying structure.

## GraphQL

POST https://{shop}.myshopify.com/api/{api\_version}/graphql.json

```graphql
mutation UpdateDefinitionDetails {
  metaobjectDefinitionUpdate(
    id: "gid://shopify/MetaobjectDefinition/123"
    definition: {
      name: "Updated Size Chart"
      description: "Comprehensive sizing information for all products"
    }
  ) {
    metaobjectDefinition {
      id
      name
      description
    }
    userErrors {
      field
      message
    }
  }
}
```

### Adding a new field

Add new fields to an existing metaobject definition. This enables you to extend the data structure as your app's requirements evolve.

## GraphQL

POST https://{shop}.myshopify.com/api/{api\_version}/graphql.json

```graphql
mutation AddFieldToDefinition {
  metaobjectDefinitionUpdate(
    id: "gid://shopify/MetaobjectDefinition/123"
    definition: {
      fieldDefinitions: [
        {
          key: "hip_inches"
          name: "Hip (inches)"
          type: "number_decimal"
        }
      ]
    }
  ) {
    metaobjectDefinition {
      id
      fieldDefinitions {
        key
        name
        type { name }
      }
    }
    userErrors {
      field
      message
    }
  }
}
```

### Updating access permissions

Modify access permissions to control who can read or write metaobject entries. This is useful for adjusting visibility or editing rights as your requirements change.

## GraphQL

POST https://{shop}.myshopify.com/api/{api\_version}/graphql.json

```graphql
mutation UpdateDefinitionAccess {
  metaobjectDefinitionUpdate(
    id: "gid://shopify/MetaobjectDefinition/123"
    definition: {
      access: {
        admin: MERCHANT_READ_WRITE
        storefront: NONE
      }
    }
  ) {
    metaobjectDefinition {
      id
      access {
        admin
        storefront
      }
    }
    userErrors {
      field
      message
    }
  }
}
```

***

## Deleting definitions

Remove definitions that are no longer needed. Deleting the definition also deletes all related metaobjects, metafield definitions, and metafields asynchronously.

### Deleting with TOML

For app-owned definitions created with TOML, remove the definition block from your `shopify.app.toml` file and redeploy:

## Terminal

```bash
shopify app deploy
```

If a metaobject definition is deleted from `shopify.app.toml` and the app is re-deployed, Shopify deletes the definition and keeps the metaobjects and their values for a short period. If you add the definition back and deploy before cleanup runs, then Shopify recreates the definition with a new GID and reassociates the existing metaobjects.

### Deleting with Graph​QL

Use the GraphQL Admin API to delete metaobject definitions.

## GraphQL

POST https://{shop}.myshopify.com/api/{api\_version}/graphql.json

```graphql
mutation DeleteDefinition {
  metaobjectDefinitionDelete(
    id: "gid://shopify/MetaobjectDefinition/123"
  ) {
    deletedId
    userErrors {
      field
      message
    }
  }
}
```

### App uninstall and reinstall

**Caution:**

Shopify doesn't guarantee how long metaobjects and their values are retained after an uninstall. Don't depend on a reinstall within a specific period to preserve them.

When a user uninstalls your app, Shopify deletes its app-owned metaobject definitions and temporarily retains the metaobjects and their values without a definition. This applies to any app-owned definition, whether declared in `shopify.app.toml` or created with the GraphQL Admin API. Cleanup of the retained data runs asynchronously.

If the same app is reinstalled while that data is still retained, then Shopify recreates the definitions and associates the existing metaobjects with them.

What happens to the definition depends on how you created it:

* **TOML definitions**: Shopify deletes them during uninstall and creates them again on reinstall. The new definitions have new GIDs, so a definition ID from before the uninstall no longer resolves.
* **GraphQL definitions**: These can persist after uninstall until asynchronous cleanup removes them.

Don't treat metaobject definition IDs as stable identifiers across an uninstall and reinstall. Look up definitions by type instead.

***

## Error handling

Common errors when managing metaobject definitions:

| Error | Cause | Solution |
| - | - | - |
| `TAKEN` | Type has already been taken | Use a different type identifier or update the existing definition |
| `INVALID` | Type contains invalid characters | Use only alphanumeric characters, underscores, and dashes |
| `NOT_AUTHORIZED` | Type is reserved for another app or Shopify | For app-owned types, use `$app:`. Don't use `shopify--` or other reserved prefixes. |
| `DUPLICATE_FIELD_INPUT` | A field with this key already exists in the definition | Use a different field key for each field |

***

## Best practices

* **Use TOML for app-owned definitions**: Declarative configuration ensures consistency across deployments and enables version control.
* **Plan your type identifiers carefully**: Type identifiers can't be changed after creation.
* **Document field purposes**: Use clear names and descriptions to help merchants understand each field's purpose.
* **Set appropriate access levels**: Start with restrictive access and expand as needed.
* **Test definition changes**: Verify that field additions or updates work correctly before deploying to production.

***

## TOML reference

Metaobjects are declared in `shopify.app.toml` using the format `[metaobjects.app.<metaobject_name>]`. For example, `[metaobjects.app.author]` declares a metaobject named `author` with type `$app:author`.

### Metaobject definition properties

| Property | Description |
| - | - |
| `name` | Human-readable name displayed in the Shopify admin. |
| `description` | Descriptive text that explains the purpose of the metaobject. |
| `display_name_field` | Key of a field to reference as the display name for each entry. |
| `access.admin` | Admin API access control: `merchant_read` or `merchant_read_write`. |
| `access.storefront` | Storefront access control: `public_read` or `none`. |
| `capabilities.publishable` | When `true`, enables draft/active status for content workflow. See [metaobject capabilities](https://shopify.dev/docs/apps/build/metaobjects/use-metaobject-capabilities). |
| `capabilities.translatable` | When `true`, enables translation support for fields. See [translatable content](https://shopify.dev/docs/apps/build/metaobjects/use-metaobject-capabilities#make-your-metaobjects-translatable). |
| `capabilities.renderable` | When `true`, enables metaobject SEO fields on Liquid and the Storefront API. See [metaobjects as web pages](https://shopify.dev/docs/apps/build/metaobjects/use-metaobject-capabilities#render-metaobjects-as-web-pages). |

### Metaobject field properties

Fields are declared using the format `[metaobjects.app.<metaobject_name>.fields.<field_name>]`. For example, `[metaobjects.app.author.fields.birthday]` declares a field named `birthday` on the `author` metaobject.

| Property | Description |
| - | - |
| `type` | Data type for the field. Uses the same types as metafields. See [metafield data types](https://shopify.dev/docs/apps/build/metafields/list-of-data-types). |
| `name` | Human-readable name displayed in the Shopify admin. |
| `description` | Descriptive text that explains the purpose of the field. |
| `required` | When `true`, the field must have a value when saving the metaobject. |
| `validations` | Rules to validate field values based on the field type. See [validation options](https://shopify.dev/docs/apps/build/metafields/list-of-validation-options). |

***

## TOML limitations

When using TOML-based declarative definitions, be aware of these constraints:

### App-reserved namespace

You can only declare metaobject definitions in the app-reserved namespace (`$app:`) to ensure that only the owning app can make changes to definitions. This constraint allows Shopify to guarantee a consistent state between all shops your app is installed on.

### App-scoped limits

| Limit | Value |
| - | - |
| Metaobject definitions | 32 |
| Field definitions per metaobject | 64 |
| Changes per deploy | 25 |

To ensure Shopify can quickly and reliably distribute definitions across shops, you can't make more than 25 metaobject changes (creation, update, or deletion) in a single deploy. If you need to make more than 25 changes, do so over multiple deploys.

### Read-only through Admin API

Declarative definitions are read-only through the Admin API, and can only be updated or deleted through the TOML configuration file. You can query declarative definitions through the Admin API, but mutations will return an error.

### Capability support

| Capability | Supported in TOML |
| - | - |
| [Publishable](https://shopify.dev/docs/apps/build/metaobjects/use-metaobject-capabilities#draft-custom-content) | ✅ |
| [Translatable](https://shopify.dev/docs/apps/build/metaobjects/use-metaobject-capabilities#make-your-metaobjects-translatable) | ✅ |
| [Renderable](https://shopify.dev/docs/apps/build/metaobjects/use-metaobject-capabilities#render-metaobjects-as-web-pages) | ✅ |
| [Online Store](https://shopify.dev/docs/apps/build/metaobjects/use-metaobject-capabilities#make-your-metaobjects-render-web-pages-in-the-online-store) | ❌ |

***

## Next steps

* Learn how to [work with metaobject entries](https://shopify.dev/docs/apps/build/metaobjects/manage-metaobjects).
* Learn how to [enable advanced features](https://shopify.dev/docs/apps/build/metaobjects/use-metaobject-capabilities).

***
