https://handwrites.io/api/v1. OAuth token and revocation endpoints use https://handwrites.io/api/oauth. The downloadable specification is OpenAPI 3.1, API version 1.0.0.Authorization: Bearer <token> on authenticated operations. JSON bodies need Content-Type: application/json; text replacement uses text/plain or text/markdown; OAuth uses application/x-www-form-urlencoded. A 204 response has no JSON body. File and whole-document audio requests redirect by default; ?redirect=false returns a signed link as JSON.hw_…) and fine-grained tokens (hwf_…) act for their creator. OAuth access tokens (hwo_…) act for the person who granted consent. Scope checks and document access both apply. Fine-grained tokens expire within 30 days and can be limited to 1–50 chosen documents. A document-limited token cannot register webhooks. Organisation policy can further restrict credentials and features.GET /me needs authentication but no named scope. DELETE /me is a session-only app operation: personal keys and OAuth tokens cannot delete an account. It returns 202 with a scheduled purge time, disables the account and revokes sign-in access immediately. Data is purged after 30 days unless support restores the account. An outside integration should direct the user to Handwrite account controls.{"error":{"code":"not_found","message":"Document not found."}}error and error_description.| Status | Client action |
|---|---|
| 400 | Correct the body, content type or parameter. |
| 401 | Acquire a valid credential; do not retry a revoked token forever. |
| 402 | A plan or entitlement is required; show the relevant account action. |
| 403 | Check scopes, document role and effective organisation policy. account_deleted stops account activity. |
| 404 | Missing or inaccessible resource; do not infer whether another user's document exists. |
| 409 | Reconcile conflict; live_session_active means use the socket workflow for text. For idempotency_in_progress, honor Retry-After. |
| 412 | If-Match is stale. Fetch the latest document and reconcile the local edit. |
| 413 | Reduce text, file or audio size as the operation requires. |
| 422 | Fix a semantic error such as unreadable file, empty text or reused idempotency key. |
| 429 | Distinguish rate_limited from quota_exceeded; honor Retry-After where present. |
| 500/502/503 | Preserve the user's work. Retry with bounded backoff only when the operation is safe to repeat. |
Request-Id, Handwrite-Requests-Limit and Handwrite-Requests-Remaining. An early authentication or edge rejection may not include every header. Default daily limit: 20,000 requests per key, or account for app sessions, resetting at midnight UTC. Bursts are approximately 120 requests with 10/second refill. The edge limit is 2,000 API requests per five minutes per IP. Use returned limits rather than assuming every account has the default.dailyRequests policy value, but the public request counter uses the global daily cap rather than that policy value. Do not treat a returned dailyRequests setting as an enforced per-organisation request budget. Monthly audio policy limits are a separate control.Idempotency-Key support it: in this edition, document creation, upload start, upload commit and sharing. Use a UUID for each logical action. For a retry keep the same credential, method, path/query, If-Match and exact body. Successful JSON responses up to 64 KiB are stored for 24 hours. Replay returns Idempotent-Replayed: true; a different request under that key gives 422 idempotency_mismatch. Do not blindly replay an OAuth refresh or a non-idempotent mutation after an uncertain outcome.revision, contentSha256, contentLength, createdAt and updatedAt. Text is separate at /documents/{id}/content. Where supported, send the quoted revision ETag in If-Match to avoid overwriting someone else's edit. A 412 requires reconciliation, not an unconditional overwrite.cursor; pass it unchanged until null. Default page size is 50, maximum 100. Ordinary document listing is newest first. Incremental listing with since=<ISO timestamp> is oldest first and includes deletion tombstones for 30 days. Keep a sync checkpoint only after every page is successfully processed. Deduplicate by document ID and revision; after a long offline period, rebuild the accessible inventory rather than expecting expired tombstones. shared=true lists shared documents; normal listing covers owned documents.match=all|any. It is not full-text indexing of arbitrarily large files. Export includes owned documents with text, not original file bytes; settings are included only with settings:read.uploadId and matching file metadata. Never send your API credential to the storage URL. Upload links last 900 seconds; original-file download links last 300 seconds. Treat them as bearer access to the file.extracted.error reports failure; retain the file and offer a manual text correction path. Explicit extraction replaces text and should use If-Match.viewer and editor. The owner is implicit. Principal values must be URL encoded in paths. Email sharing always returns a pending invitation (202), whether the address already has an account or not. Invitations expire 14 days after the last invite and require the matching verified email to accept. Do not use email sharing as account lookup. The current implementation does not send invitation email; recipients find the invitation after signing in with that verified address.Handwrite-Signature over the exact raw body before parsing: HMAC-SHA256 of <t>.<body> with the endpoint secret. Compare signatures in constant time and reject timestamps outside a five-minute window. Deduplicate Handwrite-Event-Id. Return 2xx within 10 seconds and process durable work asynchronously. There are eight delivery attempts total, with exponential delays of approximately 1, 2, 4, 8, 16, 32 and 64 minutes; redirects are failures. Recover missed notifications through document sync rather than treating webhooks as the sole source of truth./voices. /audio/stream returns binary audio, up to 12,000 characters per response, and accepts text up to 200,000 characters. Continue at Handwrite-Next-Offset while present. /audio/speech returns base64 audio and word timings: explicit text is limited to 1,500 characters; document requests use chunks of about 1,400. Continue with nextOffset until null. Offsets and word indices are UTF-16 code units, not bytes. Timings are seconds relative to that audio chunk./quota and handle quota failure. Supplying a document ID for speech also requires documents:read and document access.applied and kept in the response. Unknown, local-only and policy-locked settings must not be silently retried. Device storage, local sync switches and app lock settings are not remotely configurable through this endpoint.https://handwrites.io/oauth/authorize, with response_type=code, client_id, exact registered redirect_uri, space-separated scope, random state, S256 code_challenge and code_challenge_method=S256.state and iss=https://handwrites.io before exchanging the code. Codes last 60 seconds and are single-use. Access tokens last 3,600 seconds. Refresh tokens rotate on every use, expire after 30 idle days, and cannot extend the authorization beyond 365 days. Serialize refreshes and atomically store the replacement token: replaying a used refresh token revokes the whole authorization. There is no implicit, password or client-credentials grant for personal data. Revoking a refresh token revokes its authorization; revoking an access token revokes that token./api/v1/orgs/**, internal worker callbacks, staff administration and same-origin portal endpoints. The unauthenticated /openapi.json endpoint returns the full upstream spec, not this filtered edition. Machine-readable schemas describe the current contract; use the integration notes for additional policy/plan failures not exhaustively enumerated in every operation response list.403 webhooks_moved; new receivers belong to teams/classes and are managed by an administrator in the organisation Developer tab. Existing personal receivers continue to receive events in that code. Deployment of this change and live runtime behavior were not verified here. Treat the registration reference as the published contract, not proof that new personal registration is available. The Team and Classroom editions describe the organisation workflow.