Reference
Commands
Seven commands do the real work. Everything else exists because the generator emitted it, and you will never need it.
| Command | What it does |
|---|---|
post | Create a post and record it locally. |
history | Every post recorded locally, newest first. |
search | Full-text search over your own post bodies. |
forget | Remove a post from your local history only. |
unpublish | Delete the post on LinkedIn, then from local history. Irreversible. |
auth | Login, logout, status. |
doctor | Config, credential, and connectivity check. |
userinfo | Print your app-scoped member ID. See the note below. |
You do not need images. It exists, but
post --image already uploads and attaches in one step, and
images on its own wants raw headers spelled out. Same for
userinfo: it works, but it prints an identifier you never
type yourself.
post
postiee post "some text"
postiee post --file post.md
postiee post "with a picture" --image chart.png --alt "a line chart"
postiee post "connections only" -v CONNECTIONS | Flag | Meaning |
|---|---|
--file | Read the body from a file. Avoids shell quoting entirely. |
--image | Attach one image. JPG, PNG, or GIF. One, not many — see the note below. |
--alt | Alt text for that image. |
-v, --visibility | PUBLIC, CONNECTIONS, or LOGGED_IN. Defaults to PUBLIC. |
Prefer --file for anything long. Quoting a multi-line post
through a shell is how you end up with mangled apostrophes.
One image only. --image takes a single file,
and that is the whole of what a self-serve app can attach. Multi-image
posts and video both need LinkedIn partner approval, so there is no flag
for them. See Limits and scope.
history
postiee history
postiee history --limit 200
Reads only the local database. No network call, no rate-limit cost.
--limit defaults to 50.
search
postiee search harness
postiee search "harness agent" --json
Terms are ANDed, so more words narrow the result. Porter stemming is on —
authentic finds authentication.
--limit defaults to 25.
Whole words only. SQLite FTS5 indexes complete tokens, so
postiee search harn will not find harness. Type
the full word. This is documented rather than worked around, because a
search that quietly returns nothing is worse than one that states where it
stops.
Search covers posts made through postiee on this machine. Anything you posted from LinkedIn's app or website is invisible to every API, including this one.
forget
postiee forget urn:li:ugcPost:12345
Removes the post from your local history and from the search index. Take the
URN from history.
This is local only. forget deletes your
record, not the post. To take a post down on LinkedIn itself, use
unpublish, below. That is the one write that cannot be undone.
unpublish
postiee unpublish urn:li:share:12345 Deletes the post on LinkedIn, then removes it from your local history. Both halves, in that order — if LinkedIn refuses the delete, the local row stays so the post is still findable and you can retry.
This cannot be undone. There is no trash, and no API can
read a post back afterwards to reconstruct it. Take the URN from
history and check it before you run the command.
--dry-run reports what it would do and stops there.
auth and doctor
postiee auth login --port 8085
postiee auth status
postiee auth logout
postiee doctor doctor is the one to run first when something looks wrong. It
reports where it found your config and credentials, whether a token is
loaded, and whether api.linkedin.com answers.
Tokens last 60 days. Automatic refresh is partner-only,
so an expired token means one more auth login. If you are
still signed in to LinkedIn the consent screen is skipped and it takes
seconds. The login window stays open 15 minutes, which covers a password,
a 2FA prompt, and an emailed code.
Agent mode
postiee --agent search harness
Sets --json --compact --no-input --no-color together. Use it
whenever anything is parsing the output.
Environment variables
| Variable | Effect |
|---|---|
POSTIEE_SCOPES | Override the requested OAuth scopes. Useful during setup if Sign In with LinkedIn isn't added yet. |
POSTIEE_REDIRECT_URI | Override the redirect host spelling. Must match your app's registration exactly. |
POSTIEE_TOKEN | Use a specific access token instead of the stored one. |
POSTIEE_HISTORY_DB | Point the history database somewhere else. |
POSTIEE_CONFIG | Use a specific config file instead of the default location. |
Troubleshooting
Most of these fail the same way: LinkedIn answers 401 or
403 with a short reason, and postiee passes it through. Match
the first line of the error here.
| What you see | Why | What to do |
|---|---|---|
unauthorized_scope_error |
Your app wasn't granted a scope postiee asked for. Share on LinkedIn
gives w_member_social; Sign In with LinkedIn gives
openid and profile. Both are needed.
|
Open Products on your developer app and add whichever
is missing. If you're mid-setup and just want to post text, set
POSTIEE_SCOPES=w_member_social to skip the rest.
|
redirect_uri does not match the registered value |
LinkedIn compares redirect URLs as exact strings — host, port, path,
trailing slash. localhost and 127.0.0.1 are
different values to it, and postiee sends
127.0.0.1 by default.
|
Copy the exact string from your app's Auth tab into
POSTIEE_REDIRECT_URI, or re-register
http://127.0.0.1:8085/callback. auth login
prints the URI it is sending, so you can compare them directly.
|
403 from /v2/userinfo |
The token has no openid. Either the product was just
added and hasn't propagated, or you logged in before adding it.
|
Add Sign In with LinkedIn, then auth logout and
auth login again. A fresh token is required; the old one
won't gain the scope.
|
422 from /rest/posts, mention of author |
The member URN couldn't be resolved, which means the same missing
openid. It is never a bad post body.
| Same fix as the row above. Add the product, then re-login. |
state mismatch |
The callback that arrived carried a different state than
the one this run generated — a stale login tab, or two logins racing.
|
Run auth login again and use only the URL it prints this
time. Don't reuse a tab from an earlier attempt.
|
no access token found | No token on disk, or the one there has expired. Tokens last 60 days and can't be refreshed without partner access. | Run auth login. Confirm with auth status. |
401 from any endpoint, on a token that worked yesterday | The 60 days are up. | Run auth login. Nothing was lost — the local history is a file on your disk, not a LinkedIn-side record. |
| Search finds nothing for a word you can see in the text |
FTS5 matches whole tokens. harn doesn't match
harness.
| Search the full word. Stemming handles endings — authentic finds authentication. |
| Search finds nothing and the word is complete | The post was made somewhere other than postiee. Only posts postiee wrote are in the index. | Nothing to fix. LinkedIn exposes no API for reading posts you made in its own app. |
| Something fails only in a loop or a script | You've used up the daily budget: 150 requests per member per day. One post is one request. |
Wait until the budget resets. history and
search are local and never count against it.
|