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

# Required fields

> Collect and send the extra customer data some products need, such as player IDs, phone numbers, and account numbers.

Some products need more than a denomination. A game top-up needs the player's ID, a mobile top-up needs the phone number, and some regional gift cards need the recipient's phone. The catalog lists the fields each product needs. You collect the values from your customer and send them with the order line.

Most gift cards need no extra data. Top-ups, bill payments, and some regional cards do.

## Find a product's fields

Every product in `GET /b2b/products` and `GET /retailer/v1/catalog`, and in the single-product endpoints, carries a `requiredFields` array. If it is missing, `null`, or empty, the product needs nothing extra.

```json Product (trimmed) theme={null}
{
  "productId": "R7QXtsDebOF__T3kUeZNU",
  "slug": "pubg-mobile-top-up",
  "name": "PUBG MOBILE Top Up",
  "skus": [{ "id": "5TJtjnScco0r", "min": 4.99, "max": 4.99, "minCost": 4.87 }],
  "requiredFields": [
    {
      "name": "userId",
      "type": "text",
      "description": "User/Player ID for the account to top up"
    }
  ]
}
```

Each entry in `requiredFields` describes one input:

| Property | Type | Meaning |
| - | - | - |
| `name` | string | The key you send the value under. Use it exactly as written: it is case-sensitive and can contain spaces (for example `Paypal Email`). |
| `type` | string | An input hint. `text` and `string` mean free text, `email` means an email address, and `select` means one of `options`. Treat any other value as free text. |
| `description` | string | A label or help text to show your customer. Some suppliers just repeat the field name here. |
| `regex` | string, optional | The format the value must match. An empty string means no format rule. |
| `options` | array, optional | For `select` fields, the allowed choices as `{ "id", "description" }`. Show the `description` and send the `id`. An empty array means no fixed choices. |

A `select` field looks like this:

```json theme={null}
{
  "name": "serverId",
  "type": "select",
  "description": "Server/Region to top up on",
  "options": [
    { "id": "asia", "description": "Asia" },
    { "id": "eu", "description": "Europe" }
  ]
}
```

<Note>
  Field names and formats come from our suppliers and can change when the catalog syncs. Read `requiredFields` from the catalog when you build your order form instead of hardcoding them per product, and refresh your copy of the catalog at least daily.
</Note>

## Send the values

Add a `requiredFields` object to each order item. B2B orders take it on `POST /b2b/orders`; retailer checkouts take it on `POST /retailer/v1/checkouts`. The object maps each field's `name` to the customer's value.

<CodeGroup>
  ```bash B2B order theme={null}
  curl -X POST https://api.wizzgift.com/b2b/orders \
    -H "X-API-Key: wg_live_..." \
    -H "Content-Type: application/json" \
    -d '{
      "externalRef": "acme-order-1043",
      "items": [
        {
          "productId": "R7QXtsDebOF__T3kUeZNU",
          "skuId": "5TJtjnScco0r",
          "amount": 4.99,
          "quantity": 1,
          "requiredFields": { "userId": "5123456789" }
        }
      ]
    }'
  ```

  ```bash Retailer checkout theme={null}
  curl -X POST https://api.wizzgift.com/retailer/v1/checkouts \
    -H "X-API-Key: wg_live_..." \
    -H "Content-Type: application/json" \
    -d '{
      "customerEmail": "buyer@example.com",
      "externalRef": "shop-order-890",
      "customerCountry": "NP",
      "items": [
        {
          "productId": "AE8BC3RD-O-hIkxyfPJ5O",
          "skuId": "1e9QqooqN1Ic",
          "amount": 200,
          "quantity": 1,
          "requiredFields": { "destination": "+9779841234567" }
        }
      ]
    }'
  ```
</CodeGroup>

Follow these rules:

* **Every value is a JSON string**, including IDs and phone numbers that look numeric. Send `"userId": "5123456789"`, not `"userId": 5123456789`. A number is rejected.
* **Send every field the product defines**, each with a non-blank value.
* **Send only the keys the product defines**, with surrounding whitespace removed.
* **One set of values covers the whole line.** With `"quantity": 3`, all three units go to the same player or phone number. To send to different accounts, add one item per account.
* **Omit `requiredFields`** for products that define none.

<Warning>
  Retailers: collect the values in your own storefront before you create the checkout. The hosted payment page takes payment only and never asks for required fields.
</Warning>

Fields cannot be postponed over the API. Some products carry `playstorePolicy: "required_fields_hidden"`, but that flag only changes how the Wizzgift mobile app collects fields. API orders must always include the values when you create them.

## Validate before you order

When you create the order or checkout, the API checks that:

* `requiredFields` is an object whose values are all strings.
* every field the product defines is present and not blank.

It does **not** check `regex` or `options` at that point. The supplier checks the value when it fulfills the order. A value in the wrong format is accepted and paid for, and then the item fails. You can [refund](/guides/order-lifecycle) the failed item, but your customer still waits for nothing.

A value in the right format that belongs to the wrong account is worse: the top-up usually goes through to that account and cannot be reversed. Ask your customer to confirm the value before you place the order.

Check each value against the product's definition before you send it:

```js theme={null}
function validateRequiredFields(product, values) {
  const errors = {};
  for (const field of product.requiredFields ?? []) {
    const value = (values[field.name] ?? "").trim();
    if (!value) {
      errors[field.name] = "Required";
      continue;
    }
    if (field.options?.length && !field.options.some((o) => o.id === value)) {
      errors[field.name] = "Choose one of the listed options";
      continue;
    }
    if (field.regex) {
      let pattern;
      try {
        pattern = new RegExp(field.regex);
      } catch {
        continue; // a pattern your engine cannot compile: skip the format check
      }
      if (!pattern.test(value)) errors[field.name] = field.description || "Invalid format";
    }
  }
  return errors; // empty object = ready to send
}
```

Patterns are JavaScript-style regular expressions and usually carry their own `^…$` anchors. Most engines accept them unchanged. In Python, use `re.search(field["regex"], value)`, which behaves like JavaScript's `test`. In JSON the backslashes arrive escaped, so `"^\\+?[1-9][0-9]{6,14}$"` is the pattern `^\+?[1-9][0-9]{6,14}$`.

## Common fields

These are the fields currently in the catalog. Always go by the product's own definition, because formats differ between products that share a field name.

| `name` | Used by | Format | Example value |
| - | - | - | - |
| `destination` | Mobile top-ups | International number: optional `+`, country code, then digits, with no spaces or dashes (`^\+?[1-9][0-9]{6,14}$`) | `"+9779841234567"` |
| `destination` | Bill payments and utilities | The account, meter, or subscriber number on the customer's bill | `"0123456789"` |
| `userId` | Game and app top-ups | The player or user ID shown in the game or app profile | `"5123456789"` |
| `contact` | Some regional gift cards | `+`, country code, a hyphen, then the number. Where a `regex` is set, it is `^\+[1-9]\d{0,2}-\d{4,12}$` | `"+971-501234567"` |
| `name` | Some regional gift cards | The recipient's name | `"Aisha Rahman"` |
| `Paypal Email` | PayPal payouts | The recipient's PayPal email address | `"buyer@example.com"` |

<Tip>
  `destination` and `contact` both hold phone numbers but use different formats: `destination` has no hyphen (`+9779841234567`), while `contact` puts a hyphen after the country code (`+971-501234567`).
</Tip>

## Read the values back

Retailer checkout responses repeat what you sent as `items[].requiredFields`, so you can show your customer the account a checkout will top up. B2B order responses do not include the values, so keep your own copy with your order record.

## Errors

A required-field problem at order time returns `400 VALIDATION_ERROR`. The message names the product and the fields:

| Message | Cause | Fix |
| - | - | - |
| `Invalid input: items.0.requiredFields.userId — expected string, received number` | A value is not a string | Send the value in quotes |
| `Required fields are missing for product R7QXtsDebOF__T3kUeZNU: expected userId` | The item has no `requiredFields`, but the product needs some | Collect and send the listed fields |
| `Missing required field values for product AE8BC3RD-O-hIkxyfPJ5O: destination` | A listed field is absent or blank | Send a non-blank value for each listed field |

When the supplier rejects a value after payment, the order still exists, but the item ends in `failed`. Its fulfillments carry `errorMessage: "The provided details could not be processed. Please verify them and contact support."` Fix the value, place a new order, and refund the failed item. See [errors](/guides/errors) for the full error envelope.


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