概要
space ocr API は、文書画像を名前付きフィールド・Markdown・プレーンテキストのいずれかとして読み取り、値ごとに読み取り位置の座標と検証フラグを返します。その結果を保存・閲覧するのが MySpace です。spocr_* で始まる API キー 1 本で、すべての REST エンドポイントとイベント webhook を利用できます。
RESTful、JSON、CORS 対応。可変長のバッチや非同期処理は Jobs / Webhooks セクションで案内します。
5分クイックスタート
キー発行 → curl をコピペ → JSON。初回呼び出しまで 5 分あれば十分です。無料枠は毎月 100 件。
① Developer → API Keys でキーを発行します(カード不要)。
② 右の curl をそのまま実行 — サンプル画像は実際にホスティングされているので、キーを差し替えるだけで動きます。
③ レスポンスの data.values の値と、data.cells の box / quad / verified、data.review.flagged(要確認リスト)を確認してください。
④ コードを書く前に試したいときは、MySpace コンソールがそのままプレイグラウンドになります。シートにファイルを置けば API と同じ結果が並び、セルをクリックすると元画像の座標まで確認できます。API からアップロードした文書も同じシートに現れるので、自動処理と目視確認を同じ場所で扱えます。
curl -X POST https://api.space-ocr.com/ocr/fields \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"image": "https://space-ocr.com/samples/two-receipts.jpg",
"imageType": "url",
"fields": [{ "name": "store_name" }, { "name": "total" }]
}'認証
すべてのリクエストは Authorization ヘッダーで Bearer <APIキー> を送ります。キーは Developer → API Keys から発行・失効できます。
キー形式は spocr_ で始まる文字列です。漏洩した場合は即時に失効してください。
curl https://api.space-ocr.com/amount \
-H "Authorization: Bearer YOUR_API_KEY"Base URL
本番 base URL は単一です。バージョニングはキーとイベントペイロードの apiVersion で管理します。
# Production
https://api.space-ocr.com
# OpenAPI spec
https://api.space-ocr.com/openapi.jsonRate limits
60 req/min/key、600 req/min/uid。超過時は HTTP 429 と Retry-After ヘッダーで待機秒数を返します。
応答には常に X-Request-Id (req_xxx) と X-RateLimit-Remaining(その分に残る呼び出し数)が含まれます。サポート連絡時には X-Request-Id を添えてください。
/ocr/fields・/create・/upload は Idempotency-Key ヘッダーをサポートします。同一キーの再送は 24h キャッシュされ、X-Idempotent-Replay: true で示されます。
画像サイズと応答時間
応答時間はリクエストの画像サイズにほぼ比例します。観測分布は p50 7.2 秒 / p90 10.5 秒(SLA ではありません)。
JSON ボディは 2MB までなので、base64(ファイルの約 1.33 倍)では実質 ~1.5MB が上限です。それを超える場合は imageType: "url" で URL を渡すか、/upload(非同期・ファイルあたり 20MB)+ /jobs ポーリングまたは webhook をご利用ください。処理が 110 秒を超えると ocr_engine_timeout になります — その際は縮小するか非同期経路へ。
エラー
4xx / 5xx は共通エンベロープで返却されます。requestId はサポート連絡時の手がかりです。
{
"error": {
"code": "validation_failed",
"message": "imageType is required",
"requestId": "req_xxx"
},
"details": {
/* optional, endpoint-specific context (e.g. /upload returns processable count) */
}
}
// error.code: validation_failed | bad_request | invalid_image | invalid_api_key
// | key_inactive | unauthorized | forbidden | not_found
// | insufficient_balance | rate_limited | ocr_engine_error
// | ocr_engine_timeout | storage_error | internal_errorHTTP ステータス
構造化 OCR
画像から名前付きフィールドを抽出します。fields で抽出スキーマを指定するか、autoFields で自動提案させます。
ボディパラメータ
レスポンスフィールド
curl -X POST https://api.space-ocr.com/ocr/fields \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"image": "https://example.com/receipt.jpg",
"imageType": "url",
"fields": [
{ "name": "store_name", "type": "string",
"description": "店舗名" },
{ "name": "date", "type": "string",
"description": "取引日" },
{ "name": "invoice_no", "type": "string", "required": true,
"description": "伝票番号" },
{ "name": "items", "type": "array",
"description": "購入品目",
"children": [
{ "name": "name", "type": "string" },
{ "name": "qty", "type": "string" },
{ "name": "price", "type": "string" }
]
},
{ "name": "total", "type": "number", "required": true,
"label": "合計",
"description": "合計" }
]
}'{
"status": "success",
"data": {
"values": {
"store_name": "スーパー ABC",
"date": "2025-04-10",
"invoice_no": "",
"items": [
{ "name": "牛乳", "qty": "1", "price": "¥198" }
],
"total": "¥548"
},
"cells": {
"store_name": { "box": { "xmin": 14, "ymin": 36, "xmax": 210, "ymax": 58 },
"quad": [{"x":14,"y":36},{"x":210,"y":36},{"x":210,"y":58},{"x":14,"y":58}],
"verified": true, "review": null,
"evidence": { "source": "vision_symbol_match", "match_ratio": 0.98, "ocr_confidence": 0.96 } },
"date": { "box": { "xmin": 14, "ymin": 80, "xmax": 180, "ymax": 102 },
"quad": [{"x":14,"y":80},{"x":180,"y":80},{"x":180,"y":102},{"x":14,"y":102}],
"verified": true, "review": null,
"evidence": { "source": "token_id", "match_ratio": 1.0, "ocr_confidence": 0.99 } },
"items[0]": { "box": { "xmin": 263, "ymin": 460, "xmax": 738, "ymax": 523 },
"quad": [{"x":263,"y":460},{"x":738,"y":460},{"x":738,"y":523},{"x":263,"y":523}],
"verified": null, "review": null,
"evidence": { "source": "vision_symbol_match", "match_ratio": 1.0 } },
"items[0].name": { "box": { "xmin": 263, "ymin": 460, "xmax": 503, "ymax": 492 },
"quad": [{"x":263,"y":460},{"x":503,"y":460},{"x":503,"y":492},{"x":263,"y":492}],
"verified": true, "review": null,
"evidence": { "source": "token_id", "match_ratio": 1.0, "ocr_confidence": 0.97 } },
"items[0].qty": { "box": { "xmin": 333, "ymin": 460, "xmax": 338, "ymax": 490 },
"quad": [{"x":333,"y":460},{"x":338,"y":460},{"x":338,"y":490},{"x":333,"y":490}],
"verified": true, "review": null,
"evidence": { "source": "vision_symbol_match", "match_ratio": 1.0, "ocr_confidence": 0.94 } },
"items[0].price": { "box": { "xmin": 693, "ymin": 460, "xmax": 738, "ymax": 488 },
"quad": [{"x":693,"y":460},{"x":738,"y":460},{"x":738,"y":488},{"x":693,"y":488}],
"verified": false,
"review": { "reason": "text_mismatch", "reasons": ["text_mismatch"] },
"evidence": { "source": "vision_symbol_match", "match_ratio": 1.0, "ocr_confidence": 0.88 } },
"total": { "box": { "xmin": 380, "ymin": 720, "xmax": 530, "ymax": 742 },
"quad": [{"x":380,"y":720},{"x":530,"y":720},{"x":530,"y":742},{"x":380,"y":742}],
"verified": true, "review": null,
"evidence": { "source": "vision_symbol_match", "match_ratio": 1.0, "ocr_confidence": 0.98 },
"normalized": { "value": 548, "type": "number", "method": "deterministic" } }
},
"review": {
"unit": "field",
"declared": 7,
"returned": 6,
"boxed": 6,
"verified": 5,
"flagged": [
{ "path": "items[0].price", "reason": "text_mismatch", "reasons": ["text_mismatch"] },
{ "path": "invoice_no", "reason": "missing", "reasons": ["missing"] }
],
"by_reason": { "text_mismatch": 1, "missing": 1 },
"notes": [
{ "path": "total", "declared_type": "number", "applied_type": "string",
"description": "\"total\" was declared as number and read as string. Values are returned exactly as printed on the page so the text can be matched to coordinates and verified; the declared type was kept as an extraction hint, not applied as formatting." }
]
},
// 宣言した型は values を書き換えず、この層に出ます
"normalized": { "total": 548 },
"image": { "width": 1654, "height": 2339 }
}
}Markdown 変換
レイアウトを保ったまま画像を Markdown に変換します。見出し・段落・リスト・表が要素として返り、要素ごとに座標が付きます。
ボディパラメータ
レスポンスフィールド
curl -X POST https://api.space-ocr.com/ocr/markdown \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"image": "https://example.com/report.jpg",
"imageType": "url"
}'{
"status": "success",
"data": {
"values": {
"markdown": "# 四半期レポート\n\n売上は前年同期比で増加した。\n\n| 項目 | 金額 |\n| --- | --- |\n| 売上 | 12,000 |",
"elements": [
{ "type": "heading", "level": 1, "text": "四半期レポート" },
{ "type": "paragraph", "text": "売上は前年同期比で増加した。" },
{ "type": "table", "rows": 2, "cols": 2, "cells": [
{ "row": 0, "col": 0, "header": true, "text": "項目" },
{ "row": 0, "col": 1, "header": true, "text": "金額" },
{ "row": 1, "col": 0, "header": false, "text": "売上" },
{ "row": 1, "col": 1, "header": false, "text": "12,000" }
] }
]
},
"cells": {
"elements[0]": { "box": { "xmin": 60, "ymin": 48, "xmax": 520, "ymax": 92 },
"quad": [{"x":60,"y":48},{"x":520,"y":48},{"x":520,"y":92},{"x":60,"y":92}],
"verified": true, "review": null,
"evidence": { "source": "token_id", "ocr_confidence": 0.98 } },
"elements[1]": { "box": { "xmin": 60, "ymin": 120, "xmax": 900, "ymax": 160 },
"quad": [{"x":60,"y":120},{"x":900,"y":120},{"x":900,"y":160},{"x":60,"y":160}],
"verified": true, "review": null,
"evidence": { "source": "token_id" } },
"elements[2]": { "box": { "xmin": 60, "ymin": 200, "xmax": 640, "ymax": 320 },
"quad": [{"x":60,"y":200},{"x":640,"y":200},{"x":640,"y":320},{"x":60,"y":320}],
"verified": null, "review": null, "evidence": {} },
"elements[2].cells[0]": { "box": { "xmin": 60, "ymin": 200, "xmax": 350, "ymax": 260 },
"quad": [{"x":60,"y":200},{"x":350,"y":200},{"x":350,"y":260},{"x":60,"y":260}],
"verified": true, "review": null, "evidence": { "source": "token_id" } },
"elements[2].cells[1]": { "box": { "xmin": 350, "ymin": 200, "xmax": 640, "ymax": 260 },
"quad": [{"x":350,"y":200},{"x":640,"y":200},{"x":640,"y":260},{"x":350,"y":260}],
"verified": false,
"review": { "reason": "text_mismatch" },
"evidence": { "source": "token_id", "ocr_confidence": 0.71 } },
"elements[2].cells[2]": { "box": { "xmin": 60, "ymin": 260, "xmax": 350, "ymax": 320 },
"quad": [{"x":60,"y":260},{"x":350,"y":260},{"x":350,"y":320},{"x":60,"y":320}],
"verified": true, "review": null, "evidence": { "source": "token_id" } },
"elements[2].cells[3]": { "box": { "xmin": 350, "ymin": 260, "xmax": 640, "ymax": 320 },
"quad": [{"x":350,"y":260},{"x":640,"y":260},{"x":640,"y":320},{"x":350,"y":320}],
"verified": true, "review": null, "evidence": { "source": "token_id" } }
},
"review": {
"unit": "element",
"total": 6,
"boxed": 6,
"verified": 5,
"flagged": [{ "path": "elements[2].cells[1]", "reason": "text_mismatch" }],
"by_reason": { "text_mismatch": 1 },
"coverage": { "recovered_blocks": 0, "vision_tokens": 40, "tokens_claimed": 40, "token_coverage": 1.0 }
},
"image": { "width": 1654, "height": 2339 }
}
}プレーンテキスト OCR
スキーマも Markdown 文法もなしに、文書の文字だけを全部返します。モデルが画像を見て本当の読み順にブロックを並べ替えるので、多段組みや傾いたスキャンでも文が入り混じりません。
ボディパラメータ
レスポンスフィールド
curl -X POST https://api.space-ocr.com/ocr/text -H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" -d '{
"image": "https://example.com/note.jpg",
"imageType": "url",
"includeBlocks": true
}'{
"status": "success",
"data": {
"values": {
"text": "株式会社サクラ商事\n請求書\n合計 1,451円",
"blocks": [
{ "text": "株式会社サクラ商事" }
]
},
"cells": {
"blocks[0]": { "box": { "xmin": 60, "ymin": 48, "xmax": 470, "ymax": 92 },
"quad": [{"x":60,"y":48},{"x":470,"y":48},{"x":470,"y":92},{"x":60,"y":92}],
"verified": true, "review": null,
"evidence": { "source": "token_id", "ocr_confidence": 0.98 } }
},
"review": {
"unit": "block",
"total": 12,
"boxed": 12,
"verified": 11,
"flagged": [{ "path": "blocks[7]", "reason": "text_mismatch" }],
"by_reason": { "text_mismatch": 1 },
"coverage": { "recovered_blocks": 0, "vision_tokens": 96, "tokens_claimed": 96, "token_coverage": 1.0 }
},
"image": { "width": 1654, "height": 2339 },
"source": "llm"
}
}ツリー閲覧
MySpace のフォルダ/シート/メモを一覧します。path と depth でスコープを絞れます。
クエリパラメータ
レスポンスフィールド
curl https://api.space-ocr.com/space?path=/&depth=1 \
-H "Authorization: Bearer YOUR_API_KEY"{
"path": "/",
"depth": 1,
"items": [
{ "path": "/invoices", "name": "invoices", "type": "folder", "createdAt": 1716700000000 },
{ "path": "/memo_2024", "name": "メモ", "type": "memo",
"uniqueKey": "...", "createdAt": 1716700000000, "extensions": null }
]
}
// type: folder | sheet | doc | memo | img。folder 以外は uniqueKey / extensions を持ちます。中身を見る
フォルダ/シート/ドキュメント束/メモ/画像、すべての種類の中身を返します。ドキュメント束は pages 配列、シートは rows 配列です。**クエリ(where / sort / select / limit / offset / boxes)はシート専用** — 他の種類では無視され、全件がそのまま返ります。 シートの行は **アップロード時刻(createdAt)の昇順**で返ります。これは POST /edit・POST /remove の `row: N` と同じ順番なので、レスポンスの N 番目の行がそのまま `row: N` です。
クエリパラメータ
レスポンスフィールド
# 複数 where (AND) + ソート + 投影 + ページネーション
curl "https://api.space-ocr.com/view?path=/invoices/sheet1\
&where=total>=10000\
&where=vendor~ABC\
&sort=-invoice_date\
&select=vendor,total,invoice_date\
&limit=20&offset=0" \
-H "Authorization: Bearer YOUR_API_KEY"// type=sheet
{
"type": "sheet",
"path": "/invoices/sheet1",
"name": "sheet1",
"columns": [ /* ... */ ],
"total": 128, // シート全体の行数
"matched": 12, // where 通過行数
"offset": 0,
"limit": 20,
"nextOffset": 20, // 次ページ用 / 終端は null
"rows": [
{
"rowKey": "img_abc",
"name": "invoice_2025_04_10.jpg",
"createdAt": 1744243200000, // アップロード時刻 / 既定の行順
"imageUrl": "https://...",
"ocrStatus": "done",
"values": { "vendor": "ABC Corp", "total": "12000", "invoice_date": "2025-04-10" },
"cells": { /* POST /ocr/fields と同じ box / quad / verified / review — boxes=0 で省略 */ },
"review": { "unit": "field" /* ... */ },
"image": { "width": 1654, "height": 2339 }
}
]
}
// type=folder
{
"type": "folder",
"path": "/invoices",
"items": [
{ "path": "/invoices/2024", "name": "2024", "type": "folder" },
{ "path": "/invoices/Kvho45OXMKw…", "name": "sheet1", "type": "sheet", "uniqueKey": "Kvho45OXMKw…" }
]
}
// type=doc — ページ全件がそのまま返ります(where / sort / limit / offset / select / boxes は無視)。
// type=doc (mode=markdown)
{
"type": "doc",
"path": "/reports/quarterly",
"name": "quarterly",
"mode": "markdown",
"total": 2,
"pages": [
{
"pageKey": "img_abc",
"name": "page1.jpg",
"imageUrl": "https://...",
"ocrStatus": "done",
"values": { "markdown": "# ...", "elements": [ /* ... */ ] },
"cells": { /* POST /ocr/markdown と同じ box / quad / verified / review */ },
"review": { "unit": "element" /* ... */ },
"image": { "width": 1654, "height": 2339 }
}
]
}
// type=doc (mode=text)
{
"type": "doc",
"path": "/notes/scan",
"name": "scan",
"mode": "text",
"total": 1,
"pages": [
{
"pageKey": "img_def",
"name": "note.jpg",
"imageUrl": "https://...",
"ocrStatus": "done",
"values": { "text": "...", "blocks": [ /* ... */ ] },
"cells": { /* POST /ocr/text と同じ box / quad / verified / review */ },
"review": { "unit": "block" /* ... */ },
"image": { "width": 1654, "height": 2339 }
}
]
}
// type=memo
{ "type": "memo", "path": "...", "name": "todo", "text": "..." }
// type=img
{ "type": "img", "path": "...", "name": "...", "imageUrl": "...", "ocrStatus": "done" }作成
親フォルダの下に folder / sheet / doc / memo を作成します。シートは OCR スキーマ (columns) と prompt を、doc(ドキュメント束)は mode を持ちます。
ボディパラメータ
レスポンスフィールド
# sheet
curl -X POST https://api.space-ocr.com/create \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"path": "/invoices",
"type": "sheet",
"name": "sheet1",
"columns": [
{ "id": "amount", "name": "amount", "type": "string", "required": true },
{ "id": "date", "name": "date", "type": "string" }
],
"prompt": "請求書から金額と日付を抽出"
}'// HTTP 201 Created
// sheet/memo は uniqueKey が path に組み込まれて返却される
{ "path": "/invoices/Kvho45OXMKw…", "type": "sheet", "uniqueKey": "Kvho45OXMKw…" }
// 作成と同時に item.created Webhook が発火します。Idempotency-Key ヘッダーを
// 付けると 24h 以内の再送は同じレスポンスをそのまま返します。
// required: true の列(上の amount)は、以後のアップロードで値が空だと
// その行の review.flagged に reason "missing" として現れます。画像アップロード
シートまたはドキュメント束に画像を 1 枚以上アップロードします。multipart/form-data。既定は非同期 (jobs を返却 → webhook で完了通知)。
フォームフィールド (multipart)
レスポンスフィールド
curl -X POST https://api.space-ocr.com/upload \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "path=/invoices/sheet1" \
-F "files=@invoice1.jpg" \
-F "files=@invoice2.jpg"// async (default)
{
"path": "/invoices/sheet1",
"jobs": [
{ "uniqueKey": "...", "originalName": "invoice1.jpg", "jobId": "job_...", "status": "pending" },
{ "uniqueKey": "...", "originalName": "invoice2.jpg", "jobId": "job_...", "status": "pending" }
]
}
// ドキュメント束にアップロードした場合 — jobs の形は同じで、束の mode で変換方法が決まります
{
"path": "/reports/quarterly",
"jobs": [
{ "uniqueKey": "...", "originalName": "page1.jpg", "jobId": "job_...", "status": "pending" }
]
}
// wait=true — jobs ではなく results が返ります
{
"path": "/invoices/sheet1",
"results": [
{ "uniqueKey": "...", "originalName": "invoice1.jpg", "jobId": "job_...",
"status": "done", "mode": "sheet",
"result": { /* { values, cells, review, image } — GET /jobs と同じ v2 構造 */ } },
{ "uniqueKey": "...", "originalName": "invoice2.jpg", "jobId": "job_...",
"status": "pending" } // 30s 以内に終わらなかった分は /jobs でポーリング
]
}
// 402 — 残高不足
{
"error": { "code": "insufficient_balance", "message": "...", "requestId": "req_..." },
"details": {
"requested": 5,
"processable": 3,
"breakdown": {
"freeRemaining": 0,
"flatfeeRemaining": 3,
"balance": 0,
"perCallCost": 1,
"currency": "scans"
}
}
}シート行/メモを編集
シートのセル値、またはメモの本文を上書きします。anyOf: (path, row, column, value) または (path, text)。**編集できるのはシートとメモだけ** — ドキュメント束(.md / .txt)は OCR 判読結果そのものなので 400 で拒否されます。
ボディパラメータ
レスポンスフィールド
# sheet
curl -X POST https://api.space-ocr.com/edit \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"path":"/invoices/sheet1","row":"img_abc","column":"amount","value":"12000"}'
# memo
curl -X POST https://api.space-ocr.com/edit \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"path":"/todo","text":"新しい本文"}'{ "ok": true, "patched": { "row": "img_abc", "column": "amount", "value": "12000" } }削除(cascade)
フォルダ/シート/メモ/画像を削除します。フォルダ削除は配下メタデータ・flat エントリ・Storage を全てカスケード。
ボディパラメータ
レスポンスフィールド
curl -X POST https://api.space-ocr.com/remove \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"path":"/invoices/2024"}'{ "ok": true }OCR ジョブのポーリング
POST /upload(非同期)が返した jobId の状態を確認します。webhook を使わない場合に使用。
パスパラメータ
レスポンスフィールド
curl https://api.space-ocr.com/jobs/job_xxx \
-H "Authorization: Bearer YOUR_API_KEY"{
"jobId": "job_xxx",
"status": "done",
"uniqueKey": "img_abc",
"path": "/invoices/sheet1/img_abc",
"sheetRef": "Kvho45OXMKw…",
"docRef": null,
"mode": "sheet",
"result": {
"values": { "amount": "12000", "date": "2025-04-10" },
"cells": { /* POST /ocr/fields と同じ box / quad / verified / review */ },
"review": { "unit": "field" /* ... */ },
"image": { "width": 1654, "height": 2339 }
}
}残高・無料枠
現在の残高と利用可能な無料枠を返します。
レスポンスフィールド
curl https://api.space-ocr.com/amount \
-H "Authorization: Bearer YOUR_API_KEY"{
"free": { // 毎月の無料枠
"used": 12,
"limit": 100,
"remaining": 88,
"cycleStart": 1716700000000,
"cycleEnd": 1719378400000
},
"flatfee": { // 定額プラン(未加入なら enabled:false)
"enabled": true,
"used": 340,
"limit": 3000,
"remaining": 2660,
"cycleStart": 1716700000000,
"cycleEnd": 1719378400000,
"nextBillingAt": 1719378400000,
"interval": "monthly",
"renewal": true,
"plan": "pro"
},
"balance": 1240, // チャージ済み残高(スキャン数)
"currency": "scans", // 残高の単位は通貨ではなくスキャン数
"perCallCost": 1 // 1 スキャン = 1 コール
}
// 処理可能枚数 = free.remaining + (flatfee.enabled ? flatfee.remaining : 0) + balance。
// この順で消費されます(無料枠 → 定額 → 残高)。ヘルスチェック
認証不要のヘルスチェック。
レスポンスフィールド
curl https://api.space-ocr.com/health{ "status": "ok", "version": "v1", "time": 1716700000000 }概要
スペース全体に 1 つの Webhook URL を登録すると、すべてのイベントが HMAC 署名付きで配信されます。設定は Developer → Webhooks か、後述の Webhook 管理エンドポイントから行います。
イベント
全イベントは同じ envelope(event / deliveryId / occurredAt / apiVersion / data)を持ちます。
ペイロード例 — ocr.completed
{
"event": "ocr.completed",
"deliveryId": "dlv_xxx",
"occurredAt": 1716700000000,
"apiVersion": "v1",
"data": {
"uid": "...",
"path": "/invoices/sheet1/img_abc",
"parentPath": "/invoices/sheet1",
"uniqueKey": "img_abc",
"sheetRef": "sht_xxx",
"docRef": null,
"mode": "sheet",
"result": {
"values": { "amount": "12000", "date": "2025-04-10" },
"cells": { /* box / quad / verified / review */ },
"review": { "unit": "field" /* ... */ },
"image": { "width": 1654, "height": 2339 }
}
}
}mode はアップロード先で決まります — シートなら "sheet"、ドキュメント束なら "markdown" / "text"。result は GET /jobs と同じ { values, cells, review, image }(v2)で、values の中身だけが mode に従います(sheet: フィールド値 / markdown: { markdown, elements } / text: { text, blocks })。ドキュメント束の場合は sheetRef が null になり、docRef に束の uniqueKey が入ります。
配信ヘッダー
受信エンドポイントには下記ヘッダーが付きます。署名検証で必要なものは Signature / Timestamp。
X-Spaceocr-Signature: t=<unix_ms>,v1=<hex>
X-Spaceocr-Timestamp: <unix_ms>
X-Spaceocr-Event: ocr.completed
X-Spaceocr-Delivery: dlv_<id>
Content-Type: application/json署名検証
X-Spaceocr-Signature は t=<unix_ms>,v1=<hex> 形式です。canonical 文字列は `${t}.${rawBody}`、アルゴリズムは HMAC-SHA256。リプレイ攻撃を防ぐため timestamp が 5 分以上ずれている場合は拒否してください。
import crypto from "crypto";
export function verify(secret, headers, rawBody) {
const sig = headers["x-spaceocr-signature"] || "";
const m = sig.match(/^t=(\d+),v1=([a-f0-9]+)$/);
if (!m) return false;
const [, t, v1] = m;
if (Math.abs(Date.now() - Number(t)) > 5 * 60 * 1000) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${t}.${rawBody}`)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(v1, "hex"),
);
}再送ポリシー
2xx 以外は exponential backoff (1m → 5m → 30m → 2h) で最大 5 回試行。5xx / 408 / 429 / timeout のみ retry 対象、その他 4xx は即 dead。配信ログは 30 日保存。
現在の Webhook 設定
スペース全体に登録されている webhook URL と状態を返します。
レスポンスフィールド
curl https://api.space-ocr.com/webhook \
-H "Authorization: Bearer YOUR_API_KEY"{
"configured": true,
"url": "https://example.com/hooks/space-ocr",
"active": true,
"secretMasked": "••••a1b2",
"createdAt": 1716700000000,
"updatedAt": 1716700000000
}
// 未設定のとき
{ "configured": false }Webhook 設定の作成・更新
スペース全体の webhook URL を登録または更新します。rotateSecret で署名鍵を再発行できます。
ボディパラメータ
レスポンスフィールド
curl -X PUT https://api.space-ocr.com/webhook \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/hooks/space-ocr","active":true}'{
"configured": true,
"url": "https://example.com/hooks/space-ocr",
"active": true,
"secretMasked": "••••a1b2",
"secret": "kJ8s…", // 新規発行・回転時のみ、この一度だけ平文
"createdAt": 1716700000000,
"updatedAt": 1716700000000
}Webhook 設定の削除
登録済みの webhook を削除します。以後イベントは配信されません。
レスポンスフィールド
curl -X DELETE https://api.space-ocr.com/webhook \
-H "Authorization: Bearer YOUR_API_KEY"{ "ok": true }テストイベントを送信
configured URL に webhook.test イベントを即時送信します。受信側の実装確認用。
レスポンスフィールド
curl -X POST https://api.space-ocr.com/webhook/test \
-H "Authorization: Bearer YOUR_API_KEY"{ "ok": true, "deliveryId": "dlv_xxx" }最近の配信履歴
最新の webhook 配信ログを返します。debug 用。
クエリパラメータ
レスポンスフィールド
curl https://api.space-ocr.com/webhooks/deliveries \
-H "Authorization: Bearer YOUR_API_KEY"{
"items": [
{
"deliveryId": "dlv_xxx",
"event": "ocr.completed",
"url": "https://example.com/hooks/space-ocr",
"path": "/invoices/sheet1/img_abc",
"uniqueKey": "img_abc",
"status": "success", // pending | success | dead
"attempts": 1, // 試行回数
"lastAttempt": {
"at": 1716700000000,
"attemptIndex": 0,
"responseStatus": 200,
"error": null,
"durationMs": 143,
"responsePreview": "ok"
},
"occurredAt": 1716700000000,
"nextAttemptAt": null,
"completedAt": 1716700000143
}
]
}配信の詳細
指定配信の完全なペイロードと配信試行履歴を返します。
パスパラメータ
レスポンスフィールド
curl https://api.space-ocr.com/webhooks/deliveries/dlv_xxx \
-H "Authorization: Bearer YOUR_API_KEY"{
"deliveryId": "dlv_xxx",
"event": "ocr.completed",
"occurredAt": 1716700000000,
"payload": { /* full event body */ },
"attempts": [
{ "at": 1716700000000, "responseStatus": 200, "ok": true }
]
}配信の手動再送
失敗した配信を手動で再送します。
パスパラメータ
レスポンスフィールド
curl -X POST https://api.space-ocr.com/webhooks/deliveries/dlv_xxx/redeliver \
-H "Authorization: Bearer YOUR_API_KEY"{ "ok": true, "deliveryId": "dlv_xxx" }
// 同じ deliveryId を再利用します(新しい ID は発行されません)。配信ログの
// status は pending に戻り、attempts に試行が追記されます。概要
MCP サーバーは、この API をそのまま AI エージェントのツールとして開きます。読み取り 3 種に加えて、フォルダやシートを作り、画像を上げ、溜まった行を条件付きで取り出すところまでエージェントが行えます。中身は同じ REST ルートで、課金も同じです。
接続
インストールは不要です。ヘッダーを設定できるクライアントは API キーをそのままベアラートークンとして送ってください。ヘッダーを設定できない Claude デスクトップ/モバイル/claude.ai では、URL をカスタムコネクタとして追加すると OAuth の同意画面が開き、どの API キーで動かすかを選べます。どちらの場合もキーはそのリクエストのみに使われ、サーバー側には保存されません。
# Claude Code
claude mcp add --transport http space-ocr https://mcp.space-ocr.com/mcp \
--header "Authorization: Bearer YOUR_API_KEY"Cursor / VS Code / Windsurf (mcp.json)
{
"mcpServers": {
"space-ocr": {
"url": "https://mcp.space-ocr.com/mcp",
"headers": { "Authorization": "Bearer YOUR_API_KEY" }
}
}
}ツール一覧
読み取り 3 つとワークスペース 8 つ。課金は対応する REST ルートと同じで、読み取りと画像アップロードだけがクレジットを消費します。