Skip to content

Managing Posts

Create, list, delete, and reconnect OpenQuok posts/post group from the command line.

8 min read

Connect your agent today

Draft from chat, review in your calendar, and publish only what you approve.

Start for $0

Overview

The posts:* commands wrap the Posts APIs. They drive every step of the publishing lifecycle: pick channels and a schedule time, create a post group, iterate on it, then delete it or reconnect it to its provider-native id once it is published.

Create a post

The simplest case: one channel, one body, one scheduled timestamp. You can use short flags (-c, -s, -i, …) or the long names that match POST /public/posts.

openquok posts:create 
  -c "Hello from OpenQuok" 
  -s "2026-01-15T12:00:00Z" 
  -i "<integration-id>"
FlagDescription
-c --contentPost body. Repeat for thread-style segments; extra segments map to provider replies when supported (see Threads examples). Long form: --body.
-s --dateSchedule time (ISO-8601, required unless -j). Same as --scheduledAt.
-t --typeschedule (default) or draft — maps to API scheduled / draft. Long form: --status scheduled | draft.
-i --integrationsComma-separated channel ids (required unless -j). Same as --integrationIds.
-m --mediaComma-separated storage paths or URLs per flag (the CLI supplies a placeholder media id when you did not run upload). Values from repeated -m are merged into the main post’s media list. Prefer upload JSON; for attachments on individual follow-up lines use Threads examples or -j.
-d --delayMilliseconds between segments when using multiple -c (default 5000); converted to reply delaySeconds.
--settingsPlatform-specific settings JSON; merged into each selected integration.
-j --jsonPath to a JSON file whose root object is the full POST /public/posts body (skips other flags).
--bodiesByIntegrationIdJSON object keyed by integration UUID; each value is the per-channel body override.
--mediaByIntegrationIdJSON object keyed by integration UUID; each value is an array of id and path media items for that channel. Channels omitted in the map inherit -m / root media.
--providerSettingsByIntegrationIdJSON map of provider-specific settings (see CLI Examples).
--tagNamesComma-separated workspace tag names.
--repeatIntervalBackend repeat enum (e.g. weekly).
-n --noteHuman review checklist / todo for the account kanban (agent creates only; max 2000 chars). Same field as posts:review-todo.

Draft instead of scheduling

openquok posts:create 
  -c "Review before publishing" 
  -s "2026-01-15T12:00:00Z" 
  -t draft 
  -i "<integration-id>"

Agent draft with a human review todo

When an AI agent creates a draft for a human to approve, pass --note so the checklist appears on the account kanban (filter Agent). The CLI always sets isAgent: true on create. Update the note later with posts:review-todo; humans clear the agent flag when they mark reviewed in the dashboard.

openquok posts:create 
  -c "Launch week announcement" 
  -s "2026-01-15T12:00:00Z" 
  -t draft 
  -i "<integration-id>" 
  --note "Confirm CTA URL and UTM params before scheduling"

See Update Review Todo and Development environment → Kanban smoke test.

Post for Different Body per Channel

Pass --bodiesByIntegrationId to customize each row; the canonical --body becomes a fallback:

openquok posts:create 
  -s "2026-01-15T12:00:00Z" 
  -i "<threads-integration-id>,<instagram-integration-id>" 
  -c "Fallback body" 
  --bodiesByIntegrationId '{"<threads-integration-id>":"Threads-only caption","<instagram-integration-id>":"Instagram-only caption #photography"}'

Post with different media per channel

Upload assets first, then pass shared defaults with -m and per-channel lists with --mediaByIntegrationId:

THREADS_MEDIA=$(openquok upload ./threads.png | jq -c '{id: .data.id, path: .data.filePath}')
IG_MEDIA=$(openquok upload ./instagram.png | jq -c '{id: .data.id, path: .data.filePath}')

openquok posts:create 
  -s "2026-01-15T12:00:00Z" 
  -c "Same caption everywhere" 
  -i "<threads-integration-id>,<instagram-integration-id>" 
  -m '[{"id":"global","path":"uploads/fallback.png"}]' 
  --mediaByIntegrationId "{"<threads-integration-id>":[${THREADS_MEDIA}],"<instagram-integration-id>":[${IG_MEDIA}]}"

Post and Attach media

Upload the asset first (returns data.id and data.path), then pass them back as JSON:

MEDIA=$(openquok upload ./photo.jpg | jq -c '{id: .data.id, path: .data.filePath}')

openquok posts:create 
  -s "2026-01-15T12:00:00Z" 
  -c "Check this out!" 
  -i "<integration-id>" 
  -m "[${MEDIA}]"

Threads and follow-up comments

Pass -c more than once to build a chain of messages: the first becomes the main body; each extra -c becomes a scheduled follow-up in replies (with message and delaySeconds).

Providers that support that shape (for example Threads) are covered in depth on CLI Examples → Threads.

You can still repeat -m for paths or upload JSON blobs; the CLI collects them into one root-level media array on the post. If a follow-up line needs its own media, build providerSettingsByIntegrationId by hand, use -j with a full JSON body, or follow the jq recipes on the Threads page.

openquok posts:create 
  -c "Thread 1/3" -m "image1.jpg" 
  -c "Thread 2/3" -m "image2.jpg" 
  -c "Thread 3/3" 
  -s "2026-01-15T10:00:00Z" 
  -i "<integration-id>"

Use -d to set the gap in milliseconds between consecutive follow-ups (default 5000 when omitted).

openquok posts:create 
  -c "First segment" 
  -c "Second segment" 
  -c "Third segment" 
  -s "2026-01-15T10:00:00Z" 
  -d 2000 
  -i "<integration-id>"

Multi-channel post (same body)

Send one canonical --body / -c to every channel by listing several integration ids in a single comma-separated -i argument (same wire shape as integrationIds: string[] on POST /public/posts).

openquok posts:create 
  -c "Posting everywhere!" 
  -s "2026-01-15T12:00:00Z" 
  -i "<integration-id-1>,<integration-id-2>,<integration-id-3>"

When channels need different copy, use --bodiesByIntegrationId (see above) instead of relying on a single body.

Platform-specific settings

Some providers expect extra fields (post type, article title, tags, thread replies, and so on). The CLI accepts a JSON object with --settings and merges it into providerSettingsByIntegrationId for each integration id you passed with -i. Deeper per-channel control lives in --providerSettingsByIntegrationId; keys from --settings override the same keys from that map when both are present.

Dev.to title and tags (markdown body in -c):

openquok posts:create 
  -c "Queue this markdown article before it stalls in drafts." 
  -s "2026-01-15T12:00:00Z" 
  --settings '{"title":"Keep technical posts shipping","tags":["webdev","productivity"]}' 
  -i "<integration-id>"

Other providers use the same flag with their own keys (for example a planned subreddit payload). Discover valid shapes with openquok integrations:settings <integration-id> and the Integrations CLI page.

Create from a JSON file

For posts with detailed platform-specific content (large providerSettingsByIntegrationId, many tags, or bodies per channel), use the Payload Wizard to build the request and copy a JSON body:

openquok posts:create -j post.json

Example post.json (placeholders only — replace ids and timestamps with values from your workspace):

{
  "scheduledAt": "2026-01-15T12:00:00.000Z",
  "status": "scheduled",
  "body": "Short default caption when a channel has no override.",
  "integrationIds": [
    "<integration-id-1>",
    "<integration-id-2>"
  ],
  "bodiesByIntegrationId": {
    "<integration-id-1>": "Short version for channel A.",
    "<integration-id-2>": "Longer version for channel B."
  },
  "media": [{ "id": "<media-id>", "path": "uploads/shared.png" }],
  "mediaByIntegrationId": {
    "<integration-id-1>": [{ "id": "<media-id>", "path": "uploads/channel-a.png" }]
  },
  "providerSettingsByIntegrationId": {
    "<integration-id-1>": {
      "replies": [{ "message": "Follow-up only on this channel", "delaySeconds": 60 }]
    },
    "<integration-id-2>": {
      "__type": "instagram"
    }
  },
  "tagNames": ["launch-week"]
}

List posts

openquok posts:list

List posts (Filter by Date Range)

Override the window with explicit ISO timestamps:

openquok posts:list 
  --start "2026-01-01T00:00:00Z" 
  --end "2026-02-01T00:00:00Z"

Same window with the long names: --start / --end.

Filter to specific channels by passing a comma-separated list of integration ids:

openquok posts:list 
  --start "2026-01-01T00:00:00Z" 
  --end "2026-02-01T00:00:00Z" 
  -i "<integration-id-1>,<integration-id-2>"

List posts (Filter by customer group)

Use the channel-group id from your workspace (row id in integration_customers; assign integrations to groups in the dashboard). The CLI sends it as the customerGroupId query parameter on GET /public/posts/list.

openquok posts:list --customer <customer-group-id>

Alias for --customerGroupId (channel-group id).

You can combine —customerGroupId with -i / —integrationIds: only integrations that belong to both the group and the CSV are queried.

To find post rows whose provider id could not be mapped yet (database column release_id is the string missing; JSON responses use releaseId), see Connecting missing posts.

Connecting missing posts

Some platforms do not return a stable published asset id right away. The worker then stores release_id = ‘missing’ on the row; the list API exposes that as releaseId: “missing” (camelCase). Per-post analytics stay blocked until you link the row to the real provider id.

List available content

openquok posts:missing <post-id>

The CLI prints the API envelope, for example:

{
  "success": true,
  "data": {
    "items": [
      { "id": "example-provider-release-id", "url": "https://example.com/preview-cover.jpeg" },
      { "id": "example-provider-release-id-alt", "url": "https://example.com/preview-cover-2.jpeg" }
    ]
  }
}

Narrow to id and preview URL:

openquok posts:missing <post-id> | jq '.data.items[] | {id, url}'

Connect a post

After you pick the matching provider id from the list above, connect the row:

openquok posts:connect <post-id> -r "example-provider-release-id"

Full workflow

# 1. Find post rows that still need a release id (unwrap data.posts from the list response)
openquok posts:list 
  --start "2026-01-01T00:00:00Z" 
  --end "2026-02-01T00:00:00Z" 
  | jq '.data.posts[] | select(.releaseId == "missing") | {id, content, integrationId}'

# 2. Ask the provider for recent candidates (replace <post-id>)
openquok posts:missing <post-id> | jq '.data.items'

# 3. Link the correct provider id
openquok posts:connect <post-id> -r "example-provider-release-id"

# 4. Confirm per-post analytics resolve (pick a window the CLI accepts)
openquok analytics:post <post-id> -d 7

Update, and delete a post

Changing post status

Move a post between draft and scheduled without changing its publish date. Pass any post row id from posts:list (the id field — the same identifier you use for posts:delete).

openquok posts:status <post-id> -s draft
openquok posts:status <post-id> -s schedule
FlagDescription
-s --statusdraft — moves a scheduled group back to draft and terminates any in-flight publishing workflow, so it will not publish until you promote it again. schedule or scheduled — promotes a draft into the publishing queue and (re)starts the workflow so it publishes at the stored time.

Use this when you want to pause a scheduled post without deleting it, or hand a draft to the scheduler once it is ready.

Rescheduling posts

Move a post group to a new publish time. Pass any post row id from posts:list.

openquok posts:reschedule <post-id> -s "2026-06-15T14:30:00.000Z"
FlagDescription
-s --scheduledAtNew publish time (ISO-8601, required). Same field as POST /public/posts.
--actionupdate (default) — moves publishDate only and preserves each row’s state (draft, scheduled, or published). schedule — re-queues publishing at the new time and clears releaseId, releaseUrl, and errors.
--republishSet when —action schedule and the group already has published rows; otherwise the API returns 400.
# Draft or scheduled — move slot without changing state
openquok posts:reschedule <post-id> -s "2026-06-15T14:30:00.000Z"

# Published — re-queue at a new future time (clears provider release ids)
openquok posts:reschedule <post-id> -s "2026-06-20T10:00:00.000Z" --action schedule --republish

See Reschedule post for SDK and curl examples.

Deleting posts

openquok posts:delete <post-id>

Soft-deletes the post row you name and the whole post group it belongs to (a row never publishes in isolation). To target a group by id alone, use the workspace; the public API deletes by row id (DELETE /public/posts/{postId}).

End-to-end workflow

INTEGRATION_ID=$(openquok integrations:list | jq -r '.[] | select(.identifier=="threads") | .id')

MEDIA=$(openquok upload ./hero.png | jq -c '{id: .data.id, path: .data.filePath}')

POST_ID=$(openquok posts:create 
  -s "2026-01-20T15:00:00Z" 
  -t schedule 
  -c "Scheduled with media" 
  -i "$INTEGRATION_ID" 
  -m "[${MEDIA}]" 
  | jq -r '.data.posts[0].id')

openquok posts:status "$POST_ID" -s draft
Search documentation
Find a docs page
Discord Support