SocialAPI.ai
Blog

Why Does the Threads API Return a 500 Error With an Empty Body? Missing Permission on POST /{threads-user-id}/threads (2026)

Erwan Prost

Erwan Prost

Founder, SocialAPI.ai

Published
Reading time
10 min
On this page

POST /{threads-user-id}/threads returns HTTP 500 with an empty body when a container request carries reply_control on a token without threads_manage_replies. Meta returns no permission error and no error object. In our production traffic the request failed in under half a second, while the identical request without reply_control succeeded. We reproduced it against a live account in September 2026, and Meta's docs checked on 18 October 2026 mention neither a 500 nor an empty response.

A parameter Meta does not document produces the identical response, so the status code cannot tell you which cause you hit. This post gives the parameter and permission table, a procedure that isolates the culprit, and the second Threads surprise: the webhook payload has no entry[] or changes[] envelope.

What does Meta document about errors on POST /{threads-user-id}/threads?

Very little: the endpoint has no documented error codes and no permissions section. Meta's publishing reference lists 24 optional and conditional parameters plus access_token for POST /{threads-user-id}/threads (checked 18 October 2026). The posts guide names no permissions either.

The troubleshooting guide documents container status errors such as FAILED_DOWNLOADING_VIDEO and INVALID_DURATION, which arrive in a status read after creation. It does not mention HTTP 500 or an empty response (checked 18 October 2026).

The one documented creation-time failure is the link limit: "Starting December 22, 2025, Threads posts containing more than 5 links will fail to post during the media creation step", with the error code THREADS_API__LINK_LIMIT_EXCEEDED (posts guide, checked 18 October 2026). That failure returns a named error. A missing permission, in our traffic, did not.

What permission does reply_control need, and what do other parameters need?

reply_control needs threads_manage_replies, which Meta describes as permission to "control who can reply to a thread on the Threads profile" (permissions reference, checked 18 October 2026). The table maps each creation parameter to the scope whose description covers it. Where the description does not mention the parameter, the cell says so rather than guessing.

ParameterPermission Meta ties to itWithout the permission
reply_controlthreads_manage_replies: "control who can reply to a thread on the Threads profile" (permissions reference)Observed: HTTP 500, empty body (September 2026)
location_idthreads_location_tagging: "publish media with a location tagged" (permissions reference)Not tested
enable_reply_approvalsNot stated on the reply management page or the publishing referenceNot tested
topic_tagNone named beyond the publish scopesNot applicable
link_attachmentNone named beyond the publish scopesNot applicable
quote_post_idNone named beyond the publish scopesNot applicable
allowlisted_country_codesNone named beyond the publish scopesNot applicable
poll_attachment, gif_attachment, text_attachment, text_entitiesNone named beyond the publish scopesNot applicable
is_ghost_post, is_spoiler_media, alt_text, auto_publish_text, crossreshare_to_ig, crossreshare_to_ig_dark_modeNone named beyond the publish scopesNot applicable
Any key not in the publishing referenceNoneObserved: HTTP 500, empty body

"The publish scopes" means threads_basic, which "allows an app to get a user's Threads profile information and the media and text content that they posted," and threads_content_publish, which "allows a business app to create and publish content on behalf of a Threads profile" (permissions reference, checked 18 October 2026). The reference does not say these two are sufficient for every parameter in the table. It only names nothing else.

Read the "Not applicable" cells for what they are: Meta names no extra permission for those parameters, and we did not observe any of them failing for permission reasons. That is an absence of evidence in Meta's documentation, not a guarantee. The enable_reply_approvals and location_id rows stay "Not tested" because we have not run them against a token that lacks the scope.

Your token's scope list comes from the scope you sent to authorize, and a user who connects without threads_manage_replies holds a token that cannot set a reply audience. Meta's access token guide says the token response returns "the app user's short-lived access token, the token type, and User ID" and documents no endpoint that reads the granted permissions back (checked 18 October 2026). Keep your own record of which scopes each user approved.

How do you find which parameter causes the empty 500?

Strip optional parameters until the 500 stops, then add them back one at a time. The first one that brings the 500 back is the culprit. The four steps below are the whole procedure, and the diagram shows where each outcome leads.

Bisecting an empty 500 on the Threads container endpoint

Swipe sideways to see the whole diagram

BISECTING AN EMPTY 500 ON THE THREADS CONTAINER ENDPOINTNoYesYesNo, next paramLog request paramstoken maskedSend minimum requestmedia_type, text,access_tokenMinimum succeeds?Retry laterread fbtrace_idif presentAdd one optional paramreply_control,location_id first500 returns?Culprit foundcheck scope vsMeta's listLog the request, the 500 has no body

Start from the documented minimum request, then reintroduce optional parameters one at a time. A failure of the minimum request points away from your parameters.

  1. 1.Log the full request parameters at the moment of failure, with the token masked. The 500 carries no body, so the request is your only evidence.
  2. 2.Re-send the container request with only the documented minimum: media_type, text (or image_url or video_url), and access_token. If it succeeds, the cause is an optional parameter.
  3. 3.Add the optional parameters back one at a time. Start with reply_control and location_id, the two with a named permission.
  4. 4.Compare the culprit with Meta's list of parameters and with the scopes the user approved. A documented parameter whose scope the user never approved is the permission case. A key missing from the reference is the undocumented case.
bash
# minimum request: should succeed on any token with threads_content_publish
curl -X POST "https://graph.threads.net/v1.0/{threads-user-id}/threads" \
  -d "media_type=TEXT" \
  -d "text=hello" \
  -d "access_token=$TOKEN"

# same request plus the suspect parameter: HTTP 500, empty body when the scope is missing
curl -i -X POST "https://graph.threads.net/v1.0/{threads-user-id}/threads" \
  -d "media_type=TEXT" \
  -d "text=hello" \
  -d "reply_control=everyone" \
  -d "access_token=$TOKEN"

One caution from our traffic: a bare 500 is not proof of a bad parameter or a missing scope. We have seen Threads container creation fail for a short period for requests that carried only the documented minimum, while other accounts published normally. If step 2 fails too, retry later and read the fbtrace_id header if one is present before you change your parameter set.

What does a Threads webhook payload look like?

A Threads webhook is one flat JSON object per request, with app_id, topic, target_id, time, subscription_id, has_uid_field and values at the top level. Meta's Threads webhooks page mentions neither entry nor changes (checked 18 October 2026). A handler written for the Instagram shape reads an empty list and drops every Threads event without an error.

Both blocks below are examples with placeholder values, not captured traffic. First, the Instagram and Facebook envelope, where events sit inside entry[] and changes[]:

json
{
  "object": "instagram",
  "entry": [
    {
      "id": "17841400000000000",
      "time": 1723226877,
      "changes": [
        {
          "field": "comments",
          "value": { "id": "17900000000000000", "text": "Example comment" }
        }
      ]
    }
  ]
}

Second, the Threads shape for a reply, shortened from Meta's replies sample (checked 18 October 2026). It drops permalink, shortcode, timestamp and media_type:

json
{
  "app_id": "123456",
  "topic": "moderate",
  "target_id": "78901",
  "time": 1723226877,
  "subscription_id": "234567",
  "has_uid_field": false,
  "values": {
    "value": {
      "id": "8901234",
      "username": "test_username",
      "text": "Reply",
      "replied_to": { "id": "567890" },
      "root_post": { "id": "123456", "owner_id": "123456" }
    },
    "field": "replies"
  }
}

Three details matter to a handler. First, target_id changes meaning by field. Meta's Fields table defines it as "The media's ID for a reply or delete webhook, or the mentioned Threads user app-scoped user ID for a mentions webhook." For a reply the account that owns the conversation is in values.value.root_post.owner_id.

Second, topic differs by field: the replies sample uses "moderate" and the mentions sample uses "interaction". Route on values.field, not on topic. Third, the scopes differ by field: replies need threads_basic and threads_read_replies, mentions need threads_basic and threads_manage_mentions, delete needs threads_basic and threads_delete, and publish needs threads_basic (Threads webhooks page, checked 18 October 2026).

The mentions sample carries media_product_type, has_replies, is_reply and text. Meta says has_replies, is_reply, replied_to and root_post are removed without threads_read_replies.

Why am I not receiving Threads mention webhooks?

The likeliest cause is missing Advanced Access. Meta states that an app without Advanced Access on its permissions will not receive webhook notifications, and that to receive all fields the app must complete App Review for Advanced Access (checked 18 October 2026). Check the subscription scopes too: mentions need threads_basic and threads_manage_mentions.

In our traffic this showed up sharply on mentions. Before the app held Advanced Access for threads_manage_mentions, no mention webhook reached us for accounts other than the app's own testers, and a polling read returned an empty list for them. Meta's page does not mention testers, so treat that detail as a first-party observation, not documented behaviour.

For the publish flow itself (the two-step container and publish calls, the 250-posts-per-24-hours cap and carousel rules) see the Threads publishing developer guide. The signature check on the X-Hub-Signature-256 header is covered in webhook signature verification.

Questions developers ask about Threads API errors and webhooks

Why does the Threads API return a 500 error with an empty body?
In our production traffic (September 2026), POST /{threads-user-id}/threads returned HTTP 500 with an empty body for reply_control on a token without threads_manage_replies, and for a parameter Meta does not document. Meta's docs checked 18 October 2026 mention neither. Remove optional parameters one at a time to find the cause.
What permission does reply_control need?
threads_manage_replies, which Meta's permissions reference describes as the ability to "control who can reply to a thread on the Threads profile" (checked 18 October 2026). Without it, the container request returned HTTP 500 with an empty body in our September 2026 traffic. Meta's publishing reference itself names no permission for the parameter.
Which permission does location_id need?
threads_location_tagging, described by Meta as "publish media with a location tagged" (permissions reference, checked 18 October 2026). We have not tested what location_id returns on a token without it, so do not assume it matches the reply_control behaviour.
What does a Threads webhook payload look like?
A flat object with app_id, topic, target_id, time, subscription_id, has_uid_field and values at the top level, and no entry[] or changes[] envelope (Meta's Threads webhooks page, checked 18 October 2026). The event itself sits in values.value, and values.field names the type, such as replies.
Why am I not receiving Threads mention webhooks?
Meta says an app without Advanced Access on its permissions will not receive webhook notifications (checked 18 October 2026), and mentions need threads_basic and threads_manage_mentions. In our traffic, no mention webhook arrived for non-tester accounts until the app held Advanced Access for threads_manage_mentions.
What does target_id mean in a Threads webhook?
It is the media id for a reply or delete webhook and the mentioned user's app-scoped user id for a mentions webhook (Meta's Threads webhooks page, checked 18 October 2026). For a reply, read the conversation owner from values.value.root_post.owner_id.

SocialAPI.ai covers Threads through one API: see the Threads platform page. If a Meta fact on this page is wrong or has changed, tell us and it gets corrected with a date.

Every Meta claim above traces to a page on developers.facebook.com, each checked 18 October 2026. Statements labelled as observed come from our own production traffic in September 2026 and are not in Meta's documentation. Meta, Threads publishing reference, Meta, Threads posts guide, Meta, Threads troubleshooting guide, Meta, Permissions reference, Meta, Threads reply management, Meta, Threads access tokens and permissions, Meta, Threads webhooks

Keep reading

Ready to unify your social interactions?

Free tier available. No credit card required. Ships with the MCP server.