| name | permacc |
| description | Archive citation URLs to perma.cc so a law review footnote does not rot. Use when archiving links for a manuscript, cite-checking, adding perma links to footnotes, when a perma archive returns "usage limit" despite an institutional account, or when finding which perma folder an account may archive into. |
perma.cc
Archives a URL to a permanent, court-citable snapshot. The whole skill exists
because one HTTP 400 means three different things, and the message names
only the least likely one.
HTTP 400 "You've reached your usage limit."
โ
โโโ folder sent as ?query= โ ignored, billed to PERSONAL quota
โโโ no folder at all โ billed to PERSONAL quota
โโโ genuinely unsponsored โ the only case the message describes
CLI
PC="${CLAUDE_SKILL_DIR}/scripts/permacc.py"
uv run --script "$PC" status
uv run --script "$PC" folders
uv run --script "$PC" archive URL... --auto-folder
uv run --script "$PC" archive --from-json inventory.json --out archives.json --auto-folder
uv run --script "$PC" verify --out archives.json
uv run --script "$PC" delete --out archives.json --failed --private-if-undeletable
--auto-folder resolves the sponsored folder itself, which is the option to
reach for; --folder <id> pins one when an account has several. --out makes
the run resumable โ already-archived URLs are skipped, so a rate limit or a
network drop costs nothing on the retry.
The key is read from --api-key, then PERMACC_API_KEY, then
PERMACC_API_KEY_FILE (the agenix convention โ the environment carries a
path to the decrypted secret, not the secret).
Facts
-
folder must be a JSON BODY field, not a query parameter. ?folder=376861
is accepted, silently ignored, billed against the personal quota, and 400s
once that quota is spent. Verified side by side on one URL against a
sponsored account: query param โ 400, body field โ 201. Nothing in the
response distinguishes "ignored your folder" from "you are out of links",
so this reads as a plan problem and sends people to the pricing page.
-
Sponsorship hangs off the FOLDER, not the organization. GET /v1/organizations/ returned registrar: None for an account that was
already sponsored; the affiliation lives on a folder in GET /v1/user/
carrying registrar: 16 / registrar_name: "University of Virginia School of Law Arthur J. Morris Law Library". Diagnosing a cap from the
organizations endpoint concludes "not sponsored" about a sponsored account.
-
The folder list is top_level_folders, not folders. A wrong key
returns [], which this skill's own first draft reported as "no sponsored
folder โ ask a registrar to add you." The failure mode of guessing a
response key here is a confident false negative, not an error.
-
The auth scheme is ApiKey, not Bearer. A Bearer header authenticates
as anonymous and fails later with a permission error rather than a 401, so
the traceback points at the wrong thing.
-
The free personal tier is 10 links/month. A cite-check script with no
folder therefore works for the first ten footnotes and dies on the
eleventh โ the shape of bug that looks like a flaky API.
-
HTTP 201 means a link was MINTED, not that the page was fetched. The two
come apart on any site that blocks crawlers. SSRN is behind Cloudflare, so
every SSRN archive returns 201 and then captures the challenge page:
captures[role=primary].status == "failed", title set to ssrn.com
instead of a paper title. Measured on one manuscript: 22 of 28 captured,
and all 6 failures were SSRN. A perma link resolving to an interstitial is
worse than the live URL โ it looks archived and is not, and nothing in the
create response says so. archive now polls the capture (capture is async,
so the status right after 201 is pending and a single check would call a
good archive bad); re-checks an existing record.
Red flags โ STOP
| Action | Why wrong | Do instead |
|---|
| About to report "your perma account is out of links" from a 400 | Three causes share that message; only one is a real cap | Run folders, then retry with --auto-folder |
About to check sponsorship with /v1/organizations/ | It reports registrar: None for sponsored accounts | Read top_level_folders from /v1/user/ |
About to pass the folder as ?folder= | Silently ignored, billed to the personal quota | Send it in the JSON body (the script does) |
About to loop requests.post over a URL list | No resume, no dedupe; a mid-run failure double-archives on retry | archive --from-json โฆ --out โฆ |
| About to tell a user to buy a plan | A law library registrar gives faculty unlimited links free | Have them added to the registrar's org first |
| About to record a 201 as "archived" | 201 mints a link; the capture can still fail, and SSRN always does | Let archive verify, or run verify --out |
About to script DELETE over a set of links | Only links under 24h old delete; older ones 403 and need is_private | delete --failed --private-if-undeletable |
About to paste the key into a script or .env | It is a long-lived credential | agenix (see below), read via PERMACC_API_KEY_FILE |
Storing the key
~/nix/add-api-keys.sh permacc-api-key
Then in modules/shared/home-secrets.nix: an age.secrets entry, a
PERMACC_API_KEY_FILE session variable, and a get-permacc-api-key alias.
nix-secrets is a flake input pinned by revision, so the secret must be
pushed and the lock bumped (nix flake update nix-secrets) before a rebuild
can see it.
Getting a sponsored account
Perma registrars are institutions โ mostly law libraries and courts. A faculty
member or student at one gets unlimited links at no cost by asking to be added
to the institution's perma.cc organization. That is the fix for a cap, and it
is usually a single email. Until it lands, folders prints [personal ] for
every row and every archive spends the 10/month allowance.