Working with Drafts
Drafts are a way to manage ideas not yet ready for a post, collaborate with teammates, or stage content for reuse. They support titles, statuses, due dates, user assignment, and media attachments.
Available fields
A draft exposes the following fields:
| Field | Type | Description |
|---|---|---|
id | ID | Unique identifier |
title | String | Optional title (max 100 characters) |
message | String | The draft's text content |
status | DraftStatus | Workflow status: none, in_progress, paused, done, archived |
assignedUser | User | Relation to the assigned user. Select sub-fields (e.g. { id first_name last_name }). The scalar assigned_user_id is only an input argument on draftCreate / draftUpdate, not a selectable field. |
due_date | Date | Optional due date used for sorting |
workspace_id | ID | The workspace the draft belongs to |
attachments | [Attachment] | Media attachments associated with this draft |
createdBy | User | Relation to the user who created the draft. Select sub-fields. |
updated_at | DateTime | ISO 8601 last-modified timestamp |
Create
Only workspace_id is required. All other fields are optional. Attachments can also be provided on create - the input format is the same as for draftUpdate ( see Handling attachments below), minus the " existing-by-id" mode.
mutation draftCreate {
draftCreate(
workspace_id: <WORKSPACE_ID>
title: "Q3 campaign copy"
message: "Here's the draft text for our next campaign."
status: in_progress
assigned_user_id: <USER_ID>
due_date: "2025-06-01"
) {
id
title
message
status
assignedUser {
id
first_name
last_name
}
due_date
}
}
Update
All fields are optional. Only the fields you provide will be changed.
mutation draftUpdate {
draftUpdate(
id: <DRAFT_ID>
title: "Q3 campaign copy - revised"
message: "Updated draft text."
status: done
assigned_user_id: <USER_ID>
due_date: "2025-06-15"
) {
id
title
message
status
assignedUser {
id
first_name
last_name
}
due_date
updated_at
}
}
Draft statuses
The status field reflects the draft's position in your team's workflow. The supported values are:
| Status | Description |
|---|---|
none | No status set (default) |
in_progress | Actively being worked on |
paused | Work temporarily stopped |
done | Ready for review or publishing |
archived | Soft-removed; no longer active |
Handling attachments
Drafts support media attachments. The attachment handling for draftUpdate follows specific rules that differ from posts:
- Passing
null(or omitting theattachmentsfield entirely) leaves existing attachments untouched. - Passing an empty array
[]removes all attachments. - Passing a list is treated as authoritative: any existing attachment not included in the list will be deleted.
There are two modes for items in the list:
| Mode | Required fields | Notes |
|---|---|---|
| New attachment | category, source_uploaded | Adds a new attachment |
| Existing attachment | id only | Keeps it in place; category and source_uploaded are not accepted and will cause an error |
Adding an attachment to a draft
mutation draftAddAttachment {
draftUpdate(
id: <DRAFT_ID>
attachments: [
{
category: photo
source_uploaded: "<PUBLIC_URL_TO_IMAGE>"
}
]
) {
id
attachments {
id
source_uploaded
}
}
}
Keeping existing attachments while adding a new one
Fetch the current attachment IDs first, then pass them alongside the new item. Omitting an existing attachment will delete it.
mutation draftAddSecondAttachment {
draftUpdate(
id: <DRAFT_ID>
attachments: [
{ id: <EXISTING_ATTACHMENT_ID> } # kept as-is
{
category: photo
source_uploaded: "<PUBLIC_URL_TO_NEW_IMAGE>"
}
]
) {
id
attachments {
id
source_uploaded
}
}
}
Removing all attachments
mutation draftRemoveAttachments {
draftUpdate(
id: <DRAFT_ID>
attachments: []
) {
id
attachments {
id
}
}
}
Finding a user ID for assignment
The assigned_user_id field requires a user ID, which is not visible in the Swat.io UI. Use the me query to retrieve the ID of the currently authenticated user:
query whoAmI {
me {
id
email
first_name
last_name
}
}
For a full example including curl, see User ID in the API overview.
List
Returns up to 50 drafts per call, sorted by last-updated descending.
query draftList {
draftList(workspace_id: <WORKSPACE_ID>) {
drafts {
id
message
updated_at
}
pagination {
after
}
}
}
Filtering and sorting drafts
draftList accepts the following optional arguments in addition to workspace_id:
| Argument | Type | Notes |
|---|---|---|
status | DraftStatus | Filter by workflow status. |
assigned_to_me | Boolean | Only drafts assigned to the authenticated user. |
date_from | Date | Only drafts with a due_date from this date on. Must be used together with date_to. |
date_to | Date | Only drafts with a due_date up to this date. Must be used together with date_from. |
search_term | String | Full-text search in the draft's title. |
sort | DraftSortEnum | Sort by updated_at (default) or due_date. |
order | SortOrderEnum | Sort direction. Defaults to desc. |
Paginating through drafts
Use the pagination.after token from the previous response to fetch the next page. Pass the same workspace_id each time.
query draftListNextPage {
draftList(
workspace_id: <WORKSPACE_ID>
after: "<AFTER_TOKEN>"
) {
drafts {
id
message
}
pagination {
after
}
}
}
When pagination.after is null, you have reached the last page.
Delete
mutation draftDelete {
draftDelete(id: <DRAFT_ID>)
}
Converting a draft into a post
Drafts have no direct "publish" mutation - the intended workflow is to read the draft content and then create a post from it using postCreate. The draft is not automatically deleted and can be reused.
# Step 1 - fetch the draft content
query getDraft {
draftList(workspace_id: <WORKSPACE_ID>) {
drafts {
id
message
}
}
}
# Step 2 - create a post using the draft's message
mutation postFromDraft {
postCreate(
post: {
category: text
channel_id: "<CHANNEL_ID>"
publication_at: "2024-04-16T14:00:00+02:00"
message: "<MESSAGE FROM DRAFT>"
status: suggested
}
) {
id
status
}
}
# Step 3 - optionally delete the draft once the post is created
mutation draftCleanup {
draftDelete(id: <DRAFT_ID>)
}
