CLI
Managing posts
Create, schedule, list, edit and delete posts from the terminal: drafts, several networks, media, dry runs and a full JSON body.
Every post belongs to an establishment, so you pass its id with -e. Find it with sofia establishments:list (see accounts).
Publish a post
Without a date, the post goes out right away.
sofia posts:create -e $ESTABLISHMENT_ID -p instagram -c "Tarte tatin du jour"Schedule a post
Pass an ISO 8601 date with -s, --date.
sofia posts:create -e $ESTABLISHMENT_ID -p instagram -c "Brunch ce samedi" -s 2026-11-07T09:00:00ZSave a draft
--draft saves the post without publishing or scheduling it.
sofia posts:create -e $ESTABLISHMENT_ID -p instagram -c "Brunch ce samedi" --draftSeveral networks at once
List the networks separated by commas in -p, --platforms. Each one receives the same text.
sofia posts:create -e $ESTABLISHMENT_ID -p instagram,facebook,linkedin -c "Tarte tatin du jour"The media, format and account are shared too. These options shape the post:
-c, --content: the text of the post.-f, --format: the format (post,reel,story,carousel…).-a, --account: the connected account to publish from.
Add media
-m, --media takes media ids from your library or https addresses, separated by commas.
sofia posts:create -e $ESTABLISHMENT_ID -p instagram -c "Tarte tatin du jour" \
-m $MEDIA_ID,https://images.example.com/tarte-tatin.jpgFor an address, the type comes from the extension: video for mp4, mov, webm and m4v, image otherwise. To upload first, see media.
Dry run
--dry-run checks the post against each network’s rules and publishes nothing. See Dry run.
sofia posts:create -e $ESTABLISHMENT_ID -p instagram -c "Tarte tatin du jour" --dry-runIdempotency
Rerun a command safely with --idempotency-key: the same key gives the same post, never two. See Idempotency.
sofia posts:create -e $ESTABLISHMENT_ID -p instagram -c "Tarte tatin du jour" \
--idempotency-key brunch-2026-11-07Full body from a file
For per-network text, several media or a schedule in one go, write the whole body in a file and pass it with -j, --json. It replaces the content options.
{
"establishmentId": "$ESTABLISHMENT_ID",
"targets": [
{
"platform": "instagram",
"text": "Tarte tatin du jour",
"media": [{ "id": "$MEDIA_ID" }],
"format": "post"
},
{
"platform": "tiktok",
"text": "Tarte tatin du jour",
"media": [{ "url": "https://images.example.com/tarte-tatin.mp4", "type": "video" }]
}
],
"scheduledAt": "2026-11-07T09:00:00Z",
"status": "scheduled"
}sofia posts:create --json post.jsonList posts
Filter the list with these options. The answer is paginated: see Pagination.
-e, --establishment: only this establishment.--status: only posts in this status.--fromand--to: a date range.--created-by me|app: posts you wrote yourself, or those created through the API.--limitand--cursor: the page size and where to resume.
sofia posts:list -e $ESTABLISHMENT_ID --status scheduled --from 2026-11-01 --to 2026-11-30 --limit 20Read, edit, delete
posts:update changes the date (-s), the status (draft or scheduled), or the whole body with -j.
sofia posts:get $POST_ID
sofia posts:update $POST_ID -s 2026-11-08T09:00:00Z
sofia posts:update $POST_ID --status draft
sofia posts:delete $POST_IDScript with jq
Since the output is JSON, jq extracts what you need: ids, statuses, the cursor of the next page.
sofia posts:list -e $ESTABLISHMENT_ID --status scheduled | jq '.data[] | {id, status}'
sofia posts:list -e $ESTABLISHMENT_ID | jq -r '.nextCursor'