> Part of [WHCC Developer Documentation](https://www.whcc.com/developer/llms.txt)

# Metadata & Reference

Version 1.4 &middot; Updated 2026-10-08

Two fields carry your identifiers on a submission, and they do different jobs. `metadata` is a flat map of strings. You choose the keys, WHCC never reads them, and you get them back whenever you read the submission. `reference` is one short string that travels with the order and is printed on fulfillment paperwork and the shipping label. If you've used [Stripe's `metadata`](https://docs.stripe.com/metadata), ours works the same way.

## Which field do I want?

- **`metadata`** (map, ≤ 50 keys): Your own bookkeeping: cart IDs, customer IDs, job numbers, anything you want back exactly as you sent it. Values can be up to 500 characters. It isn't printed anywhere and can't be searched.
- **`reference`** (string, ≤ 32 characters): One identifier that needs to be readable on the paperwork or the shipping label, and that WHCC support can search for.

> Need to carry an identifier longer than 32 characters? Put it in `metadata` and send a short one as `reference`.

## metadata

### Shape and limits

- **`type`**: A flat map of string keys to string values. Nested objects, arrays, numbers and booleans are rejected. Turn them into strings first.
- **`keys per map`** (50): At most 50 entries in one map. The submission's map and each item's map are counted separately.
- **`key length`** (40): At most 40 characters per key.
- **`value length`** (500): At most 500 characters per value.
- **`character counting`**: Lengths count Unicode characters (code points), not bytes. A 500-character value in any language is fine.
- **`key syntax`**: Keys are case-sensitive, so `orderId` and `orderid` are two different keys. A key can't be empty, can't start with `$`, and can't contain `.`, `[` or `]`. Anything else is fine.
- **`reserved keys`** (__proto__): The key `__proto__` is reserved, here and anywhere else in the request body. A request that contains it returns `400` with `code: "request.reserved_key"`. Every other key is fine, and WHCC doesn't read any key you set. `reference` is the only identifier WHCC itself uses.

### Where you set it

`POST /v1/commerce/submissions` accepts `metadata` at the root of the body and on each `items[]` entry. An item's map is separate from the root map. Neither inherits from or overrides the other. The body below shows only the fields this page covers. A real call also needs the rest of the submission, including the required `shipping` object; see [Quick Start Step 5](https://www.whcc.com/developer/docs/quick-start/#step-5).

```json
{
	"items": [
		{
			"configuredProductId": "CF_bCy42bhE4xpJXXNezD2EQSkpAzrqBurvi8ICmrzmBTetrum",
			"quantity": 1,
			"metadata": {
				"lineItemId": "li_8842",
				"giftMessage": "Happy birthday!"
			}
		}
	],
	"reference": "PO-90188",
	"metadata": {
		"cartId": "cart_7f3a91",
		"customerId": "cus_4471",
		"channel": "mobile-ios"
	}
}
```

Calculate (`POST /v1/commerce/submissions/calculate`) accepts the same body, including `metadata` and `reference`, and returns them the same way.

### What comes back

Every response that carries a submission returns its `metadata`, and each item returns its own. That includes the `POST /v1/commerce/submissions` response and `GET /v1/commerce/submissions/{submissionId}`. A map you sent comes back exactly as you sent it: same keys, same values, same order. If you left `metadata` out or sent `null`, you get an empty map `{}`, never `null` and never a missing key. You can read it without checking first. The short rows from `GET /v1/commerce/submissions` don't carry `metadata`, so fetch the submission itself when you need it.

### Where it doesn't go

> **`metadata` stays with WHCC's record of your submission.** It isn't sent to fulfillment, doesn't print on paperwork or the shipping label, and can't be searched. No endpoint looks a submission up by a `metadata` key or value. Anything that needs to be visible on the order belongs in [`reference`](https://www.whcc.com/developer/docs/api/metadata/#reference). Anything you'll want to look a submission up by is best kept on your side, next to its `id`.

### Setting metadata

- **`omitted`**: `metadata` is optional. Leave it out and it comes back as `{}`.
- **`"metadata": null`**: The same as leaving it out.
- **`"metadata": { "k": null }`**: **Rejected.** Every value must be a string.

### Changing metadata later

A submission's `metadata` can't be changed after you submit it, so send the map you want kept. Retrying with the same `Idempotency-Key` and different `metadata` returns your original submission with its original `metadata`, not a `409`; see [the `Idempotency-Key` header](https://www.whcc.com/developer/docs/api/submission-lifecycle/#idempotency).

## reference

- **`reference`** (string, ≤ 32 characters): Optional. Your identifier for the submission: an order number, a PO number, a short customer name. It's returned on every read and printed on fulfillment paperwork and the shipping label. The 32 is counted in UTF-16 units, not code points as `metadata` is, so an emoji counts as two.

> **`reference` is capped at 32 characters. A longer value is rejected, not cut short.** 32 characters is what WHCC's paperwork and labels hold, so a value that passes arrives in full. If your identifier doesn't fit, send a short one here and keep the long one in [`metadata`](https://www.whcc.com/developer/docs/api/metadata/#metadata).

When a submission becomes more than one WHCC order, each order carries the same `reference`, unchanged.

Other WHCC APIs carry the same 32-character reference and don't check the length; a longer value is cut off. `POST /v1/commerce/submissions` checks it for you up front.

Paperwork and labels print from the Windows-1252 character set, so characters outside it don't survive. Stick to plain ASCII for anything you need to read off a label or ask WHCC support to search for. Support's search ignores case.

## Rejections

A value that breaks any rule on this page fails the whole request with a `400` Problem Details body. Each problem gets its own `invalidParams` entry, so one call tells you everything that's wrong. The one exception is a reserved key: it returns `code: "request.reserved_key"` with no `invalidParams[]`. Nothing is quietly cut short or dropped to make a request fit.

```json
{
	"type": "about:blank",
	"title": "Your request parameters didn't validate.",
	"status": 400,
	"invalidParams": [
		{
			"reason": "A metadata value must be a string.",
			"name": "items.0.metadata.giftMessage"
		}
	],
	"instance": "/api/v1/commerce/submissions"
}
```

`name` is the dotted path to the value, so you can map each entry back to your body:

- **`metadata`**: The map itself is wrong: too many keys, or not a flat object.
- **`metadata.<key>`**: One entry in the root map is wrong. The key is too long or uses a character that isn't allowed, or the value is too long or isn't a string.
- **`items.<i>.metadata.<key>`**: The same, on one item's map. `<i>` is the item's zero-based position in the `items[]` you sent.
- **`reference`**: `reference` is longer than 32 characters.

A key rejected for containing a `.` (say `a.b`) shows up as `metadata.a.b`, so match the whole path against the keys you sent rather than only the last part. Build on `name`. `reason` is a sentence for people, and its wording can change. For every other error, see [Errors & Rate Limits](https://www.whcc.com/developer/docs/api/errors/index.html.md).

> ### What's Next
>
> Place a submission end to end in the [Quick Start](https://www.whcc.com/developer/docs/quick-start/index.html.md), or see the full submission on the [Response Bodies](https://www.whcc.com/developer/docs/api/responses/#submission) page.