I have started asking LLM agents to maintain two kinds of content on this site: blog posts and the finished models in my 3D project library. The work itself is straightforward, but the old path was not. An agent might need to drive a browser, wait for me to complete a login, work around an admin form, or prepare a database migration that would not become visible until the next application release. That was too much machinery for changing content that the application already knows how to store.
I wanted a narrow way to give an agent access when I ask for a specific change, without giving it my admin password or leaving a permanent credential in a conversation. The result is a content API for blog posts and 3D projects, plus a short-lived handoff flow designed for an agent to use directly.
The handoff begins on the admin dashboard. It shows a 12-character temporary code and a five-minute countdown in a compact, collapsible card. I can copy the code, cycle it immediately, or disable code exchange entirely. The code is derived with HMAC from the server secret, the admin account, and a random per-account nonce. The database keeps the nonce, issue time, failure state, and scope policy; it does not need to keep the displayed code as reusable plaintext. A successful exchange rotates the nonce, so the same code cannot be used twice. Five failed attempts lock exchange for five minutes.
That temporary code is exchanged for a random bearer token that lasts seven days. The raw token is returned once. The command-line client writes it to an owner-only file with mode 0600, while the server stores only its SHA-256 digest. The token can be revoked from the admin page, and disabling the handoff also revokes the tokens created through it. The client reports the expiry and a renewal date one day before it, so an agent can ask me for a fresh five-minute code before the weekly credential runs out.
Access is split into blog and project read, write, and delete scopes. The normal handoff grants blog and project read/write access, while delete remains an explicit admin setting. One early version let the agent request its own subset during exchange. That sounded conservative, but it meant a token created while updating a 3D project could not later be reused for a blog post. The server now grants the complete scope set selected on the admin page. The agent no longer makes that policy decision, and an existing narrow token can be renewed immediately when its access needs to be replaced.
The token is only a technical capability. It does not turn possession into permission to change anything it can reach. The repository instructions still require a request naming the blog post, project, file, or deletion. That separation lets the weekly token remain useful across ordinary work without treating every available endpoint as an authorized action.
The API itself uses JSON collection and item routes for listing, reading, creating, updating, publishing, unpublishing, and deleting blog posts and projects. Creation requires an explicit normalized slug, which gives an agent something stable to check after an uncertain request instead of blindly repeating a POST. Item reads return a strong ETag. Updates, uploads, and deletions must send that exact value in If-Match; stale state returns HTTP 412 and forces the agent to read the current item again before changing it.
The project side also accepts final model files, printable resources, and writeup media. Those uploads pass through the same file validation and preview generation used by the admin interface. The response includes the updated project, file metadata, and public URLs, which lets the client verify the completed change without scraping a browser page. Permanent deletion is more constrained: published entries must first return to draft, and project files or media can only be removed from a draft project.
A small command-line client ties the pieces together. It discovers the private token, checks its status, builds authenticated requests, streams large uploads, preserves ETags, and prints structured results without printing the bearer token. For a new blog post, an agent lists the current entries, prepares a JSON payload, sends blog create, then reads the new numeric ID back with blog get to verify the title, body, status, and public URL.
The instructions around that client turned out to matter as much as the endpoints. During the first real blog attempt, an agent still followed the old migration workflow when live access was unavailable. I removed the blog migration generator and made the content API the only delivery path for authored posts. If access is technically blocked, the agent now keeps a ready-to-submit JSON draft and reports the exact step needed to resume. It does not quietly turn the post into an application deployment.
The finished workflow is deliberately small from my side. About once a week, I can give an agent the current five-minute code. It stores a revocable seven-day token and can use the same credential for the blog and 3D library work I request. I keep control of the scope policy and every content instruction, while the agent gets a direct, machine-readable path to do the work and verify the result.