> ## Documentation Index
> Fetch the complete documentation index at: https://docs.launchmystore.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Bulk Upsert Metafields (Admin API only)

> Upsert many metafields in a single request. Used by the admin resource editors.

# Bulk Upsert Metafields

<Warning>
  This endpoint is available on the **Admin API** (merchant JWT) only. It is
  **not** exposed to OAuth-scoped apps. Apps must call
  [`POST /api/v1/metafields.json`](/api-reference/metafields/create) once
  per field.
</Warning>

Upsert N metafields in a single request. This is what powers the
the admin "Save" button on resource detail pages — when a merchant edits
multiple metafields and clicks save, the editor sends one bulk request
instead of N individual calls.

## Request

```bash theme={null}
curl -X POST "https://api.launchmystore.io/metafields/bulk" \
  -H "Authorization: Bearer <merchant-jwt>" \
  -H "Content-Type: application/json" \
  -d '{
    "metafields": [
      { "namespace": "custom", "key": "badge",          "type": "single_line_text_field", "value": "BESTSELLER",        "ownerType": "product", "ownerId": "..." },
      { "namespace": "custom", "key": "warranty_years", "type": "number_integer",         "value": 5,                   "ownerType": "product", "ownerId": "..." },
      { "namespace": "custom", "key": "care",           "type": "rich_text_field",        "value": "<p>Hand wash.</p>", "ownerType": "product", "ownerId": "..." }
    ]
  }'
```

## Auth

Merchant JWT (owner, or staff with admin/manager access).

## Body

<ParamField body="metafields" type="array" required>
  Array of metafield objects. Each item has the same shape as
  [`POST /metafields`](/api-reference/metafields/create#body-parameters).
</ParamField>

Each item is upserted independently, keyed by
`(owner type, namespace, key, owner)`. If one item fails
validation, the others still proceed (per-item error reporting in the
response).

## Response

When **every** item succeeds: HTTP `200` with `state: "success"` and an
empty `errors` array. When **some** items fail: HTTP `207` (Multi-Status)
with `state: "partial"` — successful rows in `data.metafields`, failures
in `data.errors`.

```json theme={null}
{
  "status": 207,
  "state": "partial",
  "data": {
    "metafields": [
      { "metafieldId": "...", "key": "badge", "namespace": "custom", ... },
      { "metafieldId": "...", "key": "warranty_years", "namespace": "custom", ... }
    ],
    "errors": [
      {
        "index": 2,
        "namespace": "custom",
        "key": "brand_color",
        "ownerType": "product",
        "ownerId": "f73049dc-…",
        "message": "Metafield value validation failed",
        "details": { "statusCode": 400, "message": "Metafield value validation failed", "errors": [{ "field": "value", "message": "value must be valid hex color" }] }
      }
    ]
  }
}
```

## Cache invalidation

The cache invalidation chain fires **once** after the whole batch
completes — not once per row. A 50-item save costs a single cache
invalidation, which makes bulk the right call for editor-style "save
all" flows.

## Error codes

| Code | Description |
| - | - |
| `UNAUTHORIZED` | Invalid or missing JWT |
| `FORBIDDEN` | Wrong store / insufficient role |
| `VALIDATION_ERROR` | Top-level body shape is wrong (per-row errors are returned in `results[]`) |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.