AI Demos Media API

The media API from AI Demos — 4 operation(s) for media.

Operations 4

POST /v1/media/upload Upload Media #
POST /v1/media/adopt Adopt Media Url #
POST /v1/media/stream-request Request Media Stream #
POST /v1/media/stt-callback Stt Callback #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/aidemos-com:aidemos-com-media-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

aidemos-com-media-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Aidemos Com Media API
  version: 0.1.0
  description: 'Operations tagged media across 2 of this provider''s published API definitions: aidemos-com-openapi.json, mcp-openapi.json. Each path carries the servers of the definition it was published in.'
tags:
- name: Media
paths:
  /v1/media/upload:
    post:
      tags:
      - Media
      summary: Upload Media
      description: 'Accept a single image/audio/video (or pdf) file, host it on S3/CDN, return its media metadata.


        The effective MIME is resolved from the filename extension first (browsers send inconsistent

        `content-type`), falling back to the client-supplied type — so validation can''t be bypassed by a

        wrong header. Size ceiling = settings.max_upload_mb, enforced mid-stream before the S3 PUT.'
      operationId: upload_media_v1_media_upload_post
      parameters:
      - name: authorization
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: Authorization
      - name: x-api-key
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: X-Api-Key
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/Body_upload_media_v1_media_upload_post'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MediaUploadResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /v1/media/adopt:
    post:
      tags:
      - Media
      summary: Adopt Media Url
      description: 'A video with NO identity at all was PLAYED — enrol it in the asset library, then stream it.


        The legacy corpus was uploaded before the asset library existed, so those objects carry no

        `assetId`, no `videoId` and no `streamUrl`: the play trigger has nothing to name and they are

        stuck on raw CDN playback forever (e.g. /tools/parseur). This is the missing first step —

        registration happens lazily, on demand, driven by the same signal that buys the transcode.


        That makes the bulk backfill optional rather than a prerequisite: a video becomes streamable

        the first time somebody actually watches it, and one that nobody opens costs nothing.


        ⛔ Two guards, because this endpoint SPENDS MONEY on a browser-supplied string:

        1. the url must resolve to an object in OUR media bucket (`s3_key_for_cdn_url`), and

        2. it must already appear in stored page content — otherwise a caller could enumerate CDN

        object names and bill us a transcode per guess.

        Registration is idempotent on `source_ref` upstream, so a double-play is cheap, and the

        identity is written back into the page content so the NEXT render streams without re-adopting.'
      operationId: adopt_media_url_v1_media_adopt_post
      parameters:
      - name: authorization
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: Authorization
      - name: x-api-key
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: X-Api-Key
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/Body_adopt_media_url_v1_media_adopt_post'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StreamRequestResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /v1/media/stream-request:
    post:
      tags:
      - Media
      summary: Request Media Stream
      description: 'A video was PLAYED — make sure it is being transcoded for streaming.


        The legacy corpus is registered in the asset library with streaming deferred: an asset costs

        storage until someone watches it, and only then buys a MediaConvert transcode (~$0.24). This is

        that trigger. Idempotent and cheap on the hot path: an asset that is already streaming or

        already transcoding returns its status without asking the asset library to do anything, so the

        expensive branch runs once per video, ever — not once per play.


        ⛔ Service-token guarded (this router), and only for assets THIS service produced. A transcode

        spends money, so an open trigger would be a way to bill us — or another team — by enumerating

        ids. The public site reaches it through its own server-side route, never from the browser.'
      operationId: request_media_stream_v1_media_stream_request_post
      parameters:
      - name: authorization
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: Authorization
      - name: x-api-key
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: X-Api-Key
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/Body_request_media_stream_v1_media_stream_request_post'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StreamRequestResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /v1/media/stt-callback:
    post:
      tags:
      - Media
      summary: Stt Callback
      description: 'The STT gateway delivering a finished transcript.


        Public by necessity — the gateway is a third party — and authenticated by `sig`, an HMAC over

        the object key that only this service can mint. Without it the endpoint would let anyone

        overwrite the transcript any visitor reads.


        A `job.failed` event stores nothing: the pending marker is left to expire so a later play can

        retry, which is the difference between a transient provider failure and a video that genuinely

        has no speech (that one stores `[]` and stops asking).'
      operationId: stt_callback_v1_media_stt_callback_post
      parameters:
      - name: k
        in: query
        required: true
        schema:
          type: string
          description: base64url of the media object's S3 key
          title: K
        description: base64url of the media object's S3 key
      - name: sig
        in: query
        required: true
        schema:
          type: string
          description: HMAC of that key, minted when the job was submitted
          title: Sig
        description: HMAC of that key, minted when the job was submitted
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
              title: Payload
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SttCallbackResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
components:
  schemas:
    SttCallbackResponse:
      properties:
        stored:
          type: boolean
          title: Stored
        cues:
          type: integer
          title: Cues
          default: 0
      type: object
      required:
      - stored
      title: SttCallbackResponse
    MediaUploadResponse:
      properties:
        url:
          type: string
          title: Url
        filename:
          type: string
          title: Filename
        kind:
          type: string
          title: Kind
        contentType:
          type: string
          title: Contenttype
        bytes:
          type: integer
          title: Bytes
        width:
          anyOf:
          - type: integer
          - type: 'null'
          title: Width
        height:
          anyOf:
          - type: integer
          - type: 'null'
          title: Height
        streamUrl:
          anyOf:
          - type: string
          - type: 'null'
          title: Streamurl
        assetId:
          anyOf:
          - type: string
          - type: 'null'
          title: Assetid
      type: object
      required:
      - url
      - filename
      - kind
      - contentType
      - bytes
      title: MediaUploadResponse
    Body_adopt_media_url_v1_media_adopt_post:
      properties:
        url:
          type: string
          title: Url
      type: object
      required:
      - url
      title: Body_adopt_media_url_v1_media_adopt_post
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
            - type: string
            - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
        input:
          title: Input
        ctx:
          type: object
          title: Context
      type: object
      required:
      - loc
      - msg
      - type
      title: ValidationError
    Body_request_media_stream_v1_media_stream_request_post:
      properties:
        assetId:
          type: string
          title: Assetid
      type: object
      required:
      - assetId
      title: Body_request_media_stream_v1_media_stream_request_post
    Body_upload_media_v1_media_upload_post:
      properties:
        file:
          type: string
          contentMediaType: application/octet-stream
          title: File
        prefix:
          type: string
          title: Prefix
          default: override
      type: object
      required:
      - file
      title: Body_upload_media_v1_media_upload_post
    StreamRequestResponse:
      properties:
        assetId:
          type: string
          title: Assetid
        streamStatus:
          anyOf:
          - type: string
          - type: 'null'
          title: Streamstatus
        streamUrl:
          anyOf:
          - type: string
          - type: 'null'
          title: Streamurl
        progress:
          anyOf:
          - type: integer
          - type: 'null'
          title: Progress
        intelStage:
          anyOf:
          - type: string
          - type: 'null'
          title: Intelstage
        intelStageIndex:
          anyOf:
          - type: integer
          - type: 'null'
          title: Intelstageindex
        intelStageTotal:
          anyOf:
          - type: integer
          - type: 'null'
          title: Intelstagetotal
        intelStatus:
          anyOf:
          - type: string
          - type: 'null'
          title: Intelstatus
        started:
          type: boolean
          title: Started
          default: false
        indexStarted:
          type: boolean
          title: Indexstarted
          default: false
      type: object
      required:
      - assetId
      title: StreamRequestResponse
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
x-refined-from:
- aidemos-com-openapi.json
- mcp-openapi.json