openapi: 3.1.0
info:
  title: Cygn Enterprise API
  version: 1.0.0
  description: |
    Cygn Enterprise API provides programmatic access to deepfake detection,
    C2PA content signing, sonic audio watermarking, voice ID biometrics,
    and provenance verification.

    ## Authentication

    Cygn supports two authentication schemes:

    ### Cygn-Token (Recommended)
    Cryptographic identity using Ed25519-signed JWTs. Proves your enterprise
    identity is verified by Cygn — like an SSL certificate for your API calls.
    ```
    Authorization: Cygn eyJhbGciOiJFZERTQSIsInR5cCI6ImN5Z24rand0In0...
    ```

    ### Bearer Token (Legacy)
    Plain API key authentication. Still fully supported.
    ```
    Authorization: Bearer cygn_ent_your_license_key
    ```

    Get your Cygn-Token from the API Keys tab or via `GET /enterprise/token`.

    ## Billing

    Enterprise accounts use prepaid metered billing. Each API call deducts
    the operation cost from your balance. If your balance is insufficient,
    the API returns HTTP 402 with the cost and current balance.

    Supported billing currencies: INR, USD, EUR, GBP. Your currency is
    determined by country at signup and cannot be changed. All monetary
    values in API responses are in the smallest currency unit (paise,
    cents, or pence).

    Top up your balance at https://cygn.me/app/enterprise/billing

    ## Webhooks

    Configure a webhook URL in your enterprise settings to receive real-time
    events (api_call.completed, balance.low, balance.depleted, payment.received).
    Events are signed with HMAC-SHA256 using your license key as the secret,
    delivered in the X-Cygn-Signature header.
  contact:
    name: Cygn Enterprise Support
    email: enterprise@cygn.me
    url: https://cygn.me
  license:
    name: Apache 2.0
    url: https://www.apache.org/licenses/LICENSE-2.0

servers:
  - url: https://cygn.me/api/v1
    description: Production

security:
  - cygnAuth: []
  - bearerAuth: []

components:
  securitySchemes:
    cygnAuth:
      type: http
      scheme: Cygn
      description: |
        Cygn-Token — Ed25519-signed JWT proving verified enterprise identity.
        Obtain via GET /enterprise/token or from the API Keys dashboard.
        Header: Authorization: Cygn <token>
    bearerAuth:
      type: http
      scheme: bearer
      description: Legacy API key (e.g. cygn_ent_xxxx)

  schemas:
    Error:
      type: object
      properties:
        error:
          type: string
          description: Human-readable error message
      required: [error]

    InsufficientBalance:
      type: object
      properties:
        error:
          type: string
        balance:
          type: number
          description: Current balance in smallest currency unit (paise/cents/pence)
        cost:
          type: number
          description: Operation cost in smallest currency unit

    DeepfakeResult:
      type: object
      properties:
        success:
          type: boolean
        mediaType:
          type: string
          enum: [image, audio]
        score:
          type: number
          description: 0-100 confidence that content is AI-generated
        verdict:
          type: string
          enum: [likely_authentic, suspicious, likely_ai_generated]
        signals:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
              weight:
                type: number
              description:
                type: string

    VideoDeepfakeResult:
      type: object
      properties:
        success:
          type: boolean
        score:
          type: number
        verdict:
          type: string
          enum: [likely_authentic, suspicious, likely_ai_generated]
        frameAnalysis:
          type: object
        audioAnalysis:
          type: object

    SignedImageResult:
      type: object
      properties:
        success:
          type: boolean
        signedImage:
          type: string
          description: Base64-encoded signed image with C2PA manifest
        mimeType:
          type: string
        provenance:
          type: string
          enum: [captured, cygned]
        enterprise:
          type: object
          properties:
            companyName:
              type: string
            label:
              type: string
              description: e.g. "Captured by Zomato"
        signedAt:
          type: string
          format: date-time

    SignedDocumentResult:
      type: object
      properties:
        success:
          type: boolean
        signedDocument:
          type: string
          description: Base64-encoded signed document
        mimeType:
          type: string
        enterprise:
          type: object
          properties:
            companyName:
              type: string
            label:
              type: string
        signedAt:
          type: string
          format: date-time

    ProvenanceBundleResult:
      type: object
      properties:
        success:
          type: boolean
        signedImage:
          type: string
          description: Base64-encoded C2PA-signed image
        mimeType:
          type: string
        deepfake:
          type: object
          properties:
            score:
              type: number
            verdict:
              type: string
            signals:
              type: array
              items:
                type: object
        enterprise:
          type: object
          properties:
            companyName:
              type: string
            label:
              type: string
            signedAt:
              type: string
              format: date-time
        signedAt:
          type: string
          format: date-time

    SonicEmbedResult:
      type: object
      properties:
        success:
          type: boolean
        watermarkId:
          type: string
        audioHash:
          type: string
        payloadHash:
          type: string
        embeddedAt:
          type: string
          format: date-time
        audio:
          type: string
          description: Base64-encoded watermarked audio

    SonicDetectResult:
      type: object
      properties:
        success:
          type: boolean
        detected:
          type: boolean
        watermarkId:
          type: string
        did:
          type: string
        confidence:
          type: number

    SonicRegisterResult:
      type: object
      properties:
        success:
          type: boolean
        registrationId:
          type: string
        registeredAt:
          type: string
          format: date-time

    SonicLookupResult:
      type: object
      properties:
        success:
          type: boolean
        found:
          type: boolean
        did:
          type: string
        displayName:
          type: string
        registeredAt:
          type: string
          format: date-time

    VoiceEnrollResult:
      type: object
      properties:
        success:
          type: boolean
        enrollmentId:
          type: string
        did:
          type: string
        enrolledAt:
          type: string
          format: date-time

    VoiceVerifyResult:
      type: object
      properties:
        success:
          type: boolean
        match:
          type: boolean
        confidence:
          type: number
        did:
          type: string

    WebhookEvent:
      type: object
      properties:
        id:
          type: string
        type:
          type: string
          enum: [api_call.completed, balance.low, balance.depleted, payment.received]
        timestamp:
          type: string
          format: date-time
        data:
          type: object

paths:
  /detection/deepfake:
    post:
      operationId: detectDeepfake
      summary: Detect AI-generated content (image or audio)
      description: |
        Analyzes an image or audio file for signs of AI generation.
        Cost: ₹2 (image) / ₹3 (audio) per call.
      tags: [Deepfake Detection]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [content, mediaType]
              properties:
                content:
                  type: string
                  description: Base64-encoded image or audio
                mediaType:
                  type: string
                  enum: [image, audio]
      responses:
        '200':
          description: Detection result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeepfakeResult'
        '401':
          description: Missing or invalid authorization
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '402':
          description: Insufficient balance
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InsufficientBalance'
        '413':
          description: File too large (max 50MB)

  /detection/video-deepfake:
    post:
      operationId: detectVideoDeepfake
      summary: Detect AI-generated video content
      description: |
        Analyzes a video file for deepfake indicators (frame + audio analysis).
        Cost: ₹15 per call.
      tags: [Deepfake Detection]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [content]
              properties:
                content:
                  type: string
                  description: Base64-encoded video
      responses:
        '200':
          description: Detection result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VideoDeepfakeResult'
        '402':
          description: Insufficient balance

  /signing/c2pa:
    post:
      operationId: signImage
      summary: Sign image with C2PA Content Credentials
      description: |
        Embeds C2PA Content Credentials into an image with enterprise branding.
        The signed image contains "Captured by {YourCompany}" provenance.
        Cost: ₹3 per call.
      tags: [C2PA Signing]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [image]
              properties:
                image:
                  type: string
                  description: Base64-encoded image (JPEG, PNG, or WebP)
                mimeType:
                  type: string
                  enum: [image/jpeg, image/png, image/webp]
                  default: image/jpeg
                provenance:
                  type: string
                  enum: [captured, cygned]
                  default: captured
                  description: "'captured' = original photo, 'cygned' = edited/processed"
                title:
                  type: string
                  maxLength: 500
                appName:
                  type: string
                  maxLength: 200
                  description: App name for branding (e.g. "Zomato Camera"). Defaults to company name.
                did:
                  type: string
                  description: Optional DID for the signer
                displayName:
                  type: string
                  description: Optional display name for the signer
      responses:
        '201':
          description: Image signed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SignedImageResult'
        '402':
          description: Insufficient balance
        '413':
          description: File too large (max 50MB)
        '503':
          description: C2PA signing not configured

  /signing/document:
    post:
      operationId: signDocument
      summary: Sign document with C2PA Content Credentials
      description: |
        Signs a document (PDF or image) with C2PA Content Credentials
        and enterprise branding. Cost: ₹5 per call.
      tags: [C2PA Signing]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [document, mimeType]
              properties:
                document:
                  type: string
                  description: Base64-encoded document
                mimeType:
                  type: string
                  enum: [application/pdf, image/jpeg, image/png, image/webp]
                title:
                  type: string
                  maxLength: 500
                appName:
                  type: string
                  maxLength: 200
      responses:
        '201':
          description: Document signed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SignedDocumentResult'
        '402':
          description: Insufficient balance
        '413':
          description: File too large (max 100MB)
        '415':
          description: MIME type not supported for C2PA embedding

  /provenance/bundle:
    post:
      operationId: provenanceBundle
      summary: Deepfake detection + C2PA signing in one call
      description: |
        The "enterprise capture verification" endpoint. Runs deepfake detection
        on an image, then embeds C2PA Content Credentials with enterprise
        branding. Single atomic charge. Cost: ₹8 per call.

        Use case: Camera app captures photo → this endpoint verifies
        authenticity AND adds "Captured by {YourCompany}" provenance.
      tags: [Provenance Bundle]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [image]
              properties:
                image:
                  type: string
                  description: Base64-encoded image
                mimeType:
                  type: string
                  enum: [image/jpeg, image/png, image/webp]
                  default: image/jpeg
                provenance:
                  type: string
                  enum: [captured, cygned]
                  default: captured
                title:
                  type: string
                  maxLength: 500
                appName:
                  type: string
                  maxLength: 200
                skipDeepfakeCheck:
                  type: boolean
                  default: false
                  description: Skip deepfake detection (sign only)
      responses:
        '201':
          description: Bundle processed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProvenanceBundleResult'
        '402':
          description: Insufficient balance
        '413':
          description: File too large (max 50MB)

  /sonic/embed:
    post:
      operationId: sonicEmbed
      summary: Embed invisible audio watermark
      description: |
        Embeds an inaudible Cygn Sonic watermark into audio, linked
        to the signer's DID. Cost: ₹5 per call.
      tags: [Sonic Watermarking]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [audio, did]
              properties:
                audio:
                  type: string
                  description: Base64-encoded audio
                did:
                  type: string
                  description: Signer DID
                displayName:
                  type: string
      responses:
        '201':
          description: Watermark embedded
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SonicEmbedResult'
        '402':
          description: Insufficient balance

  /sonic/detect:
    post:
      operationId: sonicDetect
      summary: Detect audio watermark
      description: |
        Detects Cygn Sonic watermarks in audio files.
        Cost: ₹2 per call.
      tags: [Sonic Watermarking]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [audio]
              properties:
                audio:
                  type: string
                  description: Base64-encoded audio
      responses:
        '200':
          description: Detection result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SonicDetectResult'
        '402':
          description: Insufficient balance

  /sonic/register:
    post:
      operationId: sonicRegister
      summary: Register watermarked audio
      description: |
        Registers a watermarked audio file for provenance tracking.
        Cost: ₹1 per call.
      tags: [Sonic Watermarking]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [watermarkId, did]
              properties:
                watermarkId:
                  type: string
                did:
                  type: string
                displayName:
                  type: string
                audioHash:
                  type: string
      responses:
        '201':
          description: Registration successful
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SonicRegisterResult'
        '402':
          description: Insufficient balance

  /sonic/lookup:
    post:
      operationId: sonicLookup
      summary: Look up watermark signer
      description: |
        Reverse lookup to identify the signer of a watermarked audio file.
        Cost: ₹0.50 per call.
      tags: [Sonic Watermarking]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [watermarkId]
              properties:
                watermarkId:
                  type: string
      responses:
        '200':
          description: Lookup result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SonicLookupResult'
        '402':
          description: Insufficient balance

  /voice-id/enroll:
    post:
      operationId: voiceIdEnroll
      summary: Enroll voice biometric profile
      description: |
        Enrolls a voice sample for speaker identification.
        Cost: ₹10 per call.
      tags: [Voice ID]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [audio, did]
              properties:
                audio:
                  type: string
                  description: Base64-encoded voice sample (10-30 seconds recommended)
                did:
                  type: string
                  description: DID to associate with voice profile
                displayName:
                  type: string
      responses:
        '201':
          description: Enrollment successful
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VoiceEnrollResult'
        '402':
          description: Insufficient balance

  /voice-id/verify:
    post:
      operationId: voiceIdVerify
      summary: Verify speaker identity
      description: |
        Verifies a voice sample against an enrolled biometric profile.
        Cost: ₹3 per call.
      tags: [Voice ID]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [audio, did]
              properties:
                audio:
                  type: string
                  description: Base64-encoded voice sample to verify
                did:
                  type: string
                  description: DID of enrolled voice profile to match against
      responses:
        '200':
          description: Verification result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VoiceVerifyResult'
        '402':
          description: Insufficient balance

  /enterprise/token:
    get:
      operationId: getCygnToken
      summary: Retrieve current Cygn-Token
      description: |
        Returns the current Cygn-Token for this enterprise account.
        Use Bearer auth (API key) to bootstrap your first token.
        Auto-issues a token if none exists yet.
      tags: [Enterprise Identity]
      security:
        - bearerAuth: []
        - cygnAuth: []
      responses:
        '200':
          description: Current Cygn-Token
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  cygnToken:
                    type: string
                    description: Ed25519-signed JWT (Cygn-Token)
                  issuedAt:
                    type: string
                    format: date-time
                  expiresAt:
                    type: string
                    format: date-time
        '401':
          description: Missing or invalid authorization
    post:
      operationId: refreshCygnToken
      summary: Refresh/reissue Cygn-Token
      description: |
        Revokes the current token and issues a new one with updated
        enterprise data (company name, industry, onboarding status).
      tags: [Enterprise Identity]
      security:
        - bearerAuth: []
        - cygnAuth: []
      responses:
        '200':
          description: New Cygn-Token issued
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  cygnToken:
                    type: string
                  issuedAt:
                    type: string
                    format: date-time
                  expiresAt:
                    type: string
                    format: date-time
        '401':
          description: Missing or invalid authorization

  /metered/topup:
    post:
      operationId: meteredTopup
      summary: Create a metered balance top-up order
      description: |
        Initiates a Razorpay payment order to add funds to your metered API balance.
        Minimum top-up varies by account currency: ₹100 (INR), $2 (USD), €2 (EUR), £2 (GBP).
        Amount is specified in the smallest currency unit (paise/cents/pence).
      tags: [Billing]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [amount]
              properties:
                amount:
                  type: integer
                  description: Top-up amount in smallest currency unit (e.g. 10000 = ₹100, 200 = $2)
      responses:
        '200':
          description: Razorpay order created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  orderId:
                    type: string
                    description: Razorpay order ID
                  amount:
                    type: integer
                  currency:
                    type: string
                    enum: [INR, USD, EUR, GBP]
        '400':
          description: Validation failed (amount below minimum)
        '403':
          description: Not a metered-tier license

  /enterprise/signup:
    post:
      operationId: enterpriseSignup
      summary: Create a new enterprise account
      description: |
        Creates a new enterprise account with a metered API license.
        Requires a corporate email (free providers like Gmail are blocked).
        The country field determines the billing currency.
      tags: [Enterprise Management]
      security:
        - {} # Session-based (no API key required)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [companyName, email, contactName]
              properties:
                companyName:
                  type: string
                  minLength: 2
                  maxLength: 200
                email:
                  type: string
                  format: email
                  description: Corporate email address (free providers blocked)
                contactName:
                  type: string
                  minLength: 2
                  maxLength: 100
                country:
                  type: string
                  minLength: 2
                  maxLength: 2
                  default: IN
                  description: ISO 3166-1 alpha-2 country code. Determines billing currency (IN→INR, US/CA→USD, GB→GBP, EU→EUR)
                taxId:
                  type: string
                  maxLength: 50
                  description: Tax identifier — GSTIN (India), VAT number (EU), EIN (US). Optional.
      responses:
        '201':
          description: Enterprise account created
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  licenseKey:
                    type: string
                  country:
                    type: string
                  currency:
                    type: string
                    enum: [INR, USD, EUR, GBP]
        '409':
          description: Enterprise account already exists for this email

  /account/delete:
    delete:
      operationId: deleteAccount
      summary: Delete enterprise account (GDPR right-to-erasure)
      description: |
        Permanently deletes all enterprise data associated with the authenticated session.
        This includes: profile, license key, Cygn-Token, metered balance, usage history,
        payment records, signing records, audit logs, and SSO data.
        Rate-limited to 1 request per 24 hours per email. Irreversible.
      tags: [Enterprise Management]
      security:
        - {} # Session-based authentication
      responses:
        '200':
          description: Account deleted successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  deletedAt:
                    type: string
                    format: date-time
                  message:
                    type: string
        '401':
          description: Authentication required
        '404':
          description: No enterprise account found
        '429':
          description: Deletion already requested (wait 24 hours)

  /account/export:
    get:
      operationId: exportAccountData
      summary: Export all personal data (GDPR Data Portability)
      description: |
        Downloads a JSON file containing all personal data associated with
        the authenticated session. Includes: account profile, enterprise data,
        billing summary, payment history, usage records (12 months), invoices,
        and audit log. Rate-limited to 1 request per hour.
      tags: [Account & Privacy]
      security:
        - {} # Session-based authentication
      responses:
        '200':
          description: JSON data export file
          content:
            application/json:
              schema:
                type: object
                properties:
                  account:
                    type: object
                    properties:
                      email:
                        type: string
                      name:
                        type: string
                      tier:
                        type: string
                      did:
                        type: string
                      createdAt:
                        type: string
                  enterprise:
                    type: object
                    nullable: true
                    description: Enterprise profile (null if not an enterprise account)
                  billing:
                    type: object
                    properties:
                      balance:
                        type: integer
                      totalSpent:
                        type: integer
                      currency:
                        type: string
                  payments:
                    type: array
                    items:
                      type: object
                  usageByMonth:
                    type: object
                    description: Monthly aggregated usage for last 12 months
                  apiCallRecords:
                    type: array
                    items:
                      type: object
                  invoices:
                    type: array
                    items:
                      type: object
                  auditLog:
                    type: array
                    items:
                      type: object
                  exportedAt:
                    type: string
                    format: date-time
          headers:
            Content-Disposition:
              schema:
                type: string
              description: 'attachment; filename="cygn-data-export-{date}.json"'
        '401':
          description: Authentication required
        '429':
          description: Rate limited (1 export per hour)

  /enterprise/invoices:
    get:
      operationId: listInvoices
      summary: List all invoices for the enterprise account
      description: |
        Returns all tax invoices generated for the authenticated enterprise
        account. Invoices are auto-generated on each successful top-up payment.
      tags: [Billing]
      security:
        - bearerAuth: []
        - cygnAuth: []
      responses:
        '200':
          description: Invoice list
          content:
            application/json:
              schema:
                type: object
                properties:
                  invoices:
                    type: array
                    items:
                      type: object
                      properties:
                        invoiceNumber:
                          type: string
                          description: Unique invoice ID (e.g. INV-20260301-a1b2c3d4)
                        paymentId:
                          type: string
                        companyName:
                          type: string
                        taxId:
                          type: string
                        amount:
                          type: integer
                          description: Amount in smallest currency unit
                        currency:
                          type: string
                          enum: [INR, USD, EUR, GBP]
                        status:
                          type: string
                          enum: [paid]
                        createdAt:
                          type: string
                          format: date-time
        '401':
          description: Authentication required
        '404':
          description: No enterprise account found

  /email/unsubscribe:
    get:
      operationId: emailUnsubscribePage
      summary: Render email unsubscribe confirmation page
      description: |
        Renders an HTML page confirming the user has been unsubscribed from
        marketing emails. Requires a valid HMAC-signed token in the query
        string. Transactional emails (account alerts, payment confirmations,
        verification codes) are not affected.
      tags: [Account & Privacy]
      security: []
      parameters:
        - name: token
          in: query
          required: true
          schema:
            type: string
          description: HMAC-signed unsubscribe token from the email footer
      responses:
        '200':
          description: HTML unsubscribe confirmation page
          content:
            text/html:
              schema:
                type: string
    post:
      operationId: emailUnsubscribeOneClick
      summary: One-click email unsubscribe (RFC 8058)
      description: |
        Processes a one-click unsubscribe request per RFC 8058
        (List-Unsubscribe-Post header). Used by email clients that support
        one-click unsubscribe.
      tags: [Account & Privacy]
      security: []
      parameters:
        - name: token
          in: query
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                List-Unsubscribe:
                  type: string
                  enum: [One-Click]
      responses:
        '200':
          description: Unsubscribe processed
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
        '400':
          description: Invalid or missing token

tags:
  - name: Enterprise Identity
    description: Cygn-Token cryptographic identity management
  - name: Enterprise Management
    description: Enterprise account signup, settings, and deletion
  - name: Billing
    description: Metered balance top-up and payment management
  - name: Deepfake Detection
    description: Detect AI-generated images, video, and audio
  - name: C2PA Signing
    description: Embed Content Credentials with enterprise branding
  - name: Provenance Bundle
    description: Combined deepfake detection + C2PA signing
  - name: Sonic Watermarking
    description: Invisible audio watermarking and detection
  - name: Voice ID
    description: Voice biometric enrollment and verification
  - name: Account & Privacy
    description: Data export (GDPR), email preferences, and privacy controls
