{
  "openapi": "3.0.3",
  "info": {
    "title": "CloudFace Vision Engine API",
    "description": "Face recognition API powered by CloudFace Vision Engine\u2122. Detect, index, and search faces across photo collections.\n\n## Authentication\n\nEvery request requires 3 headers:\n- `X-CloudFace-Key` \u2014 Your API key\n- `X-CloudFace-Signature` \u2014 HMAC-SHA256(timestamp + request_body, your_secret)\n- `X-CloudFace-Timestamp` \u2014 Current Unix timestamp (requests older than 5 minutes are rejected)\n\n## Base URL\n`https://vision.cloudface-ai.com`",
    "version": "1.0.0",
    "contact": {
      "name": "CloudFace Vision API Support",
      "email": "api@cloudface-ai.com",
      "url": "https://vision.cloudface-ai.com/docs"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://vision.cloudface-ai.com/policy"
    },
    "termsOfService": "https://vision.cloudface-ai.com/terms",
    "x-logo": {
      "url": "https://vision.cloudface-ai.com/favicon.png"
    }
  },
  "servers": [
    {
      "url": "https://vision.cloudface-ai.com",
      "description": "Production server"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": [],
      "SignatureAuth": [],
      "TimestampAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-CloudFace-Key",
        "description": "Your API key starting with `cf_live_`"
      },
      "SignatureAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-CloudFace-Signature",
        "description": "HMAC-SHA256 signature: `hmac_sha256(timestamp + request_body, api_secret)`"
      },
      "TimestampAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-CloudFace-Timestamp",
        "description": "Current Unix timestamp in seconds. Requests older than 5 minutes are rejected."
      }
    },
    "schemas": {
      "EventResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          },
          "event_id": {
            "type": "string",
            "example": "a3f9b2c1-d4e5"
          }
        }
      },
      "IndexResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          },
          "event_id": {
            "type": "string",
            "example": "a3f9b2c1-d4e5"
          },
          "queued": {
            "type": "integer",
            "example": 50
          },
          "message": {
            "type": "string",
            "example": "Indexing started."
          }
        }
      },
      "StatusResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          },
          "event_id": {
            "type": "string",
            "example": "a3f9b2c1-d4e5"
          },
          "name": {
            "type": "string",
            "example": "Wedding 2026"
          },
          "status": {
            "type": "string",
            "enum": [
              "ready",
              "indexing",
              "error"
            ],
            "example": "ready"
          },
          "photo_count": {
            "type": "integer",
            "example": 50
          },
          "face_count": {
            "type": "integer",
            "example": 187
          },
          "last_indexed": {
            "type": "string",
            "format": "date-time",
            "example": "2026-08-12T10:30:00"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SearchResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          },
          "faces_detected": {
            "type": "integer",
            "example": 1
          },
          "total_matches": {
            "type": "integer",
            "example": 3
          },
          "matches": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "similarity": {
                  "type": "number",
                  "format": "float",
                  "example": 0.8423
                },
                "confidence": {
                  "type": "string",
                  "example": "84.23%"
                },
                "photo_index": {
                  "type": "string",
                  "example": "2",
                  "description": "0-based index into the photos array submitted during indexing"
                },
                "person_id": {
                  "type": "string",
                  "example": "vision_client_eventid_2_face_0"
                },
                "bbox": {
                  "type": "array",
                  "items": {
                    "type": "integer"
                  },
                  "example": [
                    120,
                    80,
                    340,
                    380
                  ],
                  "description": "Face bounding box [x1, y1, x2, y2] in pixels"
                }
              }
            }
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "example": false
          },
          "error": {
            "type": "string",
            "example": "Invalid API key"
          }
        }
      }
    }
  },
  "paths": {
    "/v1/events": {
      "post": {
        "summary": "Create Event",
        "description": "Creates a new face index namespace. Returns an `event_id` used in all subsequent calls for this event.",
        "operationId": "createEvent",
        "tags": [
          "Events"
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "example": "Wedding 2026 \u2014 John & Sarah",
                    "description": "Human-readable name for this event. For your reference only."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Event created successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventResponse"
                },
                "example": {
                  "success": true,
                  "event_id": "a3f9b2c1-d4e5"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/events/{event_id}/index": {
      "post": {
        "summary": "Index Photos",
        "description": "Submit photo URLs for face detection and embedding. Processing happens in the background. Poll `/status` to check completion.\n\n**Important:** Photos must be publicly accessible URLs. We download temporarily, extract face embeddings, then delete the images. Only mathematical vectors are stored.",
        "operationId": "indexPhotos",
        "tags": [
          "Events"
        ],
        "parameters": [
          {
            "name": "event_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "a3f9b2c1-d4e5",
            "description": "Event ID returned from Create Event"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "photos"
                ],
                "properties": {
                  "photos": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uri"
                    },
                    "maxItems": 500,
                    "example": [
                      "https://your-storage.com/photo1.jpg",
                      "https://your-storage.com/photo2.jpg"
                    ],
                    "description": "Array of publicly accessible photo URLs. Maximum 500 per request."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Indexing job queued successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IndexResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request \u2014 missing photos or too many URLs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed"
          },
          "404": {
            "description": "Event not found"
          }
        }
      }
    },
    "/v1/events/{event_id}/status": {
      "get": {
        "summary": "Check Status",
        "description": "Check indexing progress for an event. Poll this after submitting photos. When `status` is `ready` and `photo_count > 0`, the event is ready for searching.",
        "operationId": "getEventStatus",
        "tags": [
          "Events"
        ],
        "parameters": [
          {
            "name": "event_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "a3f9b2c1-d4e5"
          }
        ],
        "responses": {
          "200": {
            "description": "Event status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatusResponse"
                },
                "example": {
                  "success": true,
                  "event_id": "a3f9b2c1-d4e5",
                  "name": "Wedding 2026",
                  "status": "ready",
                  "photo_count": 50,
                  "face_count": 187,
                  "last_indexed": "2026-08-12T10:30:00"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed"
          },
          "404": {
            "description": "Event not found"
          }
        }
      }
    },
    "/v1/events/{event_id}/search": {
      "post": {
        "summary": "Search by Selfie",
        "description": "Submit a selfie URL to find all matching photos in the event index. Returns matches sorted by similarity score (highest first).\n\n**Threshold guide:**\n- `0.8-0.9` \u2014 High-quality studio selfies, strict matching\n- `0.6-0.7` \u2014 Weddings and events (recommended default)\n- `0.4-0.5` \u2014 Outdoor sports, angled or distant shots",
        "operationId": "searchBySelfie",
        "tags": [
          "Events"
        ],
        "parameters": [
          {
            "name": "event_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "a3f9b2c1-d4e5"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "selfie_url"
                ],
                "properties": {
                  "selfie_url": {
                    "type": "string",
                    "format": "uri",
                    "example": "https://your-storage.com/guest_selfie.jpg",
                    "description": "Publicly accessible URL of the selfie image"
                  },
                  "threshold": {
                    "type": "number",
                    "format": "float",
                    "minimum": 0.0,
                    "maximum": 1.0,
                    "default": 0.7,
                    "example": 0.6,
                    "description": "Match sensitivity. Lower = more results, higher = stricter."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Search completed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SearchResponse"
                },
                "example": {
                  "success": true,
                  "faces_detected": 1,
                  "total_matches": 3,
                  "matches": [
                    {
                      "similarity": 0.8423,
                      "confidence": "84.23%",
                      "photo_index": "2",
                      "bbox": [
                        120,
                        80,
                        340,
                        380
                      ]
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Bad request \u2014 missing selfie_url or invalid image"
          },
          "401": {
            "description": "Authentication failed"
          },
          "404": {
            "description": "Event not found"
          }
        }
      }
    },
    "/health": {
      "get": {
        "summary": "Health Check",
        "description": "Check if the API is online. No authentication required.",
        "operationId": "healthCheck",
        "tags": [
          "System"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "API is online",
            "content": {
              "application/json": {
                "example": {
                  "status": "ok",
                  "service": "CloudFace Vision Engine",
                  "version": "1.0.0"
                }
              }
            }
          }
        }
      }
    }
  },
  "tags": [
    {
      "name": "Events",
      "description": "Create and manage face recognition event namespaces"
    },
    {
      "name": "System",
      "description": "System health and status"
    }
  ],
  "externalDocs": {
    "description": "Full API Documentation",
    "url": "https://vision.cloudface-ai.com/docs"
  }
}