Skip to main content

X Post ExecModule

Overview

XPostModule (social.x.post) creates one Post through POST /2/tweets on the X API host. It supports text, replies, quotes, and up to four media IDs returned by a completed X media upload.

Version 2.0 removes the former placeholder media path. The module never downloads arbitrary media_urls, never invents a mock_media_* identifier, and never reports success unless X returns a valid data.id. Upload media through the X media API first and pass the resulting IDs to this module.

Post creation is an outbound, non-idempotent action. The module validates every field before the provider request and does not automatically retry HTTP 429, 5xx, timeout, or ambiguous network failures.

Usage

  1. Create or select an X developer App that can perform user-context Post writes.
  2. Authorize the publishing identity with an OAuth 2.0 PKCE user access token.
  3. Store that token in the encrypted apiKey field of a ValkyrAI IntegrationAccount and bind the account through ExecModuleConfig.authConfig.integrationAccount.
  4. Add XPostModule to a workflow and set non-empty text.
  5. Optionally set one reply ID, one quote ID, or one to four pre-uploaded media IDs. X does not allow media_ids and quote_tweet_id together.
  6. Require outbound approval where your workflow policy demands it, then inspect the returned Post ID before continuing.

Inputs

NameTypeRequiredDescriptionConstraints
textstringYesText sent to X.Non-blank and at most 280 Unicode code points locally. X applies final weighted-length and product-rule validation.
media_idsarray of stringsNoMedia IDs from completed X media uploads.At most four numeric IDs. Cannot be combined with quote_tweet_id.
reply_to_tweet_idstringNoExisting Post ID to reply to.Numeric X identifier. Legacy camel-case replyToTweetId is accepted at runtime.
quote_tweet_idstringNoExisting Post ID to quote.Numeric X identifier. Mutually exclusive with media_ids.

media_urls is not supported. A non-empty legacy value fails before any X request instead of producing a fabricated media ID.

Outputs

NameTypeWhen presentDescription
x.post.tweet_idstringSuccessNumeric identifier returned in X data.id.
x.post.permalinkstringSuccesshttps://x.com/i/web/status/<id> URL for the created Post.
x.post.statusstringAlwaysPOSTED on verified success or ERROR on failure.

The module also marks the runtime ExecModule GOOD or ERROR and emits bounded EventLog progress. Access tokens and provider response bodies are never written to workflow state or failure events.

IntegrationAccount Requirements

SettingRequirement
ProviderX API v2
AuthenticationOAuth 2.0 Authorization Code with PKCE user context
accountNameHuman-readable publishing identity
apiKeyEncrypted X user access token used as Authorization: Bearer ...
RelationshipBind through ExecModuleConfig.authConfig.integrationAccount

An OAuth client ID, client secret, or app-only bearer token is not a substitute for the authorized user access token needed to create a Post. Do not place credentials in payload parameters, Post text, logs, or documentation examples.

Configuration

Illustrative normalized configuration:

{
"version": "2.0.0",
"authConfig": {
"authStrategy": 1,
"integrationAccount": "integration-account:x-publishing-user"
},
"payloadConfig": {
"parameters": "{\"text\":\"Launch notes are live.\",\"media_ids\":[\"1880028106020515840\"],\"reply_to_tweet_id\":\"1445880548472328192\"}"
}
}

The account value is a symbolic secure reference. Persisted workflows use the generated IntegrationAccount relationship, not a plaintext token.

The default transport uses a 10-second connection timeout and a 30-second read timeout. Post creation is not retried automatically, even when a workflow retry policy exists, because an ambiguous failure may have committed the Post.

Operations

XPostModule has one operation with four compatible shapes:

ShapeProvider payloadSide effect
Text PosttextCreates one public or account-policy-governed Post.
Replytext plus reply.in_reply_to_tweet_idCreates one reply.
Quotetext plus quote_tweet_idCreates one quote Post.
Media Posttext plus media.media_idsCreates one Post using already-uploaded media.

Media upload, delete, edit, poll, card, geo, scheduling, analytics, and bulk publishing are separate operations and are not implemented by this module.

Errors and Failure Modes

FailureCauseRetry guidance
Validation failureMissing text, more than 280 code points, malformed IDs, more than four media IDs, media plus quote, or non-empty media_urls.Correct the configuration; no X request was sent.
IntegrationAccount failureNo bound account or blank apiKey.Bind a valid user-context X account; no X request was sent.
HTTP 400/403/404Provider validation, product policy, scope, identity, or referenced-Post/media problem.Correct the account or payload before a new attempt.
HTTP 401Token is missing, expired, revoked, or unsuitable for user-context creation.Reauthorize the X user and rotate the IntegrationAccount token.
HTTP 429X rate limit.Reconcile whether the Post exists, honor provider reset guidance, then schedule a deliberate new attempt.
HTTP 5xx or network failureProvider or transport failure with an ambiguous commit boundary.Do not retry blindly. Check the publishing account or durable correlation context first.
Invalid success responseA 2xx response lacks a numeric data.id or is not valid JSON.Treat as failure and inspect provider compatibility; success is not recorded.

Provider response bodies can contain credential-like or user content, so failures expose only a sanitized HTTP status or bounded local validation message.

Example

Create a reply with one previously uploaded image:

{
"text": "Launch notes are live.",
"media_ids": ["1880028106020515840"],
"reply_to_tweet_id": "1445880548472328192"
}

Expected result shape after X returns 201 with a valid data.id:

{
"x.post.status": "POSTED",
"x.post.tweet_id": "1880030000000000000",
"x.post.permalink": "x.com/i/web/status/1880030000000000000"
}

Notes

  • Pagination: not applicable; one execution attempts one Post creation.
  • Media limits: X currently accepts one to four media IDs on the create-Post request. Upload processing and media-category constraints belong to the X media API.
  • Rate limits: the module surfaces HTTP 429 without retrying. Provider limits vary by access tier and endpoint policy.
  • Idempotency: Post creation has no durable idempotency key in this module. A workflow retry can create a duplicate Post, so reconcile ambiguous attempts before re-execution.
  • API constraints: X performs final text weighting, scope, referenced-Post, account, policy, and media compatibility checks. Local validation is intentionally narrower.
  • Destructive behavior: creation is an external outbound write. This module cannot delete or compensate a Post; removal requires a separately authorized operation.
  • Observability: EventLogs record validation, submission, success ID, or sanitized failure. Tokens and provider bodies are excluded.
  • Unverified boundary: deterministic tests cover account binding, exact request construction, validation, response parsing, non-retry behavior, metadata serialization, and secret redaction. Live X execution requires separately authorized service credentials and is not exercised in repository tests.

See the official create-Post reference and manage Posts quickstart for current provider behavior.