DevTools Hub

Search tools

Search for a developer tool

JSON Schema for API Requests

Part of the JSON Toolkit
Pattern
{"type":"array","items":{"type":"object","required":["sku","quantity"]},"minItems":1}

validates every element of a request's items array independently — see below for the complete request schema

Explanation

A form schema and an API request schema look similar on the surface, but an API request usually needs to handle a list of things — line items, tags, recipients — not just flat fields. Here's a schema for a "create order" request body:

{
  "type": "object",
  "properties": {
    "customerId": { "type": "string", "pattern": "^cus_[a-zA-Z0-9]+$" },
    "items": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "sku": { "type": "string" },
          "quantity": { "type": "integer", "minimum": 1 }
        },
        "required": ["sku", "quantity"],
        "additionalProperties": false
      },
      "minItems": 1
    },
    "couponCode": { "type": ["string", "null"] }
  },
  "required": ["customerId", "items"]
}

items is an array whose own items keyword is a full sub-schema — every element has to independently satisfy it. minItems: 1 on the outer array stops an order with an empty items list from validating at all, which a bare "type": "array" would let through silently. couponCode uses type: ["string", "null"] rather than making it optional, since a client that explicitly clears a coupon by sending null is different from one that never mentions the field at all.

Look closely and you'll notice additionalProperties: false is set inside each item's sub-schema, but deliberately not on the request as a whole. That split is intentional, not an oversight. The same tension shows up across API schema design generally: a strict schema catches typos and blocks mass assignment, but a client running a slightly newer version of your API — one that starts sending an extra giftMessage field you haven't documented yet — would get every request rejected outright by a validator that's stricter than the server itself. Locking down a well-known, rarely-changing shape like a line item is low-risk; locking down the top-level request body of an API you version and evolve over time trades away exactly the forward compatibility that let older and newer clients coexist.

There's no universal right answer — an internal API you control both ends of can reasonably go stricter everywhere, while a public API with independent client versions usually can't. The point is to choose deliberately, field group by field group, rather than defaulting to whichever way additionalProperties happens to default (permissive, unless set otherwise).

For the vocabulary behind keywords like items, minItems, and additionalProperties, see What Is JSON Schema?. For array-of-objects and nested-object patterns in more depth, see JSON Schema Examples. For reusing a sub-schema like a price across several fields instead of repeating it, see JSON Schema for Product Catalogs.

Valid examples

  • {"customerId":"cus_8f3c2a","items":[{"sku":"SKU-1","quantity":2},{"sku":"SKU-2","quantity":1}],"couponCode":null}

    Two line items and an explicitly cleared coupon — every constraint satisfied.

  • {"customerId":"cus_8f3c2a","items":[{"sku":"SKU-1","quantity":1}]}

    couponCode left out entirely — it's not required, unlike explicitly sending null.

  • {"customerId":"cus_8f3c2a","items":[{"sku":"SKU-1","quantity":1}],"idempotencyKey":"req_abc123"}

    An undocumented idempotencyKey field at the top level — passes, since additionalProperties isn't restricted there, unlike inside each item.

Invalid examples

  • {"customerId":"cus_8f3c2a","items":[]}

    An empty items array — fails minItems, which requires at least one line item.

  • {"customerId":"cus_8f3c2a","items":[{"sku":"SKU-1","quantity":1,"discount":0.5}]}

    A discount field on a line item — fails, since additionalProperties: false is set specifically inside the item sub-schema.

  • {"items":[{"sku":"SKU-1","quantity":1}]}

    No customerId — fails the top-level required array, which demands it regardless of how well-formed items is.

Try it now