{
  "openapi": "3.1.0",
  "info": {
    "title": "PawForge Art public API",
    "version": "1.2.0",
    "description": "Read-only catalogue (portrait styles, products with exact prices, shipping countries): static JSON, no authentication. Proof API for approved platforms: start a free proof and read it back, on https://api.pawforgeart.com, with a connector key in the X-PawForge-Key header. A ready proof's checkoutUrl opens our checkout page; purchases complete through our Stripe checkout. See https://pawforgeart.com/developers.",
    "contact": {
      "name": "PawForge Art",
      "email": "curator@pawforgeart.com",
      "url": "https://pawforgeart.com/developers"
    }
  },
  "servers": [
    {
      "url": "https://pawforgeart.com"
    }
  ],
  "externalDocs": {
    "url": "https://pawforgeart.com/developers"
  },
  "paths": {
    "/api/v1": {
      "get": {
        "operationId": "getIndex",
        "summary": "What this API is, its endpoints, its documentation and its contact.",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Index"
                },
                "example": {
                  "name": "PawForge Art public API",
                  "version": "1.2.0",
                  "description": "A read-only catalogue for PawForge Art, and a keyed proof API for approved platforms: AI-rendered, painted-style pet portraits, printed on canvas or a phone case. Every proof is free to review before payment.",
                  "docs": "https://pawforgeart.com/developers",
                  "openapi": "https://pawforgeart.com/api/v1/openapi.json",
                  "contact": "curator@pawforgeart.com",
                  "authentication": "None for these read endpoints. The proof endpoints (proofApi) need a connector key in the X-PawForge-Key header.",
                  "endpoints": [
                    {
                      "path": "/api/v1",
                      "url": "https://pawforgeart.com/api/v1",
                      "method": "GET",
                      "summary": "What this API is, its endpoints, its documentation and its contact."
                    },
                    {
                      "path": "/api/v1/styles",
                      "url": "https://pawforgeart.com/api/v1/styles",
                      "method": "GET",
                      "summary": "The portrait styles on offer, with sample images and each style's page."
                    },
                    {
                      "path": "/api/v1/products",
                      "url": "https://pawforgeart.com/api/v1/products",
                      "method": "GET",
                      "summary": "Canvas sizes and phone-case styles with exact prices, and the shipping countries."
                    },
                    {
                      "path": "/api/v1/openapi.json",
                      "url": "https://pawforgeart.com/api/v1/openapi.json",
                      "method": "GET",
                      "summary": "This API described in OpenAPI 3.1."
                    }
                  ],
                  "freeProofAllowance": {
                    "proofsPerEmail": 10,
                    "proofsPerVerifiedEmail": 20,
                    "windowDays": 30,
                    "note": "Applies today to every proof from the free generator (https://pawforgeart.com/free-pet-portrait-generator), and to connector proofs: 10 per email address every 30 days, and 20 once the address is confirmed."
                  },
                  "proofApi": {
                    "status": "Live for approved platforms: callable only with a connector key the studio issues. There is no public sign-up; write to the contact address to be considered.",
                    "host": "https://api.pawforgeart.com",
                    "authentication": {
                      "type": "apiKey",
                      "in": "header",
                      "name": "X-PawForge-Key"
                    },
                    "endpoints": [
                      {
                        "method": "POST",
                        "path": "/api/v1/proofs",
                        "url": "https://api.pawforgeart.com/api/v1/proofs",
                        "summary": "Start a free proof: send a pet photo, a style id, the user's email address, and an optional orientation (multipart/form-data). Answers 202 with the proof's id."
                      },
                      {
                        "method": "GET",
                        "path": "/api/v1/proofs/{id}",
                        "url": "https://api.pawforgeart.com/api/v1/proofs/{id}",
                        "summary": "Read a proof this key started: its status (processing, ready or failed) and, when ready, an image URL and a checkout URL."
                      }
                    ],
                    "limits": {
                      "connectorDailyProofs": 24,
                      "maxPhotoBytes": 12582912,
                      "maxRequestBytes": 12648448,
                      "note": "By default up to 24 free proofs a day per connector; each must also fit the per-address allowance. A photo may be at most 12 MiB, in JPEG, PNG, WEBP."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/styles": {
      "get": {
        "operationId": "getStyles",
        "summary": "The portrait styles on offer, with sample images and each style's page.",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Styles"
                },
                "example": {
                  "count": 17,
                  "medium": "Every portrait, in every style, is an AI-rendered image in that style's manner (painted-style or illustration-style), printed on canvas or a phone case. Materials a description names (ink on washi, gilt, a panel) belong to the historical style, not to the product.",
                  "note": "Not every studio collection is offered through this API. Seasonal styles are listed only while they are offered.",
                  "styles": [
                    {
                      "id": "roman_emperor",
                      "slug": "roman-emperor",
                      "label": "The Roman Imperial",
                      "era": "XXVII BC — CDLXXVI AD",
                      "description": "Portrait studies of the feline emperors who presided over the Pax Felina.",
                      "seasonal": null,
                      "url": "https://pawforgeart.com/styles/roman-emperor",
                      "sampleImages": [
                        {
                          "png": "https://pawforgeart.com/gallery/roman_emperor/1.png",
                          "webp": "https://pawforgeart.com/gallery-derived/roman_emperor/1-w640.webp"
                        }
                      ]
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/products": {
      "get": {
        "operationId": "getProducts",
        "summary": "Canvas sizes and phone-case styles with exact prices, and the shipping countries.",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Products"
                },
                "example": {
                  "currency": "USD",
                  "note": "Prices are per item and exclude shipping, which is quoted per address at checkout.",
                  "availability": "Availability is confirmed at checkout, where shipping is added and any discount applied. These sizes and styles match https://pawforgeart.com/pricing and https://pawforgeart.com/pet-phone-cases.",
                  "canvas": {
                    "description": "A print of the AI-rendered, painted-style portrait on cotton-blend canvas, gallery-wrapped and ready to hang.",
                    "sizes": [
                      {
                        "label": "12\" × 16\"",
                        "widthIn": 12,
                        "heightIn": 16,
                        "orientation": "vertical",
                        "priceCents": 6000
                      },
                      {
                        "label": "16\" × 12\"",
                        "widthIn": 16,
                        "heightIn": 12,
                        "orientation": "horizontal",
                        "priceCents": 6000
                      },
                      {
                        "label": "14\" × 14\"",
                        "widthIn": 14,
                        "heightIn": 14,
                        "orientation": "square",
                        "priceCents": 6000
                      },
                      {
                        "label": "16\" × 20\"",
                        "widthIn": 16,
                        "heightIn": 20,
                        "orientation": "vertical",
                        "priceCents": 8000
                      },
                      {
                        "label": "18\" × 24\"",
                        "widthIn": 18,
                        "heightIn": 24,
                        "orientation": "vertical",
                        "priceCents": 10000
                      },
                      {
                        "label": "24\" × 18\"",
                        "widthIn": 24,
                        "heightIn": 18,
                        "orientation": "horizontal",
                        "priceCents": 10000
                      }
                    ]
                  },
                  "phoneCase": {
                    "description": "The portrait printed on a phone case.",
                    "styles": [
                      {
                        "id": "impact",
                        "label": "Impact-resistant case",
                        "priceCents": 4500
                      },
                      {
                        "id": "magnetic",
                        "label": "Magnetic case",
                        "priceCents": 4500
                      }
                    ],
                    "devices": null,
                    "devicesNote": "Which phones a case is offered for changes as models are added, so it is not listed here. The live list is on https://pawforgeart.com/pet-phone-cases, and the phone is chosen at checkout."
                  },
                  "shipping": {
                    "countryCount": 32,
                    "countries": [
                      {
                        "code": "US",
                        "name": "United States"
                      },
                      {
                        "code": "CA",
                        "name": "Canada"
                      },
                      {
                        "code": "GB",
                        "name": "United Kingdom"
                      }
                    ],
                    "note": "We ship only to these countries. Shipping is quoted per address at checkout."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/openapi.json": {
      "get": {
        "operationId": "getOpenApiDocument",
        "summary": "This API described in OpenAPI 3.1.",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OpenApiDocument"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/proofs": {
      "servers": [
        {
          "url": "https://api.pawforgeart.com"
        }
      ],
      "post": {
        "operationId": "createProof",
        "summary": "Start a free proof: send a pet photo, a style id, the user's email address, and an optional orientation (multipart/form-data). Answers 202 with the proof's id.",
        "security": [
          {
            "PawForgeKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "photo",
                  "styleId",
                  "email"
                ],
                "properties": {
                  "photo": {
                    "type": "string",
                    "format": "binary",
                    "description": "The pet photograph: JPEG, PNG, WEBP by its bytes, at most 12582912 bytes (12 MiB). The whole request may be at most 12648448 bytes."
                  },
                  "styleId": {
                    "type": "string",
                    "description": "A style id from https://pawforgeart.com/api/v1/styles."
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "The connector user's address; the proof email goes to it."
                  },
                  "orientation": {
                    "type": "string",
                    "enum": [
                      "vertical",
                      "horizontal",
                      "square"
                    ],
                    "default": "vertical"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted: the proof is being made. A retry with the same photo, address, style and orientation within 2 minutes returns the same proof instead of starting a second.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProofAccepted"
                }
              }
            }
          },
          "400": {
            "description": "invalid_request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProofError"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string"
                },
                "description": "Names the key header."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProofError"
                }
              }
            }
          },
          "403": {
            "description": "allowance_exhausted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProofError"
                }
              }
            }
          },
          "413": {
            "description": "photo_too_large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProofError"
                }
              }
            }
          },
          "415": {
            "description": "unsupported_photo_type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProofError"
                }
              }
            }
          },
          "429": {
            "description": "connector_daily_limit",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds until the next UTC midnight."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProofError"
                }
              }
            }
          },
          "500": {
            "description": "internal_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProofError"
                }
              }
            }
          },
          "503": {
            "description": "unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProofError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/proofs/{id}": {
      "servers": [
        {
          "url": "https://api.pawforgeart.com"
        }
      ],
      "get": {
        "operationId": "getProof",
        "summary": "Read a proof this key started: its status (processing, ready or failed) and, when ready, an image URL and a checkout URL.",
        "security": [
          {
            "PawForgeKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The proof.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProofRead"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string"
                },
                "description": "Names the key header."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProofError"
                }
              }
            }
          },
          "404": {
            "description": "not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProofError"
                }
              }
            }
          },
          "500": {
            "description": "internal_error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProofError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Index": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "docs": {
            "type": "string",
            "format": "uri"
          },
          "openapi": {
            "type": "string",
            "format": "uri"
          },
          "contact": {
            "type": "string",
            "format": "email"
          },
          "authentication": {
            "type": "string"
          },
          "endpoints": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "path": {
                  "type": "string"
                },
                "url": {
                  "type": "string",
                  "format": "uri"
                },
                "method": {
                  "type": "string",
                  "enum": [
                    "GET"
                  ]
                },
                "summary": {
                  "type": "string"
                }
              },
              "required": [
                "path",
                "url",
                "method",
                "summary"
              ],
              "additionalProperties": false
            }
          },
          "freeProofAllowance": {
            "type": "object",
            "properties": {
              "proofsPerEmail": {
                "type": "integer"
              },
              "proofsPerVerifiedEmail": {
                "type": "integer"
              },
              "windowDays": {
                "type": "integer"
              },
              "note": {
                "type": "string"
              }
            },
            "required": [
              "proofsPerEmail",
              "proofsPerVerifiedEmail",
              "windowDays",
              "note"
            ],
            "additionalProperties": false
          },
          "proofApi": {
            "type": "object",
            "properties": {
              "status": {
                "type": "string"
              },
              "host": {
                "type": "string",
                "format": "uri"
              },
              "authentication": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "apiKey"
                    ]
                  },
                  "in": {
                    "type": "string",
                    "enum": [
                      "header"
                    ]
                  },
                  "name": {
                    "type": "string"
                  }
                },
                "required": [
                  "type",
                  "in",
                  "name"
                ],
                "additionalProperties": false
              },
              "endpoints": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "method": {
                      "type": "string",
                      "enum": [
                        "GET",
                        "POST"
                      ]
                    },
                    "path": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string"
                    },
                    "summary": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "method",
                    "path",
                    "url",
                    "summary"
                  ],
                  "additionalProperties": false
                }
              },
              "limits": {
                "type": "object",
                "properties": {
                  "connectorDailyProofs": {
                    "type": "integer"
                  },
                  "maxPhotoBytes": {
                    "type": "integer"
                  },
                  "maxRequestBytes": {
                    "type": "integer"
                  },
                  "note": {
                    "type": "string"
                  }
                },
                "required": [
                  "connectorDailyProofs",
                  "maxPhotoBytes",
                  "maxRequestBytes",
                  "note"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "status",
              "host",
              "authentication",
              "endpoints",
              "limits"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "name",
          "version",
          "description",
          "docs",
          "openapi",
          "contact",
          "authentication",
          "endpoints",
          "freeProofAllowance",
          "proofApi"
        ],
        "additionalProperties": false
      },
      "SampleImage": {
        "type": "object",
        "properties": {
          "png": {
            "type": "string",
            "format": "uri"
          },
          "webp": {
            "type": "string",
            "format": "uri"
          }
        },
        "required": [
          "png",
          "webp"
        ],
        "additionalProperties": false
      },
      "Style": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "era": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "seasonal": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "halloween",
              "christmas",
              null
            ]
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "sampleImages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SampleImage"
            }
          }
        },
        "required": [
          "id",
          "slug",
          "label",
          "era",
          "description",
          "seasonal",
          "url",
          "sampleImages"
        ],
        "additionalProperties": false
      },
      "Styles": {
        "type": "object",
        "properties": {
          "count": {
            "type": "integer"
          },
          "medium": {
            "type": "string"
          },
          "note": {
            "type": "string"
          },
          "styles": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Style"
            }
          }
        },
        "required": [
          "count",
          "medium",
          "note",
          "styles"
        ],
        "additionalProperties": false
      },
      "Products": {
        "type": "object",
        "properties": {
          "currency": {
            "type": "string",
            "enum": [
              "USD"
            ]
          },
          "note": {
            "type": "string"
          },
          "availability": {
            "type": "string"
          },
          "canvas": {
            "type": "object",
            "properties": {
              "description": {
                "type": "string"
              },
              "sizes": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "label": {
                      "type": "string"
                    },
                    "widthIn": {
                      "type": "integer"
                    },
                    "heightIn": {
                      "type": "integer"
                    },
                    "orientation": {
                      "type": "string",
                      "enum": [
                        "horizontal",
                        "vertical",
                        "square"
                      ]
                    },
                    "priceCents": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "label",
                    "widthIn",
                    "heightIn",
                    "orientation",
                    "priceCents"
                  ],
                  "additionalProperties": false
                }
              }
            },
            "required": [
              "description",
              "sizes"
            ],
            "additionalProperties": false
          },
          "phoneCase": {
            "type": "object",
            "properties": {
              "description": {
                "type": "string"
              },
              "styles": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "priceCents": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "id",
                    "label",
                    "priceCents"
                  ],
                  "additionalProperties": false
                }
              },
              "devices": {
                "type": "null"
              },
              "devicesNote": {
                "type": "string"
              }
            },
            "required": [
              "description",
              "styles",
              "devices",
              "devicesNote"
            ],
            "additionalProperties": false
          },
          "shipping": {
            "type": "object",
            "properties": {
              "countryCount": {
                "type": "integer"
              },
              "countries": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "code",
                    "name"
                  ],
                  "additionalProperties": false
                }
              },
              "note": {
                "type": "string"
              }
            },
            "required": [
              "countryCount",
              "countries",
              "note"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "currency",
          "note",
          "availability",
          "canvas",
          "phoneCase",
          "shipping"
        ],
        "additionalProperties": false
      },
      "OpenApiDocument": {
        "type": "object",
        "required": [
          "openapi",
          "info",
          "paths"
        ]
      },
      "ProofAccepted": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "processing"
            ]
          }
        },
        "required": [
          "id",
          "status"
        ],
        "additionalProperties": false
      },
      "ProofRead": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "processing",
              "ready",
              "failed"
            ]
          },
          "styleId": {
            "type": "string"
          },
          "imageUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "checkoutUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "no_pet_in_photo",
              null
            ]
          }
        },
        "required": [
          "id",
          "status",
          "styleId",
          "imageUrl",
          "checkoutUrl",
          "reason"
        ],
        "additionalProperties": false
      },
      "ProofError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "unauthorized",
              "invalid_request",
              "photo_too_large",
              "unsupported_photo_type",
              "not_found",
              "allowance_exhausted",
              "connector_daily_limit",
              "unavailable",
              "internal_error"
            ]
          },
          "field": {
            "type": "string",
            "enum": [
              "photo",
              "styleId",
              "email",
              "orientation"
            ]
          }
        },
        "required": [
          "error"
        ],
        "additionalProperties": false
      }
    },
    "securitySchemes": {
      "PawForgeKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-PawForge-Key"
      }
    }
  }
}
