# mothewOCR API — Developer & AI Specification > mothewOCR is a high-performance REST API for extracting structured data from Thai documents, bank transfer slips, receipts, and tax invoices using a hybrid OCR engine (Local OCR + Claude Vision AI). ## Base URL - Production: `https://api.mothew.com` ## Authentication All protected API endpoints require authentication using one of the following methods: 1. **API Key (Recommended for Server-to-Server / AI Integrations):** - Header: `X-API-Key: sk-live-your_api_key_here` - Generate keys: `POST /v1/api-keys` (raw key is returned once, on creation only) 2. **Bearer Token (JWT for Web Users):** - Header: `Authorization: Bearer ` - Access token expires in 15 minutes. --- ## Key Endpoints ### 1. Perform OCR (Single Image) - **HTTP Method:** `POST` - **Endpoint:** `/v1/ocr` - **Content-Type:** `multipart/form-data` or `application/json` #### Request Body (multipart/form-data): - `file` or `image` (File, required): Image file. Maximum size 10MB. #### Request Body (application/json): ```json { "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg...", "imageUrl": "https://example.com/slip.jpg" } ``` Provide either `image` (base64 or URL string) or `imageUrl`. #### Response Example (200 OK): ```json { "meta": { "request_id": "req_1712345678901", "timestamp": "2026-08-07T10:15:30.000Z", "source": "free_ocr", "confidence": 0.98, "processing_ms": 420, "document_type": "transfer_slip", "platform": "api" }, "document": { "sender": { "name": "นาย สมชาย ใจดี", "bank": "KBANK", "accountNo": "xxx-x-x1234-x" }, "receiver": { "name": "บริษัท เอ บี ซี จำกัด", "bank": "SCB", "accountNo": "xxx-x-x5678-x" }, "amount": 1500.00, "currency": "THB", "transferredAt": "2026-08-07T10:15:30+07:00", "refNo": "2026080712345678" }, "raw": { "text": "..." }, "doc_id": "doc_123456789", "drive_url": null } ``` A duplicate image (same hash, same user) returns `"duplicate": true` with the original `doc_id` instead of re-running OCR. --- ### 2. Perform Batch OCR (Up to 20 Images) - **HTTP Method:** `POST` - **Endpoint:** `/v1/ocr/batch` - **Content-Type:** `application/json` only (multipart is not supported on this endpoint) #### Request Body: ```json { "images": [ { "image": "data:image/jpeg;base64,..." }, { "imageUrl": "https://example.com/receipt.jpg" } ] } ``` `images` accepts 1–20 items; each item needs `image` (base64/URL string) or `imageUrl`. #### Response Example (200 OK): ```json { "total": 2, "success": 2, "failed": 0, "results": [ { "index": 0, "success": true, "doc_id": "doc_123456789", "confidence": 0.98, "document_type": "transfer_slip", "drive_url": null }, { "index": 1, "duplicate": true, "doc_id": "doc_000000001" } ], "errors": [] } ``` --- ### 3. List Processed Documents - **HTTP Method:** `GET` - **Endpoint:** `/v1/documents` - **Query Parameters:** - `limit` (integer, optional, default: 20) - `offset` (integer, optional, default: 0) — pagination is offset-based, not page-based - `type` (string, optional): Filter by `transfer_slip`, `receipt`, `tax_invoice` - `date_from`, `date_to` (string, optional, `YYYY-MM-DD`) #### Response Example (200 OK): ```json { "documents": [ { "id": "doc_123456789", "documentType": "transfer_slip", "amount": 1500.00, "merchantName": "SCB Transfer", "confidence": 0.98, "createdAt": "2026-08-07T10:15:30Z" } ], "total": 1, "page": 1, "per_page": 20 } ``` ### 4. Get Document Detail - **HTTP Method:** `GET` - **Endpoint:** `/v1/documents/:id` ### 5. Monthly Summary - **HTTP Method:** `GET` - **Endpoint:** `/v1/documents/stats/monthly` ### 6. Export Documents (CSV / Excel) - **HTTP Method:** `POST` - **Endpoint:** `/v1/documents/export` - **Content-Type:** `application/json` ```json { "format": "csv", "dateFrom": "2026-08-01", "dateTo": "2026-08-31", "docType": "receipt" } ``` Returns a file download (`text/csv` or `.xlsx`). Max 5,000 rows per export. --- ## Account & Billing - `GET /v1/api-keys` — list API keys - `POST /v1/api-keys` — create a key (`{ "name": "..." }`), returns `raw_key` once - `DELETE /v1/api-keys/:id` — revoke a key - `GET /v1/billing/plan` — current plan and quota usage - `POST /v1/billing/subscribe` — change plan - `POST /v1/billing/topup` — buy top-up credits (PromptPay QR, expires in 15 min) - `GET /v1/billing/history` — payment history - `GET /v1/billing/invoices` — list invoices --- ## Error Codes | HTTP Code | Error Code | Description | | :--- | :--- | :--- | | 400 | `BAD_REQUEST` | Missing or malformed image/parameter | | 401 | `UNAUTHORIZED` | Invalid or missing API key / Bearer token | | 404 | `NOT_FOUND` | Document not found | | 429 | `QUOTA_EXCEEDED` | Monthly credit quota exhausted | | 429 | `RATE_LIMIT_EXCEEDED` | Request rate limit per minute exceeded | | 500 | `OCR_FAILED` | Processing engine error | **Timeouts and retries on `/v1/ocr`:** set your client timeout to at least 30 seconds. The OCR engine can take up to ~25 seconds in the rare case it's cold-starting (e.g. the request right after a deploy, or after a long quiet period) — this is normal, not a sign of an outage. A `500 OCR_FAILED` from a cold-start timeout is safe to retry: quota is only deducted after OCR succeeds, and the image is only marked seen (for duplicate detection) after a successful result, so a failed attempt never double-charges you and a retry with the same image is never mistaken for a duplicate. --- ## Webhook Notifications ### Payment Notifications (Beam PromptPay) - Signature Header: `X-Beam-Signature` (HMAC-SHA256, verified against your webhook secret) ### OCR Result Webhook (outbound, sent by mothewOCR to your server) Set a Webhook URL under Dashboard > Settings. Every time a document submitted via LINE Bot or the web uploader finishes OCR, mothewOCR sends `POST ` with a JSON body: ```json { "event": "ocr.completed", "requestId": "req_1712345678901_ab12", "documentType": "transfer_slip", "confidence": 0.95, "processingMs": 420, "summary": { "amount": 1500.00, "currency": "THB", "date": "2026-08-07", "title": "SCB Transfer" }, "details": { "reference": "2026080712345678", "sender": {...}, "receiver": {...} }, "document": {...}, "created_at": "2026-08-07T10:15:30.000Z" } ``` **Verify every request before trusting it.** The body is signed with HMAC-SHA256 using your `webhook_secret` (retrieve it from `GET /auth/connected-accounts` or `PUT /auth/webhook`), sent as header `X-mothewOCR-Signature` (lowercase hex digest of the raw request body, computed with your secret). Reject the request if the signature does not match — do not act on an unverified payload. `requestId` is the same value as the corresponding document's `request_id` in your `GET /v1/documents` records. Always key your own storage by this field; it is stable and unique per document, so two documents processed close together in time can never be confused with each other. ### Confirming or Correcting a Result (inbound, you call mothewOCR) If your system reviews the OCR result and finds it needs correction (or just wants to confirm it's correct), call back: - **Endpoint:** `POST /webhook/receipts/confirm` - **Signature Header:** `X-mothewOCR-Signature` — HMAC-SHA256 of the raw request body, using the same `webhook_secret` - **Body:** ```json { "requestId": "req_1712345678901_ab12", "status": "confirmed" } ``` or ```json { "requestId": "req_1712345678901_ab12", "status": "corrected", "correctedFields": { "amount": 1250.00 } } ``` Only applies to documents submitted via the LINE Bot. If `status` is `corrected`, mothewOCR pushes a follow-up LINE message with the corrected data to the original sender. Calling this endpoint again for an already-resolved `requestId` is a no-op (idempotent, returns `{ "received": true, "already_resolved": true }`).