SeloraXDEVELOPERS

Metafields

Metafields

Metafields let your app attach custom key-value data to store resources. Each metafield is scoped to your app — other apps cannot read or modify your data, and your app cannot access metafields created by other apps.

Use metafields to store fraud scores on orders, custom attributes on products, loyalty tiers on customers, or app-wide configuration on the store itself.

Concepts

Definitions

Before you can store values, you must create a metafield definition. A definition declares the namespace, key, resource type, and value type for a metafield. Think of it as a schema declaration.

Definition: my-app.risk_score
  namespace:     my-app
  key:           risk_score
  resource_type: order
  value_type:    integer

Definitions are app-scoped. The namespace + key combination must be unique within your app.

Values

A metafield value is a single data point attached to a specific resource instance. For example, order #1045 has a my-app.risk_score value of 85.

Values are stored per-store. If your app is installed on three stores, each store has its own independent metafield values.

Namespacing

Use your app's slug or a descriptive prefix as the namespace. This prevents collisions if you have multiple metafield groups:

my-app.risk_score        — fraud-related data
my-app.review_count      — product reviews
my-app.loyalty_tier      — customer data

Namespace format: lowercase alphanumeric with optional hyphens (e.g., my-app, fulfillment, analytics2).

Key format: lowercase alphanumeric with underscores (e.g., risk_score, total_reviews, tier_level).

Resource Types

Resource TypeDescriptionExample Usage
orderOrder recordsFraud score, shipping priority, custom status
productProduct recordsSEO metadata, vendor notes, custom tags
customerCustomer recordsLoyalty tier, lifetime value, preferences
storeStore-level dataApp configuration, feature flags, sync state

Value Types

TypeDescriptionExample
stringText value"Express shipping"
integerWhole number85
decimalFloating-point number4.99
booleanTrue/falsetrue
jsonArbitrary JSON object or array{"tags": ["vip", "premium"]}
dateISO 8601 date string"2025-06-15"
urlValid URL"https://example.com/doc.pdf"
colorHex color code"#ff5733"

Validation is enforced server-side. Setting an integer metafield to "hello" returns an error.

API Reference

All endpoints require app authentication (client credentials or OAuth token) and the read:metafields or write:metafields scope.

Create Definition

POST /api/apps/v1/metafields/definitions

Scope: write:metafields

Request Body:

FieldTypeRequiredDescription
namespacestringYesNamespace (lowercase alphanumeric + hyphens)
keystringYesKey name (lowercase alphanumeric + underscores)
namestringYesHuman-readable display name
descriptionstringNoDescription of the metafield
resource_typestringYesOne of: order, product, customer, store
value_typestringNoOne of the valid value types. Default: string
validation_rulesobjectNoType-specific validation rules

Validation rules by type:

Value TypeAvailable Rules
stringmax_length, regex, choices (array of allowed values)
integermin, max
decimalmin, max
All othersNo additional rules
curl -X POST "https://api.selorax.io/api/apps/v1/metafields/definitions" \
  -H "X-Client-Id: sx_app_..." \
  -H "X-Client-Secret: sx_secret_..." \
  -H "X-Store-Id: 22" \
  -H "Content-Type: application/json" \
  -d '{
    "namespace": "my-app",
    "key": "risk_score",
    "name": "Fraud Risk Score",
    "description": "Numerical fraud risk assessment (0-100)",
    "resource_type": "order",
    "value_type": "integer",
    "validation_rules": { "min": 0, "max": 100 }
  }'

Response:

{
  "message": "Definition created.",
  "data": {
    "id": 12,
    "app_id": 5,
    "namespace": "my-app",
    "key": "risk_score",
    "name": "Fraud Risk Score",
    "resource_type": "order",
    "value_type": "integer"
  },
  "status": 200
}

List Definitions

GET /api/apps/v1/metafields/definitions

Scope: read:metafields

Query Parameters:

ParameterTypeDescription
resource_typestringOptional. Filter by resource type.
curl -X GET "https://api.selorax.io/api/apps/v1/metafields/definitions?resource_type=order" \
  -H "X-Client-Id: sx_app_..." \
  -H "X-Client-Secret: sx_secret_..." \
  -H "X-Store-Id: 22"

Response:

{
  "message": "Definitions fetched.",
  "data": [
    {
      "id": 12,
      "app_id": 5,
      "namespace": "my-app",
      "key": "risk_score",
      "name": "Fraud Risk Score",
      "resource_type": "order",
      "value_type": "integer",
      "validation_rules": "{\"min\":0,\"max\":100}",
      "is_active": 1,
      "created_at": "2025-06-10T08:00:00.000Z",
      "updated_at": "2025-06-10T08:00:00.000Z"
    }
  ],
  "status": 200
}

Delete Definition

DELETE /api/apps/v1/metafields/definitions/:id

Scope: write:metafields

Soft-deletes the definition and hard-deletes all associated values. This is irreversible.

curl -X DELETE "https://api.selorax.io/api/apps/v1/metafields/definitions/12" \
  -H "X-Client-Id: sx_app_..." \
  -H "X-Client-Secret: sx_secret_..." \
  -H "X-Store-Id: 22"

Set Value

POST /api/apps/v1/metafields/values

Scope: write:metafields

Set a single metafield value. If a value already exists for this definition + resource, it is updated (upsert).

Request Body (single):

FieldTypeRequiredDescription
namespacestringYesDefinition namespace
keystringYesDefinition key
resource_typestringYesResource type
resource_idintegerYesResource ID
valueanyYesThe value to store
curl -X POST "https://api.selorax.io/api/apps/v1/metafields/values" \
  -H "X-Client-Id: sx_app_..." \
  -H "X-Client-Secret: sx_secret_..." \
  -H "X-Store-Id: 22" \
  -H "Content-Type: application/json" \
  -d '{
    "namespace": "my-app",
    "key": "risk_score",
    "resource_type": "order",
    "resource_id": 1045,
    "value": 85
  }'

Batch set — pass an array of metafields:

curl -X POST "https://api.selorax.io/api/apps/v1/metafields/values" \
  -H "X-Client-Id: sx_app_..." \
  -H "X-Client-Secret: sx_secret_..." \
  -H "X-Store-Id: 22" \
  -H "Content-Type: application/json" \
  -d '{
    "metafields": [
      {
        "namespace": "my-app",
        "key": "risk_score",
        "resource_type": "order",
        "resource_id": 1045,
        "value": 85
      },
      {
        "namespace": "my-app",
        "key": "risk_score",
        "resource_type": "order",
        "resource_id": 1044,
        "value": 12
      }
    ]
  }'

Response:

{
  "message": "2 metafield(s) set.",
  "data": [
    {
      "definition_id": 12,
      "resource_type": "order",
      "resource_id": 1045,
      "value": 85
    },
    {
      "definition_id": 12,
      "resource_type": "order",
      "resource_id": 1044,
      "value": 12
    }
  ],
  "status": 200
}

Definition required

You must create a definition before setting values. If no definition exists for the namespace + key combination, the API returns an error: "No definition found. Create a definition first."


Get Values

GET /api/apps/v1/metafields/values

Scope: read:metafields

Get all metafield values for a resource (scoped to your app).

Query Parameters:

ParameterTypeRequiredDescription
resource_typestringYesResource type
resource_idintegerYesResource ID
curl -X GET "https://api.selorax.io/api/apps/v1/metafields/values?resource_type=order&resource_id=1045" \
  -H "X-Client-Id: sx_app_..." \
  -H "X-Client-Secret: sx_secret_..." \
  -H "X-Store-Id: 22"

Response:

{
  "message": "Values fetched.",
  "data": [
    {
      "id": 201,
      "namespace": "my-app",
      "key": "risk_score",
      "name": "Fraud Risk Score",
      "value": 85,
      "value_type": "integer",
      "resource_type": "order",
      "resource_id": 1045,
      "app_id": 5,
      "updated_at": "2025-06-15T10:30:00.000Z"
    }
  ],
  "status": 200
}

Values are automatically cast to their declared type. An integer definition returns a JavaScript number, a boolean returns true/false, and a json definition returns a parsed object.


Get Single Value

GET /api/apps/v1/metafields/values/:namespace/:key

Scope: read:metafields

Query Parameters:

ParameterTypeRequiredDescription
resource_typestringYesResource type
resource_idintegerYesResource ID
curl -X GET "https://api.selorax.io/api/apps/v1/metafields/values/my-app/risk_score?resource_type=order&resource_id=1045" \
  -H "X-Client-Id: sx_app_..." \
  -H "X-Client-Secret: sx_secret_..." \
  -H "X-Store-Id: 22"

Returns the single matching value, or 404 if not found.


Delete Value

DELETE /api/apps/v1/metafields/values/:namespace/:key

Scope: write:metafields

Query Parameters:

ParameterTypeRequiredDescription
resource_typestringYesResource type
resource_idintegerYesResource ID
curl -X DELETE "https://api.selorax.io/api/apps/v1/metafields/values/my-app/risk_score?resource_type=order&resource_id=1045" \
  -H "X-Client-Id: sx_app_..." \
  -H "X-Client-Secret: sx_secret_..." \
  -H "X-Store-Id: 22"

Batch Delete Values

DELETE /api/apps/v1/metafields/values/batch

Scope: write:metafields

Delete all metafield values for a resource, optionally filtered by namespace.

Query Parameters:

ParameterTypeRequiredDescription
resource_typestringYesResource type
resource_idintegerYesResource ID
namespacestringNoOnly delete values in this namespace
# Delete all metafields for order 1045
curl -X DELETE "https://api.selorax.io/api/apps/v1/metafields/values/batch?resource_type=order&resource_id=1045" \
  -H "X-Client-Id: sx_app_..." \
  -H "X-Client-Secret: sx_secret_..." \
  -H "X-Store-Id: 22"
 
# Delete only metafields in the "my-app" namespace for order 1045
curl -X DELETE "https://api.selorax.io/api/apps/v1/metafields/values/batch?resource_type=order&resource_id=1045&namespace=my-app" \
  -H "X-Client-Id: sx_app_..." \
  -H "X-Client-Secret: sx_secret_..." \
  -H "X-Store-Id: 22"

Response:

{
  "message": "3 metafield(s) deleted.",
  "deleted": 3,
  "status": 200
}

SDK Usage

The @selorax/ui SDK provides a high-level API for metafields in sandbox extensions. These methods call the platform API through the dashboard's API proxy.

Get all values for a resource

const metafields = await selorax.metafields.get("order", 1045);
// Returns array of { id, namespace, key, name, value, value_type, ... }

Get a single value

const score = await selorax.metafields.getValue(
  "my-app", // namespace
  "risk_score", // key
  "order", // resource_type
  1045, // resource_id
);
// Returns { id, namespace, key, name, value, value_type, ... } or null

Set a value

await selorax.metafields.set({
  namespace: "my-app",
  key: "risk_score",
  resource_type: "order",
  resource_id: 1045,
  value: 85,
});

Set multiple values

await selorax.metafields.setMany([
  {
    namespace: "my-app",
    key: "risk_score",
    resource_type: "order",
    resource_id: 1045,
    value: 85,
  },
  {
    namespace: "my-app",
    key: "risk_score",
    resource_type: "order",
    resource_id: 1044,
    value: 12,
  },
]);

Delete a value

await selorax.metafields.remove(
  "my-app", // namespace
  "risk_score", // key
  "order", // resource_type
  1045, // resource_id
);

Rate Limiting

Metafield API requests are rate-limited to 120 requests per minute per app. This limit applies across all stores. Exceeding the limit returns HTTP 429:

{
  "message": "Rate limit exceeded. Max 120 requests per minute per app.",
  "code": "rate_limited",
  "status": 429
}

To stay within limits:

  • Use batch operations (setMany, batch delete) instead of individual calls.
  • Cache metafield values in your extension's local state.
  • Avoid polling. Load metafields once when the extension initializes.

Namespacing Best Practices

  1. Use your app slug as the namespace. This prevents collisions if you acquire or merge with another app.

  2. Keep keys descriptive and specific. risk_score is better than score. review_count is better than count.

  3. Group related data under one namespace. If your app manages both fraud detection and shipping optimization, use my-app for both rather than separate namespaces — unless you need separate cleanup.

  4. Use the json value type for complex data. Instead of creating five separate metafields for an address, store it as a single JSON value.

  5. Define validation rules. Use min/max for numbers and choices for enums. This prevents invalid data from being stored.

Cleanup on Uninstall

When a merchant uninstalls your app, the platform automatically deletes all metafield values for your app in that store. Definitions are preserved (they are app-level, not store-level) so values are not orphaned if the merchant reinstalls.

What's Next