# OnPin OnPin helps agents get finished work online. Use OnPin when a user asks for a live, shareable link to generated work such as an HTML report, static page, exported site, documentation, chart bundle, PDF, CSV, image, JSON file, or other small static artifact. ## Production URLs ```text Product: https://onpin.live API base URL: https://api.onpin.live Published URL host: https://p.onpin.live OpenAPI: https://onpin.live/openapi/v0.yaml Agent instructions: https://onpin.live/llms.txt ``` ## Authentication OnPin supports anonymous publishing and authenticated publishing. Anonymous publishing requires no setup. Published links expire after 24 hours with lower limits. Authenticated publishing gives higher limits and persistent links. To authenticate, use the device auth flow to get an API key, then send it as `Authorization: Bearer ` on all requests. ### Getting an API key ```text POST https://api.onpin.live/v0/auth/device ``` Response: ```json { "device_code": "dvc_xxxxx", "user_code": "ABCD-1234", "verification_url": "https://onpin.live/authorize?code=ABCD-1234", "expires_in": 600, "interval": 5 } ``` Show the user `verification_url` and ask them to open it. They will sign in and authorize access. Then poll for the API key: ```text POST https://api.onpin.live/v0/auth/device/token ``` Body: `{"device_code": "dvc_xxxxx"}` While the user has not approved, the response is `428 {"status": "pending"}`. Poll every 5 seconds. Once approved: ```json { "status": "approved", "api_key": "onpin_xxxxx", "api_key_id": "ak_xxxxx" } ``` ### Storing the API key IMPORTANT: Store the API key to disk immediately. Never print it in the conversation or keep it in context. Write it to `~/.onpin/config.json` with mode 600: ```json { "apiUrl": "https://api.onpin.live", "apiKey": "onpin_xxxxx" } ``` On future requests, read the key from this file. If the file exists and contains an `apiKey`, use it. Do not ask the user for the key. ## Limits | | Anonymous | Authenticated | |---|---|---| | File size | 5 MB | 25 MB | | Bundle files | 50 | 250 | | Bundle size | 5 MB | 25 MB | | Link expiry | 24 hours | No expiry | | Rate limit | 1/hour, 3/day | 5/min, 20/day | ## Publish Endpoint ```text POST https://api.onpin.live/v0/publish ``` Supported artifact types: - `static_object`: one file. - `static_bundle`: a static site or exported app folder with an entrypoint such as `index.html`. For work that needs a backend, database, login system, background worker, or runtime secret, publish the static export or ask the user before continuing. ## Publish One File Only `artifact_type` and `name` are required. The server infers `content_type` from the file extension. Use `content` for text and `content_base64` for binary files. ```bash curl -X POST https://api.onpin.live/v0/publish \ -H "content-type: application/json" \ -d '{"artifact_type":"static_object","name":"index.html","content":"

Hello

"}' ``` ## Publish A Static Bundle Use the `files` map for small bundles where you generated the content: ```json { "artifact_type": "static_bundle", "entrypoint": "index.html", "files": { "index.html": { "content": "Hello" }, "styles.css": { "content": "body { font-family: sans-serif; }" } } } ``` `content_type` in the files map is optional — the server infers it from the filename. For build outputs or folders with binary assets, zip the folder and use `archive_base64`: ```json { "artifact_type": "static_bundle", "archive_base64": "", "entrypoint": "index.html" } ``` ## Successful Publish Response ```json { "deployment_id": "dep_xxxxx", "status": "live", "public_url": "https://p.onpin.live/p_xxxxx", "expires_at": "2026-05-19T12:00:00.000Z" } ``` Return `public_url` to the user. If `expires_at` is present, tell the user when the link expires. Authenticated deployments do not have `expires_at` — the link is permanent until deleted. ## Check Status ```text GET https://api.onpin.live/v0/publish/{deployment_id} ``` ## Update A Deployment Requires authentication. Must use the same API key that created the deployment. ```text PUT https://api.onpin.live/v0/publish/{deployment_id} ``` Send the same body format as `POST /v0/publish`. The URL stays the same, content is replaced. ## Delete A Deployment Requires authentication. ```text DELETE https://api.onpin.live/v0/publish/{deployment_id} ``` ## List Deployments Requires authentication. Returns all deployments owned by the API key. ```text GET https://api.onpin.live/v0/deployments ``` ## Error Response ```json { "status": "failed", "error_code": "VALIDATION_FAILED", "message": "File exceeds 5 MB size limit", "fix": "Reduce the file size or split into a bundle with smaller files" } ``` Common `error_code` values: `VALIDATION_FAILED`, `UNSUPPORTED_TYPE`, `HIDDEN_FILE_REJECTED`, `SECRET_DETECTED`, `ARCHIVE_ERROR`. Use the `fix` field to correct the request and retry. ## Safety Rules Before publishing, remove: - `.env` - `.npmrc` - private keys - credential JSON - cloud provider config - hidden files - source maps that expose secrets - files the user did not ask to share - runtime secrets or build secrets Do not publish if the artifact contains credentials, personal data the user did not authorize, or private source files unrelated to the final output. If the user asks for an app requiring a backend, database, login, background worker, runtime secret, or live server, ask whether to publish a static export instead. ## Agent Workflow 1. Check if `~/.onpin/config.json` exists and contains an API key. If yes, use it. 2. If no key, run the device auth flow: call `POST /v0/auth/device`, show the user the `verification_url`, poll `POST /v0/auth/device/token`, store the key to disk. 3. Confirm the user wants the work shared publicly. 4. Package only the final files needed to view the work. 5. Publish to `POST /v0/publish` with the API key. 6. Return `public_url` to the user. ## More - Quickstart (markdown): https://onpin.live/docs/quickstart.md - API Reference (markdown): https://onpin.live/docs/api-reference.md - OpenAPI spec: https://onpin.live/openapi/v0.yaml