CContentReach

Documentation

This page is for the human setting things up. If you're an agent, or building integration code, read llms.txt (quick reference) or llms-full.txt (complete spec) instead — same information, machine-oriented format.

Add this to your project

Paste this into your coding agent (Claude Code, Cursor, whatever you use) inside the project you want posting LinkedIn from. It reads the current spec itself, asks you for credentials if you don't have them yet, and wires up whatever fits your codebase — nothing to copy from the sections below unless you want the detail.

paste into your agent
Add LinkedIn posting to this project using ContentReach (https://contentreach.io) — a pay-per-post LinkedIn API, no subscription, text posts (up to 3000 characters) optionally with one image.

1. Fetch https://contentreach.io/llms-full.txt and read it — that's the complete, current API spec (auth, endpoints, error codes, an MCP option).
2. Ask me for my ContentReach API key and Linked Account ID. If I don't have them yet, tell me to sign in at contentreach.io/settings, connect LinkedIn, and generate a key — it's shown once, at creation.
3. Store them the same way this project already stores other secrets — ask me if you're not sure how.
4. Build whatever fits this codebase: a button in an existing UI, a CLI command, a webhook — your judgment. It should call POST https://contentreach.io/api/v1/posts with the API key as a Bearer token, the finished post text, and — if this project already has an image ready for the post (e.g. a generated cover image with a public URL) — an image_url field. Don't build an image upload/generation flow just to fill this in; only pass it if one already exists here.
5. Handle the response: 201 is success. 402 means insufficient Balance — surface the top-up link from the response. Anything else, show the error message — every error follows { error: { code, message, retryable } }.

No video or carousel support yet — text and at most one image only.

Getting started

  1. 1

    Sign in

    Go to /auth/sign-in with your email — you'll get a one-time link, no password to set.

  2. 2

    Connect LinkedIn

    From /settings, click “Connect LinkedIn” and authorize on LinkedIn's own consent screen. This is the account posts will publish to.

  3. 3

    Add Balance

    Top up via Stripe when it's live, or use the manual test-balance field in settings while it isn't. Posts are prepaid — no subscription.

  4. 4

    Generate an API key

    From settings, create a key — name it, scope it to the LinkedIn account(s) you want it to reach, with an optional spend cap and rate limit. The full key is shown exactly once — save it now.

  5. 5

    Give the key to your agent

    Pass it as a Bearer token over REST, or as CONTENTREACH_API_KEY to the MCP server. Same key works for both.

Publish a post

One call, whichever transport your agent speaks. Both are capability-equivalent — nothing is REST-only or MCP-only.image_url is optional — omit it for a text-only post.

REST

POST /v1/posts
curl https://contentreach.io/api/v1/posts \
  -H "Authorization: Bearer cr_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "linked_account": "la_9f2...",
    "content": "Finished text, ready to publish.",
    "image_url": "https://example.com/cover.jpg"
  }'

# 201
{
  "id": "urn:li:share:...",
  "linked_account": "la_9f2...",
  "balance_cents": 1975
}

MCP

The hosted endpoint is https://contentreach.io/api/mcp (Streamable HTTP, stateless). Send your API key as a Bearer token on every request, exactly like REST. Two tools are registered:

For local development against a client like Claude Desktop, the source in mcp-server/ also runs standalone over stdio (npm run dev, with CONTENTREACH_API_KEY set) — same tools, same code, different transport.

Check balance

Pre-empt a 402 rather than react to one:

GET /v1/balance
curl https://contentreach.io/api/v1/balance \
  -H "Authorization: Bearer cr_live_..."

# 200
{ "balance_cents": 1975 }

Profile

Name, company, and email — readable and writable the same way whether it's you in settings or your agent over the API. Only fields you send in a PATCH change; omit a field to leave it as-is, or send an empty string to clear it.

GET /v1/profile · PATCH /v1/profile
curl https://contentreach.io/api/v1/profile \
  -H "Authorization: Bearer cr_live_..."

# 200
{ "display_name": "James Cannan", "company_name": null, "email": "you@example.com" }

curl -X PATCH https://contentreach.io/api/v1/profile \
  -H "Authorization: Bearer cr_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "company_name": "Unique Interactions" }'

Error codes

Every failure — REST or MCP — is { error: { code, message, retryable } }. Codes are stable; a code, once shipped, is never repurposed.

CodeHTTPMeaningRetryable
api_key.missing401No API key on the request.No
api_key.invalid401Key is invalid, revoked, or unknown.No
api_key.out_of_scope403Key isn't scoped to the requested Linked Account.No
api_key.spend_cap_exceeded402Key has hit its own spend cap.No
api_key.rate_limited429Too many requests for this key in the current window.Yes
linked_account.session_expired409LinkedIn token expired or was revoked — reconnect via settings.No
linked_account.not_found404linked_account ID doesn't match a connected account.No
balance.insufficient402Not enough Balance for this post — top up and retry.Yes
content.rejected422Content failed the pre-publish spam/gibberish check.No
content.invalid400Request body is malformed (missing field, over length limit).No
internal_error500Something broke on our end.No

Limits

Pricing lives on the landing page — not duplicated here, since it's the one place the rate won't go stale in two locations.