API Reference
Overview
The HashBin.org API provides programmatic access to content upload, metadata, and account management. All API requests must be made over HTTPS.
Base URL
Authentication
Programs can use a scoped API key in either header; browser save flows and DPoP allowances are described below:
Each key holds only the scopes you give it (for example content:write to publish, balance:read to read the balance) and can carry limits: a monthly spending limit, largest upload, longest retention, allowed content types, or specific CIDs. Anything else answers 403. Keys, apps, settings and deposits always need your own sign-in. Create and narrow keys from your dashboard; see the full scope reference.
For third-party browser integrations, use the hosted SDK at https://hashbin.org/sdk/hashbin.js (or an integrity-pinned release) and follow the SDK Guide and browser client safety guide. Raw files are served from 256t.us; see content security.
Browser publishing and allowances
Prefer SDK publish() for a save button: HashBin reviews each document and charge on its own page, then returns a verified CID without giving the app credentials. For autosave or other delegated operations, use authorize() and save(). See the complete SDK examples and migration steps.
| Route | Access and contract |
|---|---|
GET /api/publish/app | Public; validates client_id and exact redirect_uri, returning app name and shared-origin warnings. |
POST /api/publish (review) | Account session only. Query: client_id, redirect_uri, retention_months. Raw body with its content type. Returns CID, byte size, retention, exact cost, balance and a signed five-minute approval. |
POST /api/publish (approve) | Same account session, identical bytes/type, and X-HashBin-Approval. Consumes approval once and checks price before upload. The hosted page keeps the approval ticket; your client receives the result through the SDK. |
GET /oauth/authorize | PKCE consent: client_id, registered redirect_uri, response_type=code, scope, state, code_challenge, code_challenge_method=S256. Optional spending_limit (USD/month), max_retention_months, max_upload_bytes, dpop_jkt. |
POST /oauth/token (code) | JSON: grant_type=authorization_code, client_id, code, redirect_uri, code_verifier. A bound code requires the matching DPoP proof. |
POST /oauth/token (refresh) | JSON: grant_type=refresh_token, required client_id, refresh_token; matching-key proof for a bound token. Optional scope requests fewer granted permissions. |
GET /oauth/dpop-nonce | Returns {nonce}, valid for five minutes. No credential required; response is not cacheable. |
POST /oauth/revoke | JSON: {token, token_type_hint: "refresh_token"} revokes the grant/family, including an already-rotated refresh token. Access-token revocation also identifies the grant. Invalid/already-revoked input returns 200 quietly. |
Hosted saves accept 1 byte to 5 MiB; fragment redirect handoffs accept up to 512 KiB. These limits are separate from the raw upload API's 90 MB ceiling. Publish rejects multipart reinterpretation and binds the user, app, bytes, type, retention and price. Errors include 400 approval_mismatch, 400 approval_expired, 409 approval_used and 409 price_changed; a failed approval requires a new review.
Token lifetime and DPoP
Authorization codes last 10 minutes and work once; access tokens last 15 minutes. New refresh tokens expire after seven idle days, and a family lasts at most 90 days despite rotation. An earlier grant expiry also applies. SDK consent defaults to 30 days and offers 1, 7, 30, 90 or 365 days, or no grant expiry. Rotated-token reuse revokes the entire grant.
New SDK grants include dpop_jkt on authorization and receive token_type: "DPoP". Proofs use ES256, typ: "dpop+jwt", and a public P-256 JWK. Claims are exact htm, absolute query/fragment-free htu, integer iat within 60 seconds, unique 16–200-character jti, and the server nonce. Resource proofs also include ath, the Base64URL SHA-256 hash of the access token. The main API and dedicated content worker share replay checks. Token proof failures return 400 invalid_dpop_proof; invalid resource credentials return 401.
Raw OAuth without DPoP still issues bearer tokens; existing tokens are not retroactively bound. SDK storage and refresh locking are described in the browser safety guide.
Registration and recovery
Known GitHub/GitLab shared hosts, or origins used by another registered app, require shared_origin_acknowledged: true on app creation or redirect updates. Otherwise the API returns 400 shared_origin_acknowledgement_required with affected origins, app names and a guide link. Active registrations determine CORS; paths do not isolate sibling apps.
| Account-session route | Result |
|---|---|
GET /api/account/authorizations | authorizations with limits, monthly spend, last use and recent uploads, plus bounded security_events for first use, refresh reuse revocation and reaching 80% of a monthly cap. |
DELETE /api/account/authorizations/:id | A grant ID revokes that grant; an app ID revokes its grants. |
DELETE /api/account/authorizations | Revokes every OAuth grant/refresh family. Separately managed API keys remain active. |
Authentication
Get current user session information (signed-in session only)
Response
List all API keys for authenticated user
Response
Create a new API key
Request Body
Response
Important: The key value is only shown once. Store it securely.
Balance
Get current wallet balance
Response
Content
Upload new content (requires authentication). The cost is deducted from your balance. Maximum upload size is 90 MB; larger requests get 413 Payload Too Large.
Send the raw bytes as the request body, or a multipart/form-data body with a content file field (and an optional retention_months field). The server computes the 256t hash.
Request
| Name | Where | Description |
|---|---|---|
| Content-Type | header | MIME type stored with the content (for raw bodies), or multipart/form-data |
| max_cost_cents | query | Optional per-upload ceiling: non-negative safe integer in cents. Invalid values return 400 invalid_max_cost; a higher charge returns 409 max_cost_exceeded before debit/storage, including duplicate extensions. Separate from the grant's monthly cap; SDK save(content, {maxCostCents}) sends it. |
| retention_months | query or form field | Whole months of retention, minimum 1 (default 1). OAuth uploads use the user's default narrowed by the grant's retention cap. |
Response
Get content metadata, including the dedicated content URL
Parameters
| Parameter | Type | Description |
|---|---|---|
| cid | string | REQUIRED 256t content hash |
Response
Download raw content from the isolated content domain
Response
Binary content with the original content type and restrictive headers such as Content-Security-Policy: default-src 'none'; sandbox.
Check if content exists
Response
Error Responses
All errors follow a consistent format:
HTTP Status Codes
| Code | Description |
|---|---|
| 200 | Success |
| 400 | Bad Request - Invalid parameters |
| 401 | Unauthorized - Missing or invalid authentication |
| 402 | Payment Required - Insufficient balance |
| 404 | Not Found - Resource does not exist |
| 429 | Too Many Requests - Rate limit exceeded |
| 500 | Internal Server Error |