# Create Articles

Programmatically publish an author’s content and ensure it surfaces in target users’ apps (create → attach media → target → publish → verify).

The Content API, Channels API, and Feeds API work together to deliver content to users.

* The Content API creates and publishes posts.
* The Channels API defines where that content goes by organizing audiences and topics.
* Once published, the Feeds API makes sure each user sees the right content in their personal feed based on their memberships and permissions.


Created articles are always created as a draft, and need another step to publish them (`PUT` request).

## Create a Basic Article

This example shows how to create and publish an article using the Firstup API. This simulates how a publisher or admin might programmatically push content to the platform.

**Before You Start**

* You have an access token with `content.write` scope. See our [Authorization](/authorization/authenticationandauthorization) guidance.
* You have at least one topic (channel) to publish content to.
* A sample test user account who follows the same test topic for verifying feed visibility via the Feeds API (Feeds are user-personalized). JWT Bearer auth is available for Feeds integrations when you need server-to-server feed checks.


**Workflow Overview**

1. Use `GET /v2/channels` to list your available topics (channels) and pass the `channel_id`.
2. Use `POST /v2/content` to create a draft article and link it to the topic by passing the  `channel_id`.
3. Use `PUT /v2/content/{content_id}/publish` to publish your drafted article.
4. Use `GET /v2/feed/posts` to varify the published content. Note: You need to authenticate as the test user. Alternatively, use `GET /v2/content` with the `filter.channels.id` set to the topic used to publish the content.


**Endpoints**

GET /v2/channels
POST /v2/content
PUT /v2/content/{content_id}/publish
GET /v2/feed/posts
GET /v2/content
### Step 1: List Available Topics

Before publishing, get the list of topics to find where your content should go.


```
GET /v2/channels
Authorization: Bearer YOUR_ACCESS_TOKEN
```

Pick a `channel_id` from the response to pass in the next step.

**Example response**


```
{
    "data": [
        {
            "id": "3811518",
            "name": "My Feed",
            "description": "This is your program's default topic. Content published to this topic appears in every user's personalized feed.",
            "state": "hidden",
            "auto_publish": false,
            "recommended": false,
            "targeted": false,
            "user_submittable": true,
            "groups": [],
            "contributor_count": 1,
            "contributors": [
                18366046
            ],
            "auto_follow": false,
            "follower_count": 0,
            "created_at": "2021-10-12T22:37:01.170Z",
            "updated_at": "2025-10-27T09:41:19.181Z"
        },
        {
            "id": "4344268",
            "name": "🗞️ Business News Updated! 🗞️",
            "description": "News from around the world!",
            "state": "published",
            "auto_publish": false,
            "recommended": false,
            "targeted": false,
            "user_submittable": true,
            "groups": [],
            "contributor_count": 3,
            "contributors": [
                19957904,
                18366046,
                20161654
            ],
            "auto_follow": false,
            "follower_count": 5,
            "created_at": "2022-02-07T18:46:10.820Z",
            "updated_at": "2024-02-21T20:14:56.476Z"
        },
        {
            "id": "7538568",
            "name": "! Important News !",
            "description": "Important News",
            "state": "published",
            "auto_publish": false,
            "recommended": false,
            "targeted": false,
            "user_submittable": false,
            "groups": [],
            "contributor_count": 0,
            "contributors": [],
            "auto_follow": false,
            "follower_count": 5,
            "created_at": "2024-02-21T19:53:22.542Z",
            "updated_at": "2025-05-12T14:08:49.597Z"
        },
        {
            "id": "7538468",
            "name": "1 Sales Department",
            "description": "Sales Department",
            "state": "published",
            "auto_publish": false,
            "recommended": false,
            "targeted": false,
            "user_submittable": false,
            "groups": [],
            "contributor_count": 0,
            "contributors": [],
            "auto_follow": false,
            "follower_count": 5,
            "created_at": "2024-02-21T19:52:27.713Z",
            "updated_at": "2024-02-21T19:59:12.825Z"
        },
        {
            "id": "7538518",
            "name": "2 Marketing Department",
            "description": "Marketing Department",
            "state": "published",
            "auto_publish": false,
            "recommended": false,
            "targeted": false,
            "user_submittable": false,
            "groups": [],
            "contributor_count": 0,
            "contributors": [],
            "auto_follow": false,
            "follower_count": 5,
            "created_at": "2024-02-21T19:52:58.120Z",
            "updated_at": "2024-02-21T19:59:18.582Z"
        },
        {
            "id": "7538618",
            "name": "3 Training Department",
            "description": "Training Department",
            "state": "published",
            "auto_publish": false,
            "recommended": false,
            "targeted": false,
            "user_submittable": false,
            "groups": [],
            "contributor_count": 0,
            "contributors": [],
            "auto_follow": false,
            "follower_count": 5,
            "created_at": "2024-02-21T19:54:03.145Z",
            "updated_at": "2024-02-21T19:59:26.047Z"
        },
        {
            "id": "6346568",
            "name": "BlockHeads",
            "description": "The home of original Block Heads Content!",
            "state": "published",
            "auto_publish": false,
            "recommended": false,
            "targeted": true,
            "user_submittable": true,
            "groups": [
                "justjoeca",
                "justjoe",
                "block_heads"
            ],
            "contributor_count": 0,
            "contributors": [],
            "auto_follow": false,
            "follower_count": 1,
            "created_at": "2023-02-08T04:57:39.297Z",
            "updated_at": "2024-08-11T08:41:43.742Z"
        },
        {
            "id": "6346518",
            "name": "Block Heads",
            "description": "The home of original Block Heads Content!",
            "state": "archived",
            "auto_publish": false,
            "recommended": false,
            "targeted": true,
            "user_submittable": false,
            "groups": [
                "justjoeca",
                "justjoe"
            ],
            "contributor_count": 0,
            "contributors": [],
            "auto_follow": false,
            "follower_count": 0,
            "created_at": "2023-02-08T04:57:31.023Z",
            "updated_at": "2023-02-08T04:58:24.375Z"
        },
        {
            "id": "5517818",
            "name": "Company News",
            "description": "asdf",
            "state": "published",
            "auto_publish": false,
            "recommended": false,
            "targeted": true,
            "user_submittable": false,
            "groups": [
                "copy_and_paste_example"
            ],
            "contributor_count": 0,
            "contributors": [],
            "auto_follow": false,
            "follower_count": 1,
            "created_at": "2022-09-08T21:28:12.008Z",
            "updated_at": "2025-09-12T09:56:04.338Z"
        },
        {
            "id": "5596518",
            "name": "Company Updates",
            "description": "Company updates",
            "state": "published",
            "auto_publish": false,
            "recommended": true,
            "targeted": false,
            "user_submittable": false,
            "groups": [],
            "contributor_count": 0,
            "contributors": [],
            "auto_follow": false,
            "follower_count": 20,
            "created_at": "2022-09-21T14:57:28.346Z",
            "updated_at": "2025-10-16T13:33:14.425Z"
        }
    ],
    "meta": {
        "pagination": {
            "page": 1,
            "page_size": 10,
            "total_records": 56
        },
        "filter": {}
    }
}
```

### Step 2: Create a Draft Article

Submit the content as a draft:

* Creating your article as a draft is the default and only way. Publishing this requires another endpoint.
* This gives you a chance to review or edit before publishing.



```
POST /v2/content
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
```

**Example request**


```
{
  "content_type": "article",
  "title": "How to Automate Engagement Campaigns",
  "body": "<p>Engagement starts with relevance...</p>",
  "summary": "Learn how to programmatically publish content using Firstup.",
  "visibility": "private",
  "channels": [
    { "id": 123 } 
  ],
  "featured": false
}
```

The response contains the `content_id` of your draft article. Pass this into the next step.

### Step 3: Publish the Article

Once you're ready, move the article from `draft` to `published`.


```
PUT /v2/content/{content_id}/publish
Authorization: Bearer YOUR_ACCESS_TOKEN
```

The article is now live and visible according to its topic targeting and visibility settings.

### Step 4: Verify (optional)

Either authenticate as a test user who follows the topic you've posted to, and make a `GET` request to their feed:


```
GET /v2/feed/posts
Authorization: Bearer YOUR_ACCESS_TOKEN
```

Or, make a `GET` request as your user account to the content API.


```
GET /v2/content?filter.channels.id=123&page.size=10&page.number=1
Authorization: Bearer YOUR_ACCESS_TOKEN
```

## Attach an Image

This example shows how to create an article and attach an image to the body.

* Add up to 10 images per article.
* Images are not resized or hosted. Make sure they are production ready.
* You can update the article's body to explicitly reference or embed the image.


**Before You Start**

* You have an access token with `content.write` scope. See our [Authorization](/authorization/authenticationandauthorization) guidance.
* An image URL hosted on a public CDN or trusted server.
* You have at least one topic (channel) to publish content to.


**Workflow Overview**

1. Use `GET /v2/channels` to list your available topics (channels) and pass the `channel_id`.
2. Use `POST /v2/content` to create a draft article and link it to the topic by passing the  `channel_id`.
3. Use `PUT /v2/content/{content_id}/images` to add an image to your drafted article.
4. Use `PUT /v2/content/{content_id}/publish` to publish your drafted article.


**Endpoints**

GET /v2/channels
POST /v2/content
PUT /v2/content/{content_id}/images
PUT /v2/content/{content_id}/publish
### Step 1: List Available Topics

Before publishing, get the list of topics to find where your content should go.


```
GET /v2/channels
Authorization: Bearer YOUR_ACCESS_TOKEN
```

Pick a `channel_id` from the response to pass in the next step.

### Step 2: Create a Draft Article

Submit the content as a draft:

* Creating your article as a draft is the default and only way. Publishing this requires another endpoint.
* This gives you a chance to review or edit before publishing.



```
POST /v2/content
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
```


```
{
  "content_type": "article",
  "title": "How to Automate Engagement Campaigns",
  "body": "<p>Engagement starts with relevance...</p>",
  "summary": "Learn how to programmatically publish content using Firstup.",
  "visibility": "private",
  "channels": [
    { "id": 123 } 
  ],
  "featured": false
}
```

The response contains the `content_id` of your draft article. Pass this into the next step.

### Step 3: Add an Image to the Article

Now, attach an image using the article’s `content_id`. The image URL must be accessible to Firstup’s servers (e.g., public S3 or CDN link).


```
PUT /v2/content/{content_id}/images
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

{
  "image_url": "https://cdn.example.com/images/secure-api-guide.png"
}
```

If successful, the image is now linked to the article and will be included in the rendered view.

### Optional: Add metadata

Optionally add metadata fields. Examples:

* `featured: true`
* `featured_label: "Editor’s Pick"`


### Step 4: Publish

Once you're ready, move the article from `draft` to `published`.


```
PUT /v2/content/{content_id}/publish
Authorization: Bearer YOUR_ACCESS_TOKEN
```

The article is now live and visible according to its topic targeting and visibility settings.

## Attach a Video

This example shows how to create an article and attach a video to the body.

* You can add multiple videos, but only one at a time.
* Videos are not resized—optimize them for web streaming.
* Use `caption` and `preview_image_url` for accessibility and visual polish.


**Before You Start**

* You have an access token with `content.write` scope. See our [Authorization](/authorization/authenticationandauthorization) guidance.
* A public video URL (e.g., MP4 or embed-safe platform).
* You have at least one topic (channel) to publish content to.


**Workflow Overview**

1. Use `GET /v2/channels` to list your available topics (channels) and pass the `channel_id`.
2. Use `POST /v2/content` to create a draft article and link it to the topic by passing the  `channel_id`.
3. Use `PUT /v2/content/{content_id}/videos` to add a video to your drafted article.
4. Use `PUT /v2/content/{content_id}/publish` to publish your drafted article.


**Endpoints**

GET /v2/channels
POST /v2/content
PUT /v2/content/{content_id}/videos
PUT /v2/content/{content_id}/publish
### Step 1: List Available Topics

Before publishing, get the list of topics to find where your content should go.


```
GET /v2/channels
Authorization: Bearer YOUR_ACCESS_TOKEN
```

Pick a `channel_id` from the response to pass in the next step.

### Step 2: Create a Draft Article

Submit the content as a draft:

* Creating your article as a draft is the default and only way. Publishing this requires another endpoint.
* This gives you a chance to review or edit before publishing.



```
POST /v2/content
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
```


```
{
  "content_type": "article",
  "title": "How to Automate Engagement Campaigns",
  "body": "<p>Engagement starts with relevance...</p>",
  "summary": "Learn how to programmatically publish content using Firstup.",
  "visibility": "private",
  "channels": [
    { "id": 123 } 
  ],
  "featured": false
}
```

The response contains the `content_id` of your draft article. Pass this into the next step.

### Step 3: Add a Video to the Article

Now, attach a video using the article’s `content_id`.


```
PUT /v2/content/{content_id}/videos
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

{
  "video_url": "https://cdn.example.com/videos/ceo-interview.mp4",
  "caption": "CEO Interview: Leading in 2025",
  "preview_image_url": "https://cdn.example.com/images/ceo-video-thumbnail.jpg"
}
```

The response includes an embed_html snippet, which can be added to the article body if desired.

### Optional: Embed

If you want control over where the video appears in the article, insert the embed_html into the body:


```
PUT /v2/content/{content_id}
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

{
  "body": "<p>Watch our CEO talk about leadership:</p><iframe src='...'></iframe>"
}
```

### Step 4: Publish

Once you're ready, move the article from `draft` to `published`.


```
PUT /v2/content/{content_id}/publish
Authorization: Bearer YOUR_ACCESS_TOKEN
```

The article is now live and visible according to its topic targeting and visibility settings.