---
title: "Upload with a token"
method: POST
path: "/media/upload-by-token"
tags: ["Media"]
---

# Upload with a token

`POST /media/upload-by-token`

## Request

- Base URL: `https://api.omnisocials.com/v1`
- URL: `https://api.omnisocials.com/v1/media/upload-by-token`
- Auth: none required

Upload a file to a one-time URL from POST /media/create-upload-url. No API key: the `upload_token` query parameter is the auth, valid once for 10 minutes. Takes the same files as POST /media/upload (images, video, PDF; up to 100 MB). If storage is briefly down (503), the token stays valid and the same request can be sent again.

## Query parameters

- `upload_token` string, required

## Request body `multipart/form-data`

- object
  - `file` string, binary, required: An image, video or PDF, up to 100 MB.
  - `name` string: Optional label so the file is findable by name later.
  - `pdf_mode` 'slides' | 'document': PDF uploads only, as on POST /media/upload.

## Response `201`

The stored file. For an image or video, `data` is the media item. For a PDF the body is a PdfUploadResult.

- union
  - object
    - `id` string
    - `data` Media
      - `id` string: Media ID (use this in post creation)
      - `url` string: Permanent CDN URL for the media
      - `thumbnail_url` string, nullable: CDN URL of the generated preview thumbnail (videos only; null for images, which use `url` directly)
      - `type` 'image' | 'video' | 'document': `document` is a PDF kept as one item (see `pdf`): its id in `media_ids` expands into every page image, in order.
      - `name` string, nullable: Human-readable label set on upload or via PATCH (e.g. "pp-play5get50"). Falls back to the storage filename when unset. Search this with the `search` query param.
      - `folder_id` string, nullable: Id of the folder this file lives in, or null for the root ("All media")
      - `filename` string: Generated storage filename (stable, used internally)
      - `size` string: Human-formatted file size (e.g. "2.50 MB")
      - `status` 'ready' | 'processing' | 'failed': Processing state. 'ready' for normal uploads (usable immediately). Large videos uploaded via URL ingestion (over 100 MB) start as 'processing': poll get media / list media until 'ready' before using them in a post. 'failed' means the source couldn't be fetched or validated.
      - `created_at` string, date-time
      - `pdf` object: Present on a PDF document item (type `document`): the kept file and its rendered `pages` in order. Also present on a slide rendered from a PDF (type `image`): which original file and `page` it came from. The original PDF is kept in storage. When a LinkedIn post's images are exactly the rendered pages of one PDF, in order and unchanged, LinkedIn receives that original file (sharp text, working in-document links, the real download) instead of a document rebuilt from the page images. Opt out per channel with `document_source: "slides"` on the post's `linkedin` / `linkedin_page` options.
        - `id` string: Shared by the document item and every slide split from it.
        - `name` string: Original file name, e.g. "deck.pdf".
        - `url` string: CDN URL of the original PDF.
        - `page` integer: Slides only. 1-based page this slide renders.
        - `total_pages` integer
        - `rendered_pages` integer: Pages turned into page images (max 20).
        - `truncated` boolean: Document items only. True when the file has more pages than were rendered.
        - `pages` object[]: Document items only. The rendered page images, in page order; these are what a post carries.
          - `url` string
          - `width` integer, nullable
          - `height` integer, nullable
    - `status` string
  - PdfUploadResult: Returned when the uploaded file is a PDF. With `pdf_mode: slides` (the default) the server rasterizes each page into an image slide and stores each as its own media item, and keeps the original PDF too. `data` mirrors the FIRST slide (so callers that read a single Media still work); `slides` and `media_ids` carry the whole carousel in page order. Pass ALL of `media_ids` to POST /posts as a carousel. With `pdf_mode: document` the response has no `slides`: `data` is the single document item (type `document`, pages in `data.pdf.pages`) and `media_ids` holds its one id, which expands into every page at post time. On LinkedIn it posts as a swipeable document made from the original PDF file (text stays sharp, links keep working, viewers download the real file); on Instagram, TikTok, Threads and Pinterest as an image carousel. Slides are capped at 20 pages; the original sent to LinkedIn is the whole file.
    - `data` Media
      - `id` string: Media ID (use this in post creation)
      - `url` string: Permanent CDN URL for the media
      - `thumbnail_url` string, nullable: CDN URL of the generated preview thumbnail (videos only; null for images, which use `url` directly)
      - `type` 'image' | 'video' | 'document': `document` is a PDF kept as one item (see `pdf`): its id in `media_ids` expands into every page image, in order.
      - `name` string, nullable: Human-readable label set on upload or via PATCH (e.g. "pp-play5get50"). Falls back to the storage filename when unset. Search this with the `search` query param.
      - `folder_id` string, nullable: Id of the folder this file lives in, or null for the root ("All media")
      - `filename` string: Generated storage filename (stable, used internally)
      - `size` string: Human-formatted file size (e.g. "2.50 MB")
      - `status` 'ready' | 'processing' | 'failed': Processing state. 'ready' for normal uploads (usable immediately). Large videos uploaded via URL ingestion (over 100 MB) start as 'processing': poll get media / list media until 'ready' before using them in a post. 'failed' means the source couldn't be fetched or validated.
      - `created_at` string, date-time
      - `pdf` object: Present on a PDF document item (type `document`): the kept file and its rendered `pages` in order. Also present on a slide rendered from a PDF (type `image`): which original file and `page` it came from. The original PDF is kept in storage. When a LinkedIn post's images are exactly the rendered pages of one PDF, in order and unchanged, LinkedIn receives that original file (sharp text, working in-document links, the real download) instead of a document rebuilt from the page images. Opt out per channel with `document_source: "slides"` on the post's `linkedin` / `linkedin_page` options.
        - `id` string: Shared by the document item and every slide split from it.
        - `name` string: Original file name, e.g. "deck.pdf".
        - `url` string: CDN URL of the original PDF.
        - `page` integer: Slides only. 1-based page this slide renders.
        - `total_pages` integer
        - `rendered_pages` integer: Pages turned into page images (max 20).
        - `truncated` boolean: Document items only. True when the file has more pages than were rendered.
        - `pages` object[]: Document items only. The rendered page images, in page order; these are what a post carries.
          - `url` string
          - `width` integer, nullable
          - `height` integer, nullable
    - `slides` Media[]: Every page as a Media item, in page order.
      - `id` string: Media ID (use this in post creation)
      - `url` string: Permanent CDN URL for the media
      - `thumbnail_url` string, nullable: CDN URL of the generated preview thumbnail (videos only; null for images, which use `url` directly)
      - `type` 'image' | 'video' | 'document': `document` is a PDF kept as one item (see `pdf`): its id in `media_ids` expands into every page image, in order.
      - `name` string, nullable: Human-readable label set on upload or via PATCH (e.g. "pp-play5get50"). Falls back to the storage filename when unset. Search this with the `search` query param.
      - `folder_id` string, nullable: Id of the folder this file lives in, or null for the root ("All media")
      - `filename` string: Generated storage filename (stable, used internally)
      - `size` string: Human-formatted file size (e.g. "2.50 MB")
      - `status` 'ready' | 'processing' | 'failed': Processing state. 'ready' for normal uploads (usable immediately). Large videos uploaded via URL ingestion (over 100 MB) start as 'processing': poll get media / list media until 'ready' before using them in a post. 'failed' means the source couldn't be fetched or validated.
      - `created_at` string, date-time
      - `pdf` object: Present on a PDF document item (type `document`): the kept file and its rendered `pages` in order. Also present on a slide rendered from a PDF (type `image`): which original file and `page` it came from. The original PDF is kept in storage. When a LinkedIn post's images are exactly the rendered pages of one PDF, in order and unchanged, LinkedIn receives that original file (sharp text, working in-document links, the real download) instead of a document rebuilt from the page images. Opt out per channel with `document_source: "slides"` on the post's `linkedin` / `linkedin_page` options.
        - `id` string: Shared by the document item and every slide split from it.
        - `name` string: Original file name, e.g. "deck.pdf".
        - `url` string: CDN URL of the original PDF.
        - `page` integer: Slides only. 1-based page this slide renders.
        - `total_pages` integer
        - `rendered_pages` integer: Pages turned into page images (max 20).
        - `truncated` boolean: Document items only. True when the file has more pages than were rendered.
        - `pages` object[]: Document items only. The rendered page images, in page order; these are what a post carries.
          - `url` string
          - `width` integer, nullable
          - `height` integer, nullable
    - `media_ids` string[]: Slide ids in page order. Pass all of them to POST /posts.
    - `pdf` object
      - `total_pages` integer
      - `rendered_pages` integer
      - `truncated` boolean: True when the PDF had more than 20 pages and only the first 20 became slides. LinkedIn still receives the full original.
      - `id` string: Id of the kept original PDF (shared by every slide's `pdf.id`).
      - `name` string: Original file name, e.g. "deck.pdf".
      - `url` string: CDN URL of the kept original PDF.
      - `size_bytes` integer
    - `compatibility` MediaCompatibility: Whether the file fits the platforms connected to your workspace. `compatible` is true when no connected platform would reject it.
      - `compatible` boolean
      - `warnings` object[]
        - `platform` string
        - `display_name` string
        - `reasons` string[]: e.g. ["max 300 MB"], ["format not supported"]
      - `summary` string, nullable: One-line human summary, or null when fully compatible.
    - `message` string

## Other responses

- `400`: No file, an unsupported or damaged file, or a bad `pdf_mode` (codes `missing_file`, `invalid_file_type`, `corrupt_media`, `upload_error`, `validation_error`).
- `401`: The token is unknown, already used (`invalid_token`) or expired (`token_expired`).
- `503`: `storage_unavailable`: nothing was stored and the token is still valid. Wait for `Retry-After` and send the same request again.

## Changes

- **2026-10-21** `3aff2f2e3458`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/omnisocials/apis/omnisocials-api/changes/media/upload-by-token/post.md)

---

[API](https://skmtc.dev/omnisocials/apis/omnisocials-api.md) - [All operations](https://skmtc.dev/omnisocials/apis/omnisocials-api/llms.txt) - [OpenAPI document](https://skmtc.dev/omnisocials/apis/omnisocials-api/revisions/46e479c09474?raw)
