Install
openclaw skills install deepread-agent-setupAuthenticate AI agents with the DeepRead OCR API using OAuth device flow. The agent displays a code, the user approves it in their browser, and the agent rec...
openclaw skills install deepread-agent-setupAuthenticate AI agents with the DeepRead OCR API using the OAuth 2.0 Device Authorization Flow (RFC 8628). After setup, the agent has a DEEPREAD_API_KEY environment variable and can use the DeepRead OCR skill.
The device flow lets headless agents (no browser) authenticate securely:
Agent requests device code → User opens URL in browser → User approves → Agent receives API key
POST https://api.deepread.tech/v1/agent/device/code to get a device_code and user_codeuser_code and a verification URL to the userPOST https://api.deepread.tech/v1/agent/device/token until the user approvesapi_key (prefixed sk_live_) and stores it as the DEEPREAD_API_KEY environment variableOnly domain contacted: api.deepread.tech
api.deepread.techcurl -s -X POST https://api.deepread.tech/v1/agent/device/code \
-H "Content-Type: application/json" \
-d '{"agent_name": "my-ai-agent"}'
The agent_name field is optional — it is shown on the approval screen so the user knows which agent is requesting access.
Response:
{
"device_code": "GmRhmhcxhZAzk...EeNu5OfKhL79MQgN",
"user_code": "WDJB-MJHT",
"verification_uri": "https://www.deepread.tech/activate",
"verification_uri_complete": "https://www.deepread.tech/activate?code=WDJB-MJHT",
"expires_in": 900,
"interval": 5
}
Tell the user (using the values from the response — they change every time):
Open {verification_uri} and enter code {user_code}
Or open this direct link: {verification_uri_complete}
Poll every interval seconds (default: 5) until the user approves:
curl -s -X POST https://api.deepread.tech/v1/agent/device/token \
-H "Content-Type: application/json" \
-d '{"device_code": "GmRhmhcxhZAzk...EeNu5OfKhL79MQgN"}'
While waiting (user hasn't approved yet):
{
"error": "authorization_pending",
"api_key": null,
"key_prefix": null
}
After user approves:
{
"error": null,
"api_key": "sk_live_abc123def456...",
"key_prefix": "sk_live_abc123de"
}
The api_key is returned exactly once. The next poll after retrieval will return expired_token. Save it immediately.
If user denied:
{
"error": "access_denied",
"api_key": null,
"key_prefix": null
}
If code expired (15 minutes):
{
"error": "expired_token",
"api_key": null,
"key_prefix": null
}
Once you receive the api_key, set it for the current session:
export DEEPREAD_API_KEY="<api_key from response>"
To persist across sessions, the user should choose one of these options:
| Method | Command | Security |
|---|---|---|
| Secrets manager (recommended) | Use your OS keychain, 1Password CLI, or pass | Encrypted at rest |
| Shell profile | User manually adds export DEEPREAD_API_KEY="..." to ~/.zshrc | Plaintext file — readable by local processes |
Important:
export)sk_live_ confirms it is a valid DeepRead production keySubmit a test document to confirm the key is valid:
curl -s -X POST https://api.deepread.tech/v1/process \
-H "X-API-Key: $DEEPREAD_API_KEY" \
-F "file=@test.pdf"
A successful response returns a job ID confirming the key works:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "queued"
}
If the key is invalid you will get a 401 Unauthorized response.
#!/bin/bash
# DeepRead Device Flow — complete example
# 1. Request device code
RESPONSE=$(curl -s -X POST https://api.deepread.tech/v1/agent/device/code \
-H "Content-Type: application/json" \
-d '{"agent_name": "my-ai-agent"}')
DEVICE_CODE=$(echo "$RESPONSE" | jq -r '.device_code')
USER_CODE=$(echo "$RESPONSE" | jq -r '.user_code')
VERIFY_URI=$(echo "$RESPONSE" | jq -r '.verification_uri')
VERIFY_URI_COMPLETE=$(echo "$RESPONSE" | jq -r '.verification_uri_complete')
INTERVAL=$(echo "$RESPONSE" | jq -r '.interval')
echo "Open $VERIFY_URI and enter code: $USER_CODE"
echo "Or open directly: $VERIFY_URI_COMPLETE"
# 2. Poll for token
while true; do
TOKEN_RESPONSE=$(curl -s -X POST https://api.deepread.tech/v1/agent/device/token \
-H "Content-Type: application/json" \
-d "{\"device_code\": \"$DEVICE_CODE\"}")
ERROR=$(echo "$TOKEN_RESPONSE" | jq -r '.error // empty')
if [ -z "$ERROR" ]; then
export DEEPREAD_API_KEY=$(echo "$TOKEN_RESPONSE" | jq -r '.api_key')
echo "Authenticated. DEEPREAD_API_KEY is set for this session."
break
elif [ "$ERROR" = "authorization_pending" ]; then
sleep "$INTERVAL"
elif [ "$ERROR" = "slow_down" ]; then
INTERVAL=$((INTERVAL + 5))
sleep "$INTERVAL"
else
echo "Error: $ERROR"
exit 1
fi
done
| Endpoint | Method | Auth | Purpose |
|---|---|---|---|
https://api.deepread.tech/v1/agent/device/code | POST | None | Request device code + user code |
https://api.deepread.tech/v1/agent/device/token | POST | None | Poll for API key after user approval |
https://www.deepread.tech/activate | — | Browser | User opens this URL to enter the code and approve |
No other endpoints are contacted by this skill.
The user hasn't approved yet. Keep polling. The code expires after 15 minutes (expires_in: 900).
The device code expired before the user approved, or the API key was already retrieved (one-time retrieval). Start over from Step 1.
You're polling too fast. Increase the polling interval by 5 seconds.
The user clicked Deny on the approval screen. Start over from Step 1 if the user wants to retry.
Ensure the shell session was not restarted. If persisting to ~/.zshrc, run source ~/.zshrc to reload.
The environment variable was not persisted. Re-run the device flow or manually set:
export DEEPREAD_API_KEY="sk_live_your_key_here"
user_code is short-lived (15 minutes) and single-useapi_key is returned exactly once — subsequent polls return expired_tokenDEEPREAD_API_KEY for the current session only — it does not write to diskpass) over plaintext shell profilesclawhub install uday390/deepread-ocrclawhub install uday390/deepread-form-fillclawhub install uday390/deepread-piiclawhub install uday390/deepread-agent-setupclawhub install uday390/deepread-byok