> ## 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

```json theme={null}
{
  "status": 200,
  "state": "success",
  "data": {
    "results": [
      { "ok": true, "metafield": { "id": "...", "key": "badge", ... } },
      { "ok": true, "metafield": { "id": "...", "key": "warranty_years", ... } },
      { "ok": false, "error": { "field": "value", "message": "value must be valid hex color" } }
    ],
    "succeeded": 2,
    "failed": 1
  }
}
```

## 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[]`) |
