SimpleSharing: the complete agent guide
SimpleSharing (https://simplesharing.app) lets people and their agents share and co-edit the things agents make: HTML pages, markdown, Claude skills, and soon PDFs, Mermaid diagrams, and design files. An agent can create the account, verify it, upload, share, check limits, and (soon) manage the plan and payment, all through the HTTP API, the MCP server, or the CLI. By Nekko Labs.
This file is the whole protocol. If you are an agent that was asked to "create me a SimpleSharing account", start at section 2 and follow it top to bottom. Every API response and every error tells you what to call next in a next field, so you never have to guess.
Base URL: https://api.simplesharing.app (self-hosted and local installs differ; the person or the prompt that sent you here will say). All bodies are JSON. Authenticated calls send Authorization: Bearer <token>.
Machine-readable schema: https://simplesharing.app/openapi.json (OpenAPI 3.1, generated from the same validators the server runs).
1. The two things to know
- Accounts have a status.
pending(created, not yet verified),waitlist(verified, waiting for a beta invite),active(usable),suspended,deleted. During the beta a new account lands onwaitlistunless its email has an invite code redeemed or was approved. - Every error has a
next. Error bodies look like:
{ "error": { "code": "code_expired", "message": "That code has expired. Request a new one.",
"next": { "action": "resend_code", "method": "POST", "path": "/v1/auth/resend",
"description": "Request a fresh code with POST /v1/auth/resend {email} ..." } } }
Act on next. A 429 also carries a retry-after header in seconds: wait that long, then retry once.
2. Create an account for a person (the signup flow)
You need the person's email address. You do not need a browser, a password, or OAuth.
Step 1: signup
POST /v1/auth/signup
Content-Type: application/json
{ "email": "person@example.com", "source": "landing", "agent": "report-writer" }
sourceis where the request came from. Use the value in the prompt that sent you (for examplelanding,docs,nekko-agent,partner:<name>), ormcp/cli/unknown.agentis an optional label for yourself; it names the token.
Response 201:
{
"accountId": "9b2e…",
"status": "pending",
"token": "sst_<id>_<secret>",
"tokenExpiresAt": "2026-10-20T00:00:00.000Z",
"claimUrl": "https://simplesharing.app/claim/<48 hex>",
"codeSent": true,
"expiresAt": "2026-10-26T00:00:00.000Z",
"quota": { "files": 5, "bytes": 5242880 },
"beta": "waitlisted",
"next": { "action": "verify_code", "method": "POST", "path": "/v1/auth/verify", "description": "…" }
}
What you now hold:
accountId: the account. Report it to the person.token: a 24-hour token limited to the pending quota (5 files, 5 MB total). You can already upload and share small things with it (section 3) so you can finish your task before the person verifies.claimUrl: an alternative to the code. If the person opens it in a browser while signed in to SimpleSharing with the same email, the account activates without a code. Show it to them as a fallback.codeSent: a 6-digit code was emailed to the address. Iffalse, a code was sent less than 60 seconds ago; use that one.expiresAt: unverified pending accounts are deleted after 7 days.
If the response is 409 account_exists, the email already has a verified account: follow next (log in, or redeem an invite if it is waitlisted). If it is 201 with "token": null, a pending account already existed; the code was re-sent and you should verify it (section 2, step 2) to get a full token.
Step 2: get the code
Tell the person: "I created your SimpleSharing account. A 6-digit code was emailed to person@example.com. Paste it here, or open while signed in." If you own the inbox (an agent mailbox), read the code yourself. The email subject is <code> is your SimpleSharing code.
Codes expire after 10 minutes and lock after 5 wrong attempts. To get a fresh one:
POST /v1/auth/resend
{ "email": "person@example.com" }
(60-second cooldown; 429 with retry-after if too soon.)
Step 3: verify
POST /v1/auth/verify
{ "email": "person@example.com", "code": "123456", "agent": "report-writer" }
Response 200:
{
"accountId": "9b2e…",
"status": "waitlist",
"token": "sst_<id>_<secret>",
"scopes": ["account", "beta"],
"beta": "waitlisted",
"next": { "action": "wait_or_redeem", "method": "POST", "path": "/v1/beta/redeem", "description": "…" }
}
- Save
token. It is long-lived. The pending token from step 1 is revoked at this point. Give the token to the person (or store it where they asked, for exampleSS_TOKENin their environment) and never log it. statusisactivewhen the email is approved for the beta, otherwisewaitlist.
Step 4: the beta
During the beta a waitlist account cannot upload. Two ways in:
- The person has an invite code:
POST /v1/beta/redeem { "code": "ABCDE-FGHJK" }with the token (or{ code, email }without one). The account becomesactiveimmediately. Codes are case-insensitive. Redeeming for an already-approved email does not consume a use. - No code: check standing with
GET /v1/beta/status(token) orGET /v1/beta/status?email=…and report the waitlistposition. Tell the person they will be emailed when approved; nothing else to do.
An email can also be approved before the account exists (an invite redeemed on the website, or an admin approval). That approval carries into signup, so verification lands straight on active.
Step 5: confirm
GET /v1/account
Authorization: Bearer <token>
{ "account": { "id": "…", "email": "…", "status": "active", "plan": "free", "source": "landing", "createdBy": "agent", … },
"usage": { "files": 0, "bytes": 0 },
"limits": { "files": 50, "bytes": 52428800 },
"beta": "approved",
"next": { "action": "use_account", … } }
Report status, plan, and limits to the person. You are done with signup.
Alternative: the claim URL
If the person prefers a browser: they sign in at https://simplesharing.app/login with the same email (email + 6-digit code) and open the claimUrl. That activates the pending account for them. You can preview what they will see with GET /v1/auth/claim/<token> (masked email, source, status, expiry). The claim endpoint needs their web session, so it is not something you call.
3. Upload and share
POST /v1/shares
Authorization: Bearer <token>
{ "filename": "report.html", "content": "<!doctype html>…" }
201 { "id": "k3…", "url": "/s/k3…", "kind": "html" }. The public link is https://simplesharing.app + url. Anyone with the link can view; no account is needed to view.
- Accepted today:
.md,.html/.htm, andSKILL.md(rendered as a Claude skill). PDFs, Mermaid, and binary files follow. - Each file is limited to 1 MB until object storage lands.
- Quota by status:
pending5 files / 5 MB,waitlistnothing,activefree plan 50 files / 50 MB.403 quota_exceededor403 beta_waitlisttell you what to do. GET /v1/shares/<id>reads a share back, with theversionit serves.
Protect a link: password, expiry, revoke
The account that uploaded a share owns it and can lock it down. Omitted fields stay as they are; null clears.
PATCH /v1/shares/<id>/settings
Authorization: Bearer <token>
{ "password": "blue-otter-42", "expiresIn": "7d" }
200 { "shareId", "hasPassword": true, "expiresAt": "…", "revoked": false, "pinnedVersion": null, "updatedAt" }. The password is never returned; tell the person what you set.
expiresIn:1h,1d,7d, or30d; orexpiresAtas an ISO date up to one year away;expiresAt: nullnever expires. The owner can extend at any time.password: nullremoves the password. Changing it signs every viewer out.revoked: truekills the link at once;revoked: falsebrings it back.- Works on every plan. The owner always sees their own share, expired or not.
A viewer (or another agent) with the password unlocks, then reads with the token:
POST /v1/shares/<id>/unlock { "password": "blue-otter-42" }
→ 200 { "token": "…", "expiresAt": "…" }
GET /v1/shares/<id>
x-share-token: <token>
The token lasts 12 hours, or until the link expires if sooner. Ten wrong guesses from one client in 15 minutes return 429. No content is ever sent before the check passes: a locked link answers 401 password_required, an expired one 410 share_expired, a revoked one 410 share_revoked.
Pin a version, keep editing
A link serves the latest version by default, so it never goes stale. To freeze what a client sees while you keep working, pin it:
GET /v1/shares/<id>/versions → [{ "version", "author", "createdAt" }, …] newest first
PATCH /v1/shares/<id>/settings { "pinnedVersion": "<version>" }
PUT /v1/shares/<id>/content { "content": "<!doctype html>…" }
PUT …/content publishes a new version (owner only, same file type). While pinned, viewers keep the pinned version and the response says "servedToViewers": false; { "pinnedVersion": "latest" } follows the newest again. A republish is refused with 409 document_leased while anyone holds a section lease on the document (section 4): finish or release the lease first.
4. Edit together without collisions
When several agents or people edit one document, lease the section first:
POST /v1/leases { "documentId": "…", "section": "intro", "holderId": "report-writer", "ttlSeconds": 300 }
Every outcome is 200 with { "status": "granted" | "queued" | "denied", "lease", "reason" }. granted: edit, then POST /v1/leases/<id>/release. queued: someone holds it, you are in a FIFO queue; do other work. denied (human_editing): a person is editing it; pick another section. Sections are keyed by heading slugs (## Setup → setup). Full protocol: https://github.com/nekko-labs/simplesharing-server/blob/main/docs/agent-collaboration.md
5. Plan, billing, and payment
Billing ships in the next slice. The endpoints will be GET /v1/account/limits, POST /v1/billing/checkout (returns a Stripe-hosted URL to hand to the person; card data never touches you), GET /v1/billing/portal, and discount-code application. Until then every account is on the free plan and GET /v1/account reports the limits.
6. Error codes you will meet
| code | HTTP | meaning | do |
|---|---|---|---|
invalid_input |
400 | body failed validation | fix the body per the message |
email_domain_blocked |
400 | disposable email domain | ask for a real inbox |
account_exists |
409 | email already verified | follow next (login / redeem) |
signup_rate_limited |
429 | too many signups from this IP, email, or domain | wait retry-after seconds |
code_invalid |
400 | wrong code | ask again; attempts left in the message |
code_expired |
410 | older than 10 minutes | POST /v1/auth/resend |
code_attempts_exceeded |
429 | 5 wrong attempts | POST /v1/auth/resend |
resend_cooldown |
429 | resent less than 60 s ago | wait retry-after |
no_pending_code |
400 | nothing outstanding | POST /v1/auth/resend |
account_not_pending |
404/409 | nothing to verify | follow next |
claim_invalid |
404 | bad claim link | start over with signup |
claim_email_mismatch |
403 | signed-in email differs | sign in with the account email |
invite_invalid / invite_expired / invite_exhausted |
400/410 | the invite code cannot be used | ask the person for another or wait |
beta_waitlist |
403 | uploads are locked until approved | redeem an invite or wait |
password_required |
401 | the link has a password | POST /v1/shares/<id>/unlock, then send x-share-token |
invalid_password / invalid_unlock_token |
401 | wrong password, or the token expired or the password changed | ask the person for the password and unlock again |
share_expired / share_revoked |
410 | the owner ended the link | ask the owner for a new link |
not_owner |
403 | only the uploading account can change a share | use the owner's token |
invalid_expiry / unknown_version |
400 | expiry in the past or over a year away; version not in this share's history | fix per the message; list versions first |
document_leased |
409 | someone holds a section lease | wait, or edit through a lease |
quota_exceeded |
403 | status/plan allowance used | verify (pending) or wait for billing |
unauthorized / unauthenticated |
401 | missing or bad token | use the token from verify |
forbidden |
403 | token lacks a scope | you are calling an admin endpoint |
rate_limited |
429 | general window | wait retry-after |
not_found |
404 | unknown id | check the id |
7. Other entry points
MCP server
npx simplesharing-mcp (stdio). Tools: account_signup, account_verify, account_status, beta_redeem, create_share, read_share (unlocks with a password), protect_share, pin_share, list_share_versions, republish_share, request_lease, release_lease (plus read_document, submit_edit when editing lands). The account tools work before any token exists; account_signup and account_verify return the token, and every tool takes an optional token argument that overrides SS_TOKEN. Config: SS_TOKEN, SS_API_URL.
CLI
export SS_TOKEN=sst_...
ss share ./report.html # prints the share link
ss get <shareId>
ss lease <docId> <section> --holder me
ss release <leaseId>
ss sync ./my-library --library <id> # two-way git sync of a folder with a library
Discovery
https://simplesharing.app/llms.txt: the short version.https://simplesharing.app/llms-full.txt: this file.https://simplesharing.app/openapi.json: OpenAPI 3.1.https://simplesharing.app/.well-known/agent-card.json: the A2A agent card.https://simplesharing.app/docs/agents: the human-readable twin of this guide.
8. Rules for a well-behaved agent
- Never log, print, or share a token or a verification code beyond the person who owns the account.
- Use the
sourceyou were given; it tells us which surfaces work. - Honor
retry-after; never loop on a 429 or aqueuedlease. - Report the
accountId, the accountstatus, and the next step to the person in plain words. - If the person did not ask for an account, do not create one.