Schema Spec Reference
Every project has a schema that defines its functionality. Users can configure most of a project's functionality in its JSON schema file. Configurable features include:
- What the project's GraphQL API is capable of, including which API services it uses and what queries and mutations it exposes. (Read more in our Shapes guide)
- Which workflows should be used when a project's content is edited. (Read more in our workflows guide)
- What the admin UI interface for editing a project's content should look like.
But the JSON schema file does not contain the entire project's schema. Instead, a project's schema composes the editable JSON schema with other hidden configuration information.
There are hidden objects which are part of the project's schema, though they are not visible or configurable in the JSON schema file. These hidden objects are outlined at the end of this specification, and should be ignored in most cases.
There is also a service schema, which is hidden from the editable JSON schema file. The service schema is automatically updated when a new API service is added to a project, and cannot be directly viewed or configured.
Learn more about the Service Schema:
This specification details all configurable functionality in a project's JSON schema file.
Root objects
Root-level objects in a project's schema define the project's high-level functionality.
Projects use queries, mutations and shapes objects to generate a GraphQL API every time the project is deployed. The services object configures API service connections. The forms object configures the interface for creating content. The workflows object configures content creation workflows. The locales and defaultLocale objects configure a project's language localization features.
The following sections will explain the functionality and configuration of all of a schema's root-level objects.
shapes
A Shape is a schema object for structuring and storing data from one or more sources, including a project's data store, and connected services like Shopify.
Shapes are analogous to GraphQL types. Learn more in our shapes guide.
Usage
All shapes exist inside the root-level schema object shapes. The key for the shape should be the shape's name. For example, a shape to represent products should be called Product. A shape to represent reviews should be called Review.
"shapes": {
"Review": {
"id": "Review",
"name": "Review",
"title": "A custom Review shape",
"schema": {
...
}
}
}
Shape properties
Because shapes represent data a project needs to manipulate, they have several properties to identify them for reference in other areas of the schema and project. Those properties include:
id (required)
The shape's ID, which will be used to reference it in other places in the schema. The value of the id key is a used in the shapeIds property of an @relationship and available as _shapeId on an item.
When using built-in database to store shape data, the id value is used in the database as a stable "table name," and changing it will result in orphaning that data. Changing the ID back will make the data accessible again.
name (required)
The name of the shape. Must be in pascal-case. This value must be the same as the shape's object key value. For example, a Review shape must have a name property of "Review"
name property is important.Projects use the name property's value in multiple areas, including:
- When
@refand@mappingannotations are needed. - During the automatic generation of queries and mutations for models, like
getShapeandgetShapeList - In the
_shapeNameproperty on an item.
title (required)
The shape's title. The value of the title property will appear as the shape's title in the admin UI.
workflow
Takes the name of the workflow this shape should be scoped to as a string.
description
A description of the shape that will appear in the admin UI. This description appears in the Docs Panel.
model
The data model used to store instances of this shape. Valid properties include multiple, single, and taxonomy.
schema
Because shapes function like GraphQL types, queries and mutations can return instances of them. The fields available to such queries must be configured in the schema property.
The schema object contains three configuration properties:
Shape schema config
type
The value of this property should always be "object". It indicates the GraphQL type of the shape, which is always an object.
required
An array of strings, which match the keys of the properties that are required when creating an instance of this shape.
For example, if you have a Product shape with properties price, name and id, and all three are required when creating a Product instance, your required array will look like this:
"required":["price", "name", "id"]
properties
This object can have an arbitrary number of nested objects within it, each of which will be fields for the shape. If you have a Product shape that has a price field, you might configure the properties like so:
"Product": {
"id": "jadxIUkZV",
"name": "Product",
"title": "Product",
"schema": {
"type": "object",
"properties": {
"price": {
"type": "number",
"description": "The product's price",
"title": "Product price"
}
}
}
}
Shape fields
When creating a field in the properties object, the only required property is type, which is the GraphQL type of the data the property represents, like string, boolean, integer, etc. But there are many other possible properties, as demonstrated in the example below.
"properties": {
"customId": {
"type": "integer",
"description": "A custom ID property for this shape.",
"title": "This shape's custom ID.",
"minimum": 0
},
"handle": {
"type":"string"
},
"myResolvedProperty": {
"description": "A custom property that uses @resolver.",
"title": "My Resolved Property.",
"@ref": "shopify-storefront:Product",
"@resolver": {
"name": "graphql:query",
"service": "shopify-storefront",
"if": "$source.handle",
"fieldName": "productByHandle",
"args": {
"ops": [{"path": "handle", "mapping": "$source.handle"}]
}
}
}
}
The list of fields possible for a property include:
type (required)
The GraphQL type of the property. Possible values:
arraybooleanintegernumberstringobject
type isn't always required.Using the @ref annotation indicates that a field is an instance of a shape defined elsewhere in the schema. That means you shouldn't use type if you use @ref. Otherwise, type is required.
Configuring object fields
If the field's type is object, then properties will also be required. Within this nested properties object, you can add the properties of the object field. For example:
"Product": {
"id": "Product",
"name": "Product",
"title": "My Custom Product",
"schema": {
"type": "object",
"properties": {
"variants": {
"type": "object",
"properties": {
"variantId":{
"type":"integer",
"description": "The ID for this variant"
},
"variantTitle":{
"type":"string",
"title": "Variant Title"
}
}
},
"id": {"type": "integer"},
"price": {"type":"number"}
}
}
}
When querying for the above Product, the properties will be accessible as fields:
getProduct(id:123){
id
price
variant {
variantId
variantTitle
}
}
Objects can be nested arbitrarily deeply, and fields defined within objects can have the same annotations as top-level fields, like @resolver, @input, etc.
Configuring array fields
Array fields require the items object, which allows you to specify the data type of the items in the array. The items object can be configured the same as a standard field, meaning it requires either an @ref or a type, and can have any of the other properties specified for fields in this section, including annotations.
For example, here is a ShoppingCart shape that contains an array of Product instances, and an array of Coupon instances.
"ShoppingCart": {
"id": "PShoppingCartroduct",
"name": "ShoppingCart",
"title": "My Custom Shopping Cart",
"schema": {
"type": "object",
"properties": {
"products": {
"type": "array",
"items": {"@ref":"local:Product"}
},
"coupons": {
"type": "array",
"items": {
"type": "object",
"properties": {
"couponCode": {"type":"string"},
"discountValue": {"type":"number"}
}
}
}
}
}
},
"Product": {
"id": "Product",
"name": "Product",
"title": "My Custom Product",
"schema": {
"properties": {
"title": {"type":"string"},
"price": {"type":"integer"}
}
}
}
@ref in nested fields.In the example above, ShoppingCart has two array fields. The first, products, is an array with items that are instances of the Product shape, which is declared later in the project schema. The second, coupons, is defined within the ShoppingCart shape.
The benefit of declaring a shape in the schema is that you can re-use it everywhere with the @ref annotation. But if a field has a simple configuration, and won't be used anywhere else, it makes sense to define it within the shape that uses it.
description
A description of the property that will appear in the admin UI. For example, you will see a property's description in the docs panel of the API Explorer when you hover over it.
title
The title of the field that will appear in the admin UI.
minLength
An integer minimum length for the value of the property.
minimum
The minimum value possible for a property whose type is set to integer.
Shape annotations {shape-annotations}
Annotations are properties on fields that enable extended functionality. Similar to GraphQL directives, annotations enable defining a field as an instance of a shape defined in the schema, attaching a query resolver to a field so that its data can be fetched from remote API services, and more.
No annotation is ever required, but @ref can be used in place of the type property on a field.
The following a list of all valid annotations.
@args
This annotation lets you define the schema of the arguments that can be passed to a field that uses an @resolver. Because the @resolver can query remote API services, @args should be used for defining the arguments that will be used when querying a service.
For example, here's what an @args annotation looks like on a field with a resolver that fetches a Shopify product:
"Product": {
"id": "Product",
"name": "Product",
"title": "Product",
"schema": {
"type": "object",
"properties": {
"id": {"type": "integer"},
"shopifyProduct": {
"title": "Shopify Product",
"description": "A field that returns a shopify product",
"@ref": "shopify:Product}",
"@args": {
"type": "object",
"properties": {
"handle": {"type":"string"},
"id": {"type":"string"}
}
},
"@resolver": {
"if": "!isEmpty($args.handle) || !isEmpty($args.handle)",
"name": "graphql:query",
"service": "shopify",
"fieldName": "product",
"args": {
"ops":[
{"path": "handle", "mapping": "$args.handle"},
{"path": "id", "mapping": "$args.id"}
]
}
}
}
}
}
}
When querying for the above product, the id or handle can be passed to the shopifyProduct field.
getProduct(id:123){
shopifyProduct(handle:"example-product"){
title
id
handle
}
}
The fields available on shopifyProduct are defined in the Shopify GraphQL API spec, since shopifyProduct is an instance of shopify:Product. To learn more about using @ref this way, read our section on using @ref below.
@ref
This annotation takes the scoped name of a shape that exists in a project's schema. It's used when a field is an instance of a shape. It's useful when a field has many nested subfields, or when a field is an instance of a shape defined elsewhere in the schema.
For example, if you have a User shape that you want to associate Stripe customer data with, you might define a stripeCustomerData field on the User shape as shown below:
"User": {
"id": "User",
"name": "User",
"title": "User",
"schema": {
"type": "object",
"properties": {
"stripeCustomerData": {
"@ref": "stripe:Customer",
"description": "The stripe customer data associated with this user."
},
"username": {
"type": "string",
"description": "The username associated with this user."
}
}
}
}
The above example creates a stripeCustomerData field that can return Stripe customer data, but an @resolver annotation is required to define how that data can be fetched from stripe. read our section on @resolver below ot learn more.
Referencing scoped shapes with @ref
To define a field as an instance of a shape, that shape must fulfill one of three conditions:
-
It must be defined in the JSON schema, either manually or automatically via the visual editor.
-
It must be a hidden schema object, all of which are listed at the end of this spec.
-
It must be defined in the hidden service schema.
The service schema is neither visible nor directly editable, but all projects have them. They contain configuration information for connected API services, like Stripe or ShipEngine. Projects automatically update the service schema whenever a service is added or deleted.
To create a field that is an instance of a shape from a connected service, you must scope the shape with the service name and a colon, like so:
"@ref":"shopify:Product"
The format is service:ShapeName.
When a service is connected with a generic REST provider, or a generic GraphQL provider, there is a chance its API configuration will to be added to the service schema.
In such cases, shapes from the service must be manually defined in the JSON schema file. Trying to reference them from the service schema will fail.
Defining a field as an instance of a shape defined in the JSON schema file requires the use of the local scope, like so:
"@ref":"local:Product"
The format is local:ShapeName.
@resolver
An annotation that enables fields to have return data from remote services when queried.
For example, a User shape with a stripeCustomerData field should have customer data from stripe when that field is queried. To ensure that data is there, an @resolver annotation must be used to trigger a query to stripe's customers endpoint.
{
getUser(id: 123) {
username
# Including a below field will cause the query
# in @resolver to be executed before a response
# is returned.
stripeCustomerData(stripeCustomerId: "cus_ABC123pxLtTYUM") {
email
phone
}
}
}
Here's how the User shape might be defined:
"User": {
"id": "User",
"name": "User",
"title": "User",
"schema": {
"type": "object",
"properties": {
"stripeCustomerData": {
"@ref": "stripe:Customer",
"description": "The stripe customer data associated with this user.",
"@resolver": {
"name": "rest:get",
"service": "stripe",
"options": {"ignoreErrors": true},
"path": {
"ops": [
{"path": "customerId", "mapping": "$args.stripeCustomerId"}
],
"serialize": {"template": "/v1/customers/{customerId}"}
}
},
"@args": {
"type": "object",
"properties": {
"stripeCustomerId": {
"type": "string"
}
}
}
}
},
"username": {
"type": "string",
"description": "The username associated with this user."
}
}
}
@mapping
This annotation is usually generated for shapes created in the visual editor. Editing this value can cause a loss of data. It maps instances of the shape to a table in a project's database. Learn more in our @mapping section below.
@input
This annotation enables overriding the input type for the field, which can be useful when setting up fields that store foreign keys and resolver data from other services. The @input properties will overwrite the field's original properties, with the exception of @mapping, which is preserved.
You cannot rely on any property inheritance from the original field when using an @input annotation.
For example, this ProductPage Shape accepts a string as input for its product field but returns a product from Shopify when that field is queried.
{
"ProductPage": {
"id": "ProductPage",
"name": "ProductPage",
"title": "ProductPage",
"schema": {
"type": "object",
"properties": {
"title": {
"type": "string"
},
"product": {
"@ref": "shopify:Product",
"@resolver": {
"name": "graphql:query",
"service": "shopify",
"fieldName": "product",
"args": {
"ops": [{ "path": "id", "mapping": "$source.product" }]
}
},
"@input": {
"type": "string"
}
}
}
}
}
}
Rather than deeply nesting and copy-pasting objects to create fields, complex shapes are best composed with the @ref and @relationship annotations.
Shape schemas also have a number of hidden properties that are automatically managed, such as _created and _updated, which record the dates shape items are created and edited. Though you cannot manipulate them, remember that their names are reserved, and fields with those names cannot be created.
Shape joins
{
"Shopify_Product": {
"name": "Shopify_Product",
"id": "Shopify_Product",
"title": "Shopify_Product",
"joins": {
"sanity:Product": {
"resolver": {
"name": "delegate",
"to": "sanity:Query.allProduct",
"args": {
"ops": [
{
"path": "where.shopifyProductId.eq",
"mapping": "$source.id"
}
]
},
"results": {
"ops": [{ "path": "$", "mapping": "$finalResolver[0]" }]
}
},
"dependencies": "{id}"
}
},
"schema": {
"extends": [
{ "@ref": "shopify:Product" },
{
"type": "object",
"properties": {
"customDescription": {
"type": "string",
"@mapping": "sanity:Product.customDescription"
},
"seo": {
"@ref": "sanity:Seo",
"@mapping": "sanity:Product.seo"
}
}
}
]
}
}
}
Shape joins allow you to mesh the properties of multiple shapes into one. In this example we have our product shape
from Shopify combined with a custom product shape stored in Sanity.io. This is done by specifying the join in the joins property:
{
"joins": {
"sanity:Product": {
"resolver": {
"name": "delegate",
"to": "sanity:Query.allProduct",
"args": {
"ops": [
{
"path": "where.shopifyProductId.eq",
"mapping": "$source.id"
}
]
},
"results": {
"ops": [{ "path": "$", "mapping": "$finalResolver[0]" }]
}
},
"dependencies": "{id}"
}
}
}
joinsis an object keyed by the ref to the shape we want to join, in this casesanity:Product.- Join objects contain the following props
resolver(required) - A delegate resolver OR shapedb resolverdependencies- A GraphQL-style selection of properties from the same shape that this join depends on. This can also include properties from other joined shapes.
Once a join config has been added you can use @mapping to specify the source of each property:
{
"customDescription": {
"type": "string",
"@mapping": "sanity:Product.description"
},
"seo": {
"@ref": "sanity:Seo",
"@mapping": "sanity:Product.seo"
}
}
Shape caching/indexing
To enable caching with indexing for a given shape add the cache and loaders configuration. For a detailed walkthrough of configuring cache/loaders in your project, check out our API Indexing guide.
{
"Shopify_Product": {
"name": "Shopify_Product",
"id": "Shopify_Product",
"title": "Shopify_Product",
"schema": { "extends": [{ "@ref": "shopify:Product" }] },
"cache": {
"enabled": true,
"fragment": {
"maxDepth": 2
},
"triggers": [{ "type": "schedule", "query": "list", "interval": 1440 }]
},
"loaders": {
"list": {
"query": "shopify.Query.products"
},
"get": {
"query": "shopify.Query.product"
}
}
}
}
loaders (required)
This object contains the queries that the API Indexer will use to index remote API data. Valid properties are list and get. See the section on valid list and get properties below for more information.
Valid list and get properties
Within your list or get queries, you can also configure how indexing finds the information in the result of the query.
name (required)
The string name of the query to run.
Example: "Stripe_listCustomers"
pagination
Valid only for list queries. A configuration object that allows you to specify how the indexing function fetches paginated data. See the section below on valid pagination properties for more information.
Example:
"pagination": {
"type": "cursor",
"cursorPath": "data[(@.length-1)].id",
"itemsPath": "data",
"hasMorePath": "has_more",
"cursorArg": "starting_after"
}
Valid pagination properties
Every pagination configuration has a type property, which specifies the type of pagination. Below are the properties that are valid for all pagination types:
All path property values are parsed with jsonPath syntax.
type (required)
The type of pagination, which can be one of cursor, offset, or page.
Example:
"pagination": {
"type": "cursor",
"cursorPath": "edges[(@.length-1)].cursor",
"itemsPath": "edges",
"itemPath": "node",
"hasMorePath": "has_more",
}
Below are other properties categorized by which pagination type they're appropriate for.
cursor pagination
-
cursorArg(required) — The string name of the query argument that accepts the cursor value for the start of the next page. -
cursorPath(required) — The string path to the cursor returned in the query's response object. -
hasMorePath(required) — The string path to the property in the query's response that indicates whether there are more pages to return. -
itemsPath(required) — The path to the array of items to index in the query's response. -
pageSize(optional) — The number of items to fetch in each page. -
pageSizeArg(required) — The string name of the of the argument passed to the pagination query to indicate the size of the desired result.
Example: "first"
offset pagination
-
offsetArg(required) — The string name of the offset argument to use when querying the API. -
itemsPath(required) — The path to the array of items to index in the query's response. -
itemTotalPath(required) — The path to the property which contains the number representing the total items.
page pagination
-
pageArg(required) — The string argument passed to the pagination query to indicate where the pagination slicing should begin or end. -
pageTotalPath(required) — The path to the total number of pages in the response data. -
itemsPath(required) — The path to the array of items to index in the query's response.
cache (required)
fragment
This object contains settings that determine the query fragment that is used in indexing
maxDepth
This number dictates how deep of an indexing query we generate and in-turn how deep we index. This number accounts for known pagination schemes so wrapper objects will not count as extra depth. For example:
{
Shopify_products {
nodes {
id # depth 1
title
featuredImage {
url # depth 2
}
variants {
edges {
node {
id # depth 2 accounting for relay style pagination
title
price
}
}
}
}
}
}
Setting maxDepth will automatically index all fields to the specified depth. This value defaults to 2 unless selectionSet is specified.
selectionSet
This setting enables full control over the indexing query's fragment. This is used when you want to index deeply or specify custom args.
For example when indexing Character from https://rickandmortyapi.com/graphql our selectionSet is set to:
{
id
name
episode {
name
created
air_date
characters {
id
name
}
episode
}
}
With this configuration we can search characters that appear in the same episode.
{
search(shapeNames: ["Rick_Character"], where: { episode: { characters: { name: { eq: "Pickle Rick" } } } }) {
results {
... on Rick_Character {
id
name
}
}
}
}
maxDepth can also be used in combination with selectionSet.
In this example, we want to index all fields at depth 1 (id, name, etc) and also deeply index episode on top of that.
If we just used a custom selectionSet by itself, we’d have to specify all the depth 1 fields in the selectionSet and update it if the schema changes.
Instead, we can get the best of both worlds by specifying "maxDepth": 1 along with the following selectionSet:
{
episode {
name
created
air_date
characters {
id
name
}
episode
}
}
This results in an indexing query:
{
Rick_characters {
info {
pages
}
results {
id
name
status
species
type
gender
image
created
episode {
name
created
air_date
characters {
id
name
}
episode
}
}
}
}
ignoreFields
The array of string names of fields to ignore when using the maxDepth setting.
triggers (required)
This array contains objects that define the conditions under which the indexing function will trigger its queries. See the section on valid triggers properties below for more information.
Example:
"triggers": [
{
"type": "schedule",
"query": "list",
"interval": 1
},
{
"type": "webhook",
"query": "get",
"service": "yourServiceIdHere",
"events": ["names", "of", "events", "here"]
}
]
searchSummaryField
This property takes a string that will be the name of the field in the indexed shape that you want to use as the searchSummary. Useful for customizing the results returned for this shape when running the search query against your project's API.
Learn more about using the search query in our search recipe.
idField
This property takes a string that is the name of the property in the indexed shape that will return the shape's ID. Useful for customizing the results for this shape when running the search query on your project's API.
If the shape being indexed doesn't have a field called id, this should be set to whichever field is the actual unique identifier.
Valid triggers properties
type** (required)
A string defining the trigger's type. Valid values are schedule and webhook.
query
A string specifying which query the trigger corresponds to. Valid values are get and list.
interval** (required for schedule)
Valid only for schedule triggers. The integer number of minutes between each time the indexing function will run.
service (required for webhook)
Valid only for webhook triggers. The string ID of the service in your schema that has the webhooks to trigger this query.
events (required for webhook)
Valid only for webhook triggers. The array of string names of webhook events that can trigger this query.
Example:
"triggers": [
{
"type": "schedule",
"query": "list",
"interval": 1
},
{
"type": "webhook",
"query": "get",
"service": "bigcommerce",
"events": ["store/cart/created"]
}
]
Shape Cache Example
This example shows how you configure a Stripe_Customer shape for indexing:
{
"Stripe_Customer": {
"name": "Stripe_Customer",
"id": "Stripe_Customer",
"title": "Stripe_Customer",
"schema": { "extends": [{ "@ref": "stripe:Customer" }] },
"cache": {
"enabled": true,
"fragment": {
"maxDepth": 2
},
"triggers": [{ "type": "schedule", "query": "list", "interval": 1440 }]
},
"loaders": {
"list": {
"query": "Stripe_listCustomers",
"pagination": {
"type": "cursor",
"cursorPath": "data[(@.length-1)].id",
"itemsPath": "data",
"hasMorePath": "has_more",
"cursorArg": "starting_after",
"pageSizeArg": "limit"
}
}
}
}
}
pagination
To understand the pagination configuration refer to this example response from Stripe_listCustomers:
Consider this response object from the listCustomers query supplied by our Stripe integration
{
"object": "list",
"url": "/v1/customers",
"has_more": false,
"data": [
{
"id": "cus_KmAUtL4NbulKr4",
"object": "customer",
"address": null,
"balance": 0,
"created": 1639492731,
"currency": "usd",
"default_source": null,
"delinquent": false,
"description": null,
"discount": null,
"email": null,
"invoice_prefix": "F5DABA5",
"invoice_settings": {
"custom_fields": null,
"default_payment_method": null,
"footer": null
},
"livemode": false,
"metadata": {},
"name": null,
"next_invoice_sequence": 1,
"phone": null,
"preferred_locales": [],
"shipping": null,
"tax_exempt": "none"
}
]
}
type — Stripe's API uses cursor-based pagination, so the type in the pagination object should be cursor.
cursorPath — Because Stripe's API returns no specific cursor value, we'll specify the path to the cursor as a jsonPath string. This string will point to the id of the last object in the data array in the response object.
itemsPath — This is a jsonPath string specifying the path to the array of items expected in the response object. In this case, that array is called data.
hasMorePath — Stripe's API provides a has_more field at the root level of the response object, so you should set this value to "has_more".
cursorArg — You can check Stripe's documentation for the possible cursor arguments.
pageSizeArg — As Stripe's documentation shows, list queries take a limit argument to indicate the size of each result.
Learn more in our shapes guide.
services
By connecting new Services to your project schema, you can add shapes, queries, and mutations from 3rd party sources to your API. This allows you to mesh together exactly the API you need to interact with all of the services your app or business depends upon.
Services use an encrypted configuration to keep auth tokens secret. For this reason, services can only be added to a schema through the web client or through the Admin API.
Guides
Adding GraphQL services, shapes, and queries
Adding REST services, shapes, and queries
queries & mutations
Queries and Mutations in your schema map directly to queries and mutations in your project's GraphQL API. In the schema you can define behaviors for these queries and mutations that span one or more services.
Though they describe different functionality, query and mutation objects both have the same set of properties:
shape (required)
This string property specifies which shape is returned by the query or mutation. The shape can refer to a shape in the schema's shapes object or a shape that's available on a service schema.
resolver (required)
This configuration object describes the resolver for a query or mutation.
description
This string property provides more detail about what the query or mutation is for. This will be displayed in the automatically-generated GraphQL API docs.
args
The args field takes the name of one of the shapes you have defined in your schema as a string. This shape will be used to define the arguments your query or mutation can take as inputs. If your query or mutation takes no arguments, you can omit this string.
Guides
Working with Queries & Mutations
forms
Forms define a visual interface for editing Shapes. In most cases, users do not need to edit the forms object directly. The forms object is populated with new data when a user creates shapes in the visual editor.
Example
"Sneakers": {
"default": {
"order": ["sneakerShopId", "sneakerShop"],
"properties": {
"sneakerShopId": {
"instructions": "Format: gid://shopify/Product/11111111",
"label": "product ID",
"widget": "serviceObjectId",
"provider": "shopify",
"serviceObjectType": "product",
"service": "sneaker-shop"
},
"sneakerShop": {
"properties": {
"title": {
"widget": "serviceObjectProperty",
"provider": "shopify"
}
},
"widget": "shopify",
"wrapper": "shopifyServiceWrapper",
"order": ["title"]
}
}
}
}
workflows
Workflows describe the status of a shape item. Every schema starts with a default workflow with two steps: Disabled and Enabled.
Each step can be configured to have a custom name, title, and color. Steps have a live value to indicate whether items in the state should be returned in list queries.
Developer plans are limited to the default workflow. Professional projects can add new workflows and specify them on a per-shape basis.
Example
This is the default workflow that is applied to every project. It has two states: Disabled and Enabled.
"default": {
"name": "default",
"title": "Default",
"steps": [
{
"name": "disabled",
"title": "Disabled",
"key": "r1uCfi4ZL",
"color": "#bdbdbd",
"live": false
},
{
"name": "enabled",
"title": "Enabled",
"key": "rkhRGs4WL",
"color": "#5cd79b",
"live": true
}
]
}
locales
Locales are an array of IETF language tags your project supports for internationalization. By default, projects have a single en locale which is also the default locale.
Developer projects have a limited number of locales. Professional projects have access to additional locales.
Metadata properties
Also at the root of the schema are a number of properties that are mainly used by the web client and API. As a schema author, you should almost never need to edit these fields.
version
This is the revision number of your schema. Every time your project schema is updated, this value should be incremented. The version is used to identify versions in your schema's history.
projectId
The ID of the project this schema belongs to.
defaultLocale
The locale that should be preferred when creating new Shape items. This must be an entry in the **locales** array.
author
The ID of the user who created the schema.
created
The date the schema was created