Base URL
https://api.unisave.io/v1
For self-hosted instances, replace with your own domain.
Authentication
Most endpoints require a valid access token in the Authorization header:
Authorization: Bearer <accessToken>
Access tokens are short-lived JWTs. Use the refresh endpoint to obtain new ones.
Refresh tokens are passed in JSON request bodies only — never in headers or query strings.
- Content-Type:
application/json for all request bodies
- Timestamps: ISO 8601 / RFC 3339 in UTC (e.g.,
2026-04-12T10:30:00Z)
- IDs: Client-generated UUIDs for bookmarks and collections (
clientBookmarkId, clientCollectionId)
All 2xx responses return JSON objects. The shape varies by endpoint — see individual endpoint docs.
Error Envelope
All non-2xx responses use a consistent error envelope:
{
"code": "error-code-string",
"message": "Human-readable description",
"requestId": "uuid-for-tracing"
}
The requestId is also returned in the X-Request-Id response header.
Error Codes
These are stable strings your client can match on for UX and retry decisions:
| Code | HTTP Status | Description |
|---|
invalid-body | 400 | Malformed or missing request body fields |
invalid-query | 400 | Invalid query string parameters |
invalid-url | 400 | URL is not a valid http: or https: URL |
unauthenticated | 401 | Missing or expired access token |
forbidden | 403 | Insufficient permissions |
pro-required | 403 | Feature requires a Pro subscription |
not-found | 404 | Resource does not exist |
method-not-allowed | 405 | HTTP method not supported for this path |
conflict | 409 | Resource conflict (e.g., duplicate) |
identity-already-in-use | 409 | OAuth identity linked to another account |
quota-exceeded | 429 | Enrichment quota exhausted (free tier) |
internal | 500 | Unexpected server error |
not-configured | 501 | Feature not yet configured on this instance |
timeout | 503 | Request processing timed out |
Unknown error codes should be treated like internal — show a generic error and allow safe retry.
Rate Limiting
Auth endpoints (/v1/auth/*) are rate-limited to 2 requests/second sustained with a burst of 10 per IP.
All authenticated endpoints have a 30-second timeout per request.
Endpoint Summary
| Area | Method | Path | Auth | Description |
|---|
| Auth | POST | /auth/anonymous | None | Create anonymous user |
| Auth | POST | /auth/oauth/google | Optional | Google sign-in or link |
| Auth | POST | /auth/oauth/apple | Optional | Apple sign-in or link |
| Auth | POST | /auth/refresh | None | Rotate tokens |
| Auth | POST | /auth/logout | None | Revoke refresh token |
| User | GET | /me | Required | User profile + Pro status |
| User | DELETE | /account | Required | Delete account |
| Bookmarks | GET | /bookmarks/delta | Required | Delta sync |
| Bookmarks | PUT | /bookmarks/{id} | Required | Upsert bookmark |
| Bookmarks | DELETE | /bookmarks/{id} | Required | Soft-delete |
| Bookmarks | PUT | /bookmarks/{id}/collections | Required | Set collections |
| Collections | GET | /collections/delta | Required | Delta sync |
| Collections | PUT | /collections/{id} | Required | Upsert collection |
| Collections | DELETE | /collections/{id} | Required | Soft-delete |
| Enrichment | POST | /enrichment/enqueue | Required | Queue enrichment |
| Enrichment | GET | /enrichment/status | Required | Check status |
| Search | GET | /search/bookmarks | Required | Hybrid search |
| Recall | GET | /recall | Required | Memory recall (Pro) |
| Subscriptions | POST | /subscriptions/apple/verify | Required | Verify Apple sub |
| Subscriptions | POST | /subscriptions/google/verify | Required | Verify Google sub |
| Ops | GET | /healthz | None | Health check |