Integrate GradeOS in 5 lines.
A single REST endpoint turns a device photo into a signed Device Passport. Drop it into your WMS, returns flow, or POS — same authentication, same response shape, every time.
Quick start
Grade a device in one call. Replace YOUR_API_KEY and the image path:
# cURL
curl -X POST https://api.gradeos.net/v1/grade \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"image_b64": "<base64-encoded image>"}'
Response (1.2s typical):
{
"device_id": "DEV-4F8B27A1",
"grade": {
"letter": "B",
"label": "Light use",
"confidence": 0.89
},
"detections": [
{ "class": "scratch", "confidence": 0.91,
"x": 812, "y": 1024,
"w": 340, "h": 28 }
],
"latency_ms": 1182,
"passport_url": "https://api.gradeos.net/v1/passport/DEV-4F8B27A1.pdf",
"audit_hash": "sha256:9f3a...e2c7"
}
Authentication
Every request requires an API key passed in the X-API-Key header. Keys are scoped
per workspace — one key per warehouse, dealer, or environment.
X-API-Key: gos_live_4f8b27a1e2c7d3a91b5f...
Grade a device
Submit a device image. Returns grade, defect list, and a passport URL.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
image_b64 | string | required | Base64-encoded image (jpg/png/heic, max 10 MB) |
image_url | string | — | Alternative to image_b64. Public URL to fetch. |
device_hint | string | — | Known model (e.g. iPhone 14 Pro) to bias detection |
workspace | string | — | Sub-workspace tag for multi-warehouse customers |
confidence | int | — | Min detection confidence (0–100), default 30 |
imei | string | — | 15-digit IMEI for cross-reference & passport |
Grade letters
| Letter | Label | Trigger |
|---|---|---|
BN | Brand New | No detections above confidence threshold |
A | Near-mint | ≤1 minor scratch, no chips |
B | Light use | 2 scratches OR 1 minor chip |
C | Moderate wear | 3+ scratches OR chip with wear |
D | Heavy wear | Chip + 2+ scratches |
F | Cracked / Non-functional | Crack, display defect, or screen damage |
Full production grade
Production endpoint. Submits up to 6 photos plus an IMEI, runs multi-frame consensus on detections, looks up identity + iCloud + blacklist + warranty via IMEICheck, applies identity gates to the cosmetic grade, returns a complete signed passport.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
images_b64 | string[] | required* | 1–6 base64 images (one per angle) |
image_urls | string[] | — | *or pass URLs we'll fetch instead |
imei | string | — | 15-digit IMEI. Without it, identity checks are skipped. |
device_hint | string | — | Known model name for the passport |
skip_identity_lookup | bool | — | Cosmetic-only mode (cheaper) |
Response shape
{
"device_id": "DEV-4F8B27A1",
"grade": { "letter": "F", "label": "Cracked / Non-functional", "confidence": 0.91 },
"cosmetic_grade": { "letter": "B", "label": "Light use", "confidence": 0.81 },
"grade_adjustments": ["iCloud Activation Lock is enabled"],
"defects": [
{ "class": "scratch", "confidence": 0.92, "frames_seen": 3, "severity": "low" }
],
"identity": {
"model_name": "iPhone 14 Pro",
"storage_gb": 256,
"color": "Deep Purple",
"icloud_lock": "LOCKED",
"blacklist_status": "CLEAN",
"sim_lock": "UNLOCKED",
"original_carrier": "Verizon",
"warranty_status": "EXPIRED"
},
"frame_count": 4,
"inference_ms": 3208,
"identity_lookup_ms": 1840,
"passport_url": "/v1/passport/DEV-4F8B27A1.pdf",
"audit_hash": "sha256:..."
}
Multi-frame consensus
A defect counts toward the final grade only when it appears in ≥2 frames, or in 1 frame at ≥85% confidence. This eliminates false positives from glare, dust, packaging artifacts, and reflections.
Identity gates
Identity signals can lower a cosmetic grade but never raise it:
| Signal | Floor |
|---|---|
| IMEI reported lost or stolen | X · Do Not Sell |
| IMEI on GSMA blacklist | F |
| iCloud Activation Lock enabled | F |
| SIM-locked to carrier | C |
| Never activated | B |
Device Passport
Returns a signed PDF passport for a graded device. Contains the grade, defect map, hardware diagnostics, IMEI verification, audit hash, and timestamp.
JSON variant — same content, machine-readable for WMS ingestion.
List grades
Returns the latest grades for your workspace, newest first.
Submit correction
Submit a human correction when your QA team disagrees with the AI grade. These corrections feed the model retraining pipeline scoped to your workspace — your model gets better at your device mix over time.
{
"correct_letter": "C",
"reason": "missed_chip_bottom_right",
"reviewer": "qa-tech-04",
"notes": "Chip ~3mm at bottom-right corner, not visible in primary frame"
}
Webhooks
Subscribe to events to push grades into your WMS / ERP without polling. Configure endpoints from the dashboard or via the API:
{
"url": "https://your-wms.com/hooks/gradeos",
"events": ["grade.completed", "passport.signed"],
"secret": "whsec_..."
}
Verifying signatures
Each delivery includes an X-GradeOS-Signature header — HMAC SHA-256 of the body using your webhook secret.
# Python verification
import hmac, hashlib
def verify(payload, signature, secret):
mac = hmac.new(secret.encode(), payload, hashlib.sha256)
return hmac.compare_digest(mac.hexdigest(), signature)
Event types
| Event | Fires when |
|---|---|
grade.completed | A device finishes grading |
grade.failed | Inference errored (returns error code) |
passport.signed | Passport PDF generated and hashed |
correction.submitted | Human correction logged |
Error codes
| HTTP | Code | Meaning |
|---|---|---|
| 400 | bad_request | Malformed body or missing required field |
| 401 | invalid_api_key | Missing or invalid X-API-Key header |
| 402 | quota_exceeded | Monthly device quota reached — upgrade or wait for reset |
| 413 | image_too_large | Image exceeds 10 MB limit |
| 422 | image_unreadable | Could not decode image; check format and base64 encoding |
| 429 | rate_limited | Too many requests — see Retry-After header |
| 502 | model_unavailable | Inference engine temporarily unreachable |
| 504 | timeout | Inference exceeded 30 s — retry recommended |
Rate limits
- Pilot tier: 60 requests per minute, 500 devices per month
- Production tier: 600 requests per minute, unlimited monthly volume (metered at $0.50/device)
- Enterprise: Custom — typically 6,000 req/min with dedicated inference workers
Rate-limit headers on every response:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1716234000
SDK examples
Python
import base64, requests
with open("device.jpg", "rb") as f:
img_b64 = base64.b64encode(f.read()).decode()
r = requests.post(
"https://api.gradeos.net/v1/grade",
headers={"X-API-Key": "YOUR_KEY"},
json={"image_b64": img_b64, "imei": "353892101482047"},
timeout=30,
)
result = r.json()
print(result["grade"]["letter"], result["grade"]["confidence"])
Node.js
const fs = require("fs");
const img = fs.readFileSync("device.jpg").toString("base64");
const res = await fetch("https://api.gradeos.net/v1/grade", {
method: "POST",
headers: {
"X-API-Key": "YOUR_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ image_b64: img }),
});
const result = await res.json();
console.log(result.grade.letter);
Go
payload := map[string]string{"image_b64": imgB64}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "https://api.gradeos.net/v1/grade", bytes.NewReader(body))
req.Header.Set("X-API-Key", "YOUR_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
Questions? cesar@electronicswarehouse.solutions · Status: /health