{
  "openapi": "3.1.0",
  "info": {
    "title": "IBO Studio Agent API",
    "description": "Commission IBO campaign films and image sets. Payment is always approved by the human customer on Stripe's hosted checkout; agents never handle card data. Prices are fixed server-side.",
    "version": "1.1.0",
    "contact": {
      "email": "studio@ibouniverse.com"
    }
  },
  "servers": [
    {
      "url": "https://ibouniverse.com"
    }
  ],
  "paths": {
    "/api/offers": {
      "get": {
        "operationId": "listOffers",
        "summary": "List IBO's production packages, prices, deposits, deliverables, and policies",
        "responses": {
          "200": {
            "description": "Offer feed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (per-IP sliding window). Retry after the retry-after header.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/checkout": {
      "post": {
        "operationId": "createCheckout",
        "summary": "Create a Stripe Checkout for a package's 50% deposit. Show the amount to the user and have THEM open checkout_url to approve payment.",
        "parameters": [
          {
            "name": "package",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "film",
                "series",
                "images"
              ]
            }
          },
          {
            "name": "format",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "json"
              ]
            },
            "description": "Always pass format=json"
          }
        ],
        "responses": {
          "200": {
            "description": "checkout_url, stripe_session_id, amount_due",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "checkout_url": {
                      "type": "string"
                    },
                    "stripe_session_id": {
                      "type": "string"
                    },
                    "amount_due": {
                      "type": "number"
                    },
                    "currency": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Unknown package",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "valid_ids": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (per-IP sliding window). Retry after the retry-after header.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "502": {
            "description": "Upstream payment provider error",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        },
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "package": {
                    "type": "string",
                    "enum": [
                      "film",
                      "series",
                      "images"
                    ]
                  },
                  "client_request_id": {
                    "type": "string",
                    "description": "Stable id for idempotent retries (required for agent integrations)"
                  }
                },
                "additionalProperties": false
              }
            }
          }
        }
      }
    },
    "/api/order": {
      "get": {
        "operationId": "getOrder",
        "summary": "PII-free order status check (paid, package, deposit). For identity + order_token use POST /api/order/token.",
        "parameters": [
          {
            "name": "session_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Order status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "paid": {
                      "type": "boolean"
                    },
                    "package": {
                      "type": "string"
                    },
                    "total": {
                      "type": "string"
                    },
                    "deposit": {
                      "type": "number"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid session id",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (per-IP sliding window). Retry after the retry-after header.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/brief": {
      "post": {
        "operationId": "submitBrief",
        "summary": "Submit the creative brief. For paid orders include sessionId; identity comes from the verified payment. Anonymous submissions require a browser Turnstile token.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string"
                  },
                  "company": {
                    "type": "string"
                  },
                  "package": {
                    "type": "string"
                  },
                  "product": {
                    "type": "string"
                  },
                  "goal": {
                    "type": "string"
                  },
                  "audience": {
                    "type": "string"
                  },
                  "channels": {
                    "type": "string"
                  },
                  "launch": {
                    "type": "string"
                  },
                  "links": {
                    "type": "string"
                  },
                  "constraints": {
                    "type": "string"
                  },
                  "files": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "key": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "size": {
                          "type": "number"
                        }
                      }
                    }
                  },
                  "order_token": {
                    "type": "string",
                    "description": "Preferred order capability from /api/order/token"
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Verification required (anonymous without Turnstile token)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "413": {
            "description": "Attachments too large",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (per-IP sliding window). Retry after the retry-after header.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/brief/upload-url": {
      "post": {
        "operationId": "createUploadUrl",
        "summary": "Get a presigned URL to upload one brand-asset file (paid orders only). PUT the file bytes to the returned url, then reference key in submitBrief files[].",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "filename",
                  "size",
                  "order_token"
                ],
                "properties": {
                  "filename": {
                    "type": "string"
                  },
                  "size": {
                    "type": "number"
                  },
                  "order_token": {
                    "type": "string",
                    "description": "Preferred, from /api/order/token"
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Presigned upload",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string"
                    },
                    "key": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Disallowed file type",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Requires a paid order",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "413": {
            "description": "File or order size limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (per-IP sliding window). Retry after the retry-after header.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/api/order/token": {
      "post": {
        "operationId": "exchangeOrderToken",
        "summary": "Verify a paid order and exchange the Stripe session id for an order-scoped access token (includes customer identity; POST keeps PII out of URL logs)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "session_id"
                ],
                "properties": {
                  "session_id": {
                    "type": "string"
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Order details + order_token",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "paid": {
                      "type": "boolean"
                    },
                    "package": {
                      "type": "string"
                    },
                    "total": {
                      "type": "string"
                    },
                    "deposit": {
                      "type": "number"
                    },
                    "email": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "order_token": {
                      "type": "string"
                    },
                    "expires_at": {
                      "type": "number"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid session id",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "404": {
            "description": "Not found or not paid",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limited (per-IP sliding window). Retry after the retry-after header.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "error"
                  ]
                }
              }
            }
          }
        }
      }
    }
  }
}