{
  "openapi": "3.1.0",
  "info": {
    "title": "EatMePrint Agent Ordering API",
    "version": "1.0.0",
    "summary": "Order edible photo-printed macarons programmatically.",
    "description": "EatMePrint prints images in edible ink onto macarons and ships them across the US. This API lets an autonomous agent turn nine image URLs into a physical gift delivered to a person, with no CAPTCHA and no human in the loop. Full prose instructions: https://eatmeprint.com/for-agents/\n\nIMPORTANT RESPONSE CONVENTIONS (verified against production 2026-08-27):\n1. Successful responses are wrapped: {\"status\":\"ok\",\"data\":{...}}. The useful fields are under `data`.\n2. Failures are returned with HTTP 200 and a body of {\"status\":\"fail\",\"errors\":\"<reason>\"}. Check the `status` field, NOT the HTTP status code.\n3. An MCP server wrapping this API is available at https://eatmeprint-mcp.eatmeprint.workers.dev/mcp",
    "contact": {
      "name": "EatMePrint",
      "url": "https://eatmeprint.com/contacts/"
    }
  },
  "servers": [
    {
      "url": "https://eatmeprint.com",
      "description": "Production"
    }
  ],
  "paths": {
    "/agent-set/": {
      "post": {
        "operationId": "buildSet",
        "summary": "Build a printable set from nine image URLs",
        "description": "Send exactly nine public image URLs in grid order (left to right, top to bottom). Images are fetched and centre-cropped server-side, so there is no upload widget to drive. Returns a set_hash plus an add_to_cart object containing the exact next request to make. Returns {status,data}; the fields below are nested under `data`. Failures return HTTP 200 with {\"status\":\"fail\",\"errors\":\"...\"} - check `status`, not the HTTP code. Typical latency 0.4-2.2 s for nine remote images.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BuildSetRequest"
              },
              "example": {
                "images": [
                  "https://example.com/1.jpg",
                  "https://example.com/2.jpg",
                  "https://example.com/3.jpg",
                  "https://example.com/4.jpg",
                  "https://example.com/5.jpg",
                  "https://example.com/6.jpg",
                  "https://example.com/7.jpg",
                  "https://example.com/8.jpg",
                  "https://example.com/9.jpg"
                ],
                "email": "you@example.com"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Envelope. status=ok on success, status=fail with `errors` on rejection (wrong image count, unreachable/redirecting URL, private address, unsupported format, over 10 MB).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BuildSetResponse"
                }
              }
            }
          }
        }
      }
    },
    "/cart/add/": {
      "post": {
        "operationId": "addToCart",
        "summary": "Add a built set to the session cart",
        "description": "POST the fields exactly as returned in add_to_cart.fields. The cart is session-bound, so the cookie jar from this call must be reused through checkout.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "product_id": {
                    "type": "integer",
                    "example": 1
                  },
                  "set": {
                    "type": "string",
                    "description": "set_hash from buildSet"
                  }
                },
                "required": [
                  "product_id",
                  "set"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Added to cart. Retain cookies."
          }
        }
      }
    },
    "/checkout/": {
      "get": {
        "operationId": "checkout",
        "summary": "Complete the order",
        "description": "Browser-style flow in three short steps: contact details, delivery, payment. Requires the cookies from addToCart. Required fields: first name, email, and delivery address (recipient, state, city, ZIP, street). Last name is optional; phone is required (any format). Payment is PayPal or crypto; the crypto route needs no account and no card details. On confirmation you receive an order number and a /my/order/{id}/{code}/ link.",
        "responses": {
          "200": {
            "description": "Checkout page."
          }
        }
      }
    },
    "/my/order/{id}/{code}/": {
      "get": {
        "operationId": "getOrderStatus",
        "summary": "Check order status",
        "description": "Requires no account and no password. Keep this link and report it to the person who asked for the order.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current order status."
          }
        }
      }
    },
    "/agent-order/": {
      "post": {
        "operationId": "createAgentOrder",
        "summary": "Create an order from the current cart and get a NOWPayments crypto deposit address",
        "description": "Turns the cart built by /agent-set/ + /cart/add/ into a real order and returns a raw crypto deposit address. Use the same cookie jar as /cart/add/. Send an Idempotency-Key header; a retry with the same key returns the same order and address.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9._:-]{8,128}$"
            },
            "description": "Dedupe key. A retry with the same value returns the original order, never a duplicate."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentOrderRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Order created, deposit address issued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentOrderResponse"
                }
              }
            }
          },
          "200": {
            "description": "Idempotent replay of a previously created order."
          },
          "400": {
            "description": "Bad or missing input, empty cart, or unsupported pay_currency."
          },
          "409": {
            "description": "An order for this Idempotency-Key is still being set up; retry."
          },
          "422": {
            "description": "No shipping method serves the address."
          },
          "429": {
            "description": "Rate limited (per IP / per email)."
          },
          "502": {
            "description": "Payment gateway unreachable; retry with the same Idempotency-Key."
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "BuildSetRequest": {
        "type": "object",
        "required": [
          "images",
          "email"
        ],
        "properties": {
          "images": {
            "type": "array",
            "minItems": 9,
            "maxItems": 9,
            "description": "Exactly nine public image URLs in grid order. JPEG or PNG, up to 10 MB each, plain https, final URL with no redirect. Private or internal addresses are refused.",
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "email": {
            "type": "string",
            "format": "email",
            "description": "An address a human actually reads. Used only if the order needs clarifying."
          }
        }
      },
      "BuildSetResponse": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Envelope"
          }
        ],
        "example": {
          "status": "ok",
          "data": {
            "set_hash": "5b2d8851bc248f61182b692851ff65b2",
            "macarons": 9,
            "price": 67,
            "currency": "USD",
            "add_to_cart": {
              "url": "https://eatmeprint.com/cart/add/",
              "method": "POST",
              "note": "Send as form fields, keep the cookie jar, then GET checkout_url.",
              "fields": {
                "product_id": "1",
                "set": "5b2d8851bc248f61182b692851ff65b2",
                "features[10]": "46"
              }
            },
            "checkout_url": "https://eatmeprint.com/checkout/",
            "preview": [
              "https://eatmeprint.com/wa-data/.../thumb/0.jpg"
            ]
          }
        }
      },
      "Envelope": {
        "type": "object",
        "description": "Every response uses this envelope. Failures use HTTP 200, so check `status`.",
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "fail"
            ]
          },
          "data": {
            "type": "object",
            "description": "Present when status is ok."
          },
          "errors": {
            "type": "string",
            "description": "Human-readable reason. Present when status is fail."
          }
        }
      },
      "AgentOrderRequest": {
        "type": "object",
        "required": [
          "email",
          "recipient"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "recipient": {
            "type": "object",
            "required": [
              "first_name",
              "street",
              "city",
              "state",
              "zip",
              "phone"
            ],
            "properties": {
              "first_name": {
                "type": "string"
              },
              "last_name": {
                "type": "string"
              },
              "street": {
                "type": "string"
              },
              "city": {
                "type": "string"
              },
              "state": {
                "type": "string",
                "description": "2-letter US state code"
              },
              "zip": {
                "type": "string"
              },
              "phone": {
                "type": "string"
              }
            }
          },
          "pay_currency": {
            "type": "string",
            "default": "usdttrc20",
            "description": "Validate against NOWPayments GET /v1/currencies."
          },
          "shipping_id": {
            "type": "integer",
            "description": "Optional: force a specific shipping-method instance id."
          },
          "set_hash": {
            "type": "string",
            "description": "Optional consistency check; must be in the cart."
          },
          "note": {
            "type": "string",
            "description": "Optional order comment."
          }
        }
      },
      "AgentOrderResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "fail"
            ]
          },
          "data": {
            "type": "object",
            "properties": {
              "order_id": {
                "type": "integer"
              },
              "status_url": {
                "type": "string",
                "format": "uri"
              },
              "amount": {
                "type": "string",
                "description": "Order total as a fixed-decimal string, e.g. \"81.51\"."
              },
              "currency": {
                "type": "string"
              },
              "line_items": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "label": {
                      "type": "string"
                    },
                    "amount": {
                      "type": "string",
                      "description": "Fixed-decimal string, e.g. \"14.51\"."
                    }
                  }
                }
              },
              "payment": {
                "type": "object",
                "properties": {
                  "provider": {
                    "type": "string"
                  },
                  "payment_id": {
                    "type": "string"
                  },
                  "pay_address": {
                    "type": "string"
                  },
                  "pay_amount": {
                    "type": "string",
                    "description": "EXACT crypto amount to send, fixed-decimal string (up to 8 dp), e.g. \"89.78406400\". Send this verbatim."
                  },
                  "pay_currency": {
                    "type": "string"
                  },
                  "network": {
                    "type": "string"
                  },
                  "expires_at": {
                    "type": "string"
                  }
                }
              },
              "allergens": {
                "type": "string"
              },
              "note": {
                "type": "string",
                "nullable": true
              },
              "next": {
                "type": "string"
              }
            }
          }
        }
      }
    }
  },
  "x-product": {
    "name": "Edible photo macaron set",
    "description": "Nine macarons in a 3x3 box, each printed with one of your images in edible ink.",
    "price": 67,
    "currency": "USD",
    "shipping": {
      "model": "live_carrier_rates",
      "appliesTo": "all US addresses, California included",
      "typicalRangeUsd": "6-15",
      "note": "Priced from live USPS/UPS carrier rates at order time and varies by ZIP; the exact total is returned when the order is created. Do not quote a firm figure."
    },
    "leadTimeBusinessDays": 4,
    "allergens": [
      "almond",
      "egg",
      "milk"
    ],
    "captcha": false
  },
  "x-mcp": {
    "endpoint": "https://eatmeprint-mcp.eatmeprint.workers.dev/mcp",
    "transport": "streamable-http",
    "tools": [
      "get_pricing",
      "preview_set",
      "place_order",
      "check_order"
    ],
    "note": "Zero-install connector. Add the endpoint URL in Claude or ChatGPT connector settings."
  }
}