Posts
Post type compatibility
The API uses the same postCreate mutation for all channel types, but not every post type is supported on every channel. Submitting an unsupported combination will result in a validation error.
| Channel | text | link | photo | video | document |
|---|---|---|---|---|---|
| ✅ | ✅ | ✅ | ✅ | ❌ | |
| ❌ | ❌ | ✅ | ✅ | ❌ | |
| ✅ | ✅ | ✅ | ✅ | ✅ | |
| X (Twitter) | ✅ | ✅ | ✅ | ✅ | ❌ |
| Bluesky | ✅ | ✅ | ✅ | ✅ | ❌ |
| Threads | ✅ | ✅ | ✅ | ✅ | ❌ |
| YouTube | ❌ | ❌ | ❌ | ✅ | ❌ |
| ❌ | ❌ | ✅ | ✅ | ❌ | |
| TikTok | ❌ | ❌ | ✅ | ✅ | ❌ |
Notes:
documentposts (PDF) are LinkedIn-only.- On Facebook and Instagram, a
photopost with multiple attachments is published as a carousel. - GIFs are submitted as
category: photo. - YouTube Shorts are published as
category: video. - Instagram
videoposts are published as Reels. - Swat.io can only offer what the platform's official API supports - native platform capabilities may not always be available.
For the full breakdown including Stories, Reels, and other platform-specific formats, see the Supported Post Types help article.
Available fields
A post exposes the following fields:
| Field | Type | Description |
|---|---|---|
id | ID | Unique identifier |
category | PostCategory | Post type: text, link, photo, video, document |
channel_id | ID | Target channel |
publication_at | DateTime | ISO 8601 scheduled publish time |
message | String | Post text content |
status | PostStatus | Workflow status: suggested, approved, posted |
attachments | [Attachment] | Media attachments associated with this post |
network_created | DateTime | ISO 8601 creation timestamp on the social network |
modified | DateTime | ISO 8601 last-modified timestamp |
Creating Posts
Creating a post is part of the Swat.io Publisher. There are different post types, available, so pick the one needed.
In addition to the category-specific fields shown below, a few commonly useful optional fields apply to every postCreate call:
| Field | Type | Notes |
|---|---|---|
assigned_user_id | ID | Defaults to the token user; a post must always be assigned. |
campaign_id | ID | Link the post to a campaign. See Campaigns. |
tags | [String!] | Tags to apply on create. On update, manage tags via postsAddTag / postsRemoveTag. |
title | String | Video posts only; max 1024 bytes. |
autopublish | Boolean | If true, the post is auto-published once status is approved. Defaults to the channel's setting. |
Text Post
mutation postCreateText {
postCreate(
post: {
category: text
channel_id: "<CHANNEL ID>"
publication_at: "2024-04-04T14:00:00+02:00"
message: "this is the text of the post"
status: suggested
}
) {
id
}
}
Link Post
Link posts treat the link as an attachment.
mutation postCreateLink {
postCreate(
post: {
category: link
status: suggested
channel_id: <CHANNEL ID>
publication_at: "2024-09-09T08:35:10+02:00"
message: "a link post"
attachments: [
{
category: link
link: "https://swat.io"
}
]
}
) {
id
attachments {
id
}
}
}
Photo Post
mutation postCreatePhoto {
postCreate(
post: {
category: photo
channel_id: "<CHANNEL ID>"
publication_at: "2024-04-16T14:00:00+02:00"
message: "Look at this photo"
status: suggested
attachments: [
{
category: photo
source_uploaded: "<URL>" # URL from S3 upload
}
]
}
) {
id
}
}
Video post
mutation postCreateVideo {
postCreate(
post: {
category: video
channel_id: "<CHANNEL ID>"
publication_at: "2024-11-05T14:00:00+02:00"
message: "this is the text of the post"
status: suggested
attachments: {
category: video
source_uploaded: "<PUBLIC_URL_TO_VIDEO>"
source_alternate_uploaded: "<OPTIONAL_PUBLIC_URL_TO_VIDEO_THUMBNAIL>"
}
}
) {
id
attachments {
id
}
}
}
Document post (PDF) for LinkedIn
mutation postCreateDocument {
postCreate(
post: {
category: document
channel_id: "<CHANNEL ID>"
publication_at: "2024-04-16T14:00:00+02:00"
message: "this is the text of the post"
status: suggested
attachments: {
category: document
title: "This is a PDF document"
source_uploaded: "<PUBLIC_URL_TO_PDF>"
}
}
) {
id
attachments {
id
}
}
}
Updating Posts
The same requirements regarding the post fields as outlined in Creating Posts apply. Additionally: the attachments.category must also be sent.
Tips
When providing the attachments field, all attachments must be provided. Attachments missing will be removed, superfluous attachments will be added. See Handling Attachments below.
mutation postUpdatePhoto {
postUpdate(
id: <POST_ID>
post: {
id: <POST_ID>
channel_id: <CHANNEL_ID>
publication_at: "2024-04-16T14:00:00+02:00"
attachments: [
{
category: photo
source_uploaded: "<PUBLIC_URL_TO_IMAGE>"
}
]
}
) {
id
attachments {
id
source_uploaded
}
}
}
Tags on update
The tags field is only available on postCreate. To add or remove tags on an existing post, use postsAddTag / postsRemoveTag (see Core Resources).
Simple updates (status, assignee, publication time)
For straightforward changes - reassigning, re-scheduling, or flipping the status - postUpdate also accepts those fields at the top level, without the post: { … } input. The two modes cannot be combined.
mutation quickUpdate {
postUpdate(
id: <POST_ID>
status: approved
assigned_user_id: <USER_ID>
publication_at: "2024-04-16T14:00:00+02:00"
) {
id
status
}
}
Duplicating a post
To duplicate a post and its assets to another channel, we provide the postDuplicate mutation with these minimum required fields:
mutation postDuplicate {
postDuplicate(
# Required args
id: <POST_ID>
channel_id: <CHANNEL_ID>
assigned_user_id: <USER_ID>
publication_at: "2026-03-03T15:00:00"
# Optional args
status: approved
) {
id
attachments {
id
}
}
}
In addition to take care of also duplicating the assets, another benefit is that internally Swatio “relates” the duplicated posts to each other, which then can be seen in the Publisher.
The assets returned, and their URL they’re pointing too, will for a brief moment still reference the original ones from the original post, as the procedure to duplicate them and update the attachments happens in the background. After a brief moment (depending on the asset size), a re-fetch of the assets will show that the URLs have changed.
Compatibility between channel types
Duplicating is supported to any existing channel type.
However, due to the capabilities of individual channel types, it’s not possible in all cases to preserve all the attributes of a post or its asset. As an extreme example: when you duplicate a Facebook photo posts to a YouTube channel, the asset are in fact not duplicated but ignored: the post on the YouTube channel will have no assets attached, hence it also won’t be possible.
By default, duplicated posts are moved into status=suggested. It is possible to explicitly provide status: approved for the mutation, but if the system detects a necessary change (see above: loss of required media or other attributes of a post not making it eligible for publishing, etc.) the status of the post will be reverted back to suggested. In such a case it’s recommended to verify the returned posts status to detect this.
Handling Attachments
There are no dedicated endpoints to create/update/delete attachments, as they’re only relevant in the context of a post and therefore are part of the postCreate and postUpdate APIs.
If you want to work with the Library, please refer the the Asset Library Section.
That is, in both cases the arguments for the attachments can be passed to mutation. The minimum required fields are category and source_uploaded. Depending on the type of attachment, other fields might be relevant too (e.g. source_alternate_uploaded for Thumbnails of videos, iff supported by the respective social media network).
The field source_uploaded must be a publicly available URL of the resource. Once the attachment is created in Swat.io, with a short delay a background job will upload those attachments to Swat.ios own AWS S3 bucket storage. Therefore these URLs will change at least once.
Especially when updating a post, the absence & presence of the attachments fields and their contents will determine if attachments are created, updated, or deleted.
When providing attachments again (for re-ordering or updating non-URL information, like link text etc.) please make sure to fetch the post attachments after they’ve been first created so the Swat.io specific AWS S3 bucket URL is provided again. Otherwise, the system will detect the field source_uploaded as changed and will re-upload the attachment every time it is changed
Examples:
Assume we have a post with a single attachment
mutation postCreatePhoto { postCreate( post: { category: photo channel_id: "<CHANNEL ID>" publication_at: "2024-04-16T14:00:00+02:00" message: "this is the text of the post" status: suggested attachments: { category: photo source_uploaded: "<PUBLIC_URL_TO_IMAGE>" } } ) { id attachments { id } } }To delete all attachments, provide an empty array:
mutation postUpdatePhoto { postUpdate( id: <POST_ID> post: { id: <POST_ID> channel_id: <CHANNEL_ID> publication_at: "2024-04-16T14:00:00+02:00" attachments: [] } ) { id attachments { id source_uploaded } } }
Uploading assets for attachments to our S3 bucket
Attachments provided to our postCreate or postUpdate mutations pointing to external resources are automatically fetched and stored in our bucket. This requires that the provided URL is publicly reachable.
Should this not be possible, assets can be directly uploaded to our bucket using Browser-Based Upload using HTTP POST by requesting a pre-signed HTTP POST request URL and it’s form fields using the special mutation attachmentRegisterS3Upload . The upload size is limited to 500MiB.
This endpoint requires the content type / mime type provided (the the final upload URLs file extension will be based on this) and a post ID for which the attachments is designated. The post has to exist already in swatio (e.g. via the postCreate endpoint) and the returned URL (url_final, after it’s uploaded) can be used for the postUpdate attachments endpoint:
mutation {
attachmentRegisterS3Upload(
content_type: "content / mime type" # image/jpeg , image/png, video/mp4, etc.
post_id: <post ID>
) {
curl_command
url_upload
url_final
parameters {
acl
cache_control
content_disposition
content_type
key
policy
x_amz_algorithm
x_amz_credential
x_amz_date
x_amz_signature
}
}
}
This creates a pre-signed POST request for our S3 bucket, which is valid for 1 hour to upload the attachments binary data (image, video, etc.).
This command returns:
- the
url_upload, the URL to send thePOSTrequest to - that URL accepts a “multipart/form-data” request and requires all the
parametersbeing sent with the form
::: hint
Everywhere the parameters have an underscore (_) in their name, it needs to be replaced with (-)
:::
To ease testing, a curl_command field is provided, which can also be used “as is” for testing. We do not recommend using this for production, but rather as a debug tool to assist in case the custom implementation has issues. Here’s a nicer formatted version on how what the curl command does:
curl \
-X POST https://posts-swat-io.s3.eu-central-1.amazonaws.com \
-F Cache-Control=max-age=31536000 \
-F Content-Disposition=attachment \
-F Content-Type=image/jpeg \
-F acl=public-read \
-F key=uploads/e27f4ae6f5d94d583ae1d69443738bad51006a8aeb7c05ac8bf021e93d95d80d.jpg \
-F policy=… \
-F x-amz-algorithm=AWS4-HMAC-SHA256 \
-F x-amz-credential=…/…/eu-central-1/s3/aws4_request \
-F x-amz-date=… \
-F x-amz-signature=… \
-F file=@<path to your local file>
Please replace @file with the path to your local file, e.g. -F file=@assets/images/file.jpg.
Warning
Do not re-use the same uploaded assets with different posts: only ever use on asset with one post! If you need to need the same asset multiple times, please re-upload it again.
Fetching posts
Fetch a single post
query post {
post(id: <POST_ID>) {
id
category
publication_at
message
status
attachments {
id
source_uploaded
}
}
}
Fetch posts by IDs
This allows efficient fetching of known posts by their IDs:
- Maximum allowed
post_ids: 50 - All posts must be from the same workspace (otherwise the whole query aborts with an error)
- posts not found or with insufficient permissions are simply not returned in the result
query calendarPosts {
calendarPosts(workspace_id: <WORKSPACE_ID>, post_ids: [<POST_ID_1>, <POST_ID_2>, …]) {
id
category
publication_at
message
status
attachments {
id
source_uploaded
}
}
}
Fetch posts from a channel using the postList query
This allows to iterate / synchronize posts in the publisher.
- the query returns up to 50 posts per call (can be lowered via the optional
limitarg; max 50) - it provides a pagination token for the next page
Query example:
query list {
postList(
workspace_id: <WORKSPACE_ID>
channel_ids: [CHANNEL_ID]
order: asc # default `desc`
date_from: …
date_to: …
) {
posts {
id
publication_at
}
pagination {
after
}
}
}
To fetch the next posts, re-use the pagination.after token and feed it into the next call:
query list {
postList(
workspace_id: <WORKSPACE_ID>
channel_ids: [CHANNEL_ID]
order: asc # default `desc`
date_from: …
date_to: …
after: "<AFTER_TOKEN>"
) {
posts {
id
publication_at
}
pagination {
after
}
}
}
For pagination, it is important provide the exact same arguments to the query which were used before to retrieve the after pagination token!
Filtering by post status
To retrieve only posts with specific statuses, pass the optional post_statuses argument. Omitting it returns posts of all statuses.
query listPosted {
postList(
workspace_id: <WORKSPACE_ID>
channel_ids: [<CHANNEL_ID>]
post_statuses: [posted, approved]
) {
posts {
id
publication_at
status
}
pagination {
after
}
}
}
Tips
post_statuses accepts one or more of suggested, approved, posted. When absent, posts of all statuses are returned.
Changing post status
A post's status can be updated independently using postUpdate. The available statuses are suggested, approved, and rejected.
mutation approvePost {
postUpdate(
id: <POST_ID>
post: {
id: <POST_ID>
channel_id: <CHANNEL_ID>
publication_at: "2024-04-16T14:00:00+02:00"
status: approved
}
) {
id
status
}
}
Deleting a post
mutation deletePost {
postDelete(id: <POST_ID>)
}
Warning
Deleted posts cannot be recovered. Make sure you are targeting the correct post ID before calling this mutation.
