Why teams use it instead of building their own OCR

Generic OCR turns handwriting into plain text. Scriveno's pipeline keeps the actual structure - equations stay equations, hand-drawn diagrams and chemical structures are detected and included rather than discarded, and one transcription drives real typeset LaTeX, a compiled PDF, and an editable DOCX with genuine Word equations, not just three separate best-effort exports.

  • Async submit / status / download, or a webhook when a job finishes
  • HMAC-signed webhook payloads, with an idempotency key to make retries safe
  • Output as PDF, DOCX, Markdown, or raw LaTeX from the same request
  • Free to join - pay only $0.003 per page converted, no monthly minimum

Create a free account to get your API key

Quick start

Submit a file, poll for status, then download - the same pipeline the web app uses.

Submit

curl -X POST https://api.scriveno.com/api/v1/convert \
  -H "Authorization: Bearer isk_live_YOUR_API_KEY" \
  -H "Idempotency-Key: <your-own-uuid>" \
  -F "file=@notes.pdf" \
  -F "webhookUrl=https://example.com/webhooks/scriveno"

Check status

curl https://api.scriveno.com/api/v1/convert/{jobId} \
  -H "Authorization: Bearer isk_live_YOUR_API_KEY"

Download

curl https://api.scriveno.com/api/v1/convert/{jobId}/download/pdf \
  -H "Authorization: Bearer isk_live_YOUR_API_KEY" \
  -o result.pdf

List your jobs

curl "https://api.scriveno.com/api/v1/convert?page=1&pageSize=20" \
  -H "Authorization: Bearer isk_live_YOUR_API_KEY"

Cancel a job

curl -X POST https://api.scriveno.com/api/v1/convert/{jobId}/cancel \
  -H "Authorization: Bearer isk_live_YOUR_API_KEY"

Webhooks

Pass a webhookUrl when you submit and Scriveno POSTs this to it once the job reaches completed or failed - no need to poll if you don't want to.

POST <your webhookUrl>
Content-Type: application/json
X-Scriveno-Signature: sha256=<hex-encoded HMAC-SHA256 of the raw body below>

{
  "jobId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "status": "completed",
  "totalPages": 3,
  "completedPages": 3,
  "errorMessage": null,
  "downloads": {
    "pdf": "https://api.scriveno.com/api/v1/convert/{jobId}/download/pdf",
    "docx": "https://api.scriveno.com/api/v1/convert/{jobId}/download/docx",
    "markdown": "https://api.scriveno.com/api/v1/convert/{jobId}/download/markdown",
    "latex": "https://api.scriveno.com/api/v1/convert/{jobId}/download/latex"
  }
}

Verify the signature before trusting a payload - anyone can POST to your endpoint. Compute an HMAC-SHA256 of the exact raw request body using your key's webhook secret (shown once, at key creation time), hex-encode it, and compare to everything after sha256= in the X-Scriveno-Signature header using a constant-time comparison.

// pseudocode
expected = hex(hmac_sha256(webhookSecret, rawRequestBody))
received = header["X-Scriveno-Signature"].removePrefix("sha256=")
if !constantTimeEquals(expected, received): reject