openapi: 3.1.0
info:
  title: UseMyFace API
  version: "1.0"
  description: |
    Likeness consent for AI image and video. Partners check faces before generating, report licensed
    generations, and receive license events. Run by Kapturise FZC (UAE).
servers:
  - url: https://usemyface.pages.dev
security:
  - bearer: []
components:
  securitySchemes:
    bearer: { type: http, scheme: bearer, description: "Partner API key (umf_live_… / umf_test_…) or a user session token (umf_s_…)" }
    admin: { type: apiKey, in: header, name: X-Admin-Key }
  schemas:
    Descriptor:
      type: array
      minItems: 128
      maxItems: 128
      items: { type: number }
      description: 128-number face template from @vladmandic/face-api
    Use: { type: string, enum: [personal, social, ad, film, training] }
    Territory: { type: string, enum: [Worldwide, GCC, Middle East and North Africa, European Union, United Kingdom, United States, Asia-Pacific, Latin America, Sub-Saharan Africa] }
    Decision: { type: string, enum: [free, cleared, required, pending, blocked] }
    FaceResult:
      type: object
      properties:
        index: { type: integer }
        decision: { $ref: '#/components/schemas/Decision' }
        check_id: { type: string }
        match_pct: { type: integer }
        face_id: { type: string, description: Only for faces open to licensing }
        name: { type: string, description: Only for faces open to licensing }
        license_id: { type: string }
        per_use_cents: { type: integer }
        reasons: { type: array, items: { type: string } }
        violations: { type: array, items: { type: object, properties: { limit: { type: string }, label: { type: string }, why: { type: string } } } }
        offer:
          type: object
          properties:
            face_id: { type: string }
            use: { $ref: '#/components/schemas/Use' }
            price_cents: { type: integer }
            per_use_cents: { type: integer }
            term_days: { type: integer }
            approval: { type: boolean }
            url: { type: string }
    CheckResult:
      type: object
      properties:
        decision: { $ref: '#/components/schemas/Decision' }
        generate: { type: boolean }
        faces: { type: array, items: { $ref: '#/components/schemas/FaceResult' } }
    Error:
      type: object
      properties:
        error: { type: object, properties: { code: { type: string }, message: { type: string } } }
paths:
  /v1/likeness/check:
    post:
      summary: Check faces and prompt before generating
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [descriptors, use, territory]
              properties:
                descriptors: { type: array, maxItems: 10, items: { $ref: '#/components/schemas/Descriptor' } }
                prompt: { type: string, maxLength: 2000 }
                use: { $ref: '#/components/schemas/Use' }
                territory: { $ref: '#/components/schemas/Territory' }
                license_token: { type: string }
                end_user: { type: string }
      responses:
        '200': { description: Decision per face, content: { application/json: { schema: { $ref: '#/components/schemas/CheckResult' } } } }
        '400': { description: Bad input, content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } }
        '401': { description: Bad API key }
  /v1/usage:
    post:
      summary: Report a licensed generation and get a consent credential
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [check_id, units, unit]
              properties:
                check_id: { type: string }
                units: { type: integer, minimum: 1, maximum: 3600 }
                unit: { type: string, enum: [image, second] }
                output_sha256: { type: string, pattern: '^[a-f0-9]{64}$' }
      responses:
        '200':
          description: Recorded (idempotent per check_id)
          content:
            application/json:
              schema:
                type: object
                properties:
                  usage_id: { type: string }
                  amount_cents: { type: integer }
                  credential: { type: string, description: Ed25519-signed JWT }
                  c2pa_assertion: { type: object }
                  duplicate: { type: boolean }
        '409': { description: Check not cleared, stale, or license inactive }
  /v1/licenses/{id}/status:
    get:
      summary: License status
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses:
        '200': { description: Status, content: { application/json: { schema: { type: object, properties: { id: { type: string }, status: { type: string }, active: { type: boolean }, use: { type: string }, face_id: { type: string }, expires_at: { type: integer } } } } } }
  /v1/webhooks:
    post:
      summary: Subscribe to license events
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [url]
              properties:
                url: { type: string, format: uri }
                events: { type: array, items: { type: string, enum: ['*', license.activated, license.renewed, license.revoked, license.expired] } }
      responses:
        '201': { description: Created. The secret is returned once. }
    get:
      summary: List webhooks
      responses: { '200': { description: OK } }
  /v1/webhooks/{id}:
    delete:
      summary: Remove a webhook
      parameters: [{ name: id, in: path, required: true, schema: { type: string } }]
      responses: { '200': { description: OK } }
  /.well-known/jwks.json:
    get:
      summary: Public signing key (Ed25519) for license tokens and consent credentials
      security: []
      responses: { '200': { description: JWKS } }
  /v1/market:
    get:
      summary: Faces open to licensing
      security: []
      parameters:
        - { name: q, in: query, schema: { type: string } }
        - { name: use, in: query, schema: { $ref: '#/components/schemas/Use' } }
      responses: { '200': { description: OK } }
  /v1/auth/start:
    post: { summary: Email a sign-in code, security: [], responses: { '200': { description: OK } } }
  /v1/auth/verify:
    post: { summary: Exchange the code for a session token, security: [], responses: { '200': { description: OK } } }
  /v1/liveness/start:
    post: { summary: Start a live scan; the server picks the challenges, responses: { '200': { description: OK } } }
  /v1/faces:
    get: { summary: My registered faces, responses: { '200': { description: OK } } }
    post: { summary: Register a face (templates, liveness, consent, terms), responses: { '201': { description: Created }, '409': { description: Face already registered by someone else } } }
  /v1/faces/{id}:
    patch: { summary: Update profile or terms, parameters: [{ name: id, in: path, required: true, schema: { type: string } }], responses: { '200': { description: OK } } }
    delete: { summary: Delete the face template and revoke its licenses, parameters: [{ name: id, in: path, required: true, schema: { type: string } }], responses: { '200': { description: OK } } }
  /v1/licenses:
    get: { summary: My licenses (as owner or holder), responses: { '200': { description: OK } } }
    post: { summary: Buy or request a license, responses: { '201': { description: Created } } }
  /v1/licenses/{id}/token:
    get: { summary: Fresh 24-hour license token, parameters: [{ name: id, in: path, required: true, schema: { type: string } }], responses: { '200': { description: OK } } }
  /v1/licenses/{id}/renew:
    post: { summary: Renew for another term, parameters: [{ name: id, in: path, required: true, schema: { type: string } }], responses: { '200': { description: OK } } }
  /v1/licenses/{id}/revoke:
    post: { summary: Owner withdraws consent (or holder cancels), parameters: [{ name: id, in: path, required: true, schema: { type: string } }], responses: { '200': { description: OK } } }
  /v1/licenses/{id}/approve:
    post: { summary: Owner approves a request, parameters: [{ name: id, in: path, required: true, schema: { type: string } }], responses: { '200': { description: OK } } }
  /v1/generations:
    post: { summary: Generate a licensed face through UseMyFace (Seedance), responses: { '200': { description: OK } } }
  /v1/generations/{id}:
    get: { summary: Generation status; bills per second on success, parameters: [{ name: id, in: path, required: true, schema: { type: string } }], responses: { '200': { description: OK } } }
  /v1/activity:
    get: { summary: AI checks on my faces, responses: { '200': { description: OK } } }
  /v1/billing:
    get: { summary: What I owe and what I have earned, responses: { '200': { description: OK } } }
  /v1/reports:
    post: { summary: Report a fake, responses: { '200': { description: OK } } }
    get: { summary: Reports on my faces and reports I filed, responses: { '200': { description: OK } } }
  /v1/reports/{id}/takedown:
    post: { summary: Draft and mark a takedown notice as sent, parameters: [{ name: id, in: path, required: true, schema: { type: string } }], responses: { '200': { description: OK } } }
