> ## 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.

# Mint a video upload URL

> Returns a tenant-scoped, short-lived (15 min) SAS URL the client uses
to PUT video bytes **directly to blob storage** — bypassing the API
for the upload itself. The returned `requestId` is the handle for the
subsequent `POST /compliance/check-video` call.

**Why direct upload?** Videos can be up to 200 MB. Streaming them
through the API endpoint would add latency, double bandwidth costs,
and hit serverless function timeout limits. Direct-to-blob upload
is the same pattern major cloud object stores use for large files.

**Allowed content types:** `video/mp4`, `video/webm`, `video/quicktime`.
The duration limits (3–180s) and file-size cap (200 MB) are enforced
by the subsequent `check-video` call's probe step — not at upload time.

**The PUT must include the returned `uploadInstructions.headers`
verbatim** (they carry ownership tags + the storage `x-ms-blob-type` header).




## OpenAPI

````yaml /openapi/zebratruth-v1.yaml post /compliance/media/upload-url
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:
  /compliance/media/upload-url:
    post:
      tags:
        - Video Compliance
      summary: Mint a video upload URL
      description: >
        Returns a tenant-scoped, short-lived (15 min) SAS URL the client uses

        to PUT video bytes **directly to blob storage** — bypassing the API

        for the upload itself. The returned `requestId` is the handle for the

        subsequent `POST /compliance/check-video` call.


        **Why direct upload?** Videos can be up to 200 MB. Streaming them

        through the API endpoint would add latency, double bandwidth costs,

        and hit serverless function timeout limits. Direct-to-blob upload

        is the same pattern major cloud object stores use for large files.


        **Allowed content types:** `video/mp4`, `video/webm`, `video/quicktime`.

        The duration limits (3–180s) and file-size cap (200 MB) are enforced

        by the subsequent `check-video` call's probe step — not at upload time.


        **The PUT must include the returned `uploadInstructions.headers`

        verbatim** (they carry ownership tags + the storage `x-ms-blob-type`
        header).
      operationId: createVideoUploadUrl
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VideoUploadUrlRequest'
      responses:
        '200':
          description: Upload URL minted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VideoUploadUrlResponse'
        '400':
          description: >-
            Invalid contentType (must be video/mp4 | video/webm |
            video/quicktime)
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/InsufficientCredits'
        '403':
          $ref: '#/components/responses/TenantScopeDenied'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  parameters:
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      schema:
        type: string
        format: uuid
      description: >
        Unique request identifier to prevent duplicate execution and
        double-charging.

        Generate a new UUID for each distinct request. Reuse the same key to
        safely retry.
  schemas:
    VideoUploadUrlRequest:
      type: object
      required:
        - contentType
      properties:
        contentType:
          type: string
          description: MIME type of the video being uploaded
          enum:
            - video/mp4
            - video/webm
            - video/quicktime
          example: video/mp4
    VideoUploadUrlResponse:
      type: object
      required:
        - uploadUrl
        - blobPath
        - requestId
        - requiredTags
        - uploadInstructions
      properties:
        uploadUrl:
          type: string
          format: uri
          description: |
            Short-lived (15 min) SAS URL. PUT the raw video bytes here using
            the headers from `uploadInstructions`. Do NOT add auth headers.
          example: >-
            https://zebratruthstorage.blob.core.windows.net/videos/.../source.mp4?...sig=...
        blobPath:
          type: string
          description: >-
            Pass this verbatim as `blobPath` in the subsequent `POST
            /compliance/check-video`.
          example: videos/kp_abc123.../82b68316-.../source.mp4
        requestId:
          type: string
          description: >
            Unique handle for this upload. Pass it verbatim as `requestId` in
            the

            subsequent `POST /compliance/check-video` AND use it to poll

            `GET /compliance/check-video/{requestId}` afterward.
          example: 82b68316-ed85-489c-991e-2ff0f2b95f29
        requiredTags:
          type: object
          description: |
            Blob index tags that the signed URL enforces at upload time.
            Already encoded in `uploadInstructions.headers` — present here for
            inspection / debugging only.
          properties:
            tenantId:
              type: string
            requestId:
              type: string
            kind:
              type: string
              enum:
                - video
        uploadInstructions:
          type: object
          required:
            - method
            - headers
          properties:
            method:
              type: string
              enum:
                - PUT
            headers:
              type: object
              description: >-
                Headers the client MUST include on the PUT to `uploadUrl`.
                Include verbatim.
              additionalProperties:
                type: string
  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
    InsufficientCredits:
      description: >
        Not enough credits in the tenant's current period to process this
        request.

        Standard error envelope plus a `data` object with credit specifics

        and an upgrade URL.
      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: 402
              statusMessage:
                type: string
                example: Insufficient credits
              message:
                type: string
                example: Insufficient credits
              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:
                  creditsRemaining:
                    type: number
                    example: 3
                  creditsNeeded:
                    type: number
                  upgradeUrl:
                    type: string
                    format: uri
                    example: https://developers.zebratruth.ai/dashboard/billing
    TenantScopeDenied:
      description: >
        The request asks for a jurisdiction or platform not in your
        subscription, or your

        tenant record is missing / incomplete. The human-readable reason is in
        the

        `statusMessage` and `message` fields (both carry the same string —
        `message` is

        the canonical field in the error envelope). Five possible messages:


        - **Not onboarded** — no tenant record exists yet.
          `"Tenant not onboarded. Complete setup at https://developers.zebratruth.ai before using the API."`
        - **Incomplete config** — tenant exists but has no default jurisdictions
        or platforms set.
          `"Tenant configuration incomplete. Set default jurisdictions and platforms in your dev portal."`
        - **Jurisdiction not subscribed** — request includes jurisdictions
        outside the subscription.
          `"Requested jurisdictions [india, china] are not in your subscription. Allowed: [us, eu]."`
        - **Platform not subscribed** — request includes platforms outside the
        subscription.
          `"Requested platforms [tiktok] are not in your subscription. Allowed: [youtube, instagram]."`
        - **Scope wider than the plan allows** — carries `data.code:
          "scope_limit_exceeded_config"`, the only one of the five that does.
          `"Your plan allows at most 2 jurisdictions, but your account has 3 selected. Reduce your
          jurisdictions in the developer portal, or request at most 2 on this call."`
          Reachable after a plan change, because a scope chosen during the unlimited trial can be
          wider than the plan you end up on.

        Fix: either adjust the request to stay within the subscribed set, or
        update your

        defaults at `https://developers.zebratruth.ai/dashboard/settings`.
      content:
        application/json:
          schema:
            type: object
            description: >-
              Standard error envelope. Read `message` (or `statusMessage`) for
              the human-readable reason.
            required:
              - statusCode
              - message
            properties:
              error:
                type: boolean
                description: Always `true` for error responses.
                example: true
              url:
                type: string
                format: uri
                description: The URL that was requested.
                example: https://api.zebratruth.ai/v1/compliance/check
              statusCode:
                type: integer
                example: 403
              statusMessage:
                type: string
                description: Human-readable reason — same content as `message`.
                example: >-
                  Requested jurisdictions [india, china] are not in your
                  subscription. Allowed: [us, eu].
              message:
                type: string
                description: >-
                  Canonical human-readable reason — pattern-match on this field
                  in client code.
                example: >-
                  Requested jurisdictions [india, china] are not in your
                  subscription. Allowed: [us, eu].
    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.