---
title: "Upsert a post"
method: POST
path: "/posts"
tags: ["Posts"]
---

# Upsert a post

`POST /posts`

## Request

- Base URL: `https://api.savanto.ai`
- URL: `https://api.savanto.ai/posts`
- Auth: HTTP bearer

Create a new post / knowledge article or update the existing one whose `externalId` matches the body. Embeddings are generated synchronously so the post is immediately retrievable by chat and search. Required: `externalId`, `title`, `content` (markdown or plain text). Optional: `author`, `publishedAt` (ISO 8601), `url`, `tags`, `category`. Use this for help-center articles, FAQ entries, or arbitrary brand content that should be answerable via chat. For bulk backfills prefer `POST /posts/bulk`. Idempotent on `externalId`.

## Request body

- PostInput
  - `id` string, required
  - `title` string, required
  - `content` string
  - `searchableText` string
  - `excerpt` string
  - `postType` string
  - `status` 'publish' | 'draft' | 'private' | 'pending'
  - `authorId` string
  - `authorName` string
  - `publishedAt` string, nullable
  - `categories` string[]
  - `tags` string[]
  - `url` string
  - `featuredImage` string, nullable
  - `source` string
  - `indexStatus` 'active' | 'hidden' | 'disabled'
  - `metadata` object
  - `attributes` object, nullable

## Response `200`

Post upserted successfully

- object
  - `object` 'post', required
  - `requestId` string, required
  - `data` object, required
    - `id` string, required
    - `message` string, required

## Other responses

- `400`: Validation error
- `401`: Unauthorized
- `403`: Forbidden
- `404`: Not found
- `409`: Conflict
- `413`: Payload too large
- `429`: Rate limit exceeded

---

[API](https://skmtc.dev/savanto/apis/savanto-api.md) · [All operations](https://skmtc.dev/savanto/apis/savanto-api/llms.txt) · [OpenAPI document](https://skmtc.dev/savanto/apis/savanto-api/revisions/82437327a878?raw)
