PostsPublish a post

Posts

Publish a post

Live apps publish at once, schedule with a date or save a draft; test apps only simulate. With ?dry_run=true it checks everything and writes nothing.

POSThttps://api.sofia-post.com/api/v1/posts
POSThttps://api.sofia-post.com/api/v1/posts

Publish a post

Live apps publish at once, schedule with a date or save a draft; test apps only simulate. With ?dry_run=true it checks everything and writes nothing.

Authorizations

Authorizationstringrequired

Your personal key or OAuth access token, sent as Bearer <token>.

Kept in this tab only, and sent only to api.sofia-post.com. Get an API key

Query parameters

dry_runstring

true: runs every check the send would run (network rules, accounts, media, creation, live subscription) and writes nothing. Returns 200 ready or 422 post_refused.

Headers

Idempotency-Keystring

Replayed with the same body, the request returns the same response without creating a second post. A dry run does not consume the key.

Body

This request acts on your real account.

A post belongs to an establishment and goes out to one or more networks, its targets. Each target has its own text, media, format and state.

Post type: now, scheduled or draft

Two body fields decide when, and whether, the post goes out:

CaseBodyBehavior
Nowno scheduledAtThe post goes out right away.
ScheduledscheduledAt: a future ISO 8601 date-timeIt will go out at scheduledAt.
Draft"status": "draft", no scheduledAtIt is saved and does not go out.

The body of a post

FieldMeaning
establishmentIdThe establishment that publishes (GET /v1/establishments)
targets[]One entry per network: platform, text, and optionally media, format and accountId
targets[].media[]A media file from the library by its id, or a public https url with its type (image or video)
targets[].accountIdThe account to use when the establishment has several on that network
scheduledAtA future ISO 8601 date-time; when absent, the post goes out right away
statusdraft to save a draft that does not go out (with no scheduledAt then)

A target’s media

Each entry of targets[].media designates a media file from the library by its id, or a public https url with its type (image or video). Instagram and TikTok require a photo or a video. To add a media file to the library, see Import media.

Duplicating a post

There is no dedicated endpoint to duplicate a post: read it with `GET /v1/posts/{id}`, then send its content again in a new POST /v1/posts request.

Before sending it again, remove the fields that Sofia fills in herself. At a minimum:

  • the post’s id and createdAt;
  • the status of the post read (it summarizes the sending state): set the one you want, or omit it;
  • in each target: state, publishedAt, permalink and error.

Keep establishmentId, and for each target platform and accountId. The read does not return the targets’ text or their media: supply them again. Finally set a new scheduledAt (or omit it to publish right away, or send "status": "draft").

Your first post

1. Check the connected networks

A post goes out through the accounts connected to the establishment. List them; if one is missing, create a connection link. See Connected accounts.

2. Add a media file

Instagram and TikTok require a photo or a video. Import one from a public https URL; Sofia downloads it into the library and returns its id. See Import media.

3. Check with a dry run

Add ?dry_run=true: Sofia judges each network by its real rules and the connected account, and writes nothing.

curl -X POST 'https://api.sofia-post.com/api/v1/posts?dry_run=true' \
  -H "Authorization: Bearer $SOFIA_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "establishmentId": "'$ESTABLISHMENT_ID'",
    "targets": [
      { "platform": "instagram", "text": "Tarte tatin du jour", "media": [{ "id": "'$MEDIA_ID'" }] },
      { "platform": "facebook", "text": "Tarte tatin du jour", "media": [{ "id": "'$MEDIA_ID'" }] }
    ]
  }'
{
  "status": "ready",
  "dryRun": true,
  "targets": [
    { "platform": "instagram", "accountId": "fecd0904-…", "handle": "sof_ia_officiel", "state": "ready" },
    { "platform": "facebook", "accountId": "a423b3a4-…", "handle": "Sofia post", "state": "ready" }
  ]
}

4. Publish

Send the same body without dry_run, with an Idempotency-Key so that a retry never publishes twice. Without scheduledAt, the post goes out right away; with a future scheduledAt, it is scheduled.

curl -X POST https://api.sofia-post.com/api/v1/posts \
  -H "Authorization: Bearer $SOFIA_API_KEY" \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $(uuidgen)" \
  -d @post.json

5. Track it

The post answers publishing, then each network moves to published with its permanent link, or to failed with its reason. Read it with `GET /v1/posts/{id}`, or register a webhook to be notified.

Dry run

POST /v1/posts?dry_run=true runs the checks of a real post: each network’s rules, the connected account, the media, the date and your plan. It saves nothing and sends nothing.

Ready

When every network would accept the post, the response is 200 with status: "ready" and, for each target, the account that would publish.

Refused

Otherwise, the response is 422 post_refused, with one detail per reason. Each detail names its network, its code, and says what to fix.

{
  "error": {
    "code": "post_refused",
    "message": "The post would not be published: x: the text is too long (300 characters, at most 280)",
    "details": [
      {
        "platform": "x",
        "code": "caption_too_long",
        "max": 280,
        "actual": 300,
        "message": "x: the text is too long (300 characters, at most 280)"
      }
    ],
    "docUrl": "https://www.sofia-post.com/developers/errors#post_refused"
  }
}

Each reason is listed in the errors.

For apps’ test mode, see Test mode and dry run. An Idempotency-Key also protects POST /v1/posts: see Idempotency.

Authorizations

  • Authorizationstringheaderrequired

    Your personal key or OAuth access token, sent as Bearer <token>.

Query parameters

  • dry_runstring

    true: runs every check the send would run (network rules, accounts, media, creation, live subscription) and writes nothing. Returns 200 ready or 422 post_refused.

    Allowed values: truefalse

Headers

  • Idempotency-Keystring

    Replayed with the same body, the request returns the same response without creating a second post. A dry run does not consume the key.

Body

application/json
  • establishmentIdstringrequired

    Establishment, in the Workspace of the token grant (GET /v1/establishments)

  • targetsobject[]required

    One network per target

    Show child attributes
    • platformstringrequired

      Targeted network

      Allowed values: instagramfacebookxlinkedintiktok

    • textstringrequired

      Caption published on this network

    • mediaobject[]

      Attached media

      Show child attributes
      • idstring

        Identifier of a media of the establishment library (otherwise url and type)

      • urlstring

        Public address of the media (https in live); without an identifier

      • typestring

        Kind of the media given by address

        Allowed values: imagevideo

    • formatstring

      Format (post, reel, story, carousel...); by default, the one the network would serve

    • accountIdstring

      Live: the connected account that publishes, when the establishment has several on this network (GET /v1/establishments/:id/accounts)

  • scheduledAtstring<date-time>

    Scheduling date (ISO 8601), in the future; absent: publish immediately. Not allowed with status draft

  • statusstring

    draft: a draft (live only), created without a date, nothing is sent; PATCH /v1/posts/:id schedules it

    Allowed values: draft

Response

Dry run: every check passed.

  • statusstringrequired

    Always ready

    Allowed values: ready

  • dryRunbooleanrequired

    Always true

    Allowed values: true

  • targetsobject[]required

    The checked targets

    Show child attributes
    • platformstringrequired

      Network of the target

    • statestringrequired

      Always ready

      Allowed values: ready

    • accountIdstring

      Live: the account that would publish

    • handlestring

      Live: its displayed handle

Was this page helpful?