---
title: "Save draft"
method: POST
path: "/save_draft"
tags: ["Write"]
---

# Save draft

`POST /save_draft`

## Request

- Base URL: `https://pdb.postpen.ai/functions/v1/agent-api`
- URL: `https://pdb.postpen.ai/functions/v1/agent-api/save_draft`
- Auth: HTTP bearer

Saves LinkedIn post text (up to 3,000 characters) as a draft, or updates an existing unpublished post. Without id it creates a draft. With id it updates a draft or a scheduled post that has not been sent; the schedule time and destination stay as they are and nothing is sent to LinkedIn. content is required to create; an update takes content and/or media, and optionally destination, mentions, first_comment, or auto_react. first_comment and auto_react are stored on the post and applied when it is published by schedule_post or publish_now. mentions tags LinkedIn company pages named in the text; people cannot be tagged. destination selects the personal profile or a company page from list_destinations. media attaches images, a video, or a PDF. Each media item has exactly one source: { url } for a public HTTPS file, { upload_id } for a file sent through create_upload, or { data } for a small base64 file up to 4MB. PostPen checks each file and copies it into its own storage. On an update, omitting media keeps existing attachments and media: [] removes them. media_urls and LinkedIn tokens are not accepted.

## Request body

- object
  - `content` string: The post text, up to 3,000 characters (LinkedIn’s limit; longer text is refused). Required to create a draft; optional when id is set and only media or destination changes.
  - `id` string: Draft or scheduled post id to update in place. Omit to create a new draft.
  - `destination` string: Where to post: an id from list_destinations. Each destination has a network (linkedin today). "personal" (the default on a personal draft) is the personal LinkedIn profile; a company page is identified by its id from list_destinations (usually the organization URN). On schedule_post and publish_now, leaving it out keeps a company destination already saved on the draft, and "personal" switches to the profile.
  - `first_comment` string, nullable: Optional comment PostPen adds to the post right after it is published, for example a link. Up to 1,250 characters. null or "" removes a saved one; leaving it out keeps what is saved.
  - `mentions` string[]: Optional LinkedIn company pages to tag, each as a page link such as https://www.linkedin.com/company/acme. The company’s name must appear in content exactly as LinkedIn spells it; that text becomes the clickable tag when the post is published, and if it is missing the error gives the exact spelling. [] removes the tags; leaving it out keeps what is saved. Only company pages can be tagged, not people, and tags are not applied to PDF posts.
  - `auto_react` boolean: Optional. true makes PostPen like the post from the same account right after it is published; false turns that off.
  - `media` object[]: Optional attachments, one source per item: { url }, { upload_id }, or { data, content_type }. Images: jpeg, png, webp, gif, max 5MB, up to 9. Video: mp4, 75KB–200MB, one per post. Document: pdf, max 100MB, one per post (LinkedIn shows it as a swipeable carousel). Types cannot be mixed in one post.
    - `url` string: Public HTTPS URL of the image, mp4, or PDF. PostPen downloads it and stores a copy.
    - `upload_id` string: The upload_id returned by create_upload, after you PUT the file to its upload_url.
    - `data` string: Base64 file bytes (a data: URL is fine) for small files up to 4MB. create_upload handles larger files.
    - `content_type` string: MIME type for data, for example image/png or application/pdf. Optional; PostPen also reads the file header.
    - `file_name` string: Optional file name. For a PDF, LinkedIn shows it as the document title.
    - `type` 'image' | 'video' | 'document': Optional hint. Inferred from the file if omitted. Stored as photo, video, or pdf.

## Response `200`

Success. JSON object; never contains LinkedIn tokens.

- object

## Other responses

- `400`: Invalid input, or a confirm: true guard was not met.
- `401`: Missing or invalid API key.
- `402`: Plan limit reached (for example the Free plan's monthly post cap).
- `403`: Destination not allowed for this account or plan.

---

[API](https://skmtc.dev/postpen/apis/postpen-agent-api.md) · [All operations](https://skmtc.dev/postpen/apis/postpen-agent-api/llms.txt) · [OpenAPI document](https://skmtc.dev/postpen/apis/postpen-agent-api/revisions/2b07a2e1bfe7?raw)
