{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://www.fourbysix.co/docs/order.schema.json",
  "title": "Four by Six order",
  "description": "Request body for POST /v1/orders. Spec version 1.0.",
  "type": "object",
  "required": [
    "spec_version",
    "order_ref",
    "order_type",
    "items"
  ],
  "additionalProperties": false,
  "properties": {
    "spec_version": {
      "type": "string",
      "pattern": "^1\\.[0-9]+$",
      "description": "Accepts 1.x. Anything else is rejected.",
      "examples": [
        "1.0"
      ]
    },
    "order_ref": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200,
      "description": "Your own order id. Unique per partner, and the idempotency key: resubmitting the same order_ref with an identical body replays the original response, while a different body is rejected with 409."
    },
    "order_type": {
      "enum": [
        "photo_print",
        "postcard"
      ]
    },
    "items": {
      "type": "array",
      "minItems": 1,
      "maxItems": 200,
      "description": "The per-account limit is reported by GET /v1/me as max_items_per_order; 200 is the default. NOT EXPRESSIBLE IN JSON SCHEMA, but enforced by the API: every item_ref must be unique within the order. Check that yourself before submitting.",
      "items": {
        "$ref": "#/$defs/item"
      }
    },
    "submitted_at": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601. Defaults to receipt time."
    },
    "priority": {
      "enum": [
        "standard",
        "rush"
      ],
      "default": "standard"
    },
    "customer_ref": {
      "type": "string",
      "maxLength": 200,
      "description": "Opaque. Stored and returned, never interpreted."
    },
    "shipping": {
      "type": "object",
      "description": "Where the package goes. Stored verbatim and handed to fulfillment; not validated."
    },
    "metadata": {
      "type": "object",
      "description": "Free-form. 4096 bytes maximum, serialized."
    }
  },
  "allOf": [
    {
      "if": {
        "properties": {
          "order_type": {
            "const": "postcard"
          }
        },
        "required": [
          "order_type"
        ]
      },
      "then": {
        "properties": {
          "items": {
            "minItems": 2,
            "maxItems": 2,
            "allOf": [
              {
                "contains": {
                  "properties": {
                    "role": {
                      "const": "postcard_front"
                    }
                  },
                  "required": [
                    "role"
                  ]
                }
              },
              {
                "contains": {
                  "properties": {
                    "role": {
                      "const": "postcard_back"
                    }
                  },
                  "required": [
                    "role"
                  ]
                }
              }
            ]
          }
        }
      }
    },
    {
      "if": {
        "properties": {
          "order_type": {
            "const": "photo_print"
          }
        },
        "required": [
          "order_type"
        ]
      },
      "then": {
        "properties": {
          "items": {
            "items": {
              "properties": {
                "role": {
                  "const": "print"
                }
              }
            }
          }
        }
      }
    }
  ],
  "$defs": {
    "item": {
      "type": "object",
      "required": [
        "item_ref",
        "role",
        "source",
        "print"
      ],
      "additionalProperties": false,
      "properties": {
        "item_ref": {
          "type": "string",
          "minLength": 1,
          "maxLength": 200,
          "description": "Unique within this order."
        },
        "role": {
          "enum": [
            "print",
            "postcard_front",
            "postcard_back"
          ]
        },
        "source": {
          "$ref": "#/$defs/source"
        },
        "quantity": {
          "type": "integer",
          "minimum": 1,
          "maximum": 1000,
          "default": 1
        },
        "print": {
          "$ref": "#/$defs/print"
        },
        "enhance": {
          "$ref": "#/$defs/enhance"
        }
      }
    },
    "source": {
      "type": "object",
      "required": [
        "url"
      ],
      "additionalProperties": false,
      "properties": {
        "url": {
          "type": "string",
          "pattern": "^https://",
          "maxLength": 8192,
          "description": "https only. Normally a presigned URL on your own storage. It must still be valid when we fetch it, which is within seconds of this request: an expired URL is unrecoverable."
        },
        "content_type": {
          "type": "string",
          "maxLength": 128,
          "description": "Helps name the file when the URL path carries no extension."
        },
        "filename": {
          "type": "string",
          "maxLength": 255,
          "description": "Cosmetic, and a useful extension hint."
        },
        "sha256": {
          "type": "string",
          "pattern": "^[0-9a-fA-F]{64}$",
          "description": "If supplied, verified after download. A mismatch fails the item."
        }
      }
    },
    "print": {
      "type": "object",
      "required": [
        "size",
        "finish"
      ],
      "additionalProperties": true,
      "description": "Unknown keys are ACCEPTED here and stored verbatim, unlike everywhere else in this schema. That is the forward-compatibility hatch for print geometry.",
      "properties": {
        "size": {
          "enum": [
            "4x6"
          ]
        },
        "finish": {
          "enum": [
            "matte"
          ]
        },
        "zoom": {
          "type": "number",
          "minimum": 1,
          "default": 1,
          "description": "1.0 (default) is the centre crop \u2014 the most of the photo the print's shape holds. 2.0 is twice the magnification. Below 1 is clamped to 1."
        },
        "center": {
          "type": "object",
          "additionalProperties": false,
          "description": "Where the middle of the print lands on the photo, as fractions. Defaults to the middle. Clamped so the frame stays on the image.",
          "properties": {
            "x": {
              "type": "number",
              "minimum": 0,
              "maximum": 1
            },
            "y": {
              "type": "number",
              "minimum": 0,
              "maximum": 1
            }
          }
        }
      }
    },
    "enhance": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "profile": {
          "enum": [
            "default",
            "none"
          ],
          "description": "Defaults to 'none' for postcard_back items and 'default' for everything else."
        }
      }
    }
  }
}
