> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zebratruth.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Sign an image or video with Content Credentials

> Attaches a C2PA manifest, signed by one of your registered signers, and stores the
signed copy alongside the untouched original.

**Images** (multipart `file`, or JSON `url`) are signed synchronously and return 200
with a time-limited download link.

**Video** (JSON with `blobPath` from `POST /compliance/media/upload-url`) is signed
asynchronously: the call returns 202 with a `jobId`, credits are reserved rather than
charged, and you poll `GET /media/provenance/jobs/{jobId}`.

A signature is not a compliance approval; `complianceGate.enforced` is always `false`.
Requires the `provenance:sign:invoke` key scope, which is granted to service keys only.




## OpenAPI

````yaml /openapi/zebratruth-v1.yaml post /media/provenance/sign
openapi: 3.1.0
info:
  title: ZebraTruth Compliance API
  version: 1.0.0
  description: >
    AI-powered compliance checking for advertising content. Check text and
    images against

    jurisdiction laws, platform policies, and rights clearance requirements.
    Results include

    embeddable annotations mapped to content locations (text spans, video
    timestamps, image regions).
  contact:
    name: ZebraTruth Support
    email: support@zebratruth.ai
    url: https://developers.zebratruth.ai
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT
servers:
  - url: https://api.zebratruth.ai/v1
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Video Compliance
    description: >
      **Primary endpoint.** Async compliance analysis for video ads. 3-step
      flow:

      get an upload URL, upload the video bytes, submit for analysis. Poll or

      receive an HMAC-signed webhook with the report. Returns timestamped

      findings citing stable evidence IDs (`audio_NNN`, `frame_NNN`, `ocr_NNN`,

      `signal_NNN`). 3-180 second videos. mp4 / webm /

      quicktime. Built for ad agencies and creators.
  - name: Authentication
    description: Validate API keys and check account status
  - name: Compliance
    description: >-
      Run compliance checks on text + image content (sync). For video, use the
      Video Compliance endpoints above.
  - name: Agents
    description: Discover and invoke individual compliance agents
  - name: Jobs
    description: Poll async job status and view execution traces
  - name: Reports
    description: Retrieve and replay compliance reports
  - name: Observability
    description: Request logs and webhook delivery status
  - name: Billing
    description: Subscriptions, plan changes, and credit balance
  - name: Documents
    description: Extract text from PDF and Word documents before or as part of a check
  - name: Check History
    description: List and retrieve past compliance checks for the workspace
  - name: Provenance
    description: Inspect and sign Content Credentials (C2PA) on images and video
  - name: Corpus
    description: >-
      Review queue for proposed changes to the governed legal corpus (dashboard
      session)
  - name: Webhooks
    description: Saved webhook endpoints (dashboard session) and delivery logs
paths:
  /media/provenance/sign:
    post:
      tags:
        - Provenance
      summary: Sign an image or video with Content Credentials
      description: >
        Attaches a C2PA manifest, signed by one of your registered signers, and
        stores the

        signed copy alongside the untouched original.


        **Images** (multipart `file`, or JSON `url`) are signed synchronously
        and return 200

        with a time-limited download link.


        **Video** (JSON with `blobPath` from `POST
        /compliance/media/upload-url`) is signed

        asynchronously: the call returns 202 with a `jobId`, credits are
        reserved rather than

        charged, and you poll `GET /media/provenance/jobs/{jobId}`.


        A signature is not a compliance approval; `complianceGate.enforced` is
        always `false`.

        Requires the `provenance:sign:invoke` key scope, which is granted to
        service keys only.
      operationId: signProvenance
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          schema:
            type: string
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/ProvenanceSignMultipartRequest'
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/ProvenanceSignUrlRequest'
                - $ref: '#/components/schemas/ProvenanceSignVideoRequest'
      responses:
        '200':
          description: Image signed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProvenanceSignImageResponse'
        '202':
          description: Video signing job queued
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProvenanceSignVideoAccepted'
        '400':
          description: >-
            `data.code`: `file_part_missing`, `invalid_request` (the message
            names the field), `url_not_permitted`, `too_many_redirects`,
            `fetch_failed`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CodedError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          description: >-
            `data.code`: `insufficient_credits` (video; also carries `required`
            and `available`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CodedError'
        '403':
          description: >-
            `data.code`: `blob_path_invalid` (video path outside your tenant
            prefix).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CodedError'
        '404':
          description: '`data.code`: `signer_not_found`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CodedError'
        '409':
          description: >-
            `data.code`: `signer_not_active` (carries `signerStatus`),
            `already_signed` (carries `c2paStatus`), `existing_invalid_manifest`
            (carries `c2paStatus` and `resolution`), `queue_authority_changed`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CodedError'
        '413':
          $ref: '#/components/responses/PayloadTooLarge'
        '415':
          $ref: '#/components/responses/UnsupportedMediaType'
        '429':
          $ref: '#/components/responses/RateLimited'
        '502':
          description: >-
            `data.code`: `remote_signer_failed` — your signing endpoint failed.
            Not charged.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CodedError'
        '503':
          description: >-
            `data.code`: `signing_unavailable`, `company_async_cohort_disabled`,
            `source_hash_failed`, `enqueue_failed` (carries `jobId`). All carry
            `retryable: true`. Not charged.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CodedError'
components:
  schemas:
    ProvenanceSignMultipartRequest:
      allOf:
        - $ref: '#/components/schemas/ProvenanceSignOptions'
        - type: object
          required:
            - file
          properties:
            file:
              type: string
              format: binary
              description: >-
                An `image/*` file up to 20 MB. Video must be signed from a
                `blobPath`.
            acknowledgeInvalid:
              type: string
              enum:
                - 'true'
                - 'false'
              description: Form-field spelling of the boolean option.
    ProvenanceSignUrlRequest:
      allOf:
        - $ref: '#/components/schemas/ProvenanceSignOptions'
        - type: object
          required:
            - url
          properties:
            url:
              type: string
              format: uri
              description: Publicly fetchable image URL (up to 20 MB).
    ProvenanceSignVideoRequest:
      allOf:
        - $ref: '#/components/schemas/ProvenanceSignOptions'
        - type: object
          required:
            - blobPath
          properties:
            blobPath:
              type: string
              description: >-
                A video previously uploaded through `POST
                /compliance/media/upload-url`. Must be `.mp4`, `.mov` or `.m4v`.
            jurisdictions:
              type: array
              items:
                type: string
            platforms:
              type: array
              items:
                type: string
            mode:
              type: string
              enum:
                - fast
                - full
            industryOverlays:
              type: array
              items:
                type: string
    ProvenanceSignImageResponse:
      type: object
      required:
        - requestId
        - signedAt
        - signed
        - c2pa
        - signer
        - declared
        - complianceGate
        - source
        - billing
      properties:
        requestId:
          type: string
        signedAt:
          type: string
          format: date-time
        signed:
          type: object
          description: Where the signed copy lives. The original is never mutated.
          properties:
            blobPath:
              type: string
            downloadUrl:
              type: string
              format: uri
              description: >-
                Time-limited download link. Absent when `deliveryPending` is
                `true`.
            expiresAt:
              type: string
              format: date-time
            deliveryPending:
              type: boolean
              description: >-
                The asset was signed and charged but the download link could not
                be minted yet.
            byteSize:
              type: integer
            sha256:
              type: string
        c2pa:
          type: object
          properties:
            status:
              $ref: '#/components/schemas/C2paStatus'
            claimGenerator:
              type: string
              nullable: true
            inspectionError:
              type: string
        signer:
          type: object
          properties:
            signerId:
              type: string
            legalEntity:
              type: string
            algorithm:
              type: string
            certificateFingerprintSha256:
              type: string
        declared:
          type: object
          properties:
            digitalSourceType:
              $ref: '#/components/schemas/DigitalSourceType'
            indicatesGenerativeAi:
              type: boolean
        complianceGate:
          type: object
          description: >-
            A signature is not a compliance approval. `enforced` is always
            `false`.
          properties:
            enforced:
              type: boolean
            note:
              type: string
        source:
          type: object
          properties:
            contentType:
              type: string
            originalByteSize:
              type: integer
            sha256Original:
              type: string
            priorC2paStatus:
              $ref: '#/components/schemas/C2paStatus'
            sourceUrl:
              type: string
              format: uri
        billing:
          $ref: '#/components/schemas/ProvenanceBilling'
    ProvenanceSignVideoAccepted:
      type: object
      required:
        - jobId
        - status
        - estimatedCredits
        - pollUrl
        - submittedAt
      properties:
        jobId:
          type: string
        status:
          type: string
          enum:
            - queued
        estimatedCredits:
          type: number
          description: Reserved at submission; settled when the job finishes.
        pollUrl:
          type: string
          example: /v1/media/provenance/jobs/{jobId}
        submittedAt:
          type: string
          format: date-time
    CodedError:
      type: object
      description: >
        Standard error envelope. `data.code` is the stable discriminator to
        branch on;

        `statusMessage` is the human-readable reason. Unless a response
        documents extra

        named fields, `data` is reduced to `{ code }` before it is sent.
      required:
        - statusCode
        - message
      properties:
        error:
          type: boolean
          example: true
        url:
          type: string
          format: uri
        statusCode:
          type: integer
        statusMessage:
          type: string
        message:
          type: string
        data:
          type: object
          required:
            - code
          properties:
            code:
              type: string
              example: review_scope_not_enabled
          additionalProperties: true
    ProvenanceSignOptions:
      type: object
      required:
        - signerId
        - digitalSourceType
      properties:
        signerId:
          type: string
          description: >-
            A signer registered with `POST /media/provenance/signers` and in
            `active` status.
        digitalSourceType:
          $ref: '#/components/schemas/DigitalSourceType'
        title:
          type: string
          minLength: 1
          maxLength: 200
        acknowledgeInvalid:
          type: boolean
          description: >-
            Required, together with `invalidHandling: replace`, to sign an asset
            that already carries an invalid manifest.
        invalidHandling:
          type: string
          enum:
            - replace
            - reject
    C2paStatus:
      type: string
      enum:
        - present_trusted
        - present_untrusted
        - present_invalid
        - absent
        - unsupported
        - inspection_failed
      description: >
        `present_trusted`: a valid manifest signed by a trusted signer.
        `present_untrusted`:

        valid manifest, signer not on the trust list. `present_invalid`: a
        manifest is present

        but fails validation. `absent`: no manifest. `unsupported`: the format
        cannot carry one.

        `inspection_failed`: the inspection itself errored.
    DigitalSourceType:
      type: string
      enum:
        - digitalCapture
        - computationalCapture
        - humanEdits
        - minorHumanEdits
        - screenCapture
        - digitalArt
        - softwareImage
        - composite
        - trainedAlgorithmicMedia
        - compositeWithTrainedAlgorithmicMedia
        - algorithmicallyEnhanced
        - algorithmicMedia
      description: >
        The IPTC digital source type declared in the signed manifest.
        `trainedAlgorithmicMedia`,

        `compositeWithTrainedAlgorithmicMedia`, `algorithmicMedia` and
        `algorithmicallyEnhanced`

        indicate generative AI.
    ProvenanceBilling:
      type: object
      properties:
        feature:
          type: string
          enum:
            - media.provenance.inspect
            - media.provenance.sign
        creditsCharged:
          type: number
        cached:
          type: boolean
  responses:
    Unauthorized:
      description: |
        Invalid, missing, or expired API key. Standard error envelope —
        read `message` for the human-readable reason.
      content:
        application/json:
          schema:
            type: object
            description: Standard error envelope.
            required:
              - statusCode
              - message
            properties:
              error:
                type: boolean
                example: true
              url:
                type: string
                format: uri
                example: https://api.zebratruth.ai/v1/whoami
              statusCode:
                type: integer
                example: 401
              statusMessage:
                type: string
                example: Invalid API key
              message:
                type: string
                description: Canonical human-readable reason — pattern-match on this field.
                example: Invalid API key
    PayloadTooLarge:
      description: >
        The upload exceeds a limit. `data.code` is one of `document_too_large`,

        `document_too_many_pages`, `document_too_much_text`,
        `text_exceeds_tier_limit`,

        `payload_too_large` (provenance; also carries `limitBytes`),
        `video_upload_too_large`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/CodedError'
    UnsupportedMediaType:
      description: >
        The file type is not accepted. `data.code` is one of
        `document_format_unsupported`

        (legacy `.doc`, unknown signature, or a filename that does not match the
        bytes),

        `document_encrypted`, `unknown_content_type`,
        `video_signing_unavailable`,

        `unsupported_container`.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/CodedError'
    RateLimited:
      description: |
        Per-tenant rate limit exceeded. Read the `Retry-After` header for the
        wait duration. Body is the standard error envelope plus a
        `data` object with limit context.
      headers:
        Retry-After:
          schema:
            type: integer
          description: Seconds to wait before retrying.
      content:
        application/json:
          schema:
            type: object
            description: Standard error envelope with a `data` payload.
            required:
              - statusCode
              - message
            properties:
              error:
                type: boolean
                example: true
              url:
                type: string
                format: uri
              statusCode:
                type: integer
                example: 429
              statusMessage:
                type: string
                example: Rate limit exceeded
              message:
                type: string
                example: Rate limit exceeded
              data:
                type: object
                description: >-
                  Per-error context. On the wire this is reduced to `{ code }`
                  plus, where documented, a small set of named fields.
                properties:
                  retryAfter:
                    type: integer
                    description: >-
                      Mirrors the `Retry-After` header for clients that prefer
                      body fields.
                    example: 5
                  limit:
                    type: integer
                    description: Tenant's requests-per-minute cap.
                    example: 60
                  remaining:
                    type: integer
                    description: Requests remaining in the current window.
                    example: 0
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: |
        API key starting with `zt_live_` (production) or `zt_test_` (sandbox).
        Get your key at https://developers.zebratruth.ai

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.