{
  "openapi": "3.1.0",
  "info": {
    "title": "UGC Pocket: Agent Commerce API",
    "version": "1.0.0",
    "description": "Public API for AI agents. A campaign order creates a DRAFT (status=draft) that the brand must manually confirm and fund in the UGC Pocket app. Budgets are in euro cents, minimum 5000 (€50) per creator.",
    "contact": {
      "name": "UGC Pocket",
      "email": "support@ugcpocket.com",
      "url": "https://ugcpocket.com/for-agents.html"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://ugcpocket.com/api",
      "description": "Production (Netlify proxy to Supabase Edge Functions)"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "tags": [
    {
      "name": "discovery",
      "description": "Public descriptors, no authentication."
    },
    {
      "name": "campaigns",
      "description": "Campaign ordering and tracking (API key required)."
    }
  ],
  "paths": {
    "/service-info": {
      "get": {
        "operationId": "getServiceInfo",
        "summary": "Product descriptor",
        "description": "Service metadata: categories, service types (prestations), platforms, currency, budget floor, OpenAPI/MCP/docs links. No authentication.",
        "tags": [
          "discovery"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Product descriptor.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceInfo"
                }
              }
            }
          }
        }
      }
    },
    "/estimate": {
      "post": {
        "operationId": "estimateCampaign",
        "summary": "Estimate a campaign from a free-text brief",
        "description": "Analyzes a free-text brief and returns suggested campaign fields plus an indicative total. No database writes, no authentication.",
        "tags": [
          "discovery"
        ],
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EstimateInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Suggested fields and indicative total.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EstimateResult"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/ErrorResponse"
          },
          "500": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        }
      }
    },
    "/campaigns": {
      "post": {
        "operationId": "draftCampaign",
        "summary": "Order a campaign (draft)",
        "description": "Creates a DRAFT campaign attached to the organization that owns the API key. The brand must then confirm and fund the campaign in the app. The optional Idempotency-Key header lets you safely retry a request without creating a duplicate.",
        "tags": [
          "campaigns"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Idempotency key (UUID recommended). The same key returns the cached response.",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CampaignDraftInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Draft created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignDraftResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ErrorResponse"
          },
          "401": {
            "$ref": "#/components/responses/ErrorResponse"
          },
          "403": {
            "$ref": "#/components/responses/ErrorResponse"
          },
          "422": {
            "$ref": "#/components/responses/ErrorResponse"
          },
          "429": {
            "description": "Rate limit exceeded (30 requests/minute).",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 60
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        }
      }
    },
    "/campaigns/{id}": {
      "get": {
        "operationId": "getCampaignStatus",
        "summary": "Campaign status",
        "description": "Returns the status of a campaign owned by the API key's organization. 404 if the campaign does not exist or does not belong to the organization.",
        "tags": [
          "campaigns"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Campaign UUID.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Campaign status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Campaign"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ErrorResponse"
          },
          "404": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Organization API key in the ugcp_live_… format, sent via Authorization: Bearer. Generated in the app (Profile → API Keys)."
      }
    },
    "responses": {
      "ErrorResponse": {
        "description": "Error.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "ServiceInfo": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "categories": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "dog",
                "cat",
                "car",
                "cooking",
                "couple",
                "business",
                "podcast",
                "sport"
              ]
            }
          },
          "prestations": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "ugc_only",
                "video_post",
                "post_provided"
              ]
            }
          },
          "platforms": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "TikTok",
                "Instagram",
                "YouTube",
                "Snapchat",
                "Facebook"
              ]
            }
          },
          "currency": {
            "type": "string",
            "enum": [
              "EUR"
            ]
          },
          "budget_unit": {
            "type": "string",
            "enum": [
              "cents"
            ]
          },
          "order_model": {
            "type": "string",
            "enum": [
              "human_confirmed_draft"
            ]
          },
          "min_budget_per_creator_cents": {
            "type": "integer",
            "minimum": 5000
          },
          "openapi": {
            "type": "string",
            "format": "uri"
          },
          "mcp": {
            "type": "string",
            "format": "uri"
          },
          "docs": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "EstimateInput": {
        "type": "object",
        "required": [
          "brief"
        ],
        "additionalProperties": false,
        "properties": {
          "brief": {
            "type": "string",
            "description": "Free-text campaign brief (French recommended).",
            "maxLength": 4000
          }
        }
      },
      "EstimateResult": {
        "type": "object",
        "properties": {
          "suggested": {
            "$ref": "#/components/schemas/SuggestedCampaign"
          },
          "indicative_total_cents": {
            "type": [
              "integer",
              "null"
            ],
            "description": "budget_per_creator_cents × creators_wanted, or null if either is missing."
          }
        }
      },
      "CampaignDraftInput": {
        "type": "object",
        "description": "Fields of a campaign order. Single source of schema, mirrored by the MCP inputSchema.",
        "required": [
          "title",
          "brief",
          "budget_per_creator_cents",
          "creators_wanted"
        ],
        "additionalProperties": false,
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 120,
            "description": "Short campaign title."
          },
          "brief": {
            "type": "string",
            "maxLength": 2000,
            "description": "Creative guidelines, tone, constraints."
          },
          "brief_raw": {
            "type": "string",
            "maxLength": 4000,
            "description": "Original, unrewritten brief (optional)."
          },
          "prestation": {
            "type": "string",
            "enum": [
              "ugc_only",
              "video_post",
              "post_provided"
            ],
            "default": "ugc_only",
            "description": "ugc_only = video delivered to the brand; video_post = the creator publishes on their own channels; post_provided = the creator publishes a provided video."
          },
          "objectives": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Objectives (e.g. Awareness, Conversions)."
          },
          "target_categories": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "dog",
                "cat",
                "car",
                "cooking",
                "couple",
                "business",
                "podcast",
                "sport"
              ]
            }
          },
          "platforms": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "TikTok",
                "Instagram",
                "YouTube",
                "Snapchat",
                "Facebook"
              ]
            }
          },
          "budget_per_creator_cents": {
            "type": "integer",
            "minimum": 5000,
            "maximum": 200000,
            "description": "Budget per creator, in euro cents. Minimum 5000 (€50), maximum 200000 (€2000)."
          },
          "creators_wanted": {
            "type": "integer",
            "minimum": 1,
            "maximum": 50,
            "description": "Number of creators wanted."
          },
          "payment_trigger": {
            "type": "string",
            "enum": [
              "publication",
              "views"
            ],
            "description": "Creator payment trigger."
          },
          "views_threshold": {
            "type": "integer",
            "minimum": 0,
            "description": "View count threshold (only if payment_trigger=views)."
          },
          "deadline": {
            "type": "string",
            "format": "date",
            "description": "Deadline (ISO 8601, YYYY-MM-DD)."
          }
        }
      },
      "CampaignDraftResult": {
        "type": "object",
        "properties": {
          "campaign_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft"
            ]
          },
          "ordered_by_agent": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "next_step": {
            "type": "string"
          },
          "confirm_url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "Campaign": {
        "type": "object",
        "properties": {
          "campaign_id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "open",
              "in_progress",
              "completed",
              "cancelled"
            ]
          },
          "ordered_by_agent": {
            "type": "boolean"
          },
          "confirm_url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "additionalProperties": false,
        "properties": {
          "error": {
            "type": "string",
            "description": "Stable error code.",
            "enum": [
              "invalid_api_key",
              "key_revoked",
              "key_expired",
              "insufficient_scope",
              "rate_limited",
              "validation_error",
              "not_found",
              "create_failed",
              "internal"
            ]
          },
          "detail": {
            "type": "string",
            "description": "Human-readable message (not stable)."
          }
        }
      },
      "SuggestedCampaign": {
        "type": "object",
        "description": "Campaign fields suggested by analyzing the brief (best-effort, unconstrained; an agent then passes them to POST /campaigns).",
        "additionalProperties": true,
        "properties": {
          "title": {
            "type": "string"
          },
          "brief": {
            "type": "string"
          },
          "prestation": {
            "type": "string"
          },
          "objectives": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "target_categories": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "platforms": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "budget_per_creator_cents": {
            "type": [
              "integer",
              "null"
            ]
          },
          "creators_wanted": {
            "type": [
              "integer",
              "null"
            ]
          },
          "payment_trigger": {
            "type": "string"
          },
          "views_threshold": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      }
    }
  }
}