LinkedIn Company Share ExecModule
Overview
LinkedInCompanyShareModule creates one public organic Post for a LinkedIn organization Page through POST /rest/posts. It supports text-only updates, article cards with explicit metadata, and a single image, video, or document that was uploaded to LinkedIn before execution.
Version 2.0 replaces the catalog's empty input/output schema, mismatched configuration names, deprecated /v2/ugcPosts request, and local-only idempotency claim. The OAuth access token is read only from a bound IntegrationAccount and sent only in the Bearer authorization header. The organization Page identity must come from that same account. Provider bodies and credentials are excluded from failures.
Creating a LinkedIn Post is an outbound, non-idempotent write. The module sends exactly one provider request and never automatically retries HTTP 429, HTTP 5xx, timeout, or another ambiguous transport failure.
Usage
- Create or select a LinkedIn application with approved Community Management access.
- Authorize a member who can post for the target organization and obtain the
w_organization_socialscope. - Create a ValkyrAI
IntegrationAccount, store the OAuth access token in encryptedapiKey, store the numeric organization ID or fullurn:li:organization:<id>inaccountId, and set status toREADY. - Bind that account through
ExecModuleConfig.authConfig.integrationAccount. - Add
LinkedInCompanyShareModuleand provide non-emptycommentary. - Optionally configure either an article or one pre-uploaded LinkedIn media URN. They are mutually exclusive.
- Require outbound approval where workflow policy demands it, then verify the returned Post URN before continuing.
Inputs
| Name | Type | Required | Description | Constraints |
|---|---|---|---|---|
commentary | string | Yes | Organization Post text in LinkedIn little-text format. | Non-blank; at most 3,000 Unicode code points locally. |
article_url | string | No | Source URL for an article card. | Absolute HTTPS URL, at most 2,048 characters, no embedded credentials; requires article_title; cannot be combined with media_urn. |
article_title | string | With article | Article title controlled by the workflow. | At most 200 Unicode code points; requires article_url. |
article_description | string | No | Optional article summary. | At most 500 Unicode code points; requires article_url. |
media_urn | string | No | One image, video, or document already uploaded to LinkedIn. | Must match urn:li:image:*, urn:li:video:*, or urn:li:document:*; cannot be combined with article_url. |
media_title | string | For documents | Optional media title. | At most 200 Unicode code points; required for a document URN. |
media_alt_text | string | No | Accessible alternate text for pre-uploaded media. | At most 4,086 Unicode code points; requires media_urn. |
organization_id | string | No | Legacy identity assertion. | Numeric organization ID or full organization URN; if present, it must exactly match the bound IntegrationAccount after normalization. It cannot override the account. |
api_version | string | No | LinkedIn Marketing API version header. | Six-digit YYYYMM value with a valid month; defaults to 202604. |
is_reshare_disabled | boolean | No | Disable resharing by the author. | Defaults to false. |
The legacy aliases text, link, url, title, description, and company_id are accepted for migration. They do not alter credential or identity rules. Arbitrary media_urls are no longer accepted: LinkedIn media must be uploaded first and referenced by a provider URN.
Outputs
| Name | Type | When present | Description |
|---|---|---|---|
li.share.urn | string | Success | Verified urn:li:share:* or urn:li:ugcPost:* returned in x-restli-id or a compatible response body. |
li.share.permalink | string | Success | https://www.linkedin.com/feed/update/<post-urn>/ derived from the verified Post URN. |
li.share.status | string | Always | PUBLISHED or ERROR. |
The runtime ExecModule is also marked GOOD or ERROR. EventLogs contain bounded validation, submission, success, or sanitized failure details; access tokens and provider response bodies are excluded.
IntegrationAccount Requirements
| Setting | Requirement |
|---|---|
| Provider | LinkedIn Community Management / Posts API |
| Status | Exactly READY |
accountName | Human-readable organization Page identity |
apiKey | Encrypted member OAuth access token |
accountId | Numeric organization ID or full organization URN |
| Relationship | Bind through ExecModuleConfig.authConfig.integrationAccount |
The OAuth member must be authorized to post for the organization and carry w_organization_social. LinkedIn restricts Community Management API access, so a valid OAuth token alone does not prove that the application has the required product tier, organization role, or approved access.
Do not put access tokens, client secrets, organization credentials, or signed upload URLs in module parameters, commentary, article fields, logs, or examples. Client ID and client secret belong to the provider authorization flow, not this post-creation request.
Configuration
Illustrative normalized configuration:
{
"version": "2.0.0",
"authConfig": {
"authStrategy": 1,
"integrationAccount": "integration-account:linkedin-organization-publishing"
},
"payloadConfig": {
"parameters": "{\"commentary\":\"Our release notes are live.\",\"article_url\":\"https://valkyrlabs.com/v1/releases\",\"article_title\":\"Valkyr Labs release notes\",\"api_version\":\"202604\"}"
}
}
The account value is a symbolic secure reference. Persisted workflows use the generated IntegrationAccount relationship, not plaintext token or Page identity fields.
The default transport uses a 10-second connection timeout and a 30-second read timeout. The module sends Authorization: Bearer ..., X-Restli-Protocol-Version: 2.0.0, LinkedIn-Version: <api_version>, and JSON content headers. It submits exactly one POST and performs no automatic retry.
Operations
LinkedInCompanyShareModule has one create operation with these supported shapes:
| Shape | Posts API content | Parameters | Side effect |
|---|---|---|---|
| Text Post | No content object | commentary | Creates a public main-feed organization Post. |
| Article Post | content.article | commentary, article_url, article_title, optional article_description | Creates a Post with explicit article source and metadata; LinkedIn URL scraping is not assumed. |
| Media Post | content.media | commentary, media_urn, optional title/alt text | Creates a Post referencing one LinkedIn-hosted image, video, or document. |
Every request uses the IntegrationAccount organization as author, PUBLIC visibility, MAIN_FEED distribution, empty target entities, PUBLISHED lifecycle state, and no third-party distribution channels.
The module does not upload media, create multi-image or poll Posts, target audience facets, create dark/sponsored content, reshare, edit, delete, archive, comment, react, read analytics, refresh OAuth tokens, or verify later feed propagation. Use separate approved workflows for those behaviors.
Errors and Failure Modes
| Failure | Cause | Retry guidance |
|---|---|---|
| Validation failure | Blank or oversized commentary; unsafe article URL; missing article title; conflicting article/media; malformed media URN, organization, boolean, or version. | Correct the input; no LinkedIn request was sent. |
| IntegrationAccount failure | Missing account, account not READY, blank token, missing organization ID, or identity mismatch. | Bind or repair the correct organization account; no LinkedIn request was sent. |
| HTTP 400 | Invalid Post schema, version, media state, content, or another provider rule. | Correct the request after reviewing LinkedIn diagnostics outside workflow logs. |
| HTTP 401/403 | Expired token, missing scope, insufficient organization role, unapproved application product, or policy restriction. | Reauthorize with least-required scope and confirm approved Community Management access. |
| HTTP 404 | Endpoint, API version, organization, or media mismatch. | Verify the current supported LinkedIn version and bound organization/media identities. |
| HTTP 409/422 | Provider state or semantic conflict. | Reconcile provider state and correct the request before a deliberate retry. |
| HTTP 429 | LinkedIn rate limit. | Inspect whether a Post exists, honor provider guidance, then schedule a deliberate new execution. |
| HTTP 5xx or network failure | Provider or transport failure with an ambiguous commit boundary. | Do not retry blindly; inspect the organization Page first. |
| Invalid success response | A 2xx response lacks a valid share/UGC Post URN. | Treat as failure and inspect provider compatibility; success is not recorded. |
Provider bodies can contain user content, application details, or credential-like values, so errors expose only a sanitized HTTP status or bounded local validation message.
Example
Publish one organization article:
{
"commentary": "Our release notes are live.",
"article_url": "https://valkyrlabs.com/v1/releases",
"article_title": "Valkyr Labs release notes",
"article_description": "Verified product changes and upgrade guidance."
}
Expected normalized result after LinkedIn accepts the create request and returns a valid identifier:
{
"li.share.urn": "urn:li:share:6844785523593134080",
"li.share.permalink": "https://www.linkedin.com/feed/update/urn:li:share:6844785523593134080/",
"li.share.status": "PUBLISHED"
}
API acceptance does not prove feed distribution, impressions, moderation outcome, or public visibility to every viewer. Use separately authorized LinkedIn reads or the organization Page to confirm later provider state.
Notes
- Pagination: not applicable; one execution attempts one Post creation.
- Limits: text and metadata sizes are bounded locally. LinkedIn enforces additional application, organization, content, media, daily, and policy limits.
- Media: image, video, and document bytes must be uploaded through their dedicated LinkedIn APIs before this module runs. The returned URN must remain available to the authenticated organization.
- Articles: the Posts API does not rely on URL scraping. The workflow supplies explicit source, title, and optional description; thumbnail upload is a separate Images API workflow.
- Versioning: LinkedIn Marketing APIs use monthly version headers and retire older versions. Keep
api_versionpinned to a currently supported provider release and regression-test changes before rollout. - Rate limits: HTTP 429 is surfaced without retry. Provider quotas vary by application, product tier, member, organization, and endpoint.
- Idempotency: Post creation has no provider idempotency key in this module. A workflow retry can create a duplicate, so reconcile ambiguous attempts before re-execution.
- API constraints: LinkedIn performs final scope, role, product-tier, content, media, organization, and policy validation.
- 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 bounded progress and sanitized failures. OAuth tokens, authorization headers, request bodies, and provider bodies are never stored in workflow state.
- Unverified boundary: deterministic tests cover Posts API request construction, explicit article/media shapes, header-only credentials, bounds, IntegrationAccount state and identity, response validation, non-retry behavior, metadata serialization, and secret redaction. Live LinkedIn execution requires separately authorized credentials and approved Community Management access and is not exercised in repository tests.
See LinkedIn's official Posts API, Post schema, Marketing API versioning, and organization access control for current provider behavior.