agentleFS
Sign inSign up

skills / rules

deepread-tech/skills/.cursor/rules/deepread-setup.mdc

Get started with DeepRead OCR. Automatically obtains an API key via device authorization flow (RFC 8628), then walks through first document, structured extraction, and blueprints.

Cursor rule4 starsChanged 6 days ago
  • Reads credentials
  • Sends data out
---
description: "Get started with DeepRead OCR. Automatically obtains an API key via device authorization flow (RFC 8628), then walks through first document, structured extraction, and blueprints."
alwaysApply: false
---

# Setup DeepRead

You are an AI agent helping a developer get started with DeepRead — an AI-native OCR API that extracts text and structured data from documents (PDFs, images) with 97%+ accuracy.

**API:** `https://api.deepread.tech`
**Dashboard:** `https://www.deepread.tech`
**Docs:** `https://www.deepread.tech/docs`

---

## Step 1: Get an API Key (Device Authorization Flow)

You (the agent) obtain an API key on behalf of the user. The user never needs to copy/paste a key — it goes directly to you.

> **CRITICAL — run the entire device flow as ONE terminal command block.**
> Shell variables do not persist between separate terminal executions. If you split this across multiple calls, the `device_code` will be lost and you will accidentally call `/v1/agent/device/code` again, getting a new code the user has never seen. Do it all in one script.

The complete flow — get code, open browser, poll, print key — in a single script:

```bash
# Get device code
dr_response=$(curl -s -X POST https://api.deepread.tech/v1/agent/device/code \
  -H "Content-Type: application/json" \
  -d '{"agent_name": "Cursor"}')

dr_device_code=$(echo "$dr_response" | jq -r '.device_code')
dr_user_code=$(echo "$dr_response" | jq -r '.user_code')
dr_uri=$(echo "$dr_response" | jq -r '.verification_uri_complete')
dr_interval=$(echo "$dr_response" | jq -r '.interval')

# Validate the response before proceeding
if [ "$dr_device_code" = "null" ] || [ -z "$dr_device_code" ]; then
  echo "ERROR: API did not return a device_code. Response: $dr_response"
  exit 1
fi

echo "Opening browser: $dr_uri"
open "$dr_uri" 2>/dev/null || xdg-open "$dr_uri" 2>/dev/null || echo "Open manually: $dr_uri"
echo "Waiting for approval of code: $dr_user_code"

# Poll until approved (use dr_ prefix to avoid variable name conflicts)
dr_api_key=""
for dr_i in $(seq 1 72); do
  sleep "$dr_interval"
  dr_result=$(curl -s -X POST https://api.deepread.tech/v1/agent/device/token \
    -H "Content-Type: application/json" \
    -d "{\"device_code\": \"$dr_device_code\"}")
  dr_api_key=$(echo "$dr_result" | jq -r '.api_key')
  dr_error=$(echo "$dr_result" | jq -r '.error')
  dr_prefix=$(echo "$dr_result" | jq -r '.key_prefix')

  if [ "$dr_api_key" != "null" ] && [ -n "$dr_api_key" ]; then
    echo "SUCCESS key_prefix=$dr_prefix"
    echo "DEEPREAD_API_KEY=$dr_api_key"
    break
  elif [ "$dr_error" = "access_denied" ]; then echo "DENIED"; break
  elif [ "$dr_error" = "expired_token" ]; then echo "EXPIRED"; break
  else echo "attempt=$dr_i pending..."; fi
done
```

**Variable naming:** Always use a unique prefix (e.g. `dr_`) for all variables. Never use bare `status`, `result`, or `interval` — these can conflict with shell built-ins.

**Never show the `device_code` to the user.** Only show `user_code` and the browser URL.

Responses from the token endpoint (all fields always present — check `api_key != null` for success):

| `error` | `api_key` | Meaning | Action |
|---------|-----------|---------|--------|
| `"authorization_pending"` | `null` | User hasn't approved yet | Wait `interval` seconds, poll again |
| `null` | `"sk_live_..."` | User approved | Save the key, stop polling |
| `"access_denied"` | `null` | User clicked Deny | Stop, inform user |
| `"expired_token"` | `null` | Code expired (15 min) | Start over from the top |

### Step 1d: Store the key safely

The `api_key` is returned exactly once — the server clears it after retrieval. Save it immediately.

**Safe `.env` append** — always use `printf` to guarantee a leading newline:

```bash
printf "\nDEEPREAD_API_KEY=%s\n" "$dr_api_key" >> .env
```

Never use `echo "KEY=val" >> .env` — if the file doesn't end with a newline, the key merges with the previous line.

### What happens on the user's side

**Already logged in:** Opens the URL → code is auto-validated → sees your agent name + Approve/Deny → clicks Approve → redirected to dashboard. The key arrives on your next poll.

**Not logged in:** Signs in or creates an account → redirected back with code pre-filled → clicks Approve.

In both cases the key goes directly to you — it never appears in chat.

---

## Step 2: Send Your First Document

> **Split submit and poll into separate steps.** Submit first, confirm the job ID, then poll separately. This avoids long-blocking loops.

### Step 2a: Submit the document

```bash
DR_API_KEY=$(grep ^DEEPREAD_API_KEY .env | cut -d= -f2)

curl -s -w "\nHTTP_STATUS:%{http_code}" -X POST https://api.deepread.tech/v1/process \
  -H "X-API-Key: $DR_API_KEY" \
  -F "file=@document.pdf"
```

Tell the user the job ID and that processing takes 2-3 minutes.

### Step 2b: Check results

> **Guard against empty job ID.** If `dr_job_id` is empty or "null", stop — don't poll.

> **Use `python3` for parsing results, not `jq`.** Job responses can be 200KB+ and `jq` may choke on large payloads. Save to a temp file first.

```bash
DR_API_KEY=$(grep ^DEEPREAD_API_KEY .env | cut -d= -f2)
dr_job_id="THE_JOB_ID"

if [ -z "$dr_job_id" ] || [ "$dr_job_id" = "null" ]; then
  echo "ERROR: No job ID — submit may have failed."
  exit 1
fi

curl -s "https://api.deepread.tech/v1/jobs/$dr_job_id" -H "X-API-Key: $DR_API_KEY" > /tmp/deepread_result.json
python3 -c "
import json
with open('/tmp/deepread_result.json') as f:
    data = json.load(f)
print('Status:', data.get('status'))
if data.get('status') == 'completed':
    doc = data.get('document', {})
    print(json.dumps({
        'preview_url': data.get('artifacts', {}).get('preview_url'),
        'page_count': doc.get('page_count', 0),
        'text_preview': (doc.get('content', {}).get('text', '') or '')[:500],
    }, indent=2))
elif data.get('status') == 'failed':
    print('Error:', data.get('error'))
"
```

If still `queued` or `processing`, wait and check again.

Free plan: PDF, PNG, JPEG; 15 MB and 50 pages per document. Standard adds TIFF, WebP, BMP, GIF, DOCX, TXT at 50 MB; Enterprise adds office, spreadsheet and HTML formats at 500 MB. A type your plan lacks is refused with 415.

---

## Step 3: Extract Structured Data

Add a `schema` parameter with a JSON Schema. Field descriptions guide the AI — the better the description, the better the extraction.

### Step 3a: Submit with schema

```bash
DR_API_KEY=$(grep ^DEEPREAD_API_KEY .env | cut -d= -f2)

curl -s -w "\nHTTP_STATUS:%{http_code}" -X POST https://api.deepread.tech/v1/process \
  -H "X-API-Key: $DR_API_KEY" \
  -F "file=@invoice.pdf" \
  -F 'schema={
    "type": "object",
    "properties": {
      "vendor": {"type": "string", "description": "Company or vendor name on the invoice"},
      "total": {"type": "number", "description": "Total amount due in dollars"},
      "due_date": {"type": "string", "description": "Payment due date"}
    }
  }'
```

### Step 3b: Fetch structured results

Same pattern as Step 2b — use `python3` to parse:

```bash
DR_API_KEY=$(grep ^DEEPREAD_API_KEY .env | cut -d= -f2)
curl -s "https://api.deepread.tech/v1/jobs/JOB_ID" -H "X-API-Key: $DR_API_KEY" > /tmp/deepread_structured_result.json
python3 -c "
import json
with open('/tmp/deepread_structured_result.json') as f:
    data = json.load(f)
print('Status:', data.get('status'))
if data.get('status') == 'completed':
    fields = data.get('extraction', {}).get('fields', [])
    if fields: print(json.dumps(fields, indent=2))
    print('Preview:', data.get('artifacts', {}).get('preview_url', 'N/A'))
elif data.get('status') == 'failed':
    print('Error:', data.get('error'))
"
```

Extracted fields come back as a list under `extraction.fields[]`, each with quality metadata:

```json
{
  "extraction": {
    "fields": [
      {"key": "vendor", "value": "Acme Inc", "needs_review": false, "location": {"page": 1}},
      {"key": "due_date", "value": "2025-03-15", "needs_review": true, "review_reason": "Multiple dates found", "location": {"page": 1}}
    ]
  }
}
```

- `needs_review: false` — extracted confidently, safe to auto-accept
- `needs_review: true` — needs human review, check `review_reason` for why

---

## Step 4: Blueprints (Better Accuracy)

Blueprints are optimized schemas that improve accuracy by 20-30%. You give DeepRead sample documents + expected values, it enhances field descriptions automatically.

1. Go to `https://www.deepread.tech/dashboard/optimizer`
2. Upload 4+ sample docs + ground truth JSON
3. DeepRead runs 3-5 optimization iterations
4. Use the optimized blueprint:

```bash
curl -X POST https://api.deepread.tech/v1/process \
  -H "X-API-Key: sk_live_YOUR_KEY" \
  -F "file=@invoice.pdf" \
  -F "blueprint_id=YOUR_BLUEPRINT_ID"
```

Use `schema` OR `blueprint_id`, not both.

---

## Plans

| Plan | Pages | Max file | Per-doc limit | Price |
|------|-------|----------|---------------|-------|
| Free | 2,000 a month (resets on your signup day) | 15 MB | 50 pages | $0 |
| Standard | No page limits | 50 MB | Unlimited | Prepaid credits from $10 per 1,000 pages: Parse $10, Extract $20, Deep Extract $40 |
| Enterprise | Custom | 500 MB | Unlimited | Custom — adds searchable PDF, retention, incognito, PII redaction, form fill, BYOK |

`pipeline` picks the engine: `extract` (one OCR pass; the default on Free and Standard) or `deep-extract` (two passes, judged, with a second read that checks each field value; the default on Enterprise). Every job reports its `product`: `parse`, `extract` or `deep-extract`.

---

## What's Next

See the `deepread-api` rule for the full reference — all endpoints, webhooks, error handling, blueprints API, and code examples in Python, JavaScript, and cURL.

## Help the Developer

- No API key yet → run the device flow above (Step 1)
- Has API key → help send first request (Step 2)
- Wants structured data → help write a JSON Schema with good field descriptions (Step 3)
- Wants better accuracy → explain blueprints and optimizer (Step 4)
- Wants full integration → see the `deepread-api` rule for complete reference

Discussion

Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.

Posts are public.Sign in to post

No one has posted yet. Be the first.