← all adapters

linkedin-style

simulates LinkedIn API v2

restadapter v0.1.08 routes

verification

VMVM tier — no SDK suite yet· surface 8 · not implemented 4 · differs 3

use it

# add to stunt.yaml
services:
  linkedin:
    adapter: embedded:linkedin-style

# then
$ stunt up

The embedded: source extracts this adapter from the binary — no git clone, fully offline.

how to mock the LinkedIn API locally

Install stunt, paste the service block above into stunt.yaml, run stunt up — then point your client at localhost instead of LinkedIn API. It works offline, keeps state across requests and restarts, and every response is synthetic: the route shapes, status codes, pagination, and webhook delivery follow the real LinkedIn API v2 surface — the data is fake, the behavior is real.

backing stores

tokensrefresh_tokenscodespostscomments

api surface (8)

generated from the adapter manifest — exactly what boots, nothing more

8 routes
GET/oauth/v2/authorization
POST/oauth/v2/accessToken
GET/v2/userinfo
POST/v2/ugcPosts
GET/rest/comments
POST/rest/comments
GET/rest/posts/{urn}
GET/rest/memberCreatorPostAnalytics

VM-verified behaviors (43)

  • ✓authorize without redirect_uri, state or client_id is invalid_request
  • ✓authorize redirects back with a fresh code and the state echoed
  • ✓a redirect_uri that already carries a query is joined with &
  • ✓the exchange demands grant_type=authorization_code
  • ✓an unknown code is 400 invalid_grant
  • ✓a good exchange mints a 60-day token pair for a fresh member
  • ✓the code is single-use: a replay is invalid_grant
  • ✓client mismatches are 400 invalid_client
  • ✓a mismatched attempt must not burn the code
  • ✓a second flow mints a distinct member
  • ✓the refresh grant demands client creds
  • ✓an unknown refresh token is invalid_grant
  • ✓refresh rotates the pair and keeps the member
  • ✓the presented refresh token is single-use
  • ✓rotation chains: the new refresh token refreshes again
  • ✓a missing bearer is 401 in the service error envelope
  • ✓wrong schemes and unknown bearers answer the same 401
  • ✓every API route enforces the same gate
  • ✓a bearer dies at its clock-derived 60-day expiry
  • ✓userinfo returns the OAuth member profile
  • ✓publishing as anyone but the token's member is a 403
  • ✓a good publish mints a ugcPost urn echoed in x-linkedin-id
  • ✓the post resolves to a share urn carrying its own author
  • ✓resolving an unknown urn is a 404
  • ✓unconfigured, publishing is unthrottled
  • ✓arming fail_after injects 429 REQUEST_LIMIT_EXCEEDED
  • ✓the limit is per member
  • ✓a throttled attempt creates no post
  • ✓q must be author
  • ✓reply resolves urn:li:person:me to the authenticated member
  • ✓commenting as anyone but the caller is a 403
  • ✓replying to an unknown object is a 404
  • ✓ingest lists only the token member's comments
  • ✓member B's comment resolved me and lists only under B
  • ✓createdOn is clock-stamped and monotonic
  • ✓count pages with a next link that round-trips the query
  • ✓without count the whole list returns unpaged
  • ✓a malformed start cursor is a 400
  • ✓an unknown entity is a 404
  • ✓each queryType totals base+3/5/7/11 split across two daily buckets
  • ✓entity accepts both the parenthesized and bare urn forms
  • ✓start past the data returns an empty page
  • ✓an unknown queryType falls back to the base total (deviation, as-is)

Not implemented (4)

  • No ugcPosts delete or edit (DELETE /v2/ugcPosts/{id})
  • No organizations or company page API (GET /v2/organizations)
  • No reactions or likes endpoints (socialActions likes)
  • No video upload flow or multi-image carousel posts

Differs from the real API (3)

  • Rate-limit injection on POST /v2/ugcPosts publish is a simulator test hook
  • An unknown analytics queryType silently falls back to the base total; real LinkedIn 400s
  • Refresh tokens never expire; only access tokens carry the 60-day expiry
want more of the LinkedIn API surface? adapters are YAML + Starlark — the authoring guide covers adding routes, fixtures, and state machines. Open an issue for anything big first, or just file a PR — merged routes ship in the next release.