Skip to content

ctx join

ctx join redeems an invite key on the server, adding you as a member of the repo it belongs to, and points the current project at that repo.

You are the teammate, not the owner: someone ran ctx invite mint and sent you a ctxi_… string. Run this once per project directory, after ctx login, and then ctx pull to fetch the knowledge that already exists.

Two things to get straight. The invite key is not a login credential — you must already be authenticated with your own token before joining, because the server records the membership against your user id. And joining does not fetch anything: it sets repo_id in .contexo/config.json and stops. If you have already joined the repo on the dashboard, skip this command entirely and use ctx remote set-repo, which lists the repos you belong to.

Terminal window
ctx join <invite-key>
ctx join # prompts: "Invite key: "

ctx join takes no flags.

FlagTypeDefaultDescription
--rootstringproject root directory (default: current directory)

Use --root when you want to join from outside the project directory — everything the command reads and writes is relative to that root.

Terminal window
ctx login
ctx join ctxi_kQ8Zt3vN1pR7sX2yB4dL6fH0jM5cW9uA
ctx pull

On success it prints Joined <repo_id> as <role>. 'ctx pull' to fetch existing pages. The role comes back from the server, not from the key: re-joining a repo you already own reports owner, because membership is idempotent and never downgrades an existing role.

Terminal window
echo "$INVITE_KEY" | ctx join

The prompt path reads one newline-terminated line from stdin and does not check for a terminal, so piping works — unlike ctx login --no-browser, which insists on a TTY. The newline matters: input that ends without one fails with join: read input: EOF.

Terminal window
ctx remote get
ctx remote get
Server: https://contexo.example.com
No repo configured (run 'ctx remote set-repo <id>')

That is the pre-join state — server configured, repo not. After a successful join the second line names the repo you were added to.

  1. Loads .contexo/config.json and .contexo/credentials.json from the project root. Both must already be populated: a bearer token, and server_url in config.json. Note that join reads the server URL from config.json only — it does not fall back to the copy stored in credentials.json the way ctx push does.
  2. POST /v1/repos/join with {"key": "<invite-key>"} and your token in the Authorization header.
  3. The server resolves the key to a repo, checks whether you are already a member, applies the plan’s member cap only if you are not, then inserts the membership with role member and returns repo_id plus your effective role.
  4. The CLI writes that repo_id into .contexo/config.json. Nothing else changes — no pages are downloaded, no git commit is made, and no page file under .contexo/ is touched.

Keys are time-boxed: ctx invite mint stamps them with a one-week expiry, and an owner can revoke one at any time with ctx invite revoke <keyId>. A key is not consumed by being used, so the same key can onboard several people until it expires or is revoked.

join: not authenticated — run 'ctx login --token <token>' first — no .contexo/credentials.json, or it holds an empty token. Sign in first.

join: server URL not set — run 'ctx remote set <url>' firstconfig.json has no server_url. ctx login normally sets this; you can also set it directly with ctx remote set.

an invite key is required — you pressed Enter at the prompt without pasting anything.

sync: join failed (404): {"error":"invite key not recognized"} — the key was mistyped, truncated by a chat client, or has been revoked.

sync: join failed (410): {"error":"invite key expired"} — past the one-week window. Ask the owner to mint a fresh one.

sync: join failed (403): {"error":"user session required"} — you are authenticated with a legacy shared API key, which maps to a sentinel admin rather than a person, so there is no user id to record a membership against. The same restriction blocks minting keys from that path:

ctx invite mint, authenticated with a legacy API key
Error: sync: mint invite key failed (403): {"error":"user session required to mint invite keys"}

Sign in with a personal access token instead.

A plain upgrade message with no status code — the server answered 402 and the CLI unwrapped its body, so what you see is the hosted plan’s own wording about the member cap on that repo. Adding a member who is already in the repo never hits this; only a genuinely new membership counts.