# Publish content

URL: https://docs.hanzo.ai/docs/openapi/content/post-content-publish

> Distributes one CMS content item to the channels recorded on it and returns the honest per-channel outcome.

`POST /v1/content/publish`

> [Content →](https://docs.hanzo.ai/docs/openapi/content) · [All API references →](https://docs.hanzo.ai/docs/openapi) · [Get started →](https://docs.hanzo.ai/docs/guides)

|               |                                           |
| ------------- | ----------------------------------------- |
| **Address**   | `https://api.hanzo.ai/v1/content/publish` |
| **Method**    | `POST`                                    |
| **Operation** | `post_content_publish`                    |
| **Auth**      | `Authorization: Bearer $HANZO_API_KEY`    |

Distributes one CMS content item to the channels recorded on it and
returns the honest per-channel outcome. The item names itself — its caption,
media and channel list are read from the stored document, not from this request.
It is idempotent per channel (a channel already posted for this item is skipped),
and a publish that loses the per-item lease to a live publisher answers status
"in\_progress" having posted nothing.

## Request

3 fields, body `application/json` (required).

| Field        | In   | Type   | Required | Description                                                                            |
| ------------ | ---- | ------ | -------- | -------------------------------------------------------------------------------------- |
| `doctype`    | body | string | —        | DocType is the content type holding the item: Campaign, SocialPost or Asset.           |
| `name`       | body | string | —        | Name is the document within that type — the item to distribute.                        |
| `scheduleAt` | body | string | —        | ScheduleAt hands a future go-live to the channel's own scheduler, as an ISO-8601 time. |

## Response

| Status    | Body                  | Meaning |
| --------- | --------------------- | ------- |
| `200`     | content.PublishResult | ok      |
| `default` | problem-details       | refused |

`200` body — 10 fields.

| Field                  | In   | Type                     | Always | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| ---------------------- | ---- | ------------------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `channels`             | body | string\[]                | —      | Channels is the channel list read off the content document — integration ids or provider names, as the item declares them.                                                                                                                                                                                                                                                                                                                                                |
| `externalIds`          | body | object                   | —      | ExternalIDs maps channel id → the post id that channel returned, merged with everything earlier publishes recorded.                                                                                                                                                                                                                                                                                                                                                       |
| `externalIds.*`        | body | string                   | —      |                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `results`              | body | content.ChannelResult\[] | —      | Results is the outcome per channel — which went out, which did not and why — covering the whole fan-out including failures, so partial success is never flattened into one verdict.                                                                                                                                                                                                                                                                                       |
| `results[].channel`    | body | string                   | —      | the social integration id targeted                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `results[].error`      | body | string                   | —      | short reason, when it failed                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `results[].externalId` | body | string                   | —      | social post id, when it went out                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `results[].provider`   | body | string                   | —      | "x" \| "instagram" \| ...                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `results[].status`     | body | string                   | —      | "distributed" \| "scheduled" \| "failed"                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `status`               | body | string                   | —      | Status is the ONE headline, drawn from: "distributed" (something is on record and went out now), "scheduled" (same, handed to the channel's own scheduler for later), "failed" (nothing is on record — this fan-out missed entirely and no earlier one landed), "in\_progress" (another publisher holds the item, so this call posted NOTHING and the caller retries), and "not\_configured" (no distribution edge is wired; a transition records it instead of failing). |

Failure carries the platform error shape — see [Errors](https://docs.hanzo.ai/docs/errors).

## Examples

**CLI**

```bash
hanzo content publish
```

**SDK**

**.ts**

```ts
import { Configuration, ContentApi } from 'hanzoai';

const api = new ContentApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.postContentPublish({ doctype: "<doctype>", name: "<name>" });
```

**.py**

```python
from hanzoai.cloud import ApiClient, Configuration
from hanzoai.cloud.api import ContentApi

client = ApiClient(Configuration(access_token=os.environ["HANZO_API_KEY"]))
result = ContentApi(client).post_content_publish(doctype="<doctype>", name="<name>")
```

**.go**

```go
cfg := hanzoai.NewConfiguration()
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("HANZO_API_KEY"))
client := hanzoai.NewAPIClient(cfg)

resp, _, err := client.ContentAPI.PostContentPublish(context.Background()).Execute()
if err != nil {
	return err
}
```

**.rs**

```rust
use hanzo_client::apis::{configuration::Configuration, content_api};

let mut cfg = Configuration::new();
cfg.bearer_access_token = std::env::var("HANZO_API_KEY").ok();

let result = content_api::post_content_publish(&cfg, Default::default()).await?;
```

**.java**

```java
import ai.hanzo.cloud.ApiClient;
import ai.hanzo.cloud.api.ContentApi;

ApiClient client = new ApiClient();
client.setBearerToken(System.getenv("HANZO_API_KEY"));

var result = new ContentApi(client).postContentPublish();
```

**HTTP**

```bash
curl -X POST https://api.hanzo.ai/v1/content/publish \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
       "doctype": "<doctype>",
       "name": "<name>"
     }'
```

**MCP**

MCP reaches **content** through the `content` tool, which names its 6 operations with its own verbs — this one among them, under a name only MCP declares. `describe` explains any of them:

```bash
curl -X POST https://api.hanzo.ai/v1/mcp \
  -H "Content-Type: application/json" \
  -d '{
       "jsonrpc": "2.0",
       "id": 1,
       "method": "tools/call",
       "params": {
         "name": "describe",
         "arguments": {
           "op": "get_content_board"
         }
       }
     }'
```

---

[Content API](https://docs.hanzo.ai/docs/openapi/content) · [All Hanzo APIs](https://docs.hanzo.ai/docs/openapi) · [Interactive reference](https://docs.hanzo.ai/docs/openapi/reference)
